事务的基本要素
事务是指访问并且更新数据库中的各项数据资源的一个程序执行单元,事务其实就是指数据库事务。
ACID原则是数据库事务执行的四个基本要素,分别是原子性,一致性,隔离性,持久性。
- 原子性:事务要么都执行,要么都不执行。
- 一致性:数据库在事务执行前后是一致的,比如进行转账,在事务完成后总金额应该保持一致。
- 隔离性:事务与事务之间是相互隔离的。
- 持久性:事务的提交或者回滚应该是持久保存的。
事务的隔离级别
Read-uncommitted(读未提交):一个事务可以读取另外一个事务未提交的结果。
Read-committed(读已提交):一个事务只可以读取另外一个事务已经提交的结果。
Repeated-Read(可重复读):mySql的默认隔离级别,一个事务多次查询读取的结果是一样的。
Serializable(序列化):所有事务操作按照顺序依次执行,会导致大量的超时以及锁竞争,性能最差。
不同事务级别的并发问题
读未提交:脏读,不可重复读,幻读
读已提交:不可重复读,幻读
可重复读:幻读
脏读:一个事务读取另外一个事务未提交的数据,如果此时事务回滚,读取的数据无效。
不可重复读:一个事务在重复读取数据之间,另外一个事务对数据进行了修改,两次读取的值不一致。
幻读:一个事务在重复读取数据之间,另外一个事务对对应的字段进行了增加删除等操作,两次读取的结果集不一样。
数据库锁
按照锁的粒度分:表级锁、行级锁、页面锁
表级锁:锁粒度最大的一种锁,对当前整个表进行加锁。开销小,加锁快,不会出现死锁,并发度很低,容易发生锁冲突。
行级锁:只针对当前行进行加锁。开销大,加锁慢,可能会出现死锁,并发度很高。
页面锁:介于表级锁与行级锁之间,一次锁定周围多条数据。
只有通过索引访问数据时,InnoDB才会使用行级锁,其余情况都使用表级锁。
由于行锁是针对索引加的锁,所以采用同一个索引访问不同行也有可能会发生锁冲突。
MyIsam中是不会发生死锁的,因为是表级锁一次锁住了全部数据。在InnoDB行级锁中,当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。
避免死锁
1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;
行级锁:共享锁,排他锁;MVCC版本控制
InnoDB的行级锁有两种模式:共享锁与排他锁。
共享锁又称为读锁,多个事务可以共享一把锁,都可以获取数据但是不能对其进行修改。
排他锁又称为写锁,如果一个事务获取了排他锁,其他事务就不能再获取共享锁和排他锁了,获取排他锁的事务可以对其进行读和写。
InnoDB的修改操作:update,delete,insert默认是排他锁的。而普通的select查询不会加锁,select …for update会加上排他锁。select … lock in share mode会加上共享锁。
InnoDB也可以不加锁,使用版本控制来解决并发。
MVCC多版本并发控制
MVCC可以认为是行级锁的变种,可以在很多情况下避免加锁操作,因此开销更低。
MVCC是指多版本并发控制,MVCC的实现通过保存数据在某个时间的快照来实现。
在INnoDB中,每一行数据都有两个隐藏的字段,分别是当前行创建和删除的版本号。每开启一个新的事务版本号都会递增。通过这两个字段对表进行增删改查时会通过比较版本号来进行控制。
https://blog.csdn.net/w2064004678/article/details/83012387
进行插入操作时,创建版本号为当前事务的版本号。
进行更新操作时,创建版本号与原记录的删除版本号设置为当前事务版本号
进行删除操作时,删除版本号为当前事务的版本号。
进行查询操作时,应满足当前事务版本号大于或等于创建版本号,保证当前行记录肯定是存在或者只由当前事务所更新修改。删除版本号为空或者大于当前事务版本号保证该行数据不会被删除。
快照读与当前读
快照读是指读取快照版本,也就是历史版本。基于MVCC保证数据的一致性,读取数据是不需要加锁。
当前读是指通过对读取的数据进行加锁来保证数据的一致性。
普通的SELECT就是快照读,而UPDATE、DELETE、INSERT、SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE是当前读。
RR可重复读隔离级别下解决幻读
在快照读情况下,使用MVCC版本控制来解决幻读。
在当前读情况下,使用next-key lock来解决幻读。
使用InnoDB下,mysql只在满足索引条件的记录上加锁,所以允许另一事务在锁定的记录旁边进行update/insert操作导致幻读。
next-key lock 使用gap lock对命中的记录行周围一定的区间加锁避免幻读。