分布式环境中解决主从延时的一些思路

embedded/2024/9/23 5:17:08/

目录标题

  • MySQL主从复制复习
    • 为什么要做主从复制?
    • 主从复制的原理
    • 主从延迟的原因?
  • 解决思路
    • 1. 读写分离与延迟容忍
    • 2. 异步复制优化
    • 3. 缓存机制(常用)
    • 4. 最终一致性方案(常用)
    • 5. 主从切换与自动故障恢复(DBA常用)
    • 6. 使用中间件或代理
    • 7. 数据预热(营销常用)
    • 8. 优化网络和硬件
    • 9. 多活架构
    • 10. 数据校验与修复
    • 11.少量读业务直连主库
    • 12.适当的限流、降级

MySQL主从复制复习

主从复制,是指建立一个和主数据库完全一样的数据库环境(称为从数据库),并将主库的操作行为进行复制的过程:将主数据库的DDL和DML的操作日志同步到从数据库上,然后在从数据库上对这些日志进行重新执行,来保证从数据库和主数据库的数据的一致性。

为什么要做主从复制?

1、在复杂的业务操作中,经常会有操作导致锁行甚至锁表的情况,如果读写不解耦,会很影响运行中的业务,使用主从复制,让主库负责写,从库负责读。即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运行。

2、保证数据的热备份,主库宕机后能够及时替换主库,保障业务可用性。

3、架构的演进:业务量扩大,I/O访问频率增高,单机无法满足,主从复制可以做多库方案,降低磁盘I/O访问的频率,提高单机的I/O性能。

4、本质上也是分治理念,主从复制、读写分离即是压力分拆的过程。

5、读写比也影响整个拆分方式,读写比越高,主从库比例应越高,才能保证读写的均衡,才能保证较好的运行性能。读写比下的主从分配方法下:

读写比主库从库
50:5011
66:3312
80:2014

主从复制的原理

当在从库上启动复制时,首先创建I/O线程连接主库,主库随后创建Binlog Dump线程读取数据库事件并发送给I/O线程,I/O线程获取到事件数据后更新到从库的中继日志Relay Log中去,之后从库上的SQL线程读取中继日志Relay Log中更新的数据库事件并应用,

如下图所示:
在这里插入图片描述

细化一下有如下几个步骤:

1、MySQL主库在事务提交时把数据变更(insert、delet、update)作为事件日志记录在二进制日志表(binlog)里面。

2、主库上有一个工作线程 binlog dump thread,把binlog的内容发送到从库的中继日志relay log中。

3、从库根据中继日志relay log重做数据变更操作,通过逻辑复制来达到主库和从库的数据一致性。

4、MySQL通过三个线程来完成主从库间的数据复制,其中binlog dump线程跑在主库上,I/O线程和SQL线程跑在从库上。拥有多个从库的主库会为每一个连接到主库的从库创建一个binlog dump线程。

主从延迟的原因?

MySQL主从复制,读写分离是我们常用的数据库架构,但是在并发量较大、数据变化大的场景下,主从延时会比较严重。

延迟的本质原因是:系统TPS并发较高时,主库产生的DML(也包含一部分DDL)数量超过Slave一个Sql线程所能承受的范围,效率就降低了。

我们看到这个sql thread 是单个线程,所以他在重做RelayLog的时候,能力也是有限的。

在这里插入图片描述

解决思路

分布式环境下,主从数据库的延时是一个常见的问题,尤其是在高并发场景下。以下是一些解决主从延时的思路和实践经验,并附上具体的例子:

1. 读写分离与延迟容忍

思路:将读操作分配到从库,写操作分配到主库。通过设置合理的延迟容忍时间来处理短暂的主从延时。

实践经验

  • 在应用层实现读写分离逻辑。
  • 设置一个可接受的延迟容忍时间(例如5秒),在此时间内允许数据不一致。

例子

public class UserService {private final JdbcTemplate masterJdbcTemplate;private final JdbcTemplate slaveJdbcTemplate;private static final int TOLERANCE_DELAY = 5000; // 5秒public User getUserById(int id) {// 从从库读取数据User user = slaveJdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", new Object[]{id}, new BeanPropertyRowMapper<>(User.class));// 获取当前时间和上次更新时间long currentTimeMillis = System.currentTimeMillis();long lastUpdatedTimeMillis = user.getLastUpdated().getTime();// 计算时间差long timeDifference = currentTimeMillis - lastUpdatedTimeMillis;// 如果时间差小于延迟容忍时间,则认为数据是一致的if (timeDifference < TOLERANCE_DELAY) {return user;} else {// 否则从主库读取数据return masterJdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", new Object[]{id}, new BeanPropertyRowMapper<>(User.class));}}
}

2. 异步复制优化

思路:优化主从复制的配置,减少复制延迟。可以使用半同步复制或并行复制来提高效率。

实践经验

  • 使用MySQL的半同步复制(semi-sync replication)确保至少有一个从库接收到事务后才返回成功。
  • 配置并行复制以加快复制速度。

例子

