高可用
MySQL数据库读写分离了解吗?
读写分离就是把“写操作”交给主库处理,“读操作”分给多个从库处理,从而提升系统并发性能。
应用层通过中间件(如 MyCat、ShardingSphere)自动路由请求,将 INSERT / UPDATE / DELETE 等写操作发送给主库,将 SELECT 查询操作发送给从库。
主库将数据变更通过 binlog 同步到从库,从而保持数据一致性。
主库 dump_thread 线程通过 TCP 将 binlog 推送给从库,从库 io_thread 线程,接收主库 binlog,写入 relay log,从库 sql_thread 线程读取 relay log,并顺序执行 SQL 语句,更新从库数据。
读写分离的实现方式有哪些?
实现读写分离有三种方式:最简单的是在应用层手动控制主从数据源,适用于小型项目
中等项目是通过 Spring + 多数据源插件、AOP 注解自动路由;
大型系统通常使用中间件,如 ShardingSphere、MyCat,支持自动路由、负载均衡、故障转移等功能。
Mycat 的读写分离功能依赖于 MySQL 的主从复制架构:
writeHost: 表示主节点,负责处理所有的 DML SQL 语句,如 INSERT、UPDATE 和 DELETE。
readHost: 表示从节点,负责处理查询 SQL 语句(如 SELECT),以实现读写分离。
正常情况下,Mycat 会将第一个配置的 writeHost 作为默认的写节点。所有的 DML SQL 语句会被发送到此默认写节点执行。
写节点完成数据写入后,通过 MySQL 的主从复制机制,将数据同步到所有从节点,确保主从数据一致性。
主从复制原理了解吗?
MySQL 的主从复制是一种数据同步机制,用于将数据从主数据库复制到一个或多个从数据库。主库执行事务提交时,将数据变更以事件形式记录到 Binlog。从库通过 I/O 线程从主库的 Binlog 中读取变更事件,并将这些事件写入到本地的中继日志文件中,SQL 线程会实时监控中继日志的内容,按顺序读取并执行这些事件,从而保证从库与主库数据一致。
主从同步延迟怎么处理?
主从同步延迟是因为从库需要先接收 binlog,再执行 SQL 才能同步主库数据,在高并发写或网络抖动时容易出现延迟,导致读写不一致。
第一种解决方案:对一致性要求高的查询(如支付结果查询)可以直接走主库。
第二种解决方案:对于非关键业务允许短暂数据不一致,可以提示用户“数据同步中,请稍后刷新”,然后借助异步通知机制替代实时查询。
第三种解决方案:采用半同步复制,主库在事务提交时,要等至少一个从库确认收到 binlog(但不要求执行完成),才算提交成功。
你们一般是怎么分库的呢?
分库的策略有两种,第一种是垂直分库:按照业务模块将不同的表拆分到不同的库中,比如说用户、登录、权限等表放在用户库中,商品、分类、库存放在商品库中,优惠券、满减、秒杀放在活动库中。
第二种是水平分库:按照一定的策略将一个表中的数据拆分到多个库中,比如哈希分片和范围分片,对用户 id 进行取模运算或者范围划分,将数据分散到不同的库中。
那你们是怎么分表的?
当单表超过 500 万条数据,就可以考虑水平分表了。比如说我们可以将文章表拆分成多个表,如 article_0、article_9999、article_19999 等。
水平分库分表的分片策略有哪几种?
常见的分片策略有三种,范围分片、Hash 分片和路由分片。
范围分片是根据某个字段的值范围进行水平拆分。适用于分片键具有连续性的场景。
Hash 分片是指通过对分片键的值进行哈希取模,将数据均匀分布到多个库表中,适用于分片键具有离散性的场景。
路由分片是通过路由配置来确定数据应该存储在哪个库表,适用于分片键不规律的场景。
不停机扩容怎么实现?
第一个阶段:新旧库同时写入,确保数据实时同步;可以借助消息队列实现异步补偿,幂等避免重复写入。读操作仍然走旧库。
常用的分库分表中间件有哪些?
常用的分库分表中间件有 ShardingSphere(Apache) 和 Mycat(阿里巴巴)。
你觉得分库分表会带来什么问题呢?
第一,跨库事务无法依赖单机 MySQL 的 ACID 特性,需要使用分布式事务解决方案,如 Seata 的 AT 模式、TCC 模式等。
第二,跨库后无法使用 JOIN 联表查询。可以在业务层进行拼接,或者把需要联表查询的数据放到 ES 中。
第三,自增 ID 在分片场景下容易冲突,需要使用全局唯一方案。
数据库表被切分后,不能再依赖数据库自身的主键生成机制,所以需要一些手段来保证全局主键唯一。比如说雪花算法、京东的 JD-hotkey。
雪花算法具体是怎么实现的?
雪花算法是 Twitter 开源的分布式 ID 生成算法,其核心思想是:使用一个 64 位的数字来作为全局唯一 ID。
第 1 位是符号位,永远是 0,表示正数。
接下来的 41 位是时间戳,记录的是当前时间戳减去一个固定的开始时间戳,可以使用 69 年。
然后是 10 位的工作机器 ID。
最后是 12 位的序列号,每毫秒最多可生成 4096 个 ID。
