Mysql架构&事务原理与锁机制&MVCC日志(undo log redolog binlog)两阶段提交&组提交
想要进一线互联网大厂公式,MYSQL是绕不开的坎,对于MYSQL的事务隔离机制以及日志相关知识网上众说纷纭,你抄我我抄你,今天我要详细总结一下相关知识,形成完整体系,补充一句,下面所讲全是干货,废话就不多说了,直接讲解知识,欢迎留言评论,不足纠正!
下文主要针对MYSQL的innodb存储引擎。
首先,我们从架构开始说起。mysql分为server层与存储引擎层,server层包含连接器、分析器、优化器、执行器。
接下来以一条sql查询语句执行过程介绍各个部分功能。
客户端执行一条sql:
1、首先由连接器进行身份验证,权限管理
2、若开启了缓存,会检查缓存是否有该sql对应结果(缓存存储形式key-vlaue,key是执行的sql,value是对应的值)若开启缓存又有该sql的映射,将结果直接返回;
4、分析器进行词法语法分析
5、优化器会生成执行计划、选择索引等操作,选取最优执行方
6、然后来到执行器,打开表调用存储引擎接口,逐行判断是否满足查询条件,满足放到结果集,最终返回给客户端;若用到索引,筛选行也会根据索引筛选。
基础知识:
事务:最小不可拆分单元,
事务特性:ACID
原子性:一些列操作要么全部成功,要么全部失败
隔离性:事务的结果只有提交了其他事务才可见
一致性:数据库总时从一个一致状态变到另一个一致状态
持久性:事务提交后,对数据修改永久的
事务的并发问题:
脏读:读到未提交的数据
不可重复读:一个事务下,两次读取数据不一致(侧重内容数据的修改)
幻读:一个事务下,两次读到数据总数不一致,读到了新插入的行(侧重记录行的插入删除)
2.1
隔离级别原理及解决问题分析
读未提交:直接读取数据,不能解决任何并发问题
读已提交:读操作不加锁,写操作加排他锁,解决了脏读,利用MVCC实现,每一句语句执行前都会生成Read View(一致性视图)
可重复读:MVCC实现,只有事务开始时会创建Read View,之后事务里的其他查询都用这个Read View。解决了脏读、不可重复读,快照读(普通查询,读取历史数据)使用MVCC解决了幻读,当前读(读取最新提交数据)通过间隙锁解决幻读(lock in share mode、for update、update、detete、insert),间隙锁在可重复读下才生效。(默认隔离级别)
可串行化:使用锁,读加共享锁,写加排他锁,串行执行
2.2
幻读问题详解
1、创建tx实验表
DROP TABLE IF EXISTS `tx`;
CREATE TABLE `tx` (
`age` int(5) DEFAULT NULL,
`name` varchar(5) DEFAULT NULL,
`id` int(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tx
-- ----------------------------
INSERT INTO `tx` VALUES ('20', '张三', '1');
INSERT INTO `tx` VALUES ('20', '李四', '2');
————————————————
2、
结论(仔细理解,讲收获满满,本人认真总结的):
1、发现RR隔离界别若只快照读与当前读没有幻读问题,快照读(普通查询,如select * from table)读取旧的历史版本,用MVCC实现(MVCC原理下文分析),会在事务开始时生成一个Read View,之后都用这个Read View实现RR隔离级别。当前读(select … for update ,select … lock in share mode ,update/insert/delete语句)读取最新数据版本,依靠间隙锁或则临键锁解决幻读,当你事务T1执行当前读,然后事务T2插入语句,事务T2会被阻塞住,插不进去。
2、当你事务T1中先执行快照读,事务T2插入数据并提交,事务T1再执行当前读(比如以相同条件更新数据),会发现出现幻读,更新到了新插入行的数据(白话文解释:事务1先以某个条件比如age=20的查询得到2条数据,然后事务2插入新的数据age也为20然后提交事务,此时事务1更新age=20的数据,发现更新到了3行,把事务T2新插入的那行也更新了,所以幻读注重你插入新数据都修改改到了新插入的数据,而不可重复读是你修改了某个数据,两次查询得到不一致结果。)
2.4
MVCC实现原理
多版本并发控制。
原理提炼总结:使用版本链+Read View
详解:
版本链
同一行数据可能有多个版本
innodb数据表每行数据记录会有几个隐藏字段,row_id,事务ID,回滚指针。
1、Innodb采用主键索引(聚簇索引),会利用主键维护索引,若表没有主键,就用第一个非空唯一索引,若没有唯一索引,则用row_id这个隐藏字段作为主键索引。
2、事务开启会向系统申请一个事务ID,严格递增,会向行记录插入最近操作它的那个事务的ID
3、undolog会记录事务前老版本数据,然后行记录中回滚指针会指向老版本位置,如此形成一条版本链。因此可以利用undo log实现回滚,保证原子性,同时用于实现MVCC版本链。
Read View
读已提交隔离级别下,会在每次查询都生成一个Read View,可重读读只在事务开始时生成一个Read View,以后每次查询都用这个Read View,以此实现不同隔离界别。
Read View里面包含些什么?
一个数组+up_limit_id(低水位)+low_limit_id(高水位)(这里的up,low没写错,就是这么定义的)
1、数组里包含当前活跃事务ID(未提交事务),低水位就是活跃事务最小ID,高水位就是下一次将分配的事务ID,也就是目前最大事务ID+1。
前置知识,为了保证事务ACID中的一致性与原子性,mysql采用WAL,预写日志,先写日志,合适时再写磁盘。
3.1
undo log
回滚日志
undolog记录事务开始前老版本数据,用于实现回滚,保证原子性,实现MVCC,会将数据修改前的旧版本保存在undolog,然后行记录有个隐藏字段回滚指针指向老版本。
3.2
redo log
物理日志
作用:会记录事务开启后对数据做的修改
特性:空间一定,写完后会循环写,有两个指针write pos指向当前记录位置,checkpoint指向将擦除的位置,redolog相当于是个取货小车,货物太多时来不及一件一件入库太慢了这样,就先将货物放入小车,等到货物不多或则小车满了或则店里空闲时再将小车货物送到库房。用于crash-safe,数据库异常断电等情况可用redo log恢复。
写入流程:先写redo log buffer,然后wite到文件系统的page cache,此时并没有持久化,然后fsync持久化到磁盘
写入策略:
根据innodb_flush_log_at_trx_commit参数控制(我的记忆:innodb以事务的什么提交方式刷新日志)
0——>事务提交时只把redo log留在redo log buffer
1——>将redo log直接持久化到磁盘(所以有个双“1”配置,后面会讲)
2——>只是把redo log写到page cache
3.3
bin log
用于主备同步
有3种格式:
1、row:记录整行数据,更新记录更新前后的数据
缺点:记录每行数据,占空间
2、statement:记录整条sql语句
缺点:可能造成主从不一致
如:mysql> delete from t where a>=4 and b<=5 limit 1;
主库是索引a,那么删除a=4
备库是索引b,那么删除b=5
3、mixed:会判断statement格式下sql语句是否会造成主备不一致,不造成就statement格式,否则就row格式
写入机制:
1、事务执行过程中将日志记录到binlog cache(系统为binlog分配了一块内存,每个线程一份)
2、事务提交时,执行器把binlog cache里的完整事务写入到binlog中,并清空binlog cache
write:把日志写到文件系统的page cache,没有写磁盘,速度快
fsync:将数据持久化到磁盘的操作,这时才占磁盘IOPS
根据sync_binlog参数控制:
0——>只write,不fsync
1——>每次fsync
N>1——>每次事务都write,等累积到N后才fsync,可以将sync_binlog设置大一点提高性能(可以提高IO性能,但是若发生异常,日志会丢失)
这里sync_binlog和innodb_flush_log_at_trx_commit配和设置双1模式
想要全面了解两阶段提交,我接下从这3个方面分析:
1、何为两阶段提交?
2、为什么要两阶段提交?
3、两阶段提交的过程是怎样的?
何为两阶段提交?(2PC)
mysql中在server层级别有个binlog日志,归档日志,用于备份,主从同步复制,如果采用一主多从架构,主备切换,那就必须用到binlog进行主从同步,此时事务提交就必须保证redolog与binlog的一致性,一般情况没有开启binlog日志,事务提交不会两阶段提交,若需要主从同步就必须开启binlog使用两阶段提交保证数据一致性。
为什么要两阶段提交?
保证redolog与binlog一致性,保证事务在多个引擎的原子性。
两阶段提交过程?
Prepare 阶段:InnoDB 将回滚段undolog设置为 prepare 状态;将 redolog 写文件并刷盘;
Commit 阶段:Binlog 写入文件;binlog 刷盘;InnoDB commit;
redolog与binlog怎样联系起来的?(XID)
崩溃恢复的时候,会按顺序扫描redo log,若redolog既有prepare又有commit,直接提交,如果碰到只有parepare、而没有commit的redo log,就拿着XID去binlog找对应的事务。
转载请注明:XAMPP中文组官网 » Mysql架构_事务原理与锁机制_MVCC日志