一 两段锁协议

因为数据库中有大量的并发访问,为了预防死锁,一般推荐使用一次封锁法,就是在方法的开始阶段预先知道使用哪些数据,然后全部锁住,在方法运行之后在解锁。可以避免死锁。但是数据库并不知道要用到哪些数据。

数据库遵循两段锁协议,将事物分成两个阶段。加锁阶段和解锁阶段。

加锁阶段:该阶段可以进行加锁操作,在任何数据进行读之前,都要申请并获得S锁(共享锁)。在写操作之前要获得X锁。加锁不成功,则事物进入等待状态,直到加锁成功才继续执行。

事务加锁/解锁处理

begin; insert into test .....加insert对应的锁

update test set...加update对应的锁

delete from test ....加delete对应的锁

commit;

事务提交时,同时释放insert、update、delete对应的锁。

这种方式无法避免死锁,但是两段锁可以保证事物的并发调度是串行的。

二 事物的四种隔离级别

MySQL RR隔离级别如何解决幻读的 mysql隔离级别怎么实现的_加锁

在RC 这个隔离级别,数据的读取都是不加锁的,但是数据的写入,修改,删除是需要加锁的。

事务A

事务B

begin;

begin;

update class_teacher set class_name='初三二班' where teacher_id=1;

update class_teacher set class_name='初三三班' where teacher_id=1;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

commit;

teacher_id 加了索引,锁住的是teacher_id 这一行,如果没有索引,锁住是是整个表。但实际使用过程中,MySql做了一些改进,在MySQL  Server过滤条件发现不满足后,会调用unlock_row 方法把不满足条件的记录释放锁 (违背了二段锁协议的约束)。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。

RR 隔离级别

事务A

事务B

事务C

begin;

begin;

begin;

select id,class_name,teacher_id from class_teacher where teacher_id=1;

id

class_name

teacher_id

1

初三二班

1

2

初三一班

1

update class_teacher set class_name='初三三班' where id=1; commit;

insert into class_teacher values (null,'初三三班',1);commit;

select id,class_name,teacher_id from class_teacher where teacher_id=1;

id

class_name

teacher_id

1

初三二班

1

2

初三一班

1

很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。

在可重复读中,该SQL第一次读取到数据后就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了。但是无法锁住insert 数据。

三  MVCC协议

mySql 这种成熟的数据库出于性能考虑,使用了基于乐观锁的为理论基础的MVCC(协议)

在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期