1 体系架构
理论内容阅读了mysql体系架构剖析,其他的根据岁月云的实战进行记录。
1.1 连接层
mysql最上层为连接服务,引入线程池,允许多台客户端连接,主要工作:连接处理、授权认证、安全防护、管理连接等。
连接处理:每个客户端连接都会分配一个线程,由连接池管理。
授权认证:对客户端的身份验证,基于用户名、主机信息、密码。如指定用户,%表示主机都可以访问,也可以限定网段或具体ip
安全防护:验证客户端查询权限,如下图对psi用户限制只能访问psi数据库。
1.1.1 show PROCESSLIST
查看当前连接信息。下图中TIme表示客户端从建立连接到现在的时间,
1.1.2 max_connections
下图是我的生产环境配置,临时调整set global max_connections=5000,有效期命令执行开始,宕机关闭结束。
1.1.3 timeout相关参数
下面的单位为秒(s),wait_timeout默认为8小时,没有使用就释放。
1.2 服务层
服务层用于处理核心服务:如标准sql接口、查询解析、SQL优化和统计、全局和引擎依赖的缓存与缓冲器等。
1.2.1 查询缓存
在应用中使用二级缓存,就没必要使用mysql的查询缓存,因为耗费的是mysql的内存,势必对业务产生较大影响。不想一级缓存caffeine可以通过应用分布式来缓解压力,二级缓存redis自身由独立的内存服务器或虚拟机,而mysql不一样的。是啥情况会反复执行相同的sql呢?如果出现这种情况,前端就需要作防抖处理了。故不推荐使用查询缓存。因此mysql8.0+把查询缓存去掉了。
1.2.2 SQL解析器
通过词法分析、语法分析将sql翻译为mysql能识别的结构。这个好比产品经理要把用户需求翻译成为产品需求的逻辑。
1.2.3 查询优化器
- 等价变换策略:如:where 1=1 and as_id=1001等价于where as_id=1001,早在2012年很多系统都是这么干的where 1=1,为了方便添加条件,都这么写,引擎优化会作转换
- 优化count、min、max:因为mysql索引采用的B+树,树的左子节点比当前节点小,右子节点比当前节点大,根据这个原则,min只需要查找索有最左边即可,max只需要查询最右边,这样就很快了。对于count,InnoDB 必须实际扫描索引或表来计算
COUNT。
- 提前终止查询:limit来终止后面数据的查询
- in的优化:mysql对in查询会进行排序,然后采取二分查找数据,提升查找效率。
- 条件查询:先根据where条件选择,再根据字段进行属性投影,然后形成最终的查询结果。
- 连接查询:决定使用那个索引
1.3 存储引擎层
存储引擎直接与数据交互。为上层提供接口,以屏蔽不同存储引擎的差异。
下面是mariadb的,跟mysql还是有区别。InnoDb中Supports transactions, row-level locking, foreign keys and encryption for tables代表支持事务、行级锁、外键、表加密。
mysql5.7如下,InnoDb中Supports transactions, row-level locking, and foreign keys代表支持事务、行级锁、外键。
mysql中InnoDB存储引擎,所有数据逻辑存储空间叫做表空间,表空间对应物理结构是磁盘中的文档、日志、数据等。表空间的逻辑组织:段(segment)、区(extend)、页(page,也称为块)。
表空间中的段组成:数据段、索引段、回滚段。
区:由页组成,每个区大小为1M,innodb每次从磁盘一次申请4~5个区,页大小16K,一个区由64个页。
1.4 物理存储层
文件的物理存储层(磁盘),binlog日志、数据文件、错误日志、慢查询日志、redo/undo日志(事务)等。
查看数据目录
mysql中sys数据库,可以通过视图的形式把information_schema和performance_schema两者结合起来。
2 SQL运行机制
这个不是我的图,请看上面那位博主的解读,这里过多不解释。
2.1 半双工通讯
mysql客户端/服务端通讯协议是半双工的,即要么客户端->服务端,要么服务端->客户端,这两个操作不能同时发生。一端发送消息,另一端要接收完整的消息才能响应,消息无法切割小块,也就不能进行流量管控。
如果请求的sql语句或者响应结果特别大,服务器会拒绝并抛出异常。通过max_allowed_packet参数来调整,我的生产环境设置的128M,这种情况一般会发生在批量插入或者将文件内容写入到数据库的情况。
2.2 sql执行流程
用户管理对用户进行授权, 访问控制模块用于表数据访问进行鉴权,表管理模块缩表管理,从meta信息判断表属于那种存储引擎。
3 InnoDB存储引擎
InnoDB平衡了高可靠性和高性能,也就是说mysql满足的是CA(一致性+可用性)。
3.1 架构
InnoDB的结构如下:
3.2 内存结构
设计保证高性能。
3.2.1 buffer pool
缓存表和索引数据,以数据页为单位,每页16KB,官方建议专用mysql数据库服务器,80%的物理内存通常分配给缓冲池。缓冲池针对LRU算法进行淘汰优化。
我的电脑设置了26G。
- 预读机制
线性预读单位为extend,一个extend中有64个页,innodb_read_ahead_threshold=56,代表连续访问一个extend的56个页面之后把下一个extend读入buffer pool中,这个参数不能超过64,默认56.
随机预读默认为off,因为mysql5.5已废弃,故不启用,因为影响性能。
- Page
Free Page:未被使用的页,位于free链表,用于缓冲池初始化(mysql启动时)时申请占用连续的空间。
clean Page:已被使用的页,但页面没发生修改,位于LRU链表
dirty Page:因为写入内存中,使得内存数据与磁盘不一致数据,发生变更的row所在的位置就是dirty page,内存的数据同步到磁盘后,就恢复为clean page
- 自适应hash索引
innodb中自适应hash索引使得mysql性能更接近于内存服务器。特点时降低二级索引树的频繁访问,自适应。缺点:占用innodb buffer pool,只适合是二级索引等值查询。
因为二级索引由索引列和主键列两列组成,使得自适应hash索引成为可能。当二级索引被频繁访问(最近连续3次被访问),innodb将使用索引前缀建立自适应hash索引,将索引值转换为一种指针,可以直接访问提升效率。
3.2.2 Page Directory
mysql的工程师经过大量测试,设置每隔6个数据为会有一个插槽,这个slot指定了一条记录,确实很像跳表,这样查询起来更高效。看来还是要了解一些优秀产品的设计理念,这样在做其他软件的时候也可以借鉴。这是个很好的设计。
3.2.3 LRU列表
Mysql中buffer Pool对LRU算法进行了优化,LRU列表是一个双向链表,设计了老区和新区,乍一听跟jdk1.8的内存模型有点像。为什么要这么设计,因为Mysql有预读操作,提前把页写入缓存池,但最终可能没有读取到,这个就是预读失效的情况。另外当全面扫码大量数据时,buffer中热点数据将被全部替换出去,此时mysql性能必然急剧下降,这个是缓冲池污染的问题。
New Sublist新生代、Old Sublist老生代,中间点是新生代与老生代的边界处,因为有预读,所以数据先写入中间点,真正读取的时候才会拿到LRU列表的头部。如果没有被访问过,则移动到Old sublist中。
缓冲池污染问题处理:老生代停留时间窗口(T),插入到old sublist即使立刻被访问,也不会立即放入new sublist头部,只有满足被访问并且在老生代停留时间>T,才会被放入新生代头部。
新老占比参数,如下37表示老快占比37%。innodb_old_blocks_time代表老生代停留时间窗口,单位为毫秒(ms)
3.2.4 change buffer
对于唯一索引,数据变更时在buffer pool中直接更改,针对普通索引,会将更新记录到change buffer,先判断是否在缓冲池,如果不在则将变更记录到change buffer,然后定期将数据页到内存中合并,再写入buffer pool中。也就是说要想数据写入到磁盘,必须将数据更新到buffer pool中。详细流程请看那位博主的视频,他讲的很详细。
写buffer pool是为了减少io,而changer buffer是非唯一索引,数据是分散的,故需要统一写入buffer pool。
all表示缓冲区插入、删除标记操作和清楚。innodb_change_buffer_max_size表示change buffer占buffer pool的百分比,一般最大设置为50%,读多写少应该减少。
3.2.5 log buffer
日志缓冲区,用于记录innodb引擎日志,在DML(增、删、改)操作中产生redo和undo日志。我的生产环境日志缓冲区大小为4M。并没有使用默认的16M。
缓冲区调小,是针对资源有限的情况,我们生产环境参数调小是不对的。因为日志文件满了就会做刷盘操作,那么势必增加了磁盘IO,同时对于大事务处理性能也会下降。因此这个值最后我今天写笔记的时候发现并更正过来。innodb_log_buffer_size 是一个静态变量,意味着必须要重启mysql才可以。
日志刷新频率,innodb_flush_log_at_trx_commit默认为1,但我的生产环境调整为2,
- 0:表示每隔1s写日志文件和刷盘操作,最多丢失1s数据;
- 1:事务提交,立刻写日志文件和磁盘,数据不丢失,但会频繁IO
- 2:事务提交,立即写日志文件,每隔1s进行刷盘操作。
3.4 磁盘结构
设计保证可靠性。