您的位置:新葡亰496net > 网络数据库 > 乐观锁和消极锁的界别,乐观锁与消极锁的区分

乐观锁和消极锁的界别,乐观锁与消极锁的区分

发布时间:2019-11-30 06:18编辑:网络数据库浏览(51)

    一、为什么需要了解锁

     

    乐观锁

    在关系数据库管理系统里,乐观并发控制(又名”乐观锁”,Optimistic Concurrency Control,缩写”OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的 那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回 滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出。

    1.1 死锁问题

    为什么需要锁(并发控制)?

    乐观并发控制的阶段

    乐观并发控制的事务包括以下阶段:

    1. 读取:事务将数据读入缓存,这时系统会给事务分派一个时间戳。
      2. 校验:事务执行完毕后,进行提交。这时同步校验所有事务,如果事务所读取的数据在读取之后又被其他事务修改,则产生冲突,事务被中断回滚。
    2. 写入:通过校验阶段后,将更新的数据写入数据库。

    乐观并发控制多数用于数据争用不大、冲突较少的环境中,这种环境中,偶尔回滚事务的成本会低于读取数据时锁定数据的成本,因此可以获得比其他并发控制方法更高的吞吐量。

    相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。

    数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判 断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过 期数据。

    实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。 使用版本号实现乐观锁

    使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行 1操作。并判断当前版本号是不是该数据的最新的版本号。

    1.2 并发问题导致的不正确数据的读取和存储,破坏数据一致性的

    • 丢失更新:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题--最后的更新覆盖了由其他事务所做的更新。例如,两个编辑人员制作了同一文档的电子副本。每个编辑人员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改副本的编辑人员覆盖另一个编辑人员所做的更改。如果在一个编辑人员完成并提交事务之前,另一个编辑人员不能访问同一文件,则可避免此问题
    • 脏读:一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做”脏读”。
    • 不可重复读:一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。
    • 幻读:一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。

    在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题。

    使用版本号实现乐观锁

    乐观锁和消极锁的界别,乐观锁与消极锁的区分。使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行 1操作。并判断当前版本号是不是该数据的最新的版本号。

    1.查询出商品信息
    select (status,status,version) from t_goods where id=#{id}
    2.根据商品信息生成订单
    3.修改商品status为2
    update t_goods
    set status=2,version=version 1
    where id=#{id} and version=#{version};
    

     

    二、锁的分类

    典型的冲突有:

    优点与不足

      乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预 期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。

    2.1 数据库维度

    • 共享锁:用于不更改或不更新数据的操作(只读操作)。共享锁允许并发事务读取同一个资源,数据资源上存在共享锁时,任何其他事务不允许修改数据
    • 排它锁: 用于数据修改,确保不会同时多重更新同一数据。资源上存在排他锁时,其他任何事务不允许给资源上锁,当资源上有其他锁时,也无法对其加上排它锁

    PS:只有共享锁与共享锁相互兼容,共享锁与排它锁、排它锁之间都互不兼容

    • 更新锁 
    1. 用于可更新的资源中,防止多个会话在读取、锁定以及随后可能进行的资源更新时发生的死锁问题。
    2. 通常形式的死锁:一般一个更新模式由一个事务组成,此事务读取记录,获取资源的共享锁,然后修改行,此操作要求锁转换为排它锁。如果两个事务同时获得了资源上的共享锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它锁。由于一个事务的排它锁与另一事务的共享锁的不兼容,从共享锁到排它锁的转换必须要等待一段时间,发送锁等待。而第二个事务同时也试图获取排它锁进行更新。由于两个事务都要转化为排它锁,并且每个事务都要等待另一个事务释放掉共享锁,因此发生死锁
    3. 乐观锁和消极锁的界别,乐观锁与消极锁的区分。更新锁一次只有一个事务可以获取资源的更新锁。如果事务修改资源,那么更新锁转化为排它锁,否则转化为共享锁。当资源上存在更新锁时,允许资源被读取(即更新锁与共享锁兼容),但不允许资源被修改
    4. 一般来说,在执行UPDATE操作时,SQL SERVER会使用到更新锁而不是依次加上共享锁和排它锁,已经回避了这种通常形式的死锁,更新锁与意向锁相互兼容

    l 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改为2,用户B把值从2改为6,则用户A丢失了他的更新。

     

    2.2 数据库锁机制

    DBMS SELECT UPDATE INSERT DELETE
    MySQL(InnoDB) 不加锁 排它锁 排它锁 排它锁
    SQL SERVER 共享锁 更新锁  排它锁 排它锁

     

     

     

     

     这两种锁机制的区别在于MySQL的查询与更新操作互相不阻塞;而SQL SERVER的更新锁转化成排它锁之前,其查询与更新操作互相不阻塞,当更新锁要转化为排它锁时,需要等待共享锁的释放,当更新锁转化为排它锁后,查询数据需要等待排它锁的释放。

    参考:
    [数据库锁机制]()
    [InnoDB锁机制]()
    [SQL SERVER锁机制]()

    l 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改为2,用户A读到的值仍为6。

    悲观锁

    在关系数据库管理系统里,悲观并发控制(又名”悲观锁”,Pessimistic Concurrency Control,缩写”PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了 锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。

    悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

    2.3 程序员思想维度

    • 悲观锁
    1. 悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。悲观锁的实现,往往依靠数据库提供的锁机制。悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。
    2.  悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数
    •  乐观锁
    1. 乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。
    2. 乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会主动产生任何锁和死锁。但是在并发量高的情况下,可能导致某次数据修改多次重试,影响单次成功操作的时间。
    3. 数据版本实现乐观锁:实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行 1操作。并判断当前版本号是不是该数据的最新的版本号。

      update table set date=1,version=version 1 where id=#{id} and version=#{version};

    参考:
    [乐观锁与悲观锁]()

    为了解决这些并发带来的问题。 我们需要引入并发控制机制。

    使用

    MySQL InnoDB中使用悲观锁

    要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。set autocommit=0;

    #0.开始事务
    begin;/begin work;/start transaction; (三者选一就可以)
    #1.查询出商品信息
    select status from t_goods where id=1 for update;
    #2.根据商品信息生成订单
    insert into t_orders (id,goods_id) values (null,1);
    #3.修改商品status为2
    update t_goods set status=2;
    #4.提交事务
    commit;/commit work;
    

     

      上面的查询语句中,我们使用了select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

    上面我们提到,使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认行级锁行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。

    2.4 乐观锁另一种实现方式CAS

    CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

    CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”这其实和乐观锁的冲突检查 数据更新的原理是一样的。

    java.util.concurrent(J.U.C)就是建立在CAS之上的。相对于对于synchronized这种阻塞算法,CAS是非阻塞算法的一种常见实现。所以J.U.C在性能上有了很大的提升。

    public class AtomicInteger extends Number implements java.io.Serializable {
      private volatile int value; 
    
      public final int get() { 
        return value; 
      } 
    
      public final int getAndIncrement() { 
        for (;;) { 
          int current = get(); 
          int next = current   1; 
          if (compareAndSet(current, next)) 
            return current; 
        } 
      } 
    
      public final boolean compareAndSet(int expect, int update) { 
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 
      } 
    }
    

     

    参考:
    [乐观锁的一种实现方式—CAS]()

    并发控制机制

        最常用的处理多用户并发访问的方法是加锁。当一个用户锁住数据库中的某个对象时,其他用户就不能再访问该对象。加锁对并发访问的影响体现在锁的粒度上。比如,放在一个表上的锁限制对整个表的并发访问;放在数据页上的锁限制了对整个数据页的访问;放在行上的锁只限制对该行的并发访问。可见行锁粒度最小,并发访问最好,页锁粒度最大,表锁介于2者之间。

    悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。[1]      悲观锁假定其他用户企图访问或者改变你正在访问、更改的对象的概率是很高的,因此在悲观锁的环境中,在你开始改变此对象之前就将该对象锁住,并且直到你提交了所作的更改之后才释放锁。悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好。

    乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。[1] 乐观锁不能解决脏读的问题。    乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能。但是如果第二个用户恰好在第一个用户提交更改之前读取了该对象,那么当他完成了自己的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不重新读取该对象并作出更改。这说明在乐观锁环境中,会增加并发用户读取对象的次数。

     

         从数据库厂商的角度看,使用乐观的页锁是比较好的,尤其在影响很多行的批量操作中可以放比较少的锁,从而降低对资源的需求提高数据库的性能。再考虑聚集索引。在数据库中记录是按照聚集索引的物理顺序存放的。如果使用页锁,当两个用户同时访问更改位于同一数据页上的相邻两行时,其中一个用户必须等待另一个用户释放锁,这会明显地降低系统的性能。interbase和大多数关系数据库一样,采用的是乐观锁,而且读锁是共享的,写锁是排他的。可以在一个读锁上再放置读锁,但不能再放置写锁;你不能在写锁上再放置任何锁。锁是目前解决多用户并发访问的有效手段。  

    优点与不足

    悲观并发控制实际上是”先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增 加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数 据,其他事务就必须等待该事务处理完才可以处理那行数

    三、案例分析

    乐观锁应用

    1.      使用自增长的整数表示数据版本号。更新时检查版本号是否一致,比如数据库中数据版本为6,更新提交时version=6 1,使用该version值(=7)与数据库version 1(=7)作比较,如果相等,则可以更新,如果不等则有可能其他程序已更新该记录,所以返回错误。

    2.      使用时间戳来实现.

    注:对于以上两种方式,Hibernate自带实现方式:在使用乐观锁的字段前加annotation: @Version, Hibernate在更新时自动校验该字段。

    总结

    乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机智的其实都是提供的乐观锁。 相反,如果经常发生冲突,上层应用会不断进行 retry,这样反而降低了性能,所以这种情况下用悲观锁比较合适

     

     

     

    ---------------------------------------第二种理解-------------------

    3.1 初审统计数据迁移

    • 迁移背景:原有的统计方式采用的是实时count的方法获取统计数据,造成的问题是查询慢且无法获取长时间段的统计数据(sql超时)、无法获取某日统计数据的快照(前一天的待审核数据会变成今天的审核通过数据)
    • 采用迁移方式:使用raptor迁移平台,扫描审核记录表,取出累计统计数据后进行加1操作,然后更新到统计表中。由于平台特性,数据迁移过程具有高并发性,由于强行采用先读取后更新的方式,会造成丢失更新的情况,于是这里考虑采用CAS

    step1:

     

    select id,passCount,rejectCount,hideCount,warnCount,waitCount 
    from book.TradeItemAuditCount 
    where type = #{type} and date = #{date} and editor = #{editor} 
    and isDeleted = 0 limit 1
    

    step 2:【失败重试】

    update book.TradeItemAuditCount 
    set passCount = #{passCount} , rejectCount = #{rejectCount} , hideCount = #{hideCount} , warnCount = #{warnCount} , waitCount = #{waitCount} , updated = #{updated}
    where id = #{id} and passCount = #{oldPassCount} and rejectCount = #{oldRejectCount} and hideCount = #{oldHideCount} and warnCount = #{oldWarnCount} and waitCount = #{oldWaitCount}
    and isDeleted = 0 limit 1
    
    •  处理结果 :一共扫描审核结果4335668条数据,对重试次数超过100的更新操作进行记录,发现更新操作出现大部分的重试,任务本身DB写操作的qps较低【都不需要通过控制台限制速率..】

    悲观锁应用

    需要使用数据库的锁机制,比如SQL SERVER 的TABLOCKX(排它表锁) 此选项被选中时,SQL  Server  将在整个表上置排它锁直至该命令或事务结束。这将防止其他进程读取或修改表中的数据。

    SqlServer中使用

    Begin Tran
    select top 1 @TrainNo=T_NO
             from Train_ticket   with (UPDLOCK)   where S_Flag=0

          update Train_ticket
             set T_Name=user,
                 T_Time=getdate(),
                 S_Flag=1
             where T_NO=@TrainNo
    commit

    我们在查询的时候使用了with (UPDLOCK)选项,在查询记录的时候我们就对记录加上了更新锁,表示我们即将对此记录进行更新. 注意更新锁和共享锁是不冲突的,也就是其他用户还可以查询此表的内容,但是和更新锁和排它锁是冲突的.所以其他的更新用户就会阻塞.

    新葡亰496net,乐观锁

    乐观锁不是数据库自带的,需要我们自己去实现。乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

    通常实现是这样的:在表中的数据进行操作时(更新),先给数据表加一个版本(version)字段,每操作一次,将那条记录的版本号加1。也就是先查询出那条记录,获取出version字段,如果要对那条记录进行操作(更新),则先判断此刻version的值是否与刚刚查询出来时的version的值相等,如果相等,则说明这段期间,没有其他程序对其进行操作,则可以执行更新,将version字段的值加1;如果更新时发现此刻的version值与刚刚获取出来的version的值不相等,则说明这段期间已经有其他程序对其进行操作了,则不进行更新操作。

    举例:

     

    下单操作包括3步骤:

    1.查询出商品信息

    select (status,status,version) from t_goods where id=#{id}

    2.根据商品信息生成订单

    3.修改商品status为2

    update t_goods 

    set status=2,version=version 1

    where id=#{id} and version=#{version};

     

    除了自己手动实现乐观锁之外,现在网上许多框架已经封装好了乐观锁的实现,如hibernate,需要时,可能自行搜索"hiberate 乐观锁"试试看。

     

    3.2 商品库存

    • 商品库存与上述案例1一致,都是对数据记录进行加减操作,发现库存的更新方式如下:

      update 库存表 set stock=stock-1 where id=#{id}

    • 直接使用数据库的排它锁就简单的避免了并发导致的丢失更新问题,之前提到的一次只有一个事务拥有资源的排它锁,并发的更新操作都试图占有资源的排它锁,当资源上存在排它锁时,其他更新操作需要等待锁的释放

    • 相比案例1的解决方案,案例2的解决方式直接使用了MySQL InnoDB更新操作本身就拥有的排它锁,不需要额外的开销,而案例1不必要的查询操作以及多次的重试操作严重影响到了数据迁移的性能,所以案例1是反面例子..

    结论

    在实际生产环境里边,如果并发量不大且不允许脏读,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题,所以我们就要选择乐观锁定的方法.

    悲观锁

    与乐观锁相对应的就是悲观锁了。悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似,所以悲观锁需要耗费较多的时间。另外与乐观锁相对应的,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

    说到这里,由悲观锁涉及到的另外两个锁概念就出来了,它们就是共享锁与排它锁。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。

     

    3.3 商品打标

    • 随着上打标的qps上涨,出现达标更新数据丢失的情况

      {"tags":"16,32,233,22","itemState":1,"hd":"ai:4|nd:18","au":"baoming"}

    • 处理方案

      1.乐观锁:采用CAS

    update TradeItem
    set extra=#{extra}
    where tradeItemId=#{tradeItemId} and extra=#{oldExtra}
    

    这里使用长字符串做更新条件,会影响到SQL性能

      2.乐观锁:采用数据版本 表中新增version字段标识数据版本,作为数据更新的检查方式

    update TradeItem
    set extra=#{extra} , version=version 1
    where tradeItemId=#{tradeItemId} and version=#{version}
    

    此方案改造较大,还需要为表新增字段,而且采用乐观锁拥有这一律的弊端:重试带来的时间代价,一旦并发量上涨,某次更新操作的重试次数也会随之上涨,直接影响到暴露服务的响应时间。【限制重试次数能够一定程度上控制更新操作的响应时间,但是仍然会出现更新丢失的现象(让调用方进行重试操作,分摊单次请求的响应时间?)】  

      3. 悲观锁:更新丢失的根本原因是执行查询、修改两个操作之间数据被另一事务修改了,单纯的UPDATE操作其实也是进行着先查询后修改的操作,没有产生更新丢失是因为数据上存在排它锁(sql server则是更新锁),在执行期间并不允许其他修改。同理我们将要打标的商品记录加上排它锁或者更新锁就能解决问题。 

    MySQL:

    start transaction;
    SELECT extra
    FROM TradeItem 
    WHERE tradeItemId=#{tradeItemId}
    FOR UPDATE;
    UPDATE TradeItem 
    SET extra = bdo.AddTag(tag,extra)
    WHERE tradeItemId=#{tradeItemId};
    commit;
    

    SQL SERVER:

    BEGIN TRANSACTION --开始一个事务
    SELECT extra
    FROM TradeItem WITH (UPDLOCK)
    WHERE tradeItemId=#{tradeItemId}
    UPDATE TradeItem 
    SET extra = bdo.AddTag(tag,extra)
    WHERE tradeItemId=#{tradeItemId}
    COMMIT TRANSACTION --提交事务
    

    该方案避免了重试带来的开销,同时使用排它锁(更新锁)也没有额外增加锁的开销

    参考文档

    [1]Concurrent Control

    [2] Oracle的悲观锁和乐观锁

    [3] timestamp应用——乐观锁和悲观锁【转】

    共享锁

      共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁。相当于对于同一把门,它拥有多个钥匙一样。就像这样,你家有一个大门,大门的钥匙有好几把,你有一把,你女朋友有一把,你们都可能通过这把钥匙进入你们家,进去啪啪啪啥的,一下理解了哈,没错,这个就是所谓的共享锁。

      刚刚说了,对于悲观锁,一般数据库已经实现了,共享锁也属于悲观锁的一种,那么共享锁在mysql中是通过什么命令来调用呢。通过查询资料,了解到通过在执行语句后面加上lock in share mode就代表对某些资源加上共享锁了。

    比如,我这里通过mysql打开两个查询编辑器,在其中开启一个事务,并不执行commit语句

    city表DDL如下:

    CREATE TABLE `city` (  
      `id` bigint(20) NOT NULL AUTO_INCREMENT,  
      `name` varchar(255) DEFAULT NULL,  
      `state` varchar(255) DEFAULT NULL,  
      PRIMARY KEY (`id`)  
    ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;  
    

     

    新葡亰496net 1

    begin;
    SELECT * from city where id = "1"  lock in share mode;
    

     

     

    然后在另一个查询窗口中,对id为1的数据进行更新

     

    新葡亰496net 2

     

    update  city set name="666" where id ="1";

    此时,操作界面进入了卡顿状态,过几秒后,也提示错误信息

    [SQL]update  city set name="666" where id ="1";
    [Err] 1205 - Lock wait timeout exceeded; try restarting transaction

     

    那么证明,对于id=1的记录加锁成功了,在上一条记录还没有commit之前,这条id=1的记录被锁住了,只有在上一个事务释放掉锁后才能进行操作,或用共享锁才能对此数据进行操作。

    再实验一下:

     

    新葡亰496net 3

     

    update city set name="666" where id ="1" lock in share mode;

    [Err] 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'lock in share mode' at line 1

    加上共享锁后,也提示错误信息了,通过查询资料才知道,对于update,insert,delete语句会自动加排它锁的原因

     

    于是,我又试了试SELECT * from city where id = "1" lock in share mode;

    新葡亰496net 4

     

    这下成功了。

     

     

     

     

    四、悲观锁乐观锁的取舍

    排它锁

    排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。

    与共享锁类型,在需要执行的语句后面加上for update就可以了

     

    行锁

    行锁,由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。

    比如之前演示的共享锁语句

    SELECT * from city where id = "1"  lock in share mode; 

    由于对于city表中,id字段为主键,就也相当于索引。执行加锁时,会将id这个索引为1的记录加上锁,那么这个锁就是行锁。

     

    表锁

    表锁,和行锁相对应,给这个表加上锁。

     

    MyISAM引擎里有的,暂时研究了

     

    本文由新葡亰496net发布于网络数据库,转载请注明出处:乐观锁和消极锁的界别,乐观锁与消极锁的区分

    关键词: