乐观锁和悲观锁
Posted F3nGaoXS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了乐观锁和悲观锁相关的知识,希望对你有一定的参考价值。
1、乐观锁
在关系型数据库管理系统中,乐观并发控制(又名:“乐观锁”,Optimistic Concurrency Control,缩写“OCC”),是一种并发控制的方法。多适用于读多写少的场景。
它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。
在提交数据更新之前,每个事务会先检查在事务在读取数据后,有没有其他事务由修改了该数据。如果有其他事务有更新的话,正在提交的事务就会回滚。
——摘自维基百科:乐观并发控制
Tips:
乐观锁的“乐观”体现在:操作数据的时候比较乐观,认为其他事务不会同时修改数据,所以不会对数据上锁,只不过需要在对数据进行更新的时候判断一下在此期间(拿到数据到更新数据期间)其他事务有没有去更改该数据。
需要特别注意:乐观锁机制本身并不会对数据加锁。
乐观锁的步骤
采用乐观锁的修改事务包含以下步骤:
- 读取:事务将数据读入,此时系统会给事务分派一个时间戳。
- 检验:事务执行完毕后,尝试进行提交。此时同步校验其他事务,如果当前事务所读取的数据在读取之后又被其他事务进行修改,则当前事务回滚。
- 写入:通过校验阶段后,将更新的数据写入数据库。
版本号机制
加上版本号version字段。即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
CAS算法
CAS(Compare And Swap,先比较再交换算法)是一种常见的“乐观锁”。大部分的CPU都有对应的汇编指令,它有三个操作数:内存地址V,旧值A,新值B。只有当内存地址V上的值是A,B才会被写到V上,否则操作失败。
ABA问题
ABA问题是无锁结构实现中常见的一种问题,表述为:
- 进程P1读取了数值A
- P1被挂起(时间片耗尽、中断等),进程P2开始执行
- P2修改了数值A为数值B,然后又修改回A
- P1被唤醒,比较后发现数值A没有变化,程序继续执行。
2、悲观锁
在关系型数据库管理系统中,悲观并发控制(又名:悲观锁,Pessimistic Concurrency Control,缩写“PCC”),是一种并发控制的方法。多适用于读少写多的场景。
它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。
——摘自维基百科:悲观并发控制
Tips:
悲观锁的“悲观”体现在:操作数据的时候比较悲观,认为其他事务也会同时修改数据(所以才会提前在当前事务上锁,避免同时被其他事务访问或者修改)。
悲观锁的步骤
采用悲观锁的修改事务往往会在修改数据时为该数据加上“锁”(即只允许当前事务访问,不允许其他事务访问或者修改)。
共享锁(读锁)Shared Lock
多个事务对同一数据可以共享一把锁,即能够访问到数据,但是不能修改。
在查询语句后面增加LOCK IN SHARE MODE
,数据库就会对查询结果中的记录加上共享锁。如
SELECT ... LOCK IN SHARE MODE;
当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。
排他锁(写锁)Exclusive Lock
排他锁就是不能与其他锁共存,如一个事务获取了一个数据的排他锁,该事务就不能获取该行的其他锁,但是获取到排他锁的事务是可以对数据进行读取和修改的。
在查询语句后面增加FOR UPDATE
,数据库就会对查询结果中的记录加上排他锁。如
SELECT ... FOR UPDATE
当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
数据库行锁、表锁
行锁往往是在update时候才会生效,锁住条件所命中的行。
表锁往往是在insert的时候才会生效,锁住命中的表。
以上是关于乐观锁和悲观锁的主要内容,如果未能解决你的问题,请参考以下文章