mysql 排它锁和共享锁

Mysql的排他锁和共享锁

  今天看代码看到有select name from user where id = 1 for update,有点懵逼,彻底没有见过,只能说本身见识少了,那就只能学习一下。先作一下基本知识了解(大部分都是整理了别人的文档,若有侵权还请告知):html

锁的基本概念
  当多事务争取一个资源时,有可能致使数据不一致,这个时候须要一种机制限制,而且将数据访问顺序化,用来保证数据库数据的一致性,锁就是其中的一种机制。咱们能够用商场的试衣间来作个比喻,商场里得每一个试衣间均可供多个消费者使用,所以可能出现多个消费者同时试衣服须要使用试衣间,这时候就产生冲突了,为了不冲突,试衣间装了锁(其实就是进去以后把门拴住),某一个试衣服的人在试衣间里把锁锁住了,其余顾客就不能再从外面打开了,只能等待里面的顾客,试完衣服,从里面把锁打开,外面的人才能进去(网上找到的比喻,很是形象)。不过我想要是并发了就尴尬了,哈哈。mysql

锁的基本类型
  数据库上的操做能够概括为两种:读和写。
  多个事务同时读取一个对象的时候,是不会有冲突的。同时读和写,或者同时写才会产生冲突。所以为了提升数据库的并发性能,一般会定义两种锁:共享锁和排它锁。sql

共享锁(Shared Lock,也叫S锁)
  共享锁(S)表示对数据进行读操做。所以多个事务能够同时为一个对象加共享锁。(若是试衣间的门还没被锁上,顾客都可以同时进去参观)
  产生共享锁的sql:select * from ad_plan lock in share mode;数据库

共享锁的使用场景
  SELECT ... LOCK IN SHARE MODE走的是IS锁(意向共享锁),即在符合条件的rows上都加了共享锁,这样的话,其余人能够读取这些记录,也能够继续添加IS锁,可是没法修改这些记录直到你这个加锁的过程执行完成(完成的状况有:事务的提交,事务的回滚,不然直接锁等待超时)。
  SELECT ... LOCK IN SHARE MODE的应用场景适合于两张表存在关系时的写操做,拿mysql官方文档的例子来讲,一个表是child表,一个是parent表,假设child表的某一列child_id映射到parent表的c_child_id列,那么从业务角度讲,此时我直接insert一条child_id=100记录到child表是存在风险的,由于刚insert的时候可能在parent表里删除了这条c_child_id=100的记录,那么业务数据就存在不一致的风险。正确的方法是再插入时执行select * from parent where c_child_id=100 lock in share mode,锁定了parent表的这条记录,而后执行insert into child(child_id) values (100)就不会存在这种问题了。session

排他锁(Exclusive Lock,也叫X锁)并发

  排他锁也叫写锁(X)。性能

  排他锁表示对数据进行写操做。若是一个事务对对象加了排他锁,其余事务就不能再给它加任何锁了。(某个顾客把试衣间从里面反锁了,其余顾客想要使用这个试衣间,就只有等待锁从里面给打开了)
产生排他锁的sql: select * from ad_plan for update;看到了吧,for update出现了,因此for update 是排他锁,涨知识了。
排他锁的使用场景:学习

  使用场景一:订单的商品数量
    可是若是是同一张表的应用场景,举个例子,电商系统中计算一种商品的剩余数量,在产生订单以前须要确认商品数量>=1,产生订单以后应该将商品数量减1。
    1 select amount from product where product_name='XX';
    2 update product set amount=amount-1 where product_name='XX';spa

    显然1的作法是是有问题,由于若是1查询出amount为1,可是这时正好其余session也买了该商品并产生了订单,那么amount就变成了0,那么这时第二步再执行就有问题。那么采用lock in share mode可行吗,也是不合理的,由于两个session同时锁定该行记录时,这时两个session再update时必然会产生死锁致使事务回滚。如下是操做范例(按时间顺序)htm

  使用场景一:数据表的状态

    若是存在一张表记录一个商品的状态,在订单的变化过程当中,订单的状态是不断变化的,并且变化的过程当中确定也会有并发的问题,并且不少时候与其余系统有交互,会存在补偿的状况,因此并发的可能性很大,补偿或者为了增长状态修改的成功可能性,2次改变状态的状况也有,楼主就遇到了这种状况,真操蛋。因而看到有这样的for update写法。

    1  update order set status = 1 where product_id = '1';   

    2 insert order_flow  (..............) value (.........)

    这样的状况下就有可能订单的状态已经更新完成了,可是补偿这些额外的消息把状态又更新为待处理或者插入了多条流水的状况(多条流水的可能性大,状态的那种可能补偿滞后)。这个时候就可使用select .... from order where order_id = '1' for update,先锁住要修改状态的表,这样就不会别人操做了,本身前后面把流水插入,而后更新状态,完美。可是加了锁以后性能就很慢了,担忧性能影响,并且有可能存在死锁的状况,后面我就修改成流水表中增长一个惟一索引,这样插入流水报错就是已经处理过的记录了。这样就不会存在性能问题。

  经过对比,lock in share mode适用于两张表存在业务关系时的一致性要求,for  update适用于操做同一张表时的一致性要求。

做者:鹏鹏

    

出处:http://www.cnblogs.com/liaoweipeng/

相关文章
相关标签/搜索