读书笔记:Mysql实战45讲 (22-35讲)

22、MySQL有哪些 饮鸩止渴 提高性能的办法?

    场景: 业务高峰期,生产环境的MySQL压力太大,没法正常响应,需要短期内、临时性地提高一些性能。但,如果是无损的方案的话,肯定不需要等到这个时候才上场 

   短连接风暴:

        正常的短连接模式就是连接到数据库,执行很少的SQL语句就断开,下次需要的时候再建立连接的过程,成本是很高的。除了正常的网络连接三次握手外,还要登陆权限判断和获取这个连接的数据读写权限。但是短连接存在一个风险,就是一旦数据库处理得慢一些,连接数就会暴涨。max_connections参数,用户控制一个MYSQL实例存在连接数的上线。

        一个比较自然想法就是调高这个值,但是有风险,改的太大让很多连接可以进来,那么系统的负载就会进一步加大,大量的资源消耗在权限认证上,结果可能适得其反,已经连接的线程拿不到CPU资源去执行业务的SQL请求。

        第一个办法:先处理那些占着连接但是不工作的线程

          max_connectons的计算,是只要连着就要占着一个计数位置。对于不需要保持连接,直接kill掉(这个和设置wait_timeout效果一样,标识一个线程空闲wait_timeout这么多秒以后,就会被MySQL直接断开连接)

     可以从show processlist结果看,sleep线程的两种状态,而要看具体状态,查看information_schema库的innodb_trx表

     

     可以优先断开事务外空闲太久的连接;如果这样还不够,可以考虑断开事务内空闲太久的连接。从服务端断开连接使用的是kill connection +id 命令。一个客户端处于sleep状态,它的连接被服务端断开后,这个客户端并不会马上知道。知道客户端发起下一个连接收到被断开的消息。

  第二个办法:减少连接过程的消耗

    有些业务代码会在段时间内先大量申请数据连接做备用,如果现在数据确认是被连接行为打挂了,那么一种可能做法,就是让数据跳过权限认证阶段

    跳过权限认证的办法就是:重启数据库,并使用-

参数启动。整个MySQL会跳过所有权限认证阶段,包括连接过程和语句执行过程在内。(优先极高,尤其你的库外网可以访问的话)

   在MySQL 8.0版本,如果启用-skip-grant-table参数,MySQL默认把--skip-networking参数打开,表示这个时候数据库只能被本地客户端连接。

慢查询性能问题:大体有三种可能

1、索引没有设计好

2、SQL语句没写好

3、MySQL选错了索引

索引没有设计好:

  这种场景一般都是通过紧急创建所以呢来解决。MySQL5.6版本以后创建索引都支持Online DDL了,对于那种高峰数据库已经被数据打挂了情况就是直接直接执行 alter table 语句

  比较理想的是在备库先执行,假设现在服务是一主一备,这个方案大致流程:

  1、在备库B执行 set sql_log_bin=off,不写binlog,然后执行alter table语句加上索引

  2、执行主备切换

  3、这个时候主库是B,备库A,然后A执行set sql_log_bin=off,然后执行alter table语句机上索引

    这个是一个古老的DDL方案。平常做变更的时候,考虑类似于gh_ost这样的方案,更加稳妥,但是着需要紧急处理时候,这个方案效率最高

语句没有写好:

    在18章文章中提到那些错误,导致语句没有使用上索引。可以通过修改SQL语句来处理,MySQL 5.7提供了一个query_rewrite,可以把输入语句改成另一种模式

可以使用这些方法确认改写规则是否生效:

如果MySQL选错了索引,同样的,使用查询重写功能,可以给这个语句加上 force index。解决这个问题

在上线前怎么预先发现问题?

1、上线前,在测试环境把慢查询日志slow log打开,并且把long_query_time设置为0,确保每个语句都记录在日志里面

2、在测试表中插入模拟线上的数据,做一遍回归测试

3、观察慢查询日志每类语句数据,特别留意Rows_examined字段是否与预期一致

慢查询的日志记录myql.slow_log表中,格式如下:

可以看到,不管是表还是文件,都具体记录了:是那条语句导致慢查询(sql_text),该慢查询语句的查询时间(query_time),锁表时间(Lock_time),以及扫描过的行数(rows_examined)等信息。

这个时候,需要工具帮你检查所有的SQL语句返回结果,比如使用开源工具 pt-query-digest

QPS突增问题:

  场景:有的时候由于业务突然出现高峰,或者程序bug,导致某个语句QPS突然暴涨,也可能导致MySQL压力过大,影响服务

      如果是由一个新功能的bug导致。最理想情况让业务把这个功能下掉,服务自然就恢复,而下掉一个功能,如果从数据库端处理的话,对应于不同的背景有不同方法可用。具体如下:

 这个操作风险很高,需要你特别细致,可能存在两个副作用:

所以方案3用于止血的,跟前面提到去掉权限验证一样,所有选项里优先级最低一个方案。方案1和2依赖于规范的运维体系:虚拟化、白名单机制、业务账号分离。

