事务具有原子性、一致性、隔离性和持久性。不同事务的实现所对应四个特性的实现方式略有不同。
事务是并发控制的最小单位,如果无法保证原子性,就无法保证一个事务的所有操作都被执行完成。原子性是通过事务回滚机制保证,undo log实现了事务回滚。
在执行update/delete/insert跟更新操作时,会对应生成update/insert/delete相反的更新undo log日志,在部分事务执行失败的时候,使用undo log日志将已经执行的更新操作回滚,保证了事务的原子性。
持久性是指已经更新的操作会被记录在磁盘上,即使机器宕机,更新结果也依然不会丢失。
问题引出:
MySQL数据存储在磁盘上,频繁读取I/O效率低,所以一般会在加一层缓冲区,通过缓冲区读取数据过程:
读数据:先尝试从缓冲区读取数据,缓冲区数据不存在,从磁盘读数据,并将读到的数据更新到缓冲区。
写数据:先将数据写入到缓冲区,缓冲区的数据定时刷新到磁盘中。
使用缓冲区写数据时,由于数据不是实时写入到磁盘,在更新的数据没有刷新到磁盘期间宕机会造成数据丢失,mysql使用了redo log解决该问题。redo log日志记录了所有更新操作,每更新一条记录,都会生成一个redo log日志,并将redo log日志写入磁盘,redo生成过程。
mysql 写盘有两个buffer,第一个时mysql 自己设置的log buffer,另外一个时os和磁盘间buffer,可以通过innodb_flush_log_at_trx_commit设置redo log刷盘时间。
值为0:每秒调用flush命令将redo log从log buffer 写到os_cache,同时调用fsync()将os_cache的redo log刷新到磁盘中;
值为1:默认,每次事务提交后都将redo log 写入log buffer,然后同步刷新到磁盘;
值为2,每次事务提交后都将redo log写入到 log buffer 中,然后每隔一秒将os_cache中redo log写入到磁盘中。值为1和值为2,宕机都有可能造成数据丢失不能回复。
虽然redo log也需要写盘,有IO消耗,数据刷新以页为单位,每个页默认大小为16kb,即使页中跟新非常少的数据,也需要将这个页写回磁盘,redo log 只记录更新的日志,需要刷盘的数据更小。
redo log主要用于机器宕机数据恢复,回复过程由mysql自动执行,bin log可以基于某个时间点对数据进行恢复,同时,bin log也用于主从复制。
隔离性主要是指两个事务并行执行,彼此之间相互干扰。最高级别的隔离是串行化。锁+MVCC(读快照)是实现事务隔离级。
锁:控制实现一个事务写操作对其他事务写操作的影响,防止写覆盖。
MVCC(读快照):控制一个事务写操作对其它事务读操作影响。
首先需要明确,在RU、RC和RR隔离级别下,所有更新操作都会隐式枷锁,加锁解决了写覆盖。
在并发读取表中数据时,可能会出现脏读、不可重读和幻读三种情况。
脏读:读了别人没有提交的数据。
不可重复读:指读取同一条数据,前后数据不一致,由update引起。
幻读:读取一个范围内的数据,读取的数量不一致,由insert/update组成。
通过设置不同的隔离级别可以避免不安全读情况的发生,根据自己的应用场景选择不同隔离级别。
√:表示可能发生,X:表示能够避免
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
读为提交 | √ | √ | √ |
读已提交 | x | √ | √ |
可重复读 | x | x | √ |
串行化 | x | x | x |
MVCC只在RC和RR级别工作,在RU情况的下读模式是当前读,不会创建快照信息,在searial模式下时对所有读取的行枷锁。在RC和RR模式下,读是快照读( select… in share modehe 和select …for udapte时当前读,除外),update/delete和insert都是当前读。
MVCC实现原理:https://blog.csdn.net/huhu123444/article/details/122036671
原子性、持久性和隔离性合起来共同保证了一致性,也是事务要达到的目标,修改之间满足各种约束的,修改之后也依然满足各种约束,业务上从一个A账户转账到B账户,最终应该保证A扣除金额和B增加金额相等。
执行当前读,读取数据的最新记录。除了读未提交隔离别下,select 操作时执行当前读,执行当前读的语句还有(RU、RC和RR都是):
1 update ...
2 insert ...
3 delete...
4 select ... for update /lock in share mode。
因为要对数据进行更新,所以要获得元素最新值。
在执行查询时,根据生成的read view中的所有活跃事务id和undo log链中每条记录事务版本号进行比较,判断数据可见性。
MVCC实现读已提交:https://blog.csdn.net/huhu123444/article/details/122036671
RC级别下,每次查询数据会生成一个新的readView,所以无法保证可重复读。RR级别下,除了使用MVCC,在创建事务的时候,使用trx_assign_read_view函数会创建一个global read view,每次查询都使用同一个read view,保证了可重复读。可见性判断规则和RC级别一样。
MVCC实现可重复读:https://blog.csdn.net/huhu123444/article/details/122036671
在读取数据时,对整个表加共享锁;在执行更细操作时,对整个表加排他锁。