1. 磁盘IO
1.1. 如执行 SQL 语句的时候更新了缓存池中的数据,那么这些数据会马上同步到磁盘上吗?
- 当对数据库中的记录进行修改的时候,首先会修改缓冲池中页里面的记录信息,然后数据库会以一定的频率刷新到磁盘上。并不是每次发生更新操作,都会立刻进行磁盘回写。缓冲池会采用一种叫做 checkpoint 的机制将数据回写到磁盘上,这样做的好处就是提升了数据库的整体性能。
- 当缓冲池不够用时,需要释放掉一些不常用的页,就可以采用强行采用 checkpoint 的方式,将不常用的脏页回写到磁盘上,然后再从缓冲池中将这些页释放掉。这里脏页(dirty page)指的是缓冲池中被修改过的页,与磁盘上的数据页不一致。
2. 数据库中的存储结构
2.1. 数据库的存储空间基本单位是页
- 区是比页大一级的存储结构,在 InnoDB 引擎中,一个区会分配 64 个连续的页。因为 InnoDB 中的页大小默认是 16KB,所以一个区的大小是 64*16KB=1MB。
- 表空间是一个逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。数据库由一个或多个表空间组成,表空间从管理上可以划分为系统表空间、用户表空间、撤销表空间、临时表空间等。
3. InnoDB存储引擎
3.1. InnoDB 关键特性
- 插入缓冲
- 两次写
- 自适应哈希索引
- 异步IO
- 刷新临接页
3.2. 插入缓冲
对于非聚集索引的插入或者更新,不是每一次都直接插入到索引页中,而且先判断插入的非聚集索引页是否在缓存池中。若在,则直接插入;若不在,则先放入到一个Insert Buffer对象中去。数据库这个非聚集的索引已经插入到叶子节点了,而实际上没有,只是存放到了一个位置,然后以一定的频率和情况刷新进行Insert Buffer和辅助索引叶子节点的merge操作。这时通常能将多个插入操作合并到一个操作中(因为在一个索引页中),这就大大的提高了对于非聚集索引插入的性能。
用Insert Buffer的使用需要同时满足以下两个条件
- 索引是辅助索引
- 索引不是唯一的
当满足以上两个条件的时,InnoDB存储引擎会使用Insert Buffer,这样就能提高插入操作的性能了。
不过考虑这样的情况。应用程序进行大量的插入操作,这些都涉及到了不唯一的非聚集索引,也就是使用了Insert Buffer,若此时MySQL数据库发生了宕机,这时势必有大量的Insert Buffer 并没有合到实际的非聚集索引中去。因此此时恢复可能需要很长的时间,在极端情况下甚至需要好几个小时。
3.3. 后台进程
- Master Thread
Master Thread是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲(INSERT BUFFER)、UNDO页的回收等。
- IO Thread
在InnoDB存储引擎中大量使用了AIO(Async IO)来处理写IO请求,这样可以极大提高数据库的性能。而IO Thread的工作主要是负责这些IO请求的回调(call back)处理。InnoDB 1.0版本之前共有4个IO Thread,分别是write、read、insert buffer和log IO thread。在Linux平台下,IO Thread的数量不能进行调整,但是在Windows平台下可以通过参数innodb_file_io_threads来增大IO Thread。从InnoDB 1.0.x版本开始,read thread和write thread分别增大到了4个,并且不再使用innodb_file_io_threads参数,而是分别使用innodb_read_io_threads和innodb_write_io_threads参数进行设置
- Purge Thread
事务被提交后,其所使用的undolog可能不再需要,因此需要PurgeThread来回收已经使用并分配的undo页。
- Page Cleaner Thread
Page Cleaner Thread是在InnoDB 1.2.x版本中引入的。其作用是将之前版本中脏页的刷新操作都放入到单独的线程中来完成。而其目的是为了减轻原MasterThread的工作及对于用户查询线程的阻塞,进一步提高InnoDB存储引擎的性能。
4. sql
4.1. 如何避免写慢sql
- 评估你的 SQL 涉及到的表,它的数据规模是多少?
- 你的 SQL 可能会遍历的数据量是多少?
- 选择合适的索引
- 多表联合查询的时候,尽量小表驱动大表
- 避免大事务,尽量减小事务粒度,尽量注意不同事务对表操作的顺序一致,大事务其实也包含着批量操作的隐式事务,如一个update 影响100万行数据。
- 读写分离