理解事务的ACID和隔离级别

来源:互联网 发布:钢结构三维设计软件 编辑:程序博客网 时间:2024/06/10 03:23

事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit),事务是一个数据库概念。但我理解数据库也是一款软件,只是遵循了数据库SQL标准,理解事务,先看看数据库软件的逻辑结构,这是mysql的逻辑结构:
这里写图片描述
可见mysql支持多客户端访问,对应到mysql里面就是多线程(通常一个客户端对应mysql内部一个处理线程),底层的存储引擎就是负责向持久化介质(硬盘,存储阵列)读取和写入数据,显然这些数据是被各个内部线程共享的,就需要关注线程安全问题,举个栗子,有一张数据表car,里面2个字段,分别是cars_Beijing和cars_Shanghai,表示在北京的汽车数量以及和上海的汽车数量,现在一个线程要干的事是完成把一个汽车从北京搬到上海,那么,很明显就是cars_Beijing–,cars_Shanghai++,用2条sql语句表示:
update car set cars_Beijing=cars_Beijing-1 where id = 1;
update car set cars_Shanghai=cars_Shanghai-1 where id = 1;

现在用这个例子来理解ACID,
1. 原子性(atomicity),就是说这个事务要么不执行,要么全部执行,就是上面2条语句,不允许只执行一条而不执行第二天语句的情况发生。
2. 一致性(consistency),就是说数据库从一个一致性状态转移到另一个一致性状态,怎么理解呢,原子性是表示不允许只执行一条而不执行第二条的情况发生,那么一致性就是说要么第一条第二条都执行成功(所谓执行成功就是对数据库持久化数据产生了影响),要么就第一条第二条都执行失败(都不对数据库持久化数据产生影响),不允许一条成功,一条失败的情况。
3. 隔离性(isolation)理解隔离性,就是隔离另一个线程(事务)的操作,比如线程A正在执行这个事务cars_Beijing–,cars_Shanghai++,线程B则正在查询cars_Beijing和cars_Shanghai的值,隔离性就要保证线程B只能查询到事务完全没有执行或者完全成功执行的值,不允许线程B查询到只执行了cars_Beijing–而没有执行cars_Shanghai++的值
4. 持久性(durability),这个比较好理解,就是事务一旦提交,所修改的数据就被持久化,即使掉电也不会丢失。

前面说了隔离性是防止其他线程读到事务的中间值,但实际上这会分成情况,因为有很多种不同的中间值,也就对应了SQL标准中的4个隔离级别。
理解隔离级别之前,先搞清楚两个问题。
第一个是要实现事务的ACID性,其实和多线程编程中对公共资源的线程安全性原理差不多,怎么来保证这个ACID哩,当然就是对各线程的公共资源加锁了,线程要访问公共资源,就需要先获取公共资源的锁,这里的公共资源就是数据库里面的数据,我们知道关系型数据库中的数据是按表-行来分的,这里我是可以选择是对行加锁还是对表加锁,或者干脆对整个数据库加锁的问题。这就是锁的级别。
第二个是多个线程对公共数据的操作其实也可以分成很多种情况,比如线程A在执行cars_Beijing–,cars_Shanghai++这个事务的时候,事务B可能正在读取相应字段的cars_Beijing值,也可能多次读取读取相应字段的cars_Beijing值,还有可能在统计car表的有多少条记录或者增加记录等等。
知道了锁和其他线程对公共数据表的操作组合,我们再来理解SQL标准中的4个隔离级别,就会很容易了
1. 未提交读(read uncommitted),就是不做隔离控制,可以读到“脏数据”,比如上面的cars_Beijing–,cars_Shanghai++,这个隔离级别允许其他线程读取到只做了cars_Beijing–而没有做cars_Shanghai++时候的值。显然这个隔离级别没有太大意义,现实中没有人会用,除非这个应用只有读取,没有任何写入。
2. 提交读(read committed),提交读就是不允许读取事务没有提交的数据,简单的说,就是上面的cars_Beijing–,cars_Shanghai++,不允许读取到只做了cars_Beijing–,而没有做cars_Shanghai++的记录。这个隔离级别是大多数数据库(除了mysql)的默认隔离级别。
3. 可重复读(repeatable read),什么是不可重复读,就是事务A去做cars_Beijing–,cars_Shanghai++之前,事务B启动了,先读取了一次事务A要修改的值,这个时候事务A修改了记录,但是事务B在事务A修改完后又读取了同一记录值,显然,这导致事务B相同的读取操作却读取了不同值,这就是不可重复读。可重复读就是禁止这种情况发生,比如对需要修改的数据加排他锁,事务B需要读取这个记录, 那么整个事务B没有完成之前,都允许事务A启动。可重复读的隔离级别是mysql默认的隔离级别。
4. 可串行化(serialzable),就是多个线程(事务)完全不并发,串行执行,当然不会有任何隔离问题,显而易见效率也最低,一般不采用

还有一个问题,就是有什么问题可串行化能解决而可重复读不能解决?那就是幻读问题,什么叫幻读,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。可重复读由于只锁定需要修改的记录,如果一个事务的任务是增加一个记录,这个事务是不会被修改事务阻塞的,所有可重复读的隔离级别是不能解决幻读问题的

0 0
原创粉丝点击