事务隔离级别
- 脏读:更新还未提交,其他事务能读到更新后的
- 不可重复读:事物里对一条数据读两次,读到不一样的(修改)
-
幻读:事物里对结果集读两次,读到不一样的(新增和删除)
- READ_UNCOMMITTED 有脏读 不可重复读 幻读
- READ_COMMITTED 解决脏读 有不可重复读 幻读 (大多数默认 MyISAM)
- REPEATABLE_READ 解决不可重复读 有幻读 (InnoDB默认,可以用Next-Key Lock解决幻读)
- SERIALIZABLE 解决幻读,并发差
不可重复读、幻读例子
不可重复度:
需求:查找与小明所在班级下所有人
执行:
- A事务:根据studentId查找student表得到classId,如3班
- B事务:修改小明班级为3
- A事务:查找3班下所有人,发现小明班级下找不到小明
解决:
mysql下可重复读级别下执行第三步,由于MVVC,在一个事务下仍然能查到小明;
从业务上看此时显示会有小明,但是实际上这时小明已经不在这个班级;数据上没有错误,只是有延迟
幻读:
需求:身份证唯一校验,idCardNum有唯一索引
执行
- 事务A:根据需要添加的身份证idCardNum查找数据库
- 事务B:插入相同idCardNum的记录
- 事务A:没有重复则插入,发现报唯一索引错误
mysql架构
连接器:连接mysql服务器,进行权限验证
缓存:保存上次查询的结果,提高性能
分析器:词法与语法分析
优化器:对你的查询语句做出适当的优化
执行器:操作存储引擎,读写数据
存储引擎:存储数据(myslam innodb memory)
mysql log
redo log & bin log
- redo log,innodb的log,物理日志,存储引擎层的log,记录了“在某个数据页上做了什么修改”,
- bin log,server层的log,逻辑日志,记录了“操作的初始逻辑,upadte、insert、delete”
为什么会有redo log/bin log两份日志呢?
因为最开始MySQL里并没有InnoDB引擎。MySQL自带的引擎是MyISAM,但是MyISAM没有crash-safe的能力,binlog日志只能用于归档。而InnoDB是另一个公司以插件形式引入MySQL的,既然只依靠binlog是没有crash-safe能力的,所以InnoDB使用另外一套日志系统——也就是redo log来实现crash-safe能力。
这两种日志有以下三点不同。
-
redo log是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
-
redo log是物理日志,记录的是“在某个数据页上做了什么修改”;binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。
-
redo log是循环写的,空间固定会用完;binlog是可以追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
有了对这两个日志的概念性理解,我们再来看执行器和InnoDB引擎在执行这个简单的update语句时的内部流程。
-
执行器先找引擎取ID=2这一行。ID是主键,引擎直接用树搜索找到这一行。如果ID=2这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
-
执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
-
引擎将这行新数据更新到内存中,同时将这个更新操作记录到redo log里面,此时redo log处于prepare状态(系统调用write,策略:innodb_flush_log_at_trx_commit)。然后告知执行器执行完成了,随时可以提交事务。
-
执行器生成这个操作的binlog,并把binlog写入文件系统(系统调用write策略:sync_binlog)。
-
执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成。 参考MySQL实战45讲/3讲
innodb_flush_log_at_trx_commit事务提交时写入redo log时机
0不写入,每秒写一次或者满了写
1必须写入(fsync系统调用)事务才提交
2写入文件系统缓存,让文件系统来fsync
sync_binlog控制bin log写入时机:
sync_binlog=0的时候,表示每次提交事务都只write,不fsync;
sync_binlog=1的时候,表示每次提交事务都会执行fsync;
sync_binlog=N(N>1)的时候,表示每次提交事务都write,但累积N个事务后才fsync。
写了redo log、bin log和修改内存的数据页后,事物可以提交
这时内存的数据页为脏页,需要一定的时机flush到磁盘,一般刷新时机有几种:
1 redo log写满了 2内存满了 3mysql空闲时 4mysql正常关闭
innodb_io_capacity参数定义了mysql可全力刷入磁盘的能力,实际刷入磁盘的速度取决于脏页比例和redo log写盘速度
宕机恢复:若处于3、4之间,未写入binlog,则事务回滚;若处于4、5之间,根据xid找到binlog,提交事务;
一般情况下,把生产库改成“非双1”配置,是设置innodb_flush_logs_at_trx_commit=2、sync_binlog=1000。
情景:
业务高峰期、恢复、备份数据(为了让备库尽快赶上主库)
undo log
回滚日志,用于事物回滚操作,还有多版本并发控制(MVCC),解决不可重复读,读事物开始时的版本
innodb高性能特性一 change buffer
-
change buffer作用:
当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB会将这些更新操作缓存在change buffer中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中与这个页有关的操作 -
change buffer + redo log
1若更新的数据页在内存,和chang buffer无关,直接更新内存,在磁盘写入上述的redo log和bin log,事务提交
2更新的数据未在内存,在内存的change buffer区域,记录下“Page xx修改成了page xx’”这个信息,同样在磁盘写入的redo log和bin log
若需要读修改的数据时,先读入磁盘数据,再进行change buffer操作得到最后的数据
redo log 主要节省的是随机写磁盘的IO消耗(转成顺序写),而change buffer主要节省的则是随机读磁盘的IO消耗。
修改唯一索引时,因为要检测数据唯一性,需要先读入磁盘,所以change buffer失效
innodb高性能特性二 double-write
innodb高性能特性三 自适应hash索引
二级索引自动优化为hash索引
innodb高性能特性四 预读
线性预读(读出下一个extent到内存)、随机预读(extent的所有page都预读出来到内存) extent(page数据上再一层封装,page 16Kb,extent 1Mb)