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

目录标题

  • 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.xdnf.cn/news/1542817.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

多无人机通信(多机通信)+配置ssh服务

目录 多机通信 设备 主从机通信设置 配置从机 配置主机 测试 正式启用 MAVROS通信 多机通信 多机通信是实现机器人编队的基础&#xff0c;通过网络搭建通信链路。我们这里用中心节点网络通信&#xff0c;所有数据需有经过中心节点&#xff0c;所以&#xff0c;中心节点…

Codeforces Round 974 (Div. 3)D题解析

前三道太水了&#xff0c;第三道一眼二分&#xff0c;就是需要注意要超过一半人就行&#xff0c;我因为检查了好久 D. Robert Hood and Mrs Hood 抱歉&#xff0c;我是蒟蒻&#xff0c;我看到区间问题就想到了线段树&#xff0c;我们只需要用线段树去维护每个点药经历多少任务…

6.linux文件存储

目录 一&#xff0e;文件系流 1. 简介 2. 示例 二&#xff0e;文件链接 1.符号链接 2.硬链接 三&#xff0e;RAID 1.简介和类型 2.不同场景RAID的使用 3.RAID示例 一&#xff0e;文件系流 问题1:文件是如何准确放到磁盘的某个位置的? 问题2:文件是如何在磁盘(渺茫的…

re题(40)BUUCTF-[ACTF新生赛2020]Oruga

BUUCTF在线评测 (buuoj.cn) 查壳&#xff0c;64位elf文件&#xff0c;ida打开&#xff0c;定位入口函数 进入main里面&#xff0c;再看看sub_78A 猜测是个迷宫&#xff0c;看看byte_201020里是不是地图 _BOOL8 __fastcall sub_78A(__int64 a1) {int v2; // [rspCh] [rbp-Ch]in…

【有啥问啥】深度剖析:大模型AI时代下的推理路径创新应用方法论

深度剖析&#xff1a;大模型AI时代下的推理路径创新应用方法论 随着大规模预训练模型&#xff08;Large Pretrained Models, LPMs&#xff09;和生成式人工智能的迅速发展&#xff0c;AI 在多领域的推理能力大幅提升&#xff0c;尤其是在自然语言处理、计算机视觉和自动决策领…

开启争对目标检测的100类数据集-信息收集

DataBall 助力快速掌握数据集的信息和使用方式。 请关注我们的专栏&#xff1a;DataBall数据集合 &#xff08;计算机视觉&#xff09;_DataBall的博客-CSDN博客 感谢大家&#xff01; 争对数据的种类希望获得大家建议进行收集构建&#xff0c;符合市场大众的需求&#xff0c;欢…

【C++篇】引领C++模板初体验:泛型编程的力量与妙用

文章目录 C模板编程前言第一章: 初始模板与函数模版1.1 什么是泛型编程&#xff1f;1.1.1 为什么要有泛型编程&#xff1f;1.1.1 泛型编程的优势 1.2 函数模板的基础1.2.1 什么是函数模板&#xff1f;1.2.2 函数模板的定义格式1.2.3 示例&#xff1a;通用的交换函数输出示例&am…

【解密 Kotlin 扩展函数】自定义函数(十二)

导读大纲 1.1 在 Kotlin 中创建集合1.2 自定义 joinToString 函数来实现字符串打印 1.1 在 Kotlin 中创建集合 学习如何创建集合 使用setOf函数创建集合, 使用mapOf创建映射, 使用listOf创建列表<1> to 并不是一个特殊的结构体, 而是一个普通函数 infix修饰符表示这是一…

Spring Cloud Gateway 之动态uri 自定义过滤器

背景&#xff1a;第三方公司 请求本公司入参和出参一样的同一个接口&#xff0c;根据业务类型不一样需要不同业务微服务处理 &#xff0c;和第三方公司协商在请求头中加入业务类型方便我公司在网关成分发请求。 1&#xff1a;在spring cloud gateway yml 中加入路由 重点是 -…

人工智能领域-----机器学习和深度学习的区别

