MySQL 可以分为 Server 层和存储引擎层两部分。
在大多数情况下不建议使用,因为弊大于利,查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。对于更新压力大的数据库来说,查询缓存的命中率会非常低。注意,MySQL 8.0 版本直接将查询缓存的整块功能删掉了,也就是说在MySQL 8.0 版本不会再有这个功能了。
如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。那应该怎么做呢?
为解决该问题,MySQL采用了WAL技术。WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。
具体的说,就是当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到到磁盘里面,而这个更新往往是在系统比较空闲的时候做。
InnoDB 的 redo log 是固定大小的,它从头开始写,写到末尾就又回到开头循环写。
write pos 是当前记录的位置,一边写一边后移。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。
redo log与binlog的不同:
我们可以看到,上面redo log 的写入拆成了两个步骤:prepare 和 commit,这就是"两阶段提交"。
为什么必须有“两阶段提交”呢?这是为了让两份日志之间的逻辑一致。
当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。
SQL 标准的事务隔离:
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库。
每条记录在更新的时候都会同时记录一条回滚操作。同一条记录在系统中可以存在多个版本,这就是数据库的多版本并发控制(MVCC)。
索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。一本 500 页的书,如果你想快速找到其中的某一个知识点,在不借助目录的情况下,那我估计你可得找一会儿。同样,对于数据库的表而言,索引其实就是它的“目录”。
在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。又因为前面我们提到的,InnoDB 使用了 B+ 树索引模型,所以数据都是存储在B+ 树中的。B+ 树能够很好地配合磁盘的读写特性,减少单次查询的磁盘访问次数。
每一个索引在 InnoDB 里面对应一棵 B+ 树。
区别:主键索引只要搜索ID这个B+Tree即可拿到数据。普通索引先搜索索引拿到主键值,再到主键索引树搜索一次(回表)
select * from T where k between 3 and 5有回到主键索引树搜索的过程,我们称为回表。我们发现,由于查询结果所需要的数据只在主键索引上有,所以不得不回表。那么,有没有可能经过索引优化,避免回表过程呢?
如果执行的语句是 select ID from T where k between 3 and 5,这时只需要查 ID 的值,而 ID 的值已经在 k 索引树上了,不需要回表。我们称其为覆盖索引。
由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
B+ 树这种索引结构,可以利用索引的“最左前缀”,来定位记录。
因为可以支持最左前缀,所以当已经有了 (a,b) 这个联合索引后,一般就不需要单独在 a 上建立索引了。因此,第一原则是,如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。
在MySQL5.6之前,只能从根据最左前缀查询到ID开始一个个回表。到主键索引上找出数据行,再对比字段值。MySQL5.6引入的索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。