一 : 事务的四大属性 : ACID
A(atomicity) : 原子性,指事务是一个不可分割的工作单位,要不全部提交成功,要不全部回滚到原来的状态。就比如过马路,我们要么过,要么不过,不能卡在路中间,否则就很危险。
C(consistency): 一致性,指事务的执行前后,数据从一个合法性状态变换到另一个合法性状态。合法性状态要根据实际情况而定。比如转账的情景,A一开始有100块,B也有100块,A转100给B,A变为了0元,B变为200,他们的金额之和不变,A的数据也合理,这是一致。如果在事务执行期间,出现了线程不安全的问题,导致A和B之和不等于200了,也就是出现了数据不一致。
I(isolation) : 隔离性,指一个事务的执行不能被其他事务干扰,即一个事务的内部操作和数据对并发的其他事务是隔离的。如果事务之间不满足隔离性的话,就会像下面的情景
D(durability) : 持久性,事务提交之后对数据库的影响是永恒的
二:数据并发的问题(严重性从高到低)
在真实情况下,数据库面临的是一个请求高并发的情况,如果我们一味的为了保持隔离性,让请求排队处理,那么效率会非常低。所以我们需要在隔离性和并发性之间做一个取舍。
先来看并发可能带来的问题。
脏写:如果事务A修改了事务B修改了但未提交的数据,就称为脏写
脏读:如果事务A读取了事务B修改了但未提交的数据,后来事务B回滚了,导致事务A读到的数据就是无效数据,这样的情况称为脏读
不可重复读:事务A读取了一个字段的值,事务B对这个值进行了修改,当事务A再去读这个字段的值时,发现和上一次读的值不一样,就称为不可重复读(A仍然在本次事务内)
幻读:事务A读取了某行数据,事务B之后在这张表里插入了几行新数据,当事务A再去读取数据时发现表里多了几行,就称为幻读。幻读特别指的是插入操作。
三:sql中的四种隔离级别
在上面我们提到了四种数据并发的问题,隔离级别就是用于解决这些问题(四种都解决了脏写)
READ UNCOMMITTED(读未提交) : 即事务之间可以互相看到未提交的数据。这种级别只解决了脏写问题
READ COMMITTED(读已提交) : 即事务之间只能看见已经提交后的数据。这种级别解决了脏读的问题,但没有解决不可重复读和幻读。
REPEATABLE READ(可重复读) : 事务A读取了一条数据,之后事务B对这条数据进行了修改,当事务A再去读取这条数据时,读到的仍然是第一次读到的数据。这种隔离级别解决了脏读和不可重复读,没有解决幻读。(在mysql中该隔离级别解决了幻读问题)
SERIALIZABLE(序列化) : 事务隔离的最高级别。在事务操作某张表的数据时,其他事务不能对这张表进行任何的更新操作(包括增删改),这保证了数据的绝对正确,但是对性能有较大影响。解决了所有并发问题。
四:redo日志和undo日志
我们上面提到事务具有四大特性,那么这四大特性都是由什么机制实现的呢?
事务的隔离性是由锁机制实现的(后期会更新锁机制)
原子性,一致性,持久性是由redo日志和undo日志实现的。
redo日志(重做日志):当事务非正常中断时,如果有部分数据提交了但还没有更新到硬盘,会把这部分事务写在redo日志中,在服务器重新正常启动时加载redo日志文件,完成剩下的未完成的操作,用来保证事务的持久性。Redo日志降低了刷盘频率,我们不需要通过每次提交都刷一次盘保证数据的完整
undo日志(回滚日志): 回滚行记录到某个特定版本,用于保证事务的原子性和一致性。
Redo日志:
Redo由两部分组成:redo log buffer(保存在内存中,容易丢失) 和 redo log file(保存在硬盘中),一个更新事务的redo执行过程如下
第一步:先将原始数据从磁盘中读到内存里,修改数据的内存拷贝
第二步:生成一条重做日志并且写入redo log buffer ,记录的是数据被修改后的值
第三步:当事务提交时,将redo log buffer中的内容更新到redo log file中
第四步:按照一定频率将data buffer中的数据刷新到磁盘中
通过上述我们发现,我们不希望第四步出现问题,但是就算出现问题,我们在磁盘中也有redo log file来帮助我们恢复数据。
其实,redo log buffer刷盘到redo log file的过程并不是真的刷到磁盘中的文件里,只是刷入到了文件系统缓存(page cache)中,这是操作系统为了提高文件写入效率做的优化,真正的写入文件交给操作系统来决定。
Redo的刷盘策略:
Innodb给出了innodb_flush_log_at_trx_commit参数,用于控制redo log buffer中的日志以何种频率刷入到磁盘中。
设置为0 : 系统默认每隔1s进行一次日志同步
设置为1 : 每次事务提交时进行同步(默认),刷盘
设置为2: 事务提交时只把redo log buffer的内容写入page cache中,不进行日志同步,由操作系统自己决定什么时候同步到磁盘文件中
Undo日志
redo日志是事务持久性的保证,undo日志是事务原子性的保证,在事务中更新数据的前置操作是要先写入一个undo log中。
当事务在执行过程中遇到各种错误或者服务器宕机等情况,我们需要把数据恢复成原来的样子,也就是保证事务的原子性,这个过程称之为回滚,但其实回滚不是物理层面上的回到过去,而是逻辑层面的,让事务看起来像什么都没做一样。(就像插入了一条数据,再把它删掉,就跟初始状态一样,但其实还是执行了增删的操作)。
Undo日志的作用:回滚数据,MVCC(后期会更新)