目录
高可用
Mysql的主从复制的过程是怎样的?
主从复制主要有3个阶段:
- 主库修改数据后,会写入 binlog 日志,从库连接到主库之后,主库会创建一个 log dump 线程,用于发送 binlog 的内容。
- 从库会创建一个专门的 I/O 线程 来连接主库的 log dump 线程,来接收主库的 binlog 日志,再把 binlog 信息写入 relay log 的中继日志里,再返回给主库"复制成功”的响应
- 接着从库还会创建一个用于回放 binlog 的 SQL 线程,去读 relaylog中继日志,然后回放 binlog 更新存储引擎中的数据,最终实现主从的数据一致性。
MySQL提供了几种复制模式? 默认的复制模式是什么?
主要有三种:同步复制、半同步复制、异步复制。
默认的复制模型是异步复制
- 同步复制: MySQL主库提交事务的线程要等待所有从库的复制成功响应,才返回客户端结果。这种方式是性能最差的复制模式,但是能保证数据的安全性,如果对数据安全性比较高的业务,可以考虑采用同步复制的模式
- 异步复制: MySQL主库提交事务的线程并不会等待 binlog 同步到各从库,就返回客户端结果,这种模式性能是最高的,但是一旦主库宕机,数据就会发生丢失
- 半同步复制:个于两者之间,事务线程不用等待所有的从库复制成功响应,只要一部分复制成功响应回来就行比如一主二从的集群,只要数据成功复制到任意一个从库上,主库的事务线程就可以返回给客户端。这种半同步复制的方式,兼顾了异步复制和同步复制的优点,即使出现主库宕机,至少还有一个从库有最新的数据,不存在数据丢失的风险。
主从同步延迟的原因
一个服务器开放N个链接给客户端来连接的,这样有会有大并发的更新操作, 但是从服务器的里面读取 binlog 的线程仅有一个,当某个 SQL 在从服务器上执行的时间稍长 或者由于某个 SQL 要进行锁表就会导致,主服务器的 SQL 大量积压,未被同步到从服务器里。这就导致了主从不一致, 也就是主从延迟。
MySQL主从复制的数据延迟怎么解决?
- 使用缓存解决:可以在写入数据主库的同时,把数据写到 Redis 缓存里,这样其他线程再获取数据时会优先查询缓存,也可以保证数据的一致性。不过这种方式会带来缓存和数据库的一致性问题。
- 直接查询主库:对于数据延迟敏感的业务,可以强制读主库。但是我们要提前明确查询的数据量不大,不然会出现主库写请求执行,影响读请求的执行,最终对主库造成比较大的压力。
- 关键业务读写操作全部指向主机,非关键业务采用读写分离
- 例如,对于一个用户管理系统来说,注册 + 登录的业务读写操作全部访问主机,用户的介绍、爰好、等级等业务,可以采用读写分离,因为即使用户改了自己的自我介绍,在查询时却看到了自我介绍还是旧的,业务影响与不能登录相比就小很多,还可以忍受。
- 读从机失败后再读一次主机
- 这就是通常所说的 "二次读取" ,二次读取和业务无绑定,只需要对底层数据库访问的 API 进行封装即可,实现代价较小,不足之处在于如果有很多二次读取,将大大增加主机的读操作压力。例如,黑客暴力破解账号,会导致大量的二次读取操作,主机可能顶不住读操作的压力从而崩溃。
MySQL主从架构中,读写分离怎么实现?
- 可以独立部署的代理中间件 MyCat 来实现读写分离
- 提前把所有数据源配置在工程中,每个数据源对应一个主库或者从库,然后改造代码,在代码逻辑中进行判断,将 SQL 语句发送给某一个指定的数据源来处理。
MySQL主库挂了怎么办?
MySQL主从复制没有实现发现主服务器宕机和处理故障迁移的功能,要实现自动主从故障迁移的话,我简单了解过,可以使用开源的 MySQL高可用套件 MHA,MHA 可以在主数据库发生宕机时,可以剔除原有主机,选出新的主机,然后对外提供服务,保证业务的连续性。
什么是分库分表? 什么时候需要分表? 什么时候需要分库?
分库分表的意思把原本存储于单个数据库上的数据拆分到多个数据库,把原来存储在单张数据表的数据拆分到多张数据表中,实现数据切分。
是为了解决单库单表数据量过大导致数据库性能下降的一种解决方案。
分库分表使用的场景不一样:
- 当单张数据表的数据量太大的时候,经验值是 500W以上的数据量,就会影响了事务的执行效率,这时候就要考虑分表了,通过减少每次查询数据总量来解决数据查询缓慢的问题
- 当单台 MySQL扛不住高并发流量的时候,就要考虑分库了,把并发请求分散到多台 MySQL实例中。
- 垂直分库:按照业务模块将不同的表拆分到不同的库中,例如,用户表、订单表、商品表等分到不同的库中。
- 水平分库:按照一定的策略将一个表中的数据拆分到多个库中,例如,按照用户 id 的 hash 值将用户表拆分到不同的库中。
MySQL分表怎么设计?
一般可以采用以下几种常见的分表策略:
- 按时间分表:根据数据的时间特征,按照时间范围(如年、月、日)将数据分散存储到不同的表中,便于数据归档和查询。
- 按业务分表:根据业务需求将数据按照业务逻辑进行分表,可以根据不同的业务属性将数据分散到不同的表中,实现逻辑上的分离。
- 按哈希分表:通过对数据的哈希计算,将数据均匀分布到多个表中,避免单表数据量过大导致性能问题。
- 按范围分表:根据数据的某个范围属性(如用户ID、地区ID等)将数据分散到不同的表中,便于查询和管理特定范围的数据。
- 按数据量分表:当单表数据量过大时,可以按照一定的规则将数据拆分到多个表中,避免单表数据量过大导致性能下降。
水平分表有哪几种路由方式?
为了实现水平分表,需要设计合适的路由策略来确定数据应该存储在哪个表中,具体哪个表,由分片键(Sharding Key)来决定,分片键的选择应满足以下条件:
- 高区分度:分片键的值应尽量均匀分布,以避免数据倾斜。
- 查询频率高:选择经常在查询条件中使用的字段作为分片键,有助于提高查询效率。
- 写入频率高:选择经常被写入的字段,可以均匀分布写入负载。
那常见的路由策略有三种,分别是范围路由、Hash 路由和配置路由。
什么是范围路由?
范围路由是根据某个字段的值范围进行分表。这种方式适用于分片键具有顺序性或连续性的场景。
范围路由的优点是实现简单,可以随着数据的增加平滑地扩充新的表。适用于按时间或按顺序增长的字段(如时间戳、订单号等)。缺点是可能出现数据倾斜问题,导致某些表的数据量明显大于其他表。
什么是 Hash 路由?
哈希路由是通过对分片键进行哈希计算,然后取模来确定数据存储的表。哈希值决定了数据分布,通常能较好地平衡数据量。
哈希路由的优点是数据可以均匀分布,避免了数据倾斜,但范围查询时可能会涉及多个表,性能较差。
什么是配置路由?
配置路由是通过配置表来确定数据存储的表,适用于分片键不规律的场景。
配置路由的优点是可以根据实际情况灵活配置。缺点是需要额外的配置表,维护成本较高
不停机扩容怎么实现?
实际上,不停机扩容,实操起来是个非常麻烦而且很有风险的操作,当然,面试回答起来就简单很多。
- 第一阶段:在线双写,查询走老库
- 建立好新的库表结构,数据写入旧库的同时,也写入拆分的新库
- 数据迁移,使用数据迁移程序,将旧库中的历史数据迁移到新库
- 使用定时任务,新旧库的数据对比,把差异补齐
- 第二阶段:在线双写,查询走新库
- 完成了历史数据的同步和校验
- 把对数据的读切换到新库
- 第三阶段:旧库下线
- 旧库不再写入新的数据
- 经过一段时间,确定旧库没有请求之后,就可以下线老库
常用的分库分表中间件有哪些?
- sharding-jdbc
- Mycat
那你觉得分库分表会带来什么问题呢?
从分库的角度来讲:
- 事务的问题
使用关系型数据库,有很大一点在于它保证事务完整性。
而分库之后单机事务就用不上了,必须使用分布式事务来解决。
- 跨库 JOIN 问题
在一个库中的时候我们还可以利用 JOIN 来连表查询,而跨库了之后就无法使用 JOIN 了。
解决方案
- 在业务代码中进行关联,也就是先把一个表的数据查出来,然后通过得到的结果再去查另一张表,然后利用代码来关联得到最终的结果。这种方式实现起来稍微比较复杂,不过也是可以接受的。
- 可以适当的冗余一些字段。比如以前的表就存储一个关联 ID,但是业务时常要求返回对应的 Name 或者其他字段。这时候就可以把这些字段冗余到当前表中,来去除需要关联的操作。
- 数据异构,通过 binlog 同步等方式,把需要跨库 join 的数据异构到 ES 等存储结构中,通过 ES 进行查询。
----------------------------------------------------------------------------------------
从分表的角度来看:
- 跨节点的 count,order by,group by 以及聚合函数问题
只能由业务代码来实现或者用中间件将各表中的数据汇总、排序、分页然后返回。
- 数据迁移,容量规划,扩容等问题
数据的迁移,容量如何规划,未来是否可能再次需要扩容,等等,都是需要考虑的问题。
- ID 问题(全局主键唯一ID)
数据库表被切分后,不能再依赖数据库自身的主键生成机制,所以需要一些手段来保证全局主键唯一。
- 还是自增,只不过自增步长设置一下。比如现在有三张表,步长设置为 3,三张表 ID 初始值分别是 1、2、3。这样第一张表的 ID 增长是 1、4、7。第二张表是 2、5、8。第三张表是 3、6、9,这样就不会重复了。
- UUID,这种最简单,但是不连续的主键插入会导致严重的页分裂,性能比较差。
- 分布式 ID,比较出名的就是 Twitter 开源的 sonwflake 雪花算法
运维
百万级别以上的数据如何删除? (先删除再创建)
关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的 IO,会降低增/改/删的执行效率。
所以,在我们删除数据库百万级别数据的时候,查询 MySQL 官方手册得知删除数据的速度和创建的索引数量是成正比的。
- 我们想要删除百万数据的时候可以先删除索引
- 然后删除其中无用数据
- 删除完成后重新创建索引创建索引也非常快
百万千万级大表如何添加字段?
当线上的数据库数据量到达几百万、上千万的时候,加一个字段就没那么简单,因为可能会长时间锁表。
大表添加字段,通常有这些做法:
- 通过中间表转换过去
创建一个临时的新表,把旧表的结构完全复制过去,添加字段,再把旧表数据复制过去,删除旧表,新表命名为旧表的名称,这种方式可能会丢掉一些数据。
- 用 pt-online-schema-change
pt-online-schema-change
是 percona 公司开发的一个工具,它可以在线修改表结构,它的原理也是通过中间表。
- 先在从库添加 再进行主从切换
如果一张表数据量大且是热表(读写特别频繁),则可以考虑先在从库添加,再进行主从切换,切换后再将其他几个节点上添加字段。
MySQL 数据库 cpu 飙升的话,要怎么处理呢?
排查过程:
(1)使用 top 命令观察,确定是 mysqld 导致还是其他原因。
(2)如果是 mysqld 导致的,show processlist,查看 session 情况,确定是不是有消耗资源的 sql 在运行。
(3)找出消耗高的 sql,看看执行计划是否准确, 索引是否缺失,数据量是否太大。
处理:
(1)kill 掉这些线程 (同时观察 cpu 使用率是否下降),
(2)进行相应的调整 (比如说加索引、改 sql、改内存参数)
(3)重新跑这些 SQL。
其他情况:
也有可能是每个 sql 消耗资源并不多,但是突然之间,有大量的 session 连进来导致 cpu 飙升,这种情况就需要跟应用一起来分析为何连接数会激增,再做出相应的调整,比如说限制连接数等
自己整理,借鉴很多博主,感谢他们