MySQL性能调优(7)复习补充
复习时自己产生的问题
log
缓冲池 Buffer Pool
首先,InnnoDB 的数据都是放在磁盘上的,InnoDB 操作数据有一个最小的逻辑单位,叫做页(索引页和数据页)。我们对于数据的操作,不是每次都直接操作磁盘,因为磁盘的速度太慢了。InnoDB 使用了一种缓冲池的技术,也就是把磁盘读到的页放到一 块内存区域里面。这个内存区域就叫 Buffer Pool,下一次读取相同的页,先判断是不是在缓冲池里面,如果是,就直接读取,不用再 次访问磁盘。 修改数据的时候,先修改缓冲池里面的页。内存的数据页和磁盘数据不一致的时候, 我们把它叫做脏页。InnoDB 里面有专门的后台线程把 Buffer Pool 的数据写入到磁盘, 每隔一段时间就一次性地把多个修改写入磁盘,这个动作就叫做刷脏。 Buffer Pool 是 InnoDB 里面非常重要的一个结构,它的内部又分成几块区域。这里 我们趁机到官网来认识一下 InnoDB 的内存结构和磁盘结构。
Buffer Pool
|
|
Change Buffer 写缓冲
如果这个数据页不是唯一索引,不存在数据重复的情况,也就不需要从磁盘加载索 引页判断数据是不是重复(唯一性检查)。这种情况下可以先把修改记录在内存的缓冲 池中,从而提升更新语句(Insert、Delete、Update)的执行速度。把 Change Buffer 记录到数据页的操作叫做 merge。在访问这个数据页的时候,或者通过后台线程、或者数据库 shut down、 redo log 写满时触发merge。
如果数据库大部分索引都是非唯一索引,并且业务是写多读少,不会在写数据后立 刻读取,就可以使用 Change Buffer(写缓冲)。写多读少的业务,调大这个值。 Change Buffer 占 Buffer Pool 的比例,默认 25%
Adaptive Hash Index
索引应该是放在磁盘的。InnoDB本身不支持哈希索引,所有索引检索都走B树,Adaptive Hash index可以认为是“索引的索引”。哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。
(redo)Log Buffer
如果 Buffer Pool 里面的脏页还没有刷入磁盘时,数据库宕机或者重 启,这些数据丢失。如果写操作写到一半,甚至可能会破坏数据文件导致数据库不可用。为了避免这个问题,InnoDB 把所有对页面的修改操作专门写入一个日志文件,并且 在数据库启动时从这个文件进行恢复操作(实现 crash-safe)——用它来实现事务的持久性。这 种 日 志 和 磁 盘 配 合 的 整 个 过 程 , 其 实 就 是 MySQL 里 的 WAL 技 术 (Write-Ahead Logging),它的关键点就是先写日志,再写磁盘。
|
|
问题:Redo log 和db文件都是在磁盘上的,为什么写redo log 是顺序io 写db文件就是随机IO呢?
buffer pool中有很多数据等待刷脏的时候,写入redo log是顺序写入的。 而这些数据在磁盘中的位置不是连续的,每次都要重新寻址,所以是随机I/O。 数据写入到redo log里面就有了保障,刷盘就不需要那么频繁了,提升了系统的吞吐量。MySQL很多地方都利用的这一点
双写缓冲问题
InnoDB 的页和操作系统的页大小不一致,InnoDB 页大小一般为 16K,操作系统页 大小为 4K,InnoDB 的页写入到磁盘时,一个页需要分 4 次写。如果存储引擎正在写入页的数据到磁盘时发生了宕机,可能出现页只写了一部分的 情况,比如只写了 4K,就宕机了,这种情况叫做部分写失效(partial page write),可能会导致数据丢失,如果这个页本身已经损坏了,用redo log来做崩溃恢复是没有意义的。所以在对于应用 redo log 之前,需要一个页的副本。如果出现了写入失效,就用页的副本来还原这个页,然后再应用 redo log。这个页的副本就是 double write,InnoDB 的双写技术。通过它实现了数据页的可靠性。
|
|
Binlog
binlog 以事件的形式记录了所有的 DDL 和 DML 语句(因为它记录的是操作而不是 数据值,属于逻辑日志),可以用来做主从复制和数据恢复。 跟 redo log 不一样,它的文件内容是可以追加的,没有固定大小限制。 在开启了 binlog 功能的情况下,我们可以把 binlog 导出成 SQL 语句,把所有的操 作重放一遍,来实现数据的恢复。 binlog 的另一个功能就是用来实现主从复制,它的原理就是从服务器读取主服务器 的 binlog,然后执行一遍。
|
|
- 先记录到内存,再写日志文件。
- 记录 redo log 分为两个阶段。
- 存储引擎和 Server 记录不同的日志。
- 先记录 redo,再记录 binlog。
index
页分裂
InnoDB在叶子上存储数据。一个节点就是一页。一个页可以存储多行数据,按照主键的顺序存储。当数据是顺序插入的时候,一页写满了,就申请一个新的页。如果是随机插入,在指定位置的页已经写满了(或者到达了分裂阈值)的时候,就会发生页结构的调整(即B+Tree的节点的分裂)
页的内部原理
页可以空或者填充满(100%),行记录会按照主键顺序来排列。这里有个重要的属性:MERGE_THRESHOLD
。该参数的默认值是50%页的大小,它在InnoDB的合并操作中扮演了很重要的角色
当你插入数据时,如果数据(大小)能够放的进页中的话,那他们是按顺序将页填满的。根据B树的特性,它可以自顶向下遍历,但也可以在各叶子节点水平遍历。因为每个叶子节点都有着一个指向包含下一条(顺序)记录的页的指针。那么如果我突然执行删除,页会出现什么情况?
页合并
当你删了一行记录时,实际上记录并没有被物理删除,记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用。当页中删除的记录达到MERGE_THRESHOLD
(默认页体积的50%),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。
页分裂
当前页有空间但是容纳不下我要插入的数据时。下一页又是满的无法插入数据时。
|
|
索引用到文件排序 Using filesort
|
|
为什么要固定Page大小,而不是需要多少数据,读取多少数据(按需读取)为什么16k
|
|
表没主键索引问题
|
|
辅助索引什么情况下查询需要回表,什么情况下不需要回表
|
|
事务与锁
共享锁 有什么意义呢
|
|
innodb有了MVCC为什么还需要LBCC
|
|
MySQL ACID怎么保证
|
|
优化
连接池大小应该设置多大
|
|
连接数 = ((核心数 * 2) + 有效磁盘数)
当然,这取决于线上环境,开发还是设置大一点,因为大家都在联调测试。
Count问题
|
|
COUNT(*)和COUNT(1)和count(字段)区别
InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference. 官方发话
画重点:same way,no performance difference。所以,对于COUNT(1)和COUNT(*),MySQL的优化是完全一样的,根本不存在谁比谁快!
建议使用COUNT(*)
!因为这个是SQL92定义的标准统计行数的语法。
COUNT(字段)
多了一个步骤就是判断所查询的字段是否为NULL所以他的性能要比COUNT(*)
慢。
存储引擎
InnoDB和MyISAM区别
- InnoDB 支持事务,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一
- InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败
- InnoDB 是聚集索引数据跟索引存放在一起,MyISAM 是非聚集索引数据和索引分开。
- InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。
- 由于索引分开的MyISAM 在内存中存储了row_count 值的 meta 信息可以直接获取到总行数
底层使用B+树不用B树
- AVL(平衡二叉树)树解决了索引频繁的修改,和查询效率不高的问题。
- B树相对AVL树,每个节点存储的数据更多,路数更多,树的深度减少,减少I/O次数,提升效率。
- B+树相对B树,效率更稳定,因为数据存在叶子节点;排序能力更强,因为叶子节点有下一个数据区的指针;读写能力更强,因为根节点和枝节点不用存数据,所以可以保存更多的关键字。扫库扫表能力更强,因为数据都存在叶子节点,而且叶子节点都有下一个数据区的指针,所以遍历起来很方便。
B+树数据存放量问题
|
|