-- 启用半同步复制
CHANGE MASTER TO MASTER_HOST='master_host', MASTER_PORT=3306, MASTER_USER='repl_user', MASTER_PASSWORD='repl_password', MASTER_AUTO_POSITION=1, MASTER_CONNECT_RETRY=10, MASTER_HEARTBEAT_PERIOD=10, MASTER_DELAY=0 FOR CHANNEL 'group_replication_recovery';-- 配置并行复制
[mysqld]
slave_parallel_workers=4
slave_parallel_type=LOGICAL_CLOCK

3. 缓存机制(常用)

思路:使用缓存来减轻数据库的压力,并减少主从延时的影响。对于频繁读取的数据,可以先从缓存中读取。

实践经验

  • 使用Redis或Memcached等缓存系统。
  • 在数据更新时同时更新缓存,确保缓存与数据库的一致性。

例子

public class UserService {private final JdbcTemplate jdbcTemplate;private final RedisTemplate<String, User> redisTemplate;public User getUserById(int id) {// 先从缓存中读取User user = redisTemplate.opsForValue().get("user:" + id);if (user == null) {// 缓存中没有,从数据库读取user = jdbcTemplate.queryForObject("SELECT * FROM users WHERE id = ?", new Object[]{id}, new BeanPropertyRowMapper<>(User.class));// 更新缓存redisTemplate.opsForValue().set("user:" + id, user, 1, TimeUnit.HOURS);}return user;}public void updateUser(User user) {// 更新数据库jdbcTemplate.update("UPDATE users SET name = ?, email = ? WHERE id = ?", user.getName(), user.getEmail(), user.getId());// 更新缓存redisTemplate.opsForValue().set("user:" + user.getId(), user, 1, TimeUnit.HOURS);}
}

4. 最终一致性方案(常用)

思路:采用最终一致性方案,允许短期内的数据不一致,但保证最终数据会一致。

实践经验

  • 使用消息队列(如Kafka、RabbitMQ)来异步处理数据同步。
  • 通过事件驱动架构来处理数据变更通知。

例子

// 生产者发送用户更新事件
kafkaTemplate.send("user-update-topic", user.getId(), user);// 消费者处理用户更新事件
@KafkaListener(topics = "user-update-topic")
public void onUserUpdate(ConsumerRecord<Integer, User> record) {User user = record.value();// 更新从库jdbcTemplate(slaveDataSource).update("UPDATE users SET name = ?, email = ? WHERE id = ?", user.getName(), user.getEmail(), user.getId());
}

5. 主从切换与自动故障恢复(DBA常用)

思路:通过自动化工具(如MHA、Orchestrator)实现主从切换和自动故障恢复,减少因主库故障导致的延时。

实践经验

  • 部署MHA或Orchestrator来监控主从状态。
  • 自动切换主库并在新的主库上继续服务。

例子

# 安装MHA
apt-get install mha4mysql-manager# 配置MHA
cat > /etc/masterha/app1.cnf <<EOF
[server default]
manager_workdir=/var/log/mha/app1
manager_log=/var/log/mha/app1/manager.log
ssh_user=root
repl_user=repl
repl_password=repl_password[server1]
hostname=192.168.1.1
port=3306[server2]
hostname=192.168.1.2
port=3306
EOF# 启动MHA
masterha_manager --conf=/etc/masterha/app1.cnf

当然,除了之前提到的方法,还有几种常见的解决主从延时的思路和实践经验。以下是更多的一些方法及其具体例子:

6. 使用中间件或代理

思路:使用中间件或代理来管理主从数据库之间的读写分离和负载均衡,减少应用层的复杂性。

实践经验

  • 使用如MaxScale、ProxySQL等中间件。
  • 配置中间件以智能路由读写请求,并提供延迟监控和故障切换功能。

例子

-- ProxySQL配置示例
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (10, 'master_host', 3306);
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (20, 'slave1_host', 3306);
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (20, 'slave2_host', 3306);-- 设置读写分离规则
INSERT INTO mysql_query_rules(rule_id, active, match_pattern, destination_hostgroup, apply) 
VALUES (1, 1, '^SELECT.*', 20, 1); -- 读操作路由到从库
INSERT INTO mysql_query_rules(rule_id, active, match_pattern, destination_hostgroup, apply) 
VALUES (2, 1, '^INSERT.*|^UPDATE.*|^DELETE.*', 10, 1); -- 写操作路由到主库-- 加载规则
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;

7. 数据预热(营销常用)

思路:在业务低峰期预先加载数据到从库,减少高峰时段的数据同步压力。

实践经验

  • 在夜间或其他低峰时段进行批量数据同步。
  • 使用定时任务或调度系统(如Cron)来执行数据预热。

例子

# 定时任务脚本
#!/bin/bash# 每天凌晨2点执行数据同步
0 2 * * * /usr/local/bin/pt-table-sync --sync-to-master h=master_host,u=repl_user,p=repl_password

8. 优化网络和硬件

思路:优化网络连接和硬件配置,提高数据传输速度和处理能力。

实践经验

  • 使用高速网络连接(如10Gbps)。
  • 升级服务器硬件,特别是CPU和内存。
  • 使用SSD硬盘以提高I/O性能。

例子

