小言_互联网的博客

mysql中的悲观锁和乐观锁

340人阅读  评论(0)
悲观锁:获取数据时都会直接加锁,共享资源每次只给一个线程使用,其它线程阻塞等待。在数据库中提供了行锁、表锁等,操作数据时先加锁后使用。例如售票系统
select * from ticket where id=100 for update
乐观锁:不是数据库系统自带的,需要开发实现。乐观锁是只操作数据时并不进行任何特殊处理,也就是不加锁,在进行更新时才进行冲突判
在数据表中添加一个额外列: version 数据版本号或者使用时间戳 timestamp ,每次修改数据则版号加1
update tb_users set age=80,version=2 where id=5 and version=1 第一次读取数据的版本
号为 1 ,修改时条件为 version=1 的那条数据。如果中间有事务已经修改了数据,则版本号绝
对不是 1 ,所以该语句执行返回结果为 0
如果执行结果为 0 ,则重新读取数据,重新执行修改操作
锁模式
记录锁:在行相应的索引记录上的锁,锁定一个行记录 . 它会在 id=1 的记录上加上记录锁,以阻止其他事务插入,更新,删除 id=1 这一行。
gap 锁:间隙锁是封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一 条索引记录之后的范围
临键锁 next-key 锁是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间。
MYSQL8 RR 级别下默认使用临键锁 . 临键锁的主要目的,也是为了避免幻读 (Phantom Read) 。如果 把事务的隔离级别降级为RC ,临键锁则也会失效
意向锁:是为了支持多种粒度锁同时存在;
锁分类
全局锁:就对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的 MDL DDL 语句、更
新操作的事务提交语句都将被阻塞。其典型的使用场景是做全库的逻辑备份,对所有的表进行锁
定,从而获取一致性视图,保证数据的完整性
        flush tables with read lock;
        unlock table
行级锁
        S 共享锁 :允许获取到此锁的事务读取行
        X 排他锁 :允许获取到此锁的事务 update delete
表级锁 - 意向锁
        表级锁会对当前操作的整张表加锁,最常使用的 MyISAM InnoDB 都支持表级锁定
        lock tables xxx read/write;
        IS 意向共享锁:事务打算对表中的行设置共享 S
        IX 意向排他锁:事务打算对表中的行设置排他 X
        意向锁是 InnoDB 自动加的, 不需用户干预
意向锁定协议
        事务在获得表中某行上的共享锁之前,必须先获得表上的IS 锁或更高级别的锁
        在事务可以获得表中某一行上的排他锁之前,它必须首先获得表上的IX
验证,比如表 aa, 需要开启 set AUTOCOMMIT = FALSE ;
X: lock tables aa write ;
S: lock tables aa read ;
IX: select * from aa where 1 = 2 for update ;
IS : select * from aa where 1 = 2 lock in share mode ;
按加锁方式分类
按加锁方式划分,可分为自动锁、显示锁。
隐式加锁:
        InnoDB自动加意向锁
        对于UPDATE DELETE INSERT 语句, InnoDB 会自动给涉及数据集加排他锁
        对于普通SELECT 语句, InnoDB 不会加任何锁;
        随时都可以执行锁定,InnoDB 会根据隔离级别在需要的时候自动加锁;锁只有在执行commit或者rollback 的时候才会释放,并且所有的锁都是在同一时刻被释放。
显式加锁:
        共享锁S SELECT * FROM table_name WHERE … LOCK IN SHARE MODE
        排他锁X SELECT * FROM table_name WHERE … FOR UPDATE
按照算法分类
按照算法分类,可分为间隙锁、临键锁、记录锁。
间隙锁:间隙锁基于非唯一索引,它锁定一段范围内的索引记录。使用间隙锁锁住的是一个区间,
而不仅仅是这个区间中的每一条数据。 select * from tb_users where id between 1 and 10
for update ; 即所有在(
1 10 )区间内的记录行都会被锁住,所有 id 2 3 4 5 6 7 8
9 的数据行的插入会被阻塞,但是 1 10 两条记录行并不会被锁住
临键锁是记录锁与间隙锁的组合,它的封锁范围,既包含索引记录,又包含索引区间,是一个左开
右闭区间。临键锁的主要目的,也是为了避免幻读 Phantom Read 。如果把事务的隔离级别降级为
RC ,临键锁则也会失效。每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该
数据行的临键锁时,会锁住一段左开右闭区间的数据。需要强调的一点是, InnoDB 中行级锁是基
于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。
记录锁是封锁记录,记录锁也叫行锁,如 select *from tb_users where id=1 for update ;
会在 id=1 的记录上加上记录锁,以阻止其他事务插入,更新,删除 id=1 这一行。
相关锁总结
记录锁、间隙锁、临键锁,都属于排它锁
记录锁就是锁住一行记录
间隙锁只有在事务隔离级别 RR 可重复读 中才会产生
        唯一索引只有锁住多条记录或者一条不存在的记录的时候,才会产生间隙锁,指定给某条存在的记录加锁的时候,只会加记录锁,不会产生间隙锁
        普通索引不管是锁住单条,还是多条记录,都会产生间隙锁
        间隙锁会封锁该条记录相邻两个键之间的空白区域,防止其它事务在这个区域内插入、修改、删除数据,这是为了防止出现幻读现象
普通索引的间隙,优先以普通索引排序,然后再根据主键索引排序
事务级别是 RC 读已提交级别的话,间隙锁将会失效
表级锁: 开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高 , 并发度最低。
行级锁: 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低 , 并发度也最高。
页面锁: 开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度

转载:https://blog.csdn.net/m0_56627229/article/details/127910717
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场