事物的四个特征(ACID)
- 原子性(Atomic): 事务中的所有操作,要么全都执行,要么全都不执行。
- 一致性(Consistency):事务前后数据的完整性必须保持一致。比如A向B转账,不可能A扣了钱,B却没收到。
- 隔离性(Isolation):并发的事务之间不会相互影响
- 持久性(Durability):一旦事务提交之后,所做的修改将会永久保存在数据库上。
隔离(并发)问题
-
丢失更新(Lost Update)。
当两个或多个事务选择同一行,最初的事务修改的值,会被后面的事务修改的值覆盖。 -
脏读(Dirty Reads),一个事务读取了另一个事务修改了但是未提交的数据。
-
不可重复读(Non-Repeatable Reads),一个事务读到另一个事务已提交的数据(update)。
事务 A多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
-
幻读(Phantom Reads),一个事务读到另一个事务已提交的数据(insert)。
系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
隔离级别
隔离级别 | 丢失更新 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
读未提交(Read uncommitted) | × | √ | √ | √ |
不可重复读(Read committed) | × | × | √ | √ |
可重复读(Repeatable read)(默认) | × | × | × | √ |
串行化(Serializable) | × | × | × | × |
解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
事务隔离级别为串行化时,读写数据都会锁住整张表。
隔离级别对应所能解决的隔离(并发)问题。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
mysql默认的事务隔离级别为repeatable-read
三范式
范式的英文Normal Form,简写NF
- 第一范式:每一项数据都是不可拆分的数据项。
- 第二范式:每一个非主属性,完全依赖于任何一个候选码。
- 第三范式:每一个非主属性,既不传递依赖,也不部分依赖于码。
数据库锁
从对数据操作的粒度分 :
- 表锁:操作时,会锁定整个表。
- 行锁:操作时,会锁定当前操作行。
从对数据操作的类型分:
- 读锁(共享锁,S锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
- 写锁(排它锁,X锁):当前操作没有完成之前,它会阻断其他写锁和读锁。
MyISAM 表锁
读锁:自己可读不可写(error) 其他线程:可读不可写(阻塞)
写锁:自己可读可写 其他线程:不可读(阻塞)不可写(阻塞)
InnoDB 行锁
InnoDB 实现了两种类型的行锁:共享锁(S)、排他锁(X)
- 对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);
- 对于普通SELECT语句,InnoDB不会加任何锁;
- 如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,实际效果跟表锁一样。
- 使用范围条件,而不是使用相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据进行加锁; 对于键值在条件范围内但并不存在的记录,叫做 "间隙(GAP)" , InnoDB也会对这个 “间隙” 加锁,这种锁机制就是所谓的 间隙锁(Next-Key锁) 。
间隙锁示例:
数据示例:注意没有id为2的数据。
操作示例:根据范围id<4,更新name数据
解析:id=2在id<4的条件范围内,但id=2的记录并不存在。所以id=2这条记录是间隙。
条件id<4,即对id=1,2,3 都加锁了,产生间隙锁。因此 Session-2插入id为2的记录时,处于阻塞状态。
相关学习资料:全面深入讲解MySQL数据库优化