事务:一个最小的不可再分的工作单元;一个事务通常对应一个完整的业务,例如银行账户转账业务,该业务就是一个最小的工作单元
一个完整的业务需要一组的DML( insert、update、delete)语句共同联合完成
事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同
一个事务都必须包含四条基本特性,这四条特性一般称为ACID
接下来就介绍下,MySQL在innoDB引擎下是事务特性的
为什么要先说隔离性呢?这是因为隔离性是事务最基础的特性,会涉及隔离级别、锁、MVCC等多个概念。
隔离级别说明读未提交一个事务还没提交时,它做的变更就能被别的事务看到读已提交一个事务提交之后,它做的变更才会被其他事务看到可重复读一个事务中,对同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。InnoDB默认级别。串行化事务串行化执行,每次读都需要获得表级共享锁,读写相互都会阻塞,隔离级别最高,牺牲系统并发性。
不同的隔离级别是为了解决不同的问题。也就是脏读、幻读、不可重复读。
MySQL不同的隔离级别,可能存在的问题如下表
事务隔离级别脏读不可重复读幻读读未提交可能可能可能读已提交不可能可能可能可重复读不可能不可能可能序列化不可能不可能不可能
那么不同的隔离级别是怎么保证隔离性呢?答案是 锁 和 MVCC。
MySQL中的锁从粒度上来说分为表锁、页锁、行锁。
表锁有意向共享锁(IS)、意向排他锁(IX)、自增锁等。
行锁的种类共享锁(S)、共享锁 (X),行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。
三种行锁算法
锁和MVCC
大致介绍了下锁,可以看到。有了锁,当某事务正在写数据时,其他事务获取不到写锁,就无法写数据,一定程度上保证了事务间的隔离。但前面说,加了写锁,为什么其他事务也能读数据呢,不是获取不到读锁吗?这就是依靠MVCC(Multi-Version Concurrency Control)多版本的并发控制实现的。
Innodb 在存储每一行数据有一些额外的字段:DATA_TRX_ID和DATA_ROLL_PTR。
ReadView
在每一条 SQL 开始的时候被创建,有几个重要属性:
此时查询
RR 级别的幻读
有了锁和 MVCC , 事务的隔离性得到解决。这里要引申一下,默认的 RR 的级别,解决了幻读吗?幻读通常针对的是 INSERT, 不可重复度则针对 UPDATE 。
快照读
普通的SELECT语句都是普通读,也就是读取的数据都是事务开始时那个状态的数据,普通读的幻读问题主要是通过MVCC来解决的,具体可以看上面的MVCC中的查询操作。
实时读
SELECT *** FOR UPDATE 在查询时会先申请X锁
SELECT *** LOCK IN SHARE MODE 在查询时会先申请S锁
就是实时读,就是读取的是实时的数据,而不快照数据,读的时候会加Next-Key Lock锁住当前的记录,以及左右两个区间的间隙,这样在读的时候就不能往我们的查询范围插入数据了。
前面有提到 undo log 回滚日志。隔离性的MVCC其实就是依靠它来实现的,原子性也是。实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。
当事务对数据库进行修改时,InnoDB会生成对应的 undo log;如果事务执行失败或调用了 rollback,导致事务需要回滚,便可以利用 undo log 中的信息将数据回滚到修改之前。undo log 属于逻辑日志,记录的是sql执行相关的信息。当发生回滚时,InnoDB 会根据 undo log 的内容做与之前相反的工作:
以update操作为例:当事务执行update时,其生成的undo log中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到update之前的状态。
持久性依靠的是 redo log。MySQL 里经常说到的 WAL(Write-Ahead Logging) 技术 ,它的关键点就是先写日志,再写磁盘。
在做数据更新操作时,先将对数据页的更改记录到redo log,然后再去更新内存中的数据页,在下次查询数据页或者空闲时间,将操作记录更新到磁盘。这样可以将随机I/O改为顺序I/O。
优点是减少磁盘I/O次数,即便发生故障也可以根据redo log来将数据恢复到最新状态。
缺点是会造成内存脏页,后台线程会自动对脏页刷盘,或者是淘汰数据页时刷盘,此时会暂时查询操作,影响查询。
redo log 有两个特点:
对于redo log 是有两阶段的:commit 和 prepare 如果不使用“两阶段提交”,数据库的状态就有可能和用它的日志恢复出来的库的状态不一致.
两段提交制是什么?
更新时,先改内存中的数据页,将更新操作写入redo log日志,此时redo log进入prepare状态,然后通知MySQL Server执行完了,随时可以提交,MySQL Server将更新的SQL写入bin log,然后调用innodb接口将redo log设置为提交状态,更新完成。
可能你会疑问还有个 binlog 也是写操作并用于数据的恢复,有啥区别呢。
对于语句 update T set c=c+1 where ID=2;
崩溃恢复时的判断规则(以redolog是否commit或者binlog是否完整来确定)
一致性是事务追求的最终目标,前面提到的原子性、持久性和隔离性,其实都是为了保证数据库状态的一致性。当然,上文都是数据库层面的保障,一致性的实现也需要应用层面进行保障。
也就是你的业务,比如购买操作只扣除用户的余额,不减库存,肯定无法保证状态的一致。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。