21 为什么只改一行的语句,锁这么多?
加锁规则里面:包含了两个"原则”、两个"优化"和一个"bug”。
- 原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区
间。 - 原则 2:查找过程中访问到的对象才会加锁。
- 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
- 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
- 一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。
22 MySQL有哪些"饮鸩止渴"提高性能的方法?
业务高峰期,生产环境的 MySQL 压力太大,没法正常响应,需要短期内、临时性地提升一些性能。
短连接风暴
正常的短连接模式就是连接到数据库后,执行很少的 SQL 语句就断开,下次需要的时候再重连。
如果使用的是短连接,在业务高峰期的时候,就可能出现连接数突然暴涨的情况。
除了正常的网络连接三次握手外,还需要做登录权限判断和获得这个连接的数据读写权限。
max_connections 参数,用来控制一个 MySQL 实例同时存在的连接数的上限,超过这个值,系统就会拒绝接下来的连接请求,并报错提示"Too many connections”。 第一种方法:先处理掉那些占着连接但是不工作的线程 max_connections 的计算,不是看谁在 running,是只要连着就占用一个计数位置。
对于那些不需要保持的连接,们可以通过 kill connection 主动踢掉。
这个行为跟事先设置wait_timeout 的效果是一样的。 wait_timeout 参数表示的是,一个线程空闲wait_timeout 这么多秒之后,就会被 MySQL 直接断开连接。 第二种方法:减少连接过程的消耗
有的业务代码会在短时间内先大量申请数据库连接做备用,如果现在数据库确认是被连接行为打挂了,那么一种可能的做法,是让数据库跳过权限验证阶段。
跳过权限验证的方法是:重启数据库,并使用–skip-grant-tables 参数启动。
这样,整个MySQL 会跳过所有的权限验证阶段,包括连接过程和语句执行过程在内。
但是,这种方法特别符合们标题里说的"饮鸩止渴”,风险极高,是特别不建议使用的方案。
尤其你的库外网可访问的话,就更不能这么做了。
在 MySQL 8.0 版本里,如果你启用 –skip-grant-tables 参数, MySQL 会默认把 –skip-networking 参数打开,表示这时候数据库只能被本地的客户端连接。
可见,MySQL 官方对skip-grant-tables 这个参数的安全问题也很重视。
慢查询性能问题
MySQL慢查询就是在日志中记录运行比较慢的SQL语句,这个功能需要开启才能用。
在 MySQL 中,会引发性能问题的慢查询,大体有以下三种可能:
- 索引没有设计好;
- SQL 语句没写好;
- MySQL 选错了索引。 索引没有设计好
这种场景一般就是通过紧急创建索引来解决。 MySQL 5.6 版本以后,创建索引都支持 Online DDL 了,
对于那种高峰期数据库已经被这个语句打挂了的情况,最高效的做法就是直接执行alter table 语句。
比较理想的是能够在备库先执行。假设你现在的服务是一主一备,主库 A、备库 B,这个方案的
大致流程是这样的:
- 在备库 B 上执行 set sql_log_bin=off,也就是不写 binlog,然后执行 alter table 语句加+索引;
- 执行主备切换;
- 这时候主库是 B,备库是 A。在 A 上执行 set sql_log_bin=off,然后执行 alter table 语句
加上索引。
这是一个"古老"的 DDL 方案。
平时在做变更的时候,你应该考虑类似 gh-ost 这样的方案,更加稳妥。
但是在需要紧急处理时,上面这个方案的效率是最高的。 语句没有写好
们可以通过改写 SQL 语句来处理。MySQL 5.7 提供了 query_rewrite 功能,
可以把输入的一种语句改写成另外一种模式。 MySQL 选错了索引
应急方案就是给这个语句加上 force index。 QPS 突增问题 Queries Per Second,意思是"每秒查询率”
一类情况,是由一个新功能的 bug 导致的。
当然,最理想的情况是让业务把这个功能下掉,服务自然就会恢复。
- 一种是由全新业务的 bug 导致的。 假设你的 DB 运维是比较规范的,也就是说白名单是一个个加的。 这种情况下,如果你能够确定业务方会下掉这个功能,只是时间上没那么快,那么就可以从数据库端直接把白名单去掉。
- 如果这个新功能使用的是单独的数据库用户,可以用管理员账号把这个用户删掉, 然后断开现有连接。这样,这个新功能的连接不成功,由它引发的 QPS 就会变成 0。
- 如果这个新增的功能跟主体功能是部署在一起的,那么们只能通过处理语句来限制。 这时,们可以使用上面提到的查询重写功能,把压力最大的 SQL 语句直接重写成"select 1”。
当然,这个操作的风险很高,需要你特别细致。
它可能存在两个副作用:
- 如果别的功能里面也用到了这个 SQL 语句模板,会有误伤;
- 很多业务并不是靠这一个语句就能完成逻辑的,所以如果单独把这一个语句以 select 1 的结果返回的话,可能会导致后面的业务逻辑一起失败。
所以,方案 3 是用于止血的,跟前面提到的去掉权限验证一样,应该是你所有选项里优先级最低的一个方案。
23 MySQL是怎么保证数据不丢的?
只要 redo log 和 binlog 保证持久化到磁盘,就能确保 MySQL 异常重启后,数据可以恢复。
探究 MySQL 写入 binlog 和 redo log 的流程。
binlog写入机制
binlog 的写入机制binlog 的写入逻辑比较简单:
事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中。
问题 来了:一个事务的 binlog 是不能被拆开的,因此不论这个事务多大,也要确保一次性写入。这就涉及
到了 binlog cache 的保存问题。
系统给 binlog cache 分配了一片内存,每个线程一个,
参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。
如果超过了这个参数规定的大小,就要暂存到磁盘。
事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 中,并清空 binlog cache。
可以看到,每个线程有自己 binlog cache,但是共用同一份 binlog 文件。
图中的 write,指的就是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快。
图中的 fsync,才是将数据持久化到磁盘的操作。一般情况下,们认为 fsync 才占磁盘的IOPS。 write 和 fsync 的时机,是由参数 sync_binlog 控制的:
- sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;
- sync_binlog=1 的时候,表示每次提交事务都会执行 fsync;
- sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。
因此,在出现 IO 瓶颈的场景里,将 sync_binlog 设置成一个比较大的值,可以提升性能。
在实际的业务场景中,考虑到丢失日志量的可控性,一般不建议将这个参数设成 0,比较常见的是将其设置为 100~1000 中的某个数值。
但是,将 sync_binlog 设置为 N,对应的风险是:如果主机发生异常重启,会丢失最近 N 个事务的 binlog 日志。
redo log 的写入机制
事务在执行过程中,生成的redo log 是要先写到 redo log buffer 的。
这三种状态分别是:
- 存在 redo log buffer 中,物理上是在 MySQL 进程内存中,就是图中的红色部分;
- 写到磁盘 (write),但是没有持久化(fsync),物理上是在文件系统的 page cache 里面,也就是图中的黄色部分;
- 持久化到磁盘,对应的是 hard disk,也就是图中的绿色部分。
日志写到 redo log buffer 是很快的,wirte 到 page cache 也差不多,但是持久化到磁盘的速度就慢多了。
为了控制 redo log 的写入策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 参数,
它有三种可能取值:
- 设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;
- 设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;
- 设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。 InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。
注意,事务执行中间过程的 redo log 也是直接写在 redo log buffer 中的,
这些 redo log 也会被后台线程一起持久化到磁盘。也就是说,一个没有提交的事务的 redo log,也是可能已经
持久化到磁盘的。
实际上,除了后台线程每秒一次的轮询操作外,还有两种场景会让一个没有提交的事务的 redolog 写入到磁盘中。
- 一种是,redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动写盘。
注意,由于这个事务并没有提交,所以这个写盘动作只是 write,而没有调用 fsync,也就是只留在了文件系统的 page cache。
- 另一种是,并行的事务提交的时候,顺带将这个事务的 redo log buffer 持久化到磁盘。 两阶段提交,时序上 redo log 先 prepare, 再写binlog,最后再把 redo log commit。
更新中…………(这里有地方没有理解)
24 MySQL是怎么保证主备一致的?
MySQL 主备的基本原理
在状态 1 中,虽然节点 B 没有被直接访问,但是依然建议你把节点 B(也就是备库)设置成只读(readonly)模式,
防止一些误操作
与主库保持同步更新,readonly 设置对超级 (super) 权限用户是无效的,而用于同步更新的线程,就拥有超级权限。
备库 B 跟主库 A 之间维持了一个长连接。主库 A 内部有一个线程,专门用于服务备库 B 的这个长连接。
binlog 的三种格式对比
更新中…………
循环复制问题
binlog 的特性确保了在备库执行相同的 binlog,可以得到与主库相同的状态。
因此,们可以认为正常情况下主备的数据是一致的。
上面俩个圈的图们称作"双M结构” 双 M 结构还有一个问题需要解决。
如果节点 A 同时是节点 B 的备库,
相当于又把节点 B 新生成的 binlog 拿过来执行了一次,
然后节点 A 和 B 间,会不断地循环执行这个更新语句,也就是循环复制了。
这个要怎么解决呢?
们可以用下面的逻辑,来解决两个节点间的循环复制的问题:
1. 规定两个库的 server id 必须不同,如果相同,则它们之间不能设定为主备关系;
2. 一个备库接到 binlog 并在重放的过程中,生成与原 binlog 的 server id 相同的新的binlog;
3. 每个库在收到从自己的主库发过来的日志后,先判断 server id,如果跟自己的相同,
表示这个日志是自己生成的,就直接丢弃这个日志。
25 MySQL是怎么保证高可用的?
主备延迟
主备延迟最直接的表现是,备库消费中转日志(relay log)的速度,比主库生产binlog 的速度要慢。 来源
- 备库所在机器的性能要比主库所在的机器性能差。
主好从弱,更新请求对 IOPS 的压力,在主库和备库上是无差别的,所以不好。
这种部署现在比较少了。因为主备可能发生切换,备库随时可能变成主库,所以主备库选用相同规格的机器,并且做对称部署。
- 备库的压力大
主库直接影响业务,大家使用起来会比较克制,反而忽视了备库的压力控制。
这种情况,们一般可以这么处理:
1. 一主多从。除了备库外,可以多接几个从库,让这些从库来分担读的压力。
2. 通过 binlog 输出到外部系统,比如 Hadoop 这类系统,让外部系统提供统计类查询的能力。
- 大事务
主库上必须等事务执行完成才会写入 binlog,再传给备库,如果主库的的执行时间太长,延迟就有了。
可靠性优先策略
备注:图中的 SBM,是 seconds_behind_master 参数的简写。
可用性优先策略
- 使用 row 格式的 binlog 时,数据不一致的问题更容易被发现。
而使用 mixed 或者statement 格式的 binlog 时,数据很可能悄悄地就不一致了。
如果你过了很久才发现数据不一致的问题,很可能这时的数据不一致已经不可查,或者连带造成了更多的数据逻辑不一致。
- 主备切换的可用性优先策略会导致数据不一致。
因此,大多数情况下,都建议你使用可靠性优先策略。
毕竟对数据服务来说的话,数据的可靠性一般还是要优于可用性的
这里只是一个自己的学习笔记,大家有兴趣一定去看原文!!! 谢谢大家的阅读!!
大家有兴趣一定去看原文,这只是自己的一个笔记总结!!
大家有兴趣一定去看原文,这只是自己的一个笔记总结!!
大家有兴趣一定去看原文,这只是自己的一个笔记总结!!
原文创作:ML李嘉图
原文链接:https://www.cnblogs.com/zwtblog/p/15245822.html
文章列表
- 项目MyBlog
- 阶段总结Java基础超进阶
- 链表题解
- 郭德纲罗翔语录
- 语录转载
- 设计模式更新中
- 设计模式图解
- 论坛项目
- 计算机网络汇总
- 计算机网络TCP篇
- 计算机网络IP篇
- 计算机网络HTTP篇
- 计算机组成原理图解
- 计算机组成原理初见
- 自定义starter
- 缓存穿透,击穿,雪崩详解
- 笔试格式
- 温故知新输入网址显示网页到底到底到底到底发生了什么?
- 深度学习之神经网络的结构
- 栈和队列题解方法
- 数组LeetCode笔试
- 数据结构
- 数据库事故
- 操作系统初见?见了好多次,次次都要学!
- 怎么通俗的理解Netty呢?
- 富文本编辑器SpringBoot
- 字符串题解方法
- 基础算法学习
- 双指针题解
- 刷题加油
- 分布式锁初见
- 互联网基础架构图解
- 二叉树题解方法
- 三体语录
- Swagger初见
- Spring初见
- SpringSecurity图解
- SpringSecurityShiro初见
- SpringBoot异步定时邮件任务
- SpringBootWeb初见
- SprinBootSpringData整合
- SpingBootDubboZookeeper分布式
- Shiro登陆流程认证图解
- Redis数据类型应用场景
- Redis conf分析
- Radius协议学习
- RabbitMQ进阶
- RabbitMQ初见
- Netty初见三大组件简单使用
- MySql分区、分表和分库
- MySQL实战45讲2125笔记
- MySQL实战45讲1620笔记
- MySQL实战45讲1015笔记
- Linux实战常用命令
- Java爬虫小项目
- JavaNIO
- JVM调优命令
- JVM深入
- JVM初见
- JVM优化
- JAVA中直接用Jdbc就能操作数据库了,为什么还要用spring框架?
- Hash题解方法
- HashMap的转化时机
- HashMap源码分析
- Github博客园初见
- GC优化案例
- ElasticSearch7 X X初见模仿京东搜索的实战
- Docker初见
- CI/CD企业级DevOps