mysql事务
事务就是一组原 子性的SQL査询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应 用该组査询的全部语句,
那么就执行该组査询。如果其中有任何一条语句因为崩溃或其 他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执 行成功,要么全部执行失败。
mysql的三层逻辑架构
第一层
第一层服务并不是MySQL所独有的,大多数基于网络的客户端/服务器的工具或者服务都有类似的架构。比如连接处理、授权认证、安全等等。
第二层
第二层架构是MySQL比较有意思的部分。大多数MySQL的核心服务功能都在这一层.包括査询解析、分析、优化、缓存以及所有的内置函数(例如,日期、时间、数学和加密函数),所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。
第三层
第三层包含了存储引擎。存储引擎负责MySQL中数据的存储和提取。每个存储引擎都有它的优势和劣势。服务器通过API与存储引擎 进行通信。这些接口屏蔽了不同存储引擎之间的差异,使得这些差异对上层的査询过程透明。存储引擎API包含几十个底层函数,用于执行诸如“开始一个事务”或者“根据 主键提取一行记录”等操作。但存储引擎不会去解析SQL,不同存储引擎之间也不会相互通信,而只是简单地响应上层服务器的请求。
老生常谈☞四大特性
原子性(Atomicity)
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全 部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分 操作,这就是事务的原子性。
隔离性(Isolation)
一个事务所做的修改在最终提交以前,对其他事务是不可见的。
持久性(Durability)
一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修 改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多 不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。~且 不可能有能做到100%的持久性保证的策略(如果数据库本身就能做到真正的_久 性,那么备份又怎么能增加持久性呢?)。
一致性(Consistency)
数据库总是从一个一致性的状态转换到另外一个一致性的状态,简单来说就是账户A给账户B转账5,只能从A,B过渡到A-5,B+5,不会出现A,B+5或者A-5,B的状况。
innodb中怎么实现这四大特性
原子性(Atomicity)实现机制
事务的所有修改操作(增、删、改)的相反操作都会写入undo log,比如事务执行了一条insert语句,那么undo log就会记录一条相应的delete语句。所以undo log是一个逻辑文件,记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
隔离性(Isolation)实现机制
利用的是锁和MVCC机制
持久性(Durability)实现机制
事务的所有修改操作(增、删、改),数据库的InnoDB存储引擎都会生成一条redo日志记录到redo log,除了生成 redo log,还会生成 binlog。redo log记录的是事务对数据库的哪个数据页做了什么修改,属于物理日志。数据库宕机重启的时候,会将redo log中的内容恢复到数据库中,再根据undo log和binlog内容决定回滚数据还是提交数据(两阶段提交)。
一致性(Consistency)实现机制
从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。
持久性原理详解
背景
首先从一条梦幻的sql讲起:
- update account set asset = 999999999 where id=‘666666’ and asset=0;
- 这条sql的目的就是修改账户余额,梦幻999999999从此人生巅峰。这对于Mysql来说就是修改一条磁盘的0/1而已。
- 当我们执行这条sql语句时,Mysql想去修改磁盘,但是磁盘的io实在太慢(即便现在的SSD,也不能解决这实质上的差距),而且大量请求直接请求磁盘操作,磁盘可能也扛不住。
- Mysql又想将数据读到内存操作,但这也不安全,断电即完蛋。
方案
最后Mysql采取了一种双写策略,既写内存,又写磁盘:
-
Mysql先把磁盘上的数据加载到内存中(根据磁盘的局部性原理,触发预读-见下文),在内存中对数据进行修改,再刷回磁盘上。为了避免宕机对内存数据的影响。Mysql决定在事务提交前就把数据刷到磁盘。
又又有问题了: -
上边的数据我只修改账户余额,但是由于引擎对于磁盘数据是以页为单位的,我可能需要将几个页的数据全部刷入磁盘,太浪费资源了,磁盘表示不服。
-
每个Mysql事物中可能涉及多个数据页的修改,而这些数据页可能在磁盘上不是相邻的,也就是属于随机IO,效率很低,磁盘表示不爽。
redo机制
Mysql又灵光一现决定采用redo log解决上面的问题。当做数据修改的时候,不仅在内存中操作,还会在redo log中记录这次操作。
-
当事务提交的时候,会将redo log日志进行刷盘(redo log一部分在内存中,一部分在磁盘上)。
-
当数据库宕机重启的时候,会将redo log中的内容恢复到数据库中,再根据undo log和binlog内容决定回滚数据还是提交数据。
-
采用redo log的意义呢,其实就是将redo log进行刷盘比对数据页刷盘效率高。
1、redo log体积小,毕竟只记录了哪一页修改了啥,因此体积小,刷盘快。
2、redo log是一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。又又又有问题了:
如果将数据操作写入了内存,但是在redo log内存还未同步到磁盘的时候系统宕机了,怎么办。 这个时候两阶段提交就来了。
两阶段提交:
过程
- 写入:redo log(prepare)
- 写入:binlog
- 写入:redo log(commit)
背景
先写 redo log,再写 binlog。这样会出现 redo log 写入到磁盘了,但是 binlog 还没写入磁盘,于是当发生容灾恢复时,主库会应用 redo log,恢复数据,但是由于没有 binlog,从库就不会同步这些数据,主库比从库“新”,造成主从不一致。反之一样会有这种隐患。
流程
如果 redo log 已经是 commit状态,那MySQL就会把事务提交
如果 redo log 处于 prepare状态,则去判断事务对应的 binlog 是不是完整的
- 是,则把事务提交
- 否,则事务回滚
意义
两阶段提交,其实是为了保证 redo log 和 binlog 的逻辑一致性。也是 innodb 在实现高性能写数据的同时实现事务的持久性。
局部性原理:
局部性原理是指CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中,所以一个数据被访问的时候,其临近的数据很大几率也会被使用。
mysql的预读机制:
预读价值:
-
mysql的B+树索引结构优于其他查询结构,很大程度上就是其对于局部性原理的完美契合。
-
InnoDB在I/O的优化上一个比较重要的特性就是预读。
预读流程:
1、根据局部性原理MySQL会异步地在缓冲池中预先回迁多个页面。InnoDB以64个page为一个extent。
2、数据库请求数据的时候,会将读请求交给文件系统,放入请求队列中。
3、相关进程从请求队列中将读请求取出,根据需求到相关数据区(内存、磁盘)读取数据。
4、取出的数据,放入响应队列中,最后数据库就会从响应队列中将数据取走,完成一次数据读操作过程。
5、接着进程继续处理请求队列,(如果数据库是全表扫描的话,数据读请求将会占满请求队列),判断后面几个数据读请求的数据是否相邻。
6、再根据自身系统IO带宽处理量,进行预读,进行读请求的合并处理,一次性读取多块数据放入响应队列中,再被数据库取走。
参考书籍:
《MySQL技术内幕:InnoDB存储引擎》
《高性能MySQL第三版》