mysql事务与锁

mysql事务

什么是mysql事务

数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作。

事务是一组不可再分割的操作单元

哪些存储引擎支持事务

innodb myisam

update xxx set xxx = xxx where xxx=? 有事务吗

有。通过会话层autocommit变量来控制,默认自动提交

事务的四大特性

原子性,一致性,隔离性,持久性

事务并发的三个问题

脏读:多个事务并发,导致脏读

r1 r2
update
read(脏读)
rollback

不可重复读:一个事务的前后两次读取之间,有其他事务对数据做了修改,导致两次读取数据不一致

r1 r2
read
update
commit
read (不可重复读) -

幻读

r1 r2
read (where id>1)
insert(id=2)
commit
read (where id>1)(幻读) -

解决事务并发的方案:事务隔离

通过数据库引擎的事务隔离级别

serializable(串行化)

隔离级别 并发问题
read uncommited(未提交读)
脏读
read commited(已提交读) 只读取已提交的数据,解决了脏读
不可重复读
repeatable read(可重复读) 在同一事务中多次读取同样的数据结果是一样的,解决不可重复读的问题,未解决幻读(innodb解决了幻读)
read 幻读
serializable(串行化) -

事务隔离级别的实现方案

  1. LBCC(Lock Base concurrency control) 在读取数据之前,对其加锁,阻止其他事务对数据进行修改
  2. MVCC(Multi Version concurrency control) 生成一个快照

解决脏读:在r2事务(write/rollback)开始的时候,拿到排它锁,阻塞r1的读操作

解决不可重复读: 在r1开始的时候,拿到共享锁,阻塞r2的写操作

解决幻读:使用自增主键,行锁算法(范围)走临键锁

事务隔离级别的实现细节–’读’操作

当前读

即加锁读,读取记录的最新版本,会加锁保证其他并发事务不能修改当前记录,直至获取锁的事务释放锁;

使用当前读的操作主要包括:显式加锁的读操作与插入/更新/删除等写操作,如下所示:

1
2
3
4
5
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
1
2
3
4
5
6
7
8
9
注:当Update SQL被发给MySQL后,MySQL Server会根据where条件,读取第一条满足条件的记录,然后InnoDB引擎

会将第一条记录返回,并加锁,待MySQL Server收到这条加锁的记录之后,会再发起一个Update请求,更新这条记录。

一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。因此,Update操作内部,就包含了当前读。同理,

Delete操作也一样。Insert操作会稍微有些不同,简单来说,就是Insert操作可能会触发Unique Key的冲突检查,也

会进行一个当前读。

快照读

即不加锁读,读取记录的快照版本而非最新版本,通过MVCC实现;

InnoDB默认的RR事务隔离级别下,不显式加『lock in share mode』与『for update』的『select』操作都属于快照读,保证事务执行过程中只有第一次读之前提交的修改和自己的修改可见,其他的均不可见;

事务隔离级别的实现细节–’写’操作

1
2
3
insert into table values (…);
update table set ? where ?;
delete from table where ?;

锁

myisam 只支持表锁 innodb 支持表锁和行锁

innodb 锁类型

  • 共享锁(行锁)
  • 排它锁(行锁)
  • 意向共享锁(表锁)
  • 意向排它锁(表锁)

共享锁

多个事务获取同一条数据的共享锁。在共享锁期间,其他事务不能对数据进行写操作

排它锁

只能有一个事务对一条数据创建排它锁。其他事务不能读也不能写

在一个事务中,update,delete,insert会自动针对数据加一个排它锁

锁的算法(范围)

  • 记录锁
  • 间隙锁
  • 临键锁

不同的事务隔离级别、不同的索引类型、是否为等值查询,使用的行锁算法也会有所不同;

当等值查询,有命中行数,且有命中唯一索引(唯一索引,主键索引)的时候,使用 记录锁

当范围查询,未命中的时候,使用 间隙锁。 间隙锁只在rr事务隔离级别存在

当范围查询,且有命中行数的时候,使用 临键锁。或者理解为除了(间隙锁,记录锁以外,其他都是临键锁)

主键索引和唯一索引

主键索引和唯一索引。 主键索引是特殊的唯一索引。 主键索引不允许为null值,唯一索引允许存在一个null值

关于是否阻塞的总结

定值查询

r1 r2 是否命中相同数据 r2是否阻塞
走索引 没走索引 阻塞
走索引 走索引 是 阻塞
走索引 走索引 否 不阻塞
没走索引 走索引 阻塞
没走索引 没走索引 阻塞

Q&A

非唯一索引,等值查询命中后,会使用哪种锁算法(记录锁,间隙锁,临键锁)?

如果行锁会造成表锁(意向锁),那么行锁的意义是什么?

以不可重复读或者幻读为例,为什么同一事务不能出现两次读取不一致的问题?

因为会破坏事务的一致性

对上面概念的梳理

相关链接

mysql事务与锁 视频 mysql锁