MYSQL是怎么保证数据不丢的?

   在之前介绍了WAL机制,提到的结论只要redo log 和binlog保证持久化到磁盘,就能保证MySQL异常重启后数据恢复

binlog的写入机制:

    事务执行过程,先把日志写到 binlog cache,事务提交的时候,再把binlog cache 写到binlog 文件中。

    一个事务的binlog是不能拆开的,因此不论这个事务多大,也要确保一次性写入。这就涉及到了binlog cache保存问题

    系统给binlog cache分配一个内存,每个线程一个,参数binlog_cache_size用于控制单个线程内binlog cache所占存的大小,如果超过这个参数大小就要暂存磁盘,事务提交的时候,执行器把binlog cache里的完整事务写入到binlog中,并清空binlog cache。

write:指的把日志写入文件系统的page cache,并没有持久化到磁盘所以锁读很快

fsync:才是将数据持久化到磁盘。一般这个情况,我们认为fsync才占磁盘IOPS

在出现IO瓶颈的场景中,将sync_binlog设置成一个比较大的值,可以提升性能。考虑到丢失日志可控性,一般不建议设为0,比较场景设置为100~1000中某一个数值。设置为N:风险是 如果主机发送异常重启,会丢失最近N个事务的bin log日志

扩展: 当设置成sync_binlog=0的时候,每次commit都只是write到page cache 并不会fsync,但是做试验时binlog还是有原因?

           我们看到的binlog的记录是从page cache读的,page cache是操作系统文件上的

redo log的写入机制:

   事务执行过程中,生成的redo log 是要先写到redo log buffer的

  redo log 存储的三种状态:

这三种状态分别是:

为了控制redo log写入策略,InnoDB提供了innodb_flsh_log_at_trx_commit参数:

InnoDB有一个后台线程,每隔1s,就会把redo log buffer中的日志,调用write写到文件系统的page cache,然后调用fsync持久化到磁盘。

实际上,除了后台线程每秒一次的轮询操作,还有两个场景会让一个没有提及事务的redo log写入磁盘中。

在介绍两阶段提交的时候说过,时序上redo log 先prepare,再写binlog,最后再把redo log commit,如果把innodb_flush_log_trx_commit设置为1,那么redo log 在prepare阶段就要持久化一次,因为有个崩溃恢复逻辑就是要依赖于prepare的redo log,再加上binlog来恢复。每秒一次后台轮询刷盘,再加上崩溃恢复这个逻辑,InnoDB就认为redo log在commit的时候不需要fsync了,只会write到文件系统的page cache中就够了

    通常我们说的MySQL的双1配置,指的就是sync_binlog和innodb_flush_log_at_trx_commit都设置成1。也就是说,一个事务完整提交前,需要等待两次刷盘,一次是redo log(prepare阶段),一次是binlog。

   当这样的话,意味着我从MySQL看到的TPS每秒两万的话,每秒就会写四万次磁盘,但是我从工具测试出来,磁盘能力也就是两万左右,怎么实现两万的TPS?

   redo log的一个个写入点,每次写入长度为length的redo log, LSN(日志逻辑序列号  单调递增)的值就会加上length,LSN也会写到InnoDB的数据页,来确保数据页不会被多次执行重复的redo log。如图,三个并发事务(trx1,trx2,trx3)在prepare阶段,都写完redo log buffer,持久化到磁盘的过程

  所以,一次组提交里面,组员越多,节约磁盘的IOPS的效果越好。但如果只有单线程压测,那就只能一个事务对应一次持久化操作了,在并发更新场景下,第一个事务写完redo log buffer以后,接下来这个fsync越晚调用,组员可能越多,节约IOPS的效果越好。

   根据这个原理的MySQL一个有趣的优化: 拖时间

在第二讲中,把写binlog当成一个动作。但实际上,写binlog分成两步的:

1、先把binlog从binlog cache中写入磁盘上的binlog文件

2、调用fsync持久化

MySQL为了组提交的效果更好,把redo log 做fsync的时间拖到了步骤1之后。变成这样:

 这个时候binlog fsync到磁盘也可以租提交,不过通常情况下第三步执行会很快,所以binlog的write和fsync间的间隔时间段,导致能集合到一起持久化的binlog比较少,因此binlog的组提交的效果通常不如redo log效果那么好

两者是或的关系。

WAL机制是减少磁盘写,可是每次提交事务都要写redo log 和binlog,这磁盘读写次数也没变少呀?

 WAL机制主要得益于这两个方面:

  1、redo log 和binlog都是顺序写,磁盘的顺序写比随机写要快;

  2、组提交机制,可以大幅度降低磁盘的IOPS消耗

如果你的MySQL出现性能瓶颈,而且瓶颈在IO上,可以通过哪些方法来提升性能呢?

hexdump主要用来查看“二进制”文件的十六进制编码。*注意:它能够查看任何文件,不限于与二进制文件。*

 

