事务
-
事务ACID属性
原子性(Atomicity) :事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
- 一致性(Consistent) :在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性。
- 隔离性(Isolation) :数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
持久性(Durable) :事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
并发执行下事务会发送的问题
更新丢失(脏写):两个事务同时读取一个初始值,更新时后更新的会覆盖掉前面更新的值
- 脏读:一个事务正在事务过程中修改了某个数据,数据会处于不一致的状态,另一个事务就会读取被改后数据,读到的数据就会不准确
- 不可重读:一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。事务A内部的相同查询语句在不同时刻读出的结果不一致,不符合隔离性
幻读:一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。事务A读取到了事务B提交的新增数据,不符合隔离性
隔离级别与出现问题关系图
隔离级别会解决的并发问题 | 隔离级别,并发问题 | 脏读 | 不可重复读 | 幻读 | | —- | —- | —- | —- | | 读未提交 | 会出现 | 会出现 | 会出现 | | 读已提交 | 不会出现 | 会出现 | 会出现 | | 可重读 | 不会出现 | 不会出现 | 会出现 | | 序列化 | 不会出现 | 不会出现 | 不会出现 |
锁机制
锁分类
- 从性能上分为乐观锁(mvcc实现)和悲观锁
- 从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁)
- 读锁(共享锁,S锁(Shared)):可以同时读取同一份数据,读操作不会影响数据值
- 写锁(排它锁):同一个数据同时只能一个线程操作,写操作完之后再进行其他操作
-
表锁
操作时锁住整张表,开销小,锁的粒度大,并发量不高,表数据迁移可使用
行锁
操作时对表中某一行数据加锁,锁开销较大,锁粒度较小,并发量会提高
执行引擎对锁的实现
对MyISAM表的读操作(加读锁) ,不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
- 对MylSAM表的写操作(加写锁) ,会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作
- InnoDB与MYISAM的最大不同有两点:
- InnoDB支持事务(TRANSACTION)
- InnoDB支持行级锁
- MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。
- InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。
-
间隙锁(Gap Lock)
间隙锁,锁的就是两个值之间的空隙。Mysql默认级别是repeatable-read
-
临键锁(Next-key Locks)
行锁与间隙锁的组合,比如sql条件是id在区间(1,10],那么整个区间就可以成为临键锁
- 索引行锁会升级为表锁(RR级别会升级为表锁,RC级别不会升级为表锁)
- 锁主要加在索引上,对非索引字段更新,行锁可能会变表锁
- InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效。否则都会从行锁升级为表锁
- MyIsam使用的表级锁
InnoDB有行级锁实现,锁开销比表级锁大,但是并发量比较高,并发量较少时MyIsam可能性能更好
锁优化建议
尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
- 合理设计索引,尽量缩小锁的范围
- 尽可能减少检索条件范围,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
- 尽可能低级别事务隔离
