这种适用于读多,写少,并且写有可能会用很长时间的情况。
死锁
比如有两个事物:
事物1
update table1 set name=‘1‘ where id=1;
// sleep 1
update table1 set name=‘2‘ where id=2;
commit;
事物2
update table1 set name=‘2‘ where id=2;
// sleep 2
update table1 set name=‘1‘ where id=1;
commit;
上面的语句如果恰巧一起执行到sleep1 和sleep2,那么就会造成死锁。 一般有三种做法解决:
- 一次锁所有数据
- 保持锁的顺序
- 允许死锁,然后kill掉代价最小的事务。回滚之
间隙锁
执行select .. from where id between这样的语句的时候锁定这一区间,不能插入或者删除数据,以防止幻读。
MVCC 多版本并发控制
mysql维护一个系统版本号,每次有新的事物开始的时候递增。
每行后面保存两个隐藏列。 一个创建时版本号C,一个删除时版本号D
- INSERT 新增的行往C里写入当前系统版本号。 这样新事物可以通过这个版本号保证不查到他
- DELETE 为删除的行写D字段为当前系统版本号。
- UPDATE 插入一行新数据写C为当前系统版本号,老数据写D为当前系统版本号。
- SELECT 的时候只查 C<=当前版本 && (D > 当前版本 || D is not defined) 这样主要是为了在不加锁的情况下,一个事务能够读取到事务开始前已经存在且未被删除,且没有经过修改的数据。 也就是解决脏读的问题。即该事物开始之后的其他事务的各种修改事务提交都不会对当前事务的读取产生影响。同时也解决了幻读的问题
语句与锁
- InnoDB 的行锁是基于索引的,如果没有索引,或者不能使用索引则是表锁
- select … from 一致性非阻塞读,不上锁。
- select … from where lock in share mode 共享锁
- select … from where … for update 排他锁
- update .. where 排他锁
- delete … from 排他锁
下面的例子来看锁的排他性
事务1
set autocommit=0;
begin;
SELECT * FROM biz_pay_task where id = 1 FOR UPDATE;
// wait
commit;
事务2
set autocommit=0;
begin;
SELECT * FROM biz_pay_task where id = 1 LOCK IN SHARE MODE;
commit;
事务2在执行SELECT LOCK IN SHARE MODE的时候会阻塞,知道事务1commit之后才会完成。
版权声明:本文为博主原创文章,未经博主允许不得转载。
mysql第二天 锁
标签:锁