  • 确保主从服务器之间的网络带宽足够高,减少网络延迟。
  • 使用高性能的SSD硬盘替换传统的HDD硬盘。

在这里插入图片描述

9. 多活架构

思路:采用多活架构,多个数据中心同时提供服务,每个数据中心都有自己的主从集群。

实践经验

  • 在多个地理区域部署独立的主从集群。
  • 使用全局负载均衡器(如DNS轮询、GSLB)将请求分发到最近的数据中心。

例子

  • 在北京、上海、广州分别部署一套主从集群。
  • 使用阿里云的全球负载均衡服务(GSLB)将用户请求分发到最近的数据中心。

10. 数据校验与修复

思路:定期进行数据校验,发现并修复主从数据不一致的问题。

实践经验

  • 使用工具如pt-table-checksum和pt-table-sync进行数据一致性检查和修复。
  • 定期运行数据校验任务,并记录日志以便追踪问题。

例子

# 数据一致性检查
/usr/local/bin/pt-table-checksum --host=master_host --user=repl_user --password=repl_password# 数据修复
/usr/local/bin/pt-table-sync --sync-to-master h=master_host,u=repl_user,p=repl_password

11.少量读业务直连主库

业务量不多的情况下,不做主从分离.既然主从延迟是由于从库同步写库不及时引起的,那我们也可以在有主从延迟的地方改变读库方式,由原来的读从库改为读主库。当然这也会增加代码的一些逻辑复杂性。
这边需要注意的是,直接读主库的业务量不宜多,而且是读实时一致性有刚性需求的业务才这么做。否则背离读写分离的目的。

12.适当的限流、降级

任何的服务器都是有吞吐量的限制的,没有任何一个方案可以无限制的承载用户的大量流量。所以我们必须估算好我们的服务器能够承载的流量上限是多少。
达到这个上限之后,就要采取缓存,限流,降级的这三大杀招来应对我们的流量。这也是应对主从延迟的根本处理办法。

通过这些方法,可以进一步减少主从延时,提高系统的稳定性和性能。选择哪种方法取决于具体的业务需求、技术栈以及资源条件。综合运用多种方法通常能取得更好的效果。

在这里插入图片描述


http://www.ppmy.cn/embedded/115428.html

相关文章

Maven国内镜像(四种)

配置Maven使用国内镜像是一个常见的做法&#xff0c;因为这样可以显著提高依赖下载的速度并避免网络不稳定带来的问题 在 settings.xml 文件中&#xff0c;需要添加或修改 <mirrors> 标签来指定国内镜像。 以下是几个可用的镜像 1. 阿里云 <mirrors> <mi…

【云原生监控】Prometheus之PushGateway

Prometheus之PushGateway 文章目录 Prometheus之PushGateway介绍作用资源列表基础环境一、部署PushGateway1.1、下载软件包1.2、解压软件包1.3、编辑配置systemctl启动文件1.4、创建日志目录1.5、加载并启动1.6、监控端口1.7、访问PushGateway 二、 配置Prometheus抓取PushGate…

二分查找算法(3) _x的平方根

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 二分查找算法(3) _x的平方根 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 温馨…

摆脱困境并在 Android 手机上取回删除照片的所有解决方案

没有什么比不小心从 Android 智能手机中删除所有照片更糟糕的了。这样&#xff0c;除非您在重置之前已经备份了数据&#xff0c;否则您的所有照片都会消失。如果您忘记备份照片&#xff0c;您仍然可以按照一些简单的技术在 Android 设备上恢复已删除的照片。 您的 Android 智能…

【Git】Git 打标签详解

目录 一、标签的基本概念二、如何打标签2.1 创建轻量标签2.2 创建附注标签 三、查看标签四、推送标签到远程五、删除标签5.1 本地标签5.2 远程标签 六、标签的常见场景七、可能出现的问题及解决方案7.1 标签未推送到远程7.2 标签冲突7.3 查看标签信息不全 总结 在使用 Git 进行…

如何使用 Helm 2 软件包管理器在 Kubernetes 集群上安装软件

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 Helm 是 Kubernetes 的一个包管理工具&#xff0c;允许开发人员和运维人员更轻松地在 Kubernetes 集群上配置和部署应用程序。 在…

【深入理解SpringCloud微服务】了解微服务的熔断、限流、降级,手写实现一个微服务熔断限流器

【深入理解SpringCloud微服务】了解微服务的熔断、限流、降级&#xff0c;手写实现一个微服务熔断限流器 服务雪崩熔断、限流、降级熔断降级限流 手写实现一个微服务熔断限流器架构设计代码实现整体逻辑ProtectorAspect#aroundMethod(ProceedingJoinPoint)具体实现1、获取接口对…

经典sql题(六)查找用户每月累积访问次数

使用聚合开窗查找用户每月累积访问次数&#xff0c;首先介绍一下使用 GROUP BY和开窗的区别 GROUP BY 行数变化&#xff1a;使用 GROUP BY 后&#xff0c;原始数据会按指定列进行分组&#xff0c;结果中每组只保留一行&#xff0c;因此行数通常减少。作用&#xff1a;适用于需…