机器学习和深度学习都是人工智能领域中的重要概念&#xff0c;它们之间存在以下一些区别&#xff1a; 一、定义与概念 机器学习&#xff1a; 是一种让计算机自动学习和改进的方法&#xff0c;通过从数据中学习模式和规律&#xff0c;从而能够对新的数据进行预测或决策。涵盖了…

【C++笔试强训】如何成为算法糕手Day1

学习编程就得循环渐进&#xff0c;扎实基础&#xff0c;勿在浮沙筑高台 循环渐进Forward-CSDN博客 笔试强训第一天 目录 循环渐进Forward-CSDN博客 第一题&#xff1a;两个数组的交集 暴力循环法&#xff1a; 哈希法 &#xff1a; 数组下标法&#xff1a; 第二题&#x…

MySQL:事务的ACID特性隔离级别脏读/不可重复读/幻读/Next-Key锁——场景复现

目录 1、什么是事务 2、 事务的ACID特性 2.1 事务的隔离性 3、为什么要使用事务&#xff1f; 4、查看支持事务的存储引擎 5、使用事务 5.1 控制事务 5.1.1 开启事务 5.1.2 关闭事务 5.2 开始一个事务&#xff0c;执行修改后回滚 5.3 开始一个事务&#xff0c;执行修…

句子成分——每日一划(十)

目录 一、原句 二、主要句子成分 三、 分词短语部分 四、定语从句部分 五、结构总结 六、句子改良 一、原句 Z-Library has always been a part of my study, providing many books that would otherwise require a lot of time or money to find. 来源&#xff1a;写作…

【网络安全】身份认证+wan优化+终端控制

用户身份认证 在允许用户访问你的网络时对其进行验证是至关重要的。不幸的是很多情况下&#xff0c;简单的用户名与密码验证并不可靠。公司通常需要更强大的针对访问信息价值较高系统(例如网络管理员系统与财务系统)的用户群体的验证。 双因子身份验证是根据“你知道的”和“你…

查询一条 SQL 语句的流程

查询一条sql语句的流程 连接器:建立连接&#xff0c;管理连接、校验用户身份查询缓存:查询语句如果命中查询缓存则直接返回&#xff0c;否则继续往下执行&#xff08;MSQL8.0 已删除&#xff09;解析 SQL&#xff1a;通过解析器对 SQL 查询语句进行词法分析、语法分析&#xf…

用uniapp 及socket.io做一个简单聊天 升级 9

比这之前优化了以下功能 上线通知 群聊里适时显示在线人数 约请好友 通过好友通过socket 相应端自动变化 PC端可以拉取摄象头拍照 PC端可以录音发送 拉起摄象头发送录象 <template><view class""><scroll-view scroll-y"true" class&…

Java启动Tomcat: Can‘t load IA 32-bit .dll on a AMD 64-bit platform报错问题解决

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

树莓派pico上手

0 介绍 不同于作为单板计算机的树莓派5&#xff0c;树莓派 pico 是一款低成本、高性能的微控制器板&#xff0c;具有灵活的数字接口。主要功能包括&#xff1a; 英国树莓派公司设计的 RP2040 微控制器芯片双核 Arm Cortex M0 处理器&#xff0c;弹性的时钟频率高达 133 MHz26…

Tomcat 靶场攻略

CVE-2017-12615 步骤一&#xff1a;环境搭建 cd vulhub/tomcat/CVE-2017-12615 docker-compose up -d docker ps 步骤二&#xff1a;漏洞复现 http://192.168.10.190:8080/ 步骤二&#xff1a;首页进行抓包 Tomcat允许适⽤put⽅法上传任意⽂件类型&#xff0c;但不允许js…

小程序-基础知识1

Mustache语法 小程序和vue一样提供了插值语法 但是小程序不能调用方法{{xxxx()}} hidden属性 hidden是所有组件都默认拥有的属性&#xff0c; hidden与wx:if的区别&#xff1a; wx:if是控制组件是否渲染,hidden控制显示或隐藏是通过添加hidden属性。 wx:for 除了可以遍历…