各种锁系列

各种锁系列

平时我们在开发或学习中,数据库中,某个编程语言中经常会听到各种锁,主要用来对资源并发过程做一些限制,接下来对各种锁进行一个总结与学习!

乐观锁与悲观锁

  1. 首先,乐观锁与悲观锁并不是指具体的某种东西,而是一种设计的思路

  2. 乐观锁 (Optimistic Lock)

    • 乐观锁表示非常的乐观,认为业务操作去拿锁并不会有什么问题(冲突),进行完业务操作后需要去更新数据的最后才去拿锁

    • 一般数据库中不自带乐观锁,但是我们要实现乐观锁的方式也很简单,只需要在我们需要锁的数据上加多一列版本号或者时间戳字段做检验就好了

    • 例如: 现在有个商场,我们需要取出商品信息生成订单,然后去更新状态,我们只需要直接查询出商品的记录(里面包括了版本号),在需要去更新状态的时候,就去比较此刻的版本号和之前查询记录里的版本号是否一致,一致就更新状态和新的版本号,否则则不进行更新

    • 乐观锁一般都是用于取锁失败概率比较小的场景,不会存在死锁等问题

      // 查询
      SELECT data, version AS versionBegin FROM table;
      // 更新时
      UPDATE table SET data = new_data WHERE version = versionBegin;
      if(update_row > 0){
      		// 版本号一致能更新,获取乐观锁成功
      }else{
      		// 版本号不一致,获取乐观锁失败
      }
    
  3. 悲观锁 (Pessimistic Lock)

    • 悲观锁则与乐观锁相反,比较悲观认为每次获取锁都有可能失败,那么必须先获取锁再进行业务操作.

    • 实现一般都是在数据库加锁,根据加锁对象可分为表锁或行锁,按照加锁的机制不同可分为共享锁(读锁)和排他锁(写锁)

    • 表锁与行锁

      表锁

      1. InnoDB支持行级锁,但并不是直接锁记录而是锁索引(使用了主键索引就锁主键索引,使用非主键索引就先锁非主键索引再锁主键索引),那么去查找记录就得扫描全表,就得锁定表
      2. 开销大,加锁慢且会出现死锁,锁定力度最小,并发度最高

      行锁

      1. MyISAM使用的都是表锁,更新一条记录就要锁整个表
      2. 开销小,加锁快
    • 共享锁与排他锁

      共享锁(读锁)

      1. 加共享锁:自身可以读,其他人也可以读可以继续加共享锁,但是不能修改,必须等全部的共享锁都释放
      SELECT data FROM table lock in share mode 
      

      排他锁(写锁)

      1. 加排他锁:自身可以CURD,但是其他人无法操作只能等锁释放
      SELECT data FROM table for update
      
    • 那么实现悲观锁,select之后其他事务的修改必须等待事务的结束,其他事务不能对该条记录进行修改,但乐观锁其他事务可以select

      begin
      SELECT data FROM table [lock in share mode // for update];
      
    • 对于UPDATE, INSERT, DELECT语句会自动加排他锁

公平锁与非公平锁

公平锁就是保障了多线程下各线程获取锁的顺序,先到的线程优先获取锁,而非公平锁则无法提供这个保障。

分布式锁

分布式锁: 利用锁的技术控制在分布式模型下同一份数据的修改进程数

实现方式

  1. 基于表主键唯一的特性做分布式锁
    • 对数据库强依赖,强调数据库的可用性 => 数据库主从,一旦挂掉切换备库
    • 没有失效时间,一旦解锁失败其他线程无法获得锁 => 定时任务,定期清路数据库中超时数据
    • 是非公平锁,多个线程凭运气获得锁 => 建个中间表,将等待线程记录下来,最先创建的允许获得锁
    • 是不可重入锁,没有释放锁之前因为数据存在无法再次获得锁 => 新增字段记录当前线程信息,如果与请求锁的线程匹配的话直接分配就好了
  2. 使用上述的乐观锁的方式做分布式锁
  3. 基于数据库的排他锁做分布式锁
  4. 基于redis做分布式锁
  5. 基于ZOOKEEPER

可重入锁与不可重入锁

不可重入锁: 若当前线程执行中已经获取了锁,如果再次获取该锁时,就会获取不到被阻塞。

可重入锁: 可重入,意味着线程可以进入它已经拥有的锁的同步代码块。

互斥锁与共享锁

互斥锁:同时只能有一个线程获得锁。比如,ReentrantLock 是互斥锁,ReadWriteLock 中的写锁是互斥锁。

共享锁:可以有多个线程同时或的锁。比如,Semaphore、CountDownLatch 是共享锁,ReadWriteLock 中的读锁是共享锁。

其他相关名词

CAS

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

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

这里再强调一下,乐观锁是一种思想。CAS是这种思想的一种实现方式

ABA

MVCC

CAP

任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章