# 锁粒度角度

|行锁

每次操作锁住一条行记录,行锁的开销大,锁粒度小,加锁慢,发生锁冲突的概率较低,并发程度高。
数据库能够确定哪些行记录需要锁的情况下会使用行锁,如果不知道会影响哪些行记录的时候就会使用表锁。

|页锁

操作时在页的粒度上进行锁定,由于一页上会有多个行,所以使用页锁会出现数据浪费。
页锁的开销介于行锁和表锁,会出现死锁。并发程度一般。

|表锁

每次操作锁住整张表,表锁的开销小,加锁快,发生锁冲突的概率最高,并发程度最低。

不同的数据库引擎可以使用的锁类型不同:
image.png
每个层级锁数量有限,因为锁会占用内存空间,锁空间大小有限。当锁数量超过该层级阈值,会进行锁升级,用更大粒度的锁替代多个小粒度的锁。随着锁空间的下降,并发程度也会下降。

# 管理角度

|共享锁(读锁、s锁)

共享锁锁定的数据可以被其他用户读取,但是不能修改(只读模式)。若事务T对数据对象A加上S锁,则事务T只能读A;其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这就保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。

|排他锁(写锁、x锁)

排他锁锁定的数据只允许加锁的事务使用,其他事务无法对该数据进行修改和查询。若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A。

|意向锁

当数据被加上锁后,数据库会在更高一层的空间里加上意向锁,告诉其他事务该数据页或者数据表上含有锁。
当事务获取某些记录的共享锁或者排他锁时,数据标上会添加上意向共享锁或者是意向排他锁,这样就不需要整表扫描。

|死锁

多个事务(进程)在执行过程中,因为竞争某个相同的资源而造成阻塞称为死锁。当多个事务对同一个数据获得读锁时,可能就会发生死锁。例如两个不同的客户端都获取了某数据的共享锁,这时所有的客户端都无法对该数据进行更新,因为共享锁会阻止其他事务对数据更新。如果一个客户端进行更新,就会出现死锁。死锁解决的需要一个事务进行回滚,让另外一个事务获取锁完成事务,然后将锁释放。

# 并发角度

|乐观锁

乐观锁认为对同一数据的并发操作不会经常发生,属于小概率事件。所以不用每次都对数据上锁,也不采用数据库本身的锁机制,而是通过程序来实现。可以使用版本号、时间戳和 CAS 算法实现。

* 版本号机制

在数据表中设置一个版本字段,表示数据被修改的次数。每次数据被修改时,version 值会加一。当线程对数据进行更新或者删除时,会执行UPDATE ... SET version=version+1 WHERE version=version,在提交更新时只有 version 值和当前数据库中的 version 值相等才会更新,如果有事务对这条数据进行了其他修改则重试更新操作,直到更新成功。

* 时间戳机制

时间戳和版本号机制一样,在提交更新时,将当前数据的时间戳和更新之前的时间戳进行比较,如果两者相同更新成功,否则版本冲突。

* CAS算法

CAS即compare and swap(比较并交换),是一种有名的无锁算法,在不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步。CAS算法涉及到三个操作数:

  • 要更新的变量V
  • 预期的值E
  • 新值N

仅当V值等于E值时,才会将V的值设置成N,否则什么都不做。最后CAS返回当前V的值。CAS算法需要你额外给出一个期望值,也就是你认为现在变量应该是什么样子,如果变量不是你想象的那样,就说明已经被别人修改过,就重新读取,再次尝试修改即可。

因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时就会误以为它的值没有发生变化,这个问题称为ABA问题。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A就会变成1A-2B-3A,以此来防止不恰当的写入。

|悲观锁

悲观锁假设最坏的情况,担心每次拿的数据都会被其他的事务修改,所以每次都会在取数据的时候加锁。别的事务想要修改或者拿到数据就会被阻塞直到它拿到锁。

行锁表锁共享锁排他锁都属于常用的悲观锁。

| 两个锁的适用场景

  1. 乐观锁适用于读操作多的场景,优点在于通过时间戳或者版本号来判断,节省了加锁的开销,不存在死锁的问题,系统的吞吐量也得到了提升。
  2. 悲观锁适用于写操作多的场景,写操作具有排他性,悲观锁可以在数据库层面组织其他的事务对数据的操作,防止读-写、写-写的冲突发生。

    # 参考

  3. 数据库锁分类和总结

  4. CS-NOTE数据库
  5. 面试必备之乐观锁与悲观锁
  6. 并发策略-CAS算法