详解数据库的隔离级别
1,数据库事务的四大特性
数据库事务:是作为单个工作单元而执行的一系列操作,如查询和修改数据,甚至是修改数据的定义。
- 原子性 (Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。即要么都成功,要么都失败。
如上图: 小明要给小红转200元,那么这个过程包括两个步骤:小明给银行转200:1000-200=800;银行给小红转账200:1000+200=1200。原子性规定这两个步骤一起成功,或者一起失败,不能只发生其中一个动作。如果小明只减200或者小红只加200,那么银行就会乱套。
- 一致性 (Consistency)
一致性是指事务前后数据的完整性必须保持一致。
仍是如上图:小红和小明初始的金额总数为:1000+1000=2000;在转账完成之后,小明余额800元,小红余额1200元。俩人金额总数为:800+1200=2000。一致性表示事务完成后,符合逻辑运算。
- 隔离性 (Isolation)
隔离性是指多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
如上图:多个用户同时操作,产生俩个事务:1,小明给小红转账200;2,小刚给小红转账100。隔离性是指小明和小刚给小红转账这两个事务不会相互影响。
- 持久性 (Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
仍是如上图的例子:小明给小红转账200元:
操作前小明:1000,小红:1000
操作后小明:800,小红:1200
如果在操作前(事务还没有提交)服务器宕机或者断电,那么重启数据库以后,数据状态应该为:小明:1000,小红:1000;
如果在操作后(事务已经提交)服务器宕机或者断电,那么重启数据库以后,数据状态应该仍为
A:800,B:1200,不会恢复到原始。
2,数据库的隔离级别问题
2.1脏读
脏读是指一个事务在执行过程中读取到另一个事务未提交的数据的现象就是脏读。
如上图小明和小刚给小红转账:
1、在小明给小红转账过程中,银行对小红的银行卡余额进行了修改为1200,小刚给小红转账时读取了小红的银行卡余额修改后的数据,并加100为1300了。
2、由于某些原因,小明撤销了转账的事务转账事务没有完成提交,发生了RollBack操作,则小刚读取的数据就是脏数据。这样就产生了数据错误。
PS:银行实际转账流程不是如此,该例子只是为了便于理解脏读。
2.2 不可重复读
不可重复读是指对于数据库中的某个数据,一个事务范围内的多次查询却返回了不同的结果,这是由于在查询过程中,数据被另外一个事务修改并提交了。
事务B读取了两次数据资源,在这两次读取的过程中事务A修改了数据,导致事务B在这两次读取出来的数据不一致。
注: 不可重复读和脏读的区别是,脏读读取到的是一个未提交的数据,而不可重复读读取到的是前一个事务提交的数据;而不可重复读在一些情况也并不影响数据的正确性,比如需要多次查询的数据也是要以最后一次查询到的数据为主。
2.3 虚读(幻读)
幻读是事务非独立执行时发生的一种现象。例如事务1批量对一个表中某一列列值为1的数据修改为2的变更,但是在这时,事务2对这张表插入了一条列值为1的数据,并完成提交。此时,如果事务T1查看刚刚完成操作的数据,发现还有一条列值为1的数据没有进行修改,而这条数据其实是2刚刚提交插入的,这就是幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点同脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
注:
-
幻读和不可重复读都是读取了另一条已经提交的事务。不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体
-
解决不可重复读的方法是 锁行,解决幻读的方式是 锁表。
3,数据库的锁机制
3.1 共享锁
由读表操作加上的锁,加锁后其他用户只能获取该表或行的共享锁,不能获取排它锁,也就是说只能读不能写。
3.2 排它锁
由写表操作加上的锁,加锁后其他用户不能获取该表或行的任何锁,典型是mysql事务。
3.3 表锁
给整张表加锁
3.4 行锁
给行数据加锁
因此锁可以分为表级共享锁、行级共享锁、表级排它锁、行级排它锁。
4,数据库的隔离级别
4.1 读取未提交(READ-UNCOMMITTED)
在这个隔离级别,select语句不加锁,所有事务都可以“看到”未提交事务的执行结果。读取未提交数据,也被称为“读脏”。
4.2 读取已提交(READ-COMMITTED)
大多数数据库系统的默认隔离级别(但是不是MySQL的默认隔离级别),满足了隔离的早先简单定义:一个事务开始时,只能“看见”已经提交事务所做的改变,一个事务从开始到提交前,所做的任何数据改变都是不可见的,除非已经提交。可避免脏读的发生,在互联网大数据量,高并发量的场景下,几乎不会使用上述两种隔离级别。
4.3 可重复读(REPEATABLE-READ)
MySQL数据库默认的隔离级别。该级别解决了READ UNCOMMITTED隔离级别导致的问题。它保证同一事务的多个实例在并发读取事务时,会“看到同样的”数据行。不过,这会导致另外一个棘手问题“幻读”。InnoDB和Falcon存储引擎通过多版本并发控制机制解决了幻读问题。
4.4 可串行化(SERIALIZABLE)
该级别是最高级别的隔离级。它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之,可串行化是在每个读的数据行上加锁。在这个级别,可能导致大量的超时Timeout和锁竞争Lock Contention现象,实际应用中很少使用到这个级别,但如果用户的应用为了数据的稳定性,需要强制减少并发的话,也可以选择这种隔离级。
根据数据库的隔离级别问题可以看出数据库的隔离级别的缺点:
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
READ UNCOMMITTED | 是 | 是 | 是 | 否 |
READ COMMITTED | 否 | 是 | 是 | 否 |
REPEATABLE READ | 否 | 否 | 是 | 否 |
SERIALIZABLE | 否 | 否 | 否 | 是 |
5,数据库的三大范式
范式是数据库设计时遵循的一种规范,不同的规范要求遵循不同的范式。
5.1 第一范式
数据库的属性不可分割,即每个属性都是不可分割的原子项。即保证数据库的每一列不可再分。
5.2 第二范式
前提:必须满足第一范式。不存在部分依赖,即非主属性必须完全依赖于主属性。即第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。
5.3 第三范式
前提:必须满足第一、第二范式。不存在传递依赖,即非主属性不能与非主属性之间有依赖关系,非主属性必须直接依赖于主属性,不能间接依赖主属性。即第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
作者:王二黑_Leon
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
本文章仅作为自学所用。