
索引下推:

ACID
- 原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节,而且事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样,就好比买一件商品,购买成功时,则给商家付了钱,商品到手;购买失败时,则商品在商家手中,消费者的钱也没花出去。
- 一致性(Consistency):是指事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。比如,用户 A 和用户 B 在银行分别有 800 元和 600 元,总共 1400 元,用户 A 给用户 B 转账 200 元,分为两个步骤,从 A 的账户扣除 200 元和对 B 的账户增加 200 元。一致性就是要求上述步骤操作后,最后的结果是用户 A 还有 600 元,用户 B 有 800 元,总共 1400 元,而不会出现用户 A 扣除了 200 元,但用户 B 未增加的情况(该情况,用户 A 和 B 均为 600 元,总共 1200 元)。
- 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致,因为多个事务同时使用相同的数据时,不会相互干扰,每个事务都有一个完整的数据空间,对其他并发事务是隔离的。也就是说,消费者购买商品这个事务,是不影响其他消费者购买的。
- 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?
持久性是通过 redo log (重做日志)来保证的;
原子性是通过 undo log(回滚日志) 来保证的;
隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;
一致性则是通过持久性+原子性+隔离性来保证;
并行事务会发生的问题
- 脏读:读到未提交事务的数据
- 不可重复读****在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了「不可重复读」现象。
- 幻读****在一个事务内多次查询某个符合查询条件的「记录数量」,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了「幻读」现象。
四种隔离级别
- 读未提交(*read uncommitted*),指一个事务还没提交时,它做的变更就能被其他事务看到;
- 读提交(*read committed*),指一个事务提交之后,它做的变更才能被其他事务看到;
- 可重复读(*repeatable read*),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别;
- 串行化(*serializable* );会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;
mysql读取数据实际上有两种读取模式:
当前读和快照读
- 当前读:每次读取的都是当前最新的数据,但是读的时候不允许写,写的时候也不允许读。(select ..for update\update、insert、delete)
- 快照读:读写不冲突,每次读取的是快照数据,

Read View中四个字段作用

聚簇索引记录中的两个隐藏列

·trx id,当一个事务对某条聚簇索引记录进行改动时,就会把该事务的事务id记录在trx id隐藏列里;
·roll_pointer,每次对某条聚簇索引记录进行改动时,都会把旧版本的记录写入到undo日志中,然后这个隐藏列是个指针,指向每一个旧版本记录,于是就可以通过它找到修改前的记录。
索引失效

也就是范围查询的字段可以用到联合索引,但是在范围查询字段的后面的字段无法用到联合索引。
select * from t_table where a > 1 and b = 2 只用了a,因为第二列是全局无序的
select * from t_table where a >= 1 and b = 2 a,b都用了,从符合 a = 1 and b = 2 条件的第一条记录开始扫描
MySQL优化
- 定位问题SQL:
- 查看慢查询日志:开启慢查询日志功能,记录执行时间超过设定阈值的SQL语句,通过分析这些日志来发现性能较低的SQL。
- 使用性能分析工具:如MySQL自带的Performance Schema等工具,监控数据库的性能指标,快速定位出执行效率低下的SQL语句。
- 分析SQL执行计划:
- 使用EXPLAIN关键字:在查询语句前加上EXPLAIN,查看查询的执行计划,重点关注type、rows、filtered、extra等字段,了解SQL的执行方式和效率。
- 分析关键字段:
- type字段:表示查询的类型,如ALL表示全表扫描,index表示索引全扫描,range表示索引范围扫描,ref表示使用非唯一索引扫描或唯一索引前缀扫描,eq_ref表示使用唯一索引,const/system表示单条记录,null表示不访问任何表或索引。这些类型从上至下,执行效率越来越高。
- rows字段:表示查询预计需要扫描的行数,值越小越好,可评估查询的效率。
- filtered字段:表示查询条件过滤的比例,值越高越好,表示过滤效果越好。
- extra字段:包含一些额外的信息,如Using filesort表示需要额外的排序操作,Using temporary表示使用了临时表,Using index表示使用了覆盖索引,Using index condition表示使用了索引下推等。
三种日志

所以有了redo log,再通过WAL技术,InnoDB就可以保证即使数据库发生异常重启,之前已提交的记录都不会丢失,这个能力称为crash-safe(崩溃恢复)。
脏页刷盘
- redo log写满
- Buffer Pool 不足需要淘汰数据页
- 系统空闲的时候后台会定期flush适量的脏页到磁盘
- MySQL正常关闭(shut down)时会把所有脏页都flush到磁盘
redo log刷盘策略
每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘(这个策略可由 innodb_flush_log_at_trx_commit 参数控制,下面会说)。
binlog刷盘策略
MySQL提供一个 sync_binlog 参数来控制数据库的 binlog 刷到磁盘上的频率:
sync_binlog = 0 的时候,表示每次提交事务都只 write,不 fsync,后续交由操作系统决定何时将数据持久化到磁盘;
sync_binlog = 1 的时候,表示每次提交事务都会 write,然后马上执行 fsync;
sync_binlog =N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。
两阶段提交

从图中可看出,事务的提交过程有两个阶段,就是将 redo log 的写入拆成了两个步骤:prepare 和 commit,中间再穿插写入binlog,具体如下:
prepare 阶段:将 XID(内部 XA 事务的 ID) 写入到 redo log,同时将 redo log 对应的事务状态设置为 prepare,然后将 redo log 持久化到磁盘(innodb_flush_log_at_trx_commit = 1 的作用);
commit 阶段:把 XID 写入到 binlog,然后将 binlog 持久化到磁盘(sync_binlog = 1 的作用),接着调用引擎的提交事务接口,将 redo log 状态设置为 commit,此时该状态并不需要持久化到磁盘,只需要 write 到文件系统的 page cache 中就够了,因为只要 binlog 写磁盘成功,就算 redo log 的状态还是 prepare 也没有关系,一样会被认为事务已经执行成功
可重复读级别未完全解决幻读:
- 此时,事务A更新id=5这条记录,对没错,事务A看不到id=5这条记录,但是他去更新了这条记录,这场景确实很违和,然后再次查询id=5的记录,事务A就能看到事务B插入的纪录了,幻读就是发生在这种违和的场景。
- ·T1时刻:事务A先执行「快照读语句」:selectfrom t_test where id>100得到了3条记录。
·T2时刻:事务B往插入一个id=200的记录并提交;
·T3时刻:事务A再执行「当前读语句」selectfrom t_test where id>100 for update就会得到4条记录,此时也发生了幻读现象。
要避免这类特殊场景下发生幻读的现象的话,就是尽量在开启事务之后,马上执行select…for update这类当前读的语句,因为它会对记录加next-key lock,从而避免其他事务插入一条新记录。