问题:你的生成库设置的是双1吗?如果平时是的话,什么场景下改成非双1? 我们都知道这些设置可能有损,如果发送异常,止损方案是什么?

   1、业务高峰期。一般如果有预知的高峰期,DBA会有预案把主库设置成非双1

   2、备库延迟,为了让备库尽快赶上主库

   3、用备库恢复主库的副本,应用binlog的过程

   4、批量导入数据的时候

 一般情况,把生产库改成“非双1”配置,是设置

  innodb_flush_logs_at_trx_commit=2、 sync_binlog=1000.

MYSQL怎么保证主备一致的?

       从最开始,MySQL是以容易学习和方便的高可用架构,被开发人员喜欢。而它几乎的高可用架构都直接依赖于binlog。虽然这些高可用架构已经呈现出越来越复杂的趋势,但都是从基本的一主一备演化过来的

   MySQL主备的基本原理

       以下是基本的主备切换流程。

虽然节点B没有被之间访问但是建议把节点B(备库)设置成只读(readonly)模式,原因:

备库设置成只读,怎么跟主库保持同步更新?

  因为readonly设置对超级权限用户是无效的,而用于同步更新的线程就拥有这个权限。

节点A到B这个内部流程什么样子?

 

 备库B和主库A之间维持长连接。A内部有一个线程专门服务B的这个长连接。一个事务日志同步的完整过程是:

在这里主库A从本地读取binlog,发给从库B,这里的本地指文件系统的page cache还是disk呢?

   对于A线程来说,就是读文件,如果这个文件现在还在page cache中,那就最好了,直接独奏,如果不在page cache里,就只要去磁盘读

后来由于多线程复制方案的引入,sql_thread演化成多个线程。

存在的问题:binlog 里面有什么内容,为什么备库拿去可以直接执行?

补习知识:binlog的三种格式对比:

   statement、row、和两者格式混用 mixed

binlog有三种格式:Statement、Row以及Mixed。

–基于SQL语句的复制(statement-based replication,SBR), 
–基于行的复制(row-based replication,RBR), 
–混合模式复制(mixed-based replication,MBR)。

为什么有mixed格式的binlog?

所以说,mixed格式可以利用statment格式的有点,同时又避免数据不一致的风险

现在越来越多的场景要求把MySQL格式设置为row,这么做的理由有很多:其中有恢复数据

其中以delect、insert、update这三个SQL语句的角度分析:

循环复制问题

   binlog的特性确保了在备库执行相同的binlog,可以得到与主库相同的状态

那么,如果节点A同时是节点B的备库,相当于把节点B新生成的binlog拿过来执行了一次,然后节点A和B间,会不断地循环执行这个更新语句,也就是循环复制了,这个要怎么解决呢?

  MySQL在binlog中记录这个命令第一次执行时所在实例的server id。然后用下面的逻辑来解决两个节点间的循环复制问题

 1、规定两个库的server id 必须不同。如果相同,则它们之间不能设定为主备关系

 2、一个备库接到binlog并在重放的过程中,生成与原binlog的server id相同的新的binlog;

 3、每个库在收到从自己的主库发过来的日志后,先判断server id,如果跟自己的相同,标识这个日志是自己生产的,就直接丢弃这个日志

按照这个逻辑,日志的执行流程变成这样:

问题:MySQL通过判断server id的方式,断掉死循环。但是,这个机制其实并不完备,在某些场景,仍然可能出现死循环。你能构造出这样场景嘛?又应该怎么解决?

25、MySQL是怎么保证高可用的?

     正常情况下,只要主库执行更新生成的所有binlog,都可以传到备库并被正确地执行,备库就能达到跟主库一样的状态,这就是最终一致性,但是,MySQL要提供高可用能力,只有最终一致性是不够的

             MySQL主备切换流程---双M结构

 主备切换可能是一个主动运维动作,比如软件升级、主库所在机器按计划下线等,也可能是被动擦欧洲哦,比如主库所在机器掉电。

  数据同步有关的时间点:

  1、主库A执行完一个事务,写入binlog的时间 这个时刻T1

  2、之后传给备库B,备库B接受完这个binlog的时刻 T2

  3、备库B执行完这个事务,这个时刻记为T3

主备延迟:差值就是T3-T1,可以在备库上执行 show slave status命令,返回结果里面会显示 seconds_behind_master,用于表示当前备库延迟了多少秒

seconds_behind_matser计算方法: T3-T1  精确到秒

如果主备库机器的系统时间设置不同,会不会导致主备延迟的值不准?

   不会的,因为备库连接到主库的时候,汇之星select unix_timestamp()函数来获取当前主库的系统时间,如果发现不一致,备库在执行seconds_behing_master计算的时候会自动扣掉这个差值

  网络正常的时候,日志从主库给备库所需时间很短,即T2-T1值非常小。网络正常情况下,主备延迟主要来源是备库接受完binlog和执行完这个事务之间的时间差,最直接的表现是:备库消费中转日志(relay log)的速度,比主库生成的binlog速度要慢

猜你喜欢

转载自blog.csdn.net/ligupeng7929/article/details/89282103