前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mysql 基于innoDB的一篇总结

Mysql 基于innoDB的一篇总结

作者头像
Check King
发布2021-08-09 10:45:47
2450
发布2021-08-09 10:45:47
举报

Mysql如何做到高可用

  • crash-safe

mysql 为了保证crash-safe, 是通过引入binlog(server 层的逻辑日志), redo log(innodb 存储引擎层日志), undo log(innodb 存储引擎层日志)来保证的。

binlog与存储引擎无关,每次对binlog日志的新增都是append, binlog可用于mysql的replica, 也可用于我们将MySQL的数据恢复到之前的某一个时刻。redo log是innodb存储引擎层的物理操作记录,记录把某个数据页中地址对应的数据修改什么值,为了能够回滚所有的操作,每条redo log会伴随一条undo log。 redo log的存储是一个环形的,不是无限append的。

为了保证crash-safe, 数据的提交过程是两阶段提交。

  1. 执行器通过引擎拿到数据行(可能需要读磁盘)
  2. 引擎将更新后的数据写入内存,并记录redo log, 此时redo log处于prepare 状态
  3. 执行器生成这个操作的binlog, 并append到磁盘
  4. 执行器调用引擎执行commit, redo log状态变成已提交,更新完成。

如果写binlog之前crash。MySQL恢复后,检查redo log和binlog不完备,此时根据undo log执行回滚,数据一致。

如果将redo log状态改为commit之前crash。MySQL恢复后,检查redo log和binlog,此时如果binlog不完整,则根据undo log回滚,数据一致;如果binlog完整,将redo log状态置为commit,数据一致。

  • 集群化

首先我们看下主备一致

主库A有一个专门的线程dump_thread, 专门服务于从库B。从库B通过change master命令连上主库,通过执行start slave命令,启动两个线程io_thread, sql_thread, 分别用于与主库建立连接和读取主库的binlog并应用到自己的db数据里。

上图是一个M-S的主从架构,实际生产中是互为主备的。 如果Master挂了,Slave就变成主,对外提供写操作。互为主备的时候,我们需要考虑如何避免数据循环复制。一个解决方案就是生成binlog的时候,需要记录server对应的server id, 每个server在启动的时候分配一个唯一的server id。这样如果接收binlog的服务发现binlog的server id和自己server id一样,就会忽略掉。

然后我们再看下主备切换。

当一些异常情况主库不可用了,我需要将备库升级为主,但是需要考虑主备延迟的情况,如何操作,有两种思路:

可靠性优先原则

  1. 判断备库 B 现在的 seconds_behind_master,如果小于某个值(比如 5 秒)继续下一步,否则持续重试这一步;
  2. 把主库 A 改成只读状态,即把 readonly 设置为 true;
  3. 判断备库 B 的 seconds_behind_master 的值,直到这个值变成 0 为止;
  4. 把备库 B 改成可读写状态,也就是把 readonly 设置为 false;
  5. 把业务请求切到备库 B。

可用性优先原则

如果强行把步骤 4、5 调整到最开始执行,也就是说不等主备数据同步,直接把连接切到备库 B,并且让备库 B 可以读写,那么系统几乎就没有不可用时间了。这么做的代价是可能会出现数据不一致。

  • 读写分离

在生产环境中,一般会让MySQL主Server提供线上实时业务的读写服务,备Server提供只读服务以减轻对主Server的压力。一般情况下,我们默认能够容忍从备库读到的数据较主库可能有一定的延迟,但在某些场景下,我们需要备库的数据严格与主库一致,应该如何操作呢?

备选的方案有:(1)从主库读数据;(2)延迟n秒读备库;(3)半同步+seconds_behind_master来判断主从是否已同步。

Mysql如何做到高性能

  • 通过WAL提高写操作的性能

MySQL通过写redo log避免直接写磁盘,大大提高了写入速度,这个技术就是Write-Ahead Logging。在InnoDB觉得时机“适当”时,才会把redo log的内容更新到磁盘。有4个时机: 1) 系统空闲时 2) redo log快被写满时 3) 内存快被写满时 4)Mysql关闭时。

  • Buffer Pool 与LRU

MySQL直接读写其实是操作的内存数据页,这些内存数据页被Buffer Pool所管理。Buffer Pool除了能让数据的更新操作更快,也能让数据的读取速度更快。和我们常见的cache一样,MySQL会首先从Buffer Pool读取数据,所以发现数据页缺页,就会从磁盘load数据页到Buffer Pool。为了提高Buffer Pool命中率,Buffer Pool在因为加载新数据页而空间不足时,就需要淘汰掉某些数据页。InnoDB管理Buffer Pool使用了LRU算法。普通的LRU算法这里就再详细介绍了,但是在MySQL的场景中,LRU算法可能存在一个问题:假如我现在查询的结果包含了大量数据,这些数据可能把Buffer Pool的数据都淘汰掉,但这个查询的结果可能只用一次就不再用到了,这样会导致Buffer Pool命中率下降且频繁换页。

InnoDB按照5:3的比例将LRU队列分为了young区和old区。上图的LRU old指向的是old区的起始位置,是整个链表的5/8处。优化之后的LRU算法如下:

1. 如果访问的数据在young区,将数据插入到链表头部,返回数据。

2. 如果访问的数据在old区,如果数据待在old区的时间超过了参数(innodb_old_blocks_time)设定的时间,将数据插入到链表头部,然后返回数据;否则,数据所处的位置不变,返回数据。

3. 如果访问的数据在LRU队列中不存在,则将数据插入到LRU old的位置,即old区的起始位置,并返回数据。

通过上述这种策略,就可以保证在查询大量数据时,不会导致Buffer Pool命中率急剧下降。

  • 并行复制策略

在mysql 5.6之前,主从同步是单线程复制, 5.6之后,支持多线程复制,提高了复制效率。

多版本控制

多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式。

InnoDB为了实现事务的隔离,需要在每个事务启动时保存一个“数据快照”,而这个数据快照不可能真的把整个数据拷贝一份。因此这里使用了一个“技巧”:版本号。InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。

MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版本号:(1)创建版本号:创建一行数据时,将当前系统版本号作为创建版本号赋值;(2)删除版本号:删除一行数据时,将当前系统版本号作为删除版本号赋值。如果该快照的删除版本号大于当前事务版本号表示该快照有效,否则表示该快照已经被删除了。

这里还需要定义两个概念“快照读”和“当前读”。快照读是指事务开始时保存了一个快照,后续读取的数据都是取自这个快照;当前读是指读取数据时需要检查数据当前的最新值,必要时还需要加锁。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Mysql如何做到高可用
  • Mysql如何做到高性能
  • 多版本控制
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档