当前位置:网站首页>想要做读写分离,送你一些小经验
想要做读写分离,送你一些小经验
2020-11-06 01:15:50 【尹吉欢】
读写分离是应用中提升数据访问性能最常见的一种技术,当用户量越来越多,访问量越来越大,单节点数据库难免会遇到性能瓶颈。很多场景基本上都是读多写少,所以增加多个从节点来分担主节点的压力自然是水到渠成的事情。
在应用接入读写分离后,难免会有一些我们意料之外的问题,这篇文章主要给大家介绍下一些经常会遇到的问题,有其他的问题欢迎留言补充。
分库分表之前的文章可以查看:http://mp.weixin.qq.com/mp/homepage?__biz=MzIwMDY0Nzk2Mw==&hid=4&sn=1b96093ec951a5f997bdd3225e5f2fdf&scene=18#wechat_redirect
实现方式
对于读写分离的使用,主要分为两种方式,客户端方式和代理方式。
客户端方式可以自己用 Spring 自带的 AbstractRoutingDataSource 来实现,也可以用开源的框架来实现,比如 Sharding-JDBC。
代理方式需要编写代理服务来对所有节点进行管理,应用不需要关注多个数据库节点信息。可以自己实现,也可以用开源的框架,也可以用商业的云服务。
数据延迟
谈到数据延迟,你先得理解主从架构的原理。对数据的增删改操作在主库上执行,查询在从库上执行,当数据刚插入到主库,然后马上去查询的时候,很有可能数据还没同步到从库上,就会出现查询不到的情况。
像我之前在某些网站发表文章,发表之后跳转到列表页面,发现没有新发表的文章,重新刷新下页面又有了,这一看这就是读写分离后的数据延迟导致的现象。
强制路由
数据延迟要不要解决,一般取决于业务场景。对于实时性要求没有那么高的业务场景,允许一定的延迟,对于实时性要求高的场景,唯一的方式就是直接从主库进行查询,这样才能及时读到刚插入或者修改后最新的数据。
强制路由就是一种解决方案,也就是将读请求强制分发到主库进行查询。大部分中间件都支持 Hint 语法/FORCE_MASTER/和/FORCE_SLAVE/。
以 Sharding-JDBC 举例,框架提供了 HintManager 来强制路由,使用方式如下:
HintManager hintManager = HintManager.getInstance();hintManager.setMasterRouteOnly();
为了方便使用,建议封装一个注解,在需要实时查询的业务方法上加上注解,通过切面进行强制路由的设置。
注解使用:
@MasterRoute@Overridepublic UserBO getUser(Long id) {log.info("查询用户 [{}]", id);if (id == null) {throw new BizException(ResponseCode.PARAM_ERROR_CODE, "id不能为空");}UserDO userDO = userDao.getById(id);if (userDO == null) {throw new BizException(ResponseCode.NOT_FOUND_CODE);}return userBoConvert.convert(userDO);}
切面设置:
@Aspectpublic class MasterRouteAspect {@Around("@annotation(masterRoute)")public Object aroundGetConnection(final ProceedingJoinPoint pjp, MasterRoute masterRoute) throws Throwable {HintManager hintManager = HintManager.getInstance();hintManager.setMasterRouteOnly();try {return pjp.proceed();} finally {hintManager.close();}}}
事务操作
在事务中的读请求,走主库还是从库呢?对于这个问题,最简单的方式就是所有事务中的操作都走主库,在事务中经常会存在插入,然后再重新查询的场景,此时事务没提交,就算同步很快,从库也是没有数据的,所以只能走主库。
但还有一些请求,只需要查询从库就行了,如果针对所有事务中的操作都强制路由,也不是很好。在 Sharding-JDBC 中的做法挺好的,对于同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。如果我们在数据写入之前有查询请求,还是走的从库,减轻主库压力。
动态强制路由
在功能开发的时候就决定了哪些接口要强制走主库,这个时候我们会在代码上进行路由的控制,也就是前面讲的自定义注解。如果有些是没有加的,但是在线上运行的时候发现还是要走主库才可以,这个时候就需要改代码重新发布了。
动态强制路由可以结合配置中心来实现,通过配置的方式来决定哪些接口要强制路由,然后在 Filter 中通过 HintManager 来设置,避免改代码重启。
也可以通过切面精确到业务方法级别的动态路由配置。
流量分发
场景一:
假设你有一个主节点,两个从节点,读请求较多,两个从节点压力有点大。这个时候只能增加第三个从节点来分担压力。现象是主库的压力并不大,写入较少,从成本来考虑,是否可以不增加第三个从节点呢?
场景二:
假设你有一个 8 核 64G 的主库,8 核 64G 的从库,4 核 32G 的从库,从配置上来看,4 核 32G 的从库处理能力肯定是要低于其他两个的,这个时候如果我们没有定制流量分发的比例,就会出现低配数据库压力过高而导致的问题。当然这个也能避免使用不同规则的从库。
上面的场景需要能够对请求进行管理,在 Sharding-JDBC 中提供了读写分离的路由算法,我们可以自定义算法来进行流量的分发管理。
实现算法类:
public class KittyMasterSlaveLoadBalanceAlgorithm implements MasterSlaveLoadBalanceAlgorithm {private RoundRobinMasterSlaveLoadBalanceAlgorithm roundRobin = new RoundRobinMasterSlaveLoadBalanceAlgorithm();@Overridepublic String getDataSource(String name, String masterDataSourceName, List<String> slaveDataSourceNames) {String dataSource = roundRobin.getDataSource(name, masterDataSourceName, slaveDataSourceNames);// 控制逻辑,比如不同的从节点(配置不同)可以有不同的比例return dataSource;}@Overridepublic String getType() {return "KITTY_ROUND_ROBIN";}@Overridepublic Properties getProperties() {return roundRobin.getProperties();}@Overridepublic void setProperties(Properties properties) {roundRobin.setProperties(properties);}}
基于 SPI 机制的配置:
org.apache.shardingsphere.core.strategy.masterslave.RoundRobinMasterSlaveLoadBalanceAlgorithmorg.apache.shardingsphere.core.strategy.masterslave.RandomMasterSlaveLoadBalanceAlgorithmcom.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithm
读写分离的配置:
spring.shardingsphere.masterslave.load-balance-algorithm-class-name=com.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithmspring.shardingsphere.masterslave.load-balance-algorithm-type=KITTY_ROUND_ROBIN
关于作者:尹吉欢,简单的技术爱好者,《Spring Cloud 微服务-全栈技术与案例解析》, 《Spring Cloud 微服务 入门 实战与进阶》作者, 公众号猿天地发起人。
版权声明
本文为[尹吉欢]所创,转载请带上原文链接,感谢
http://cxytiandi.com/blog/detail/36514
边栏推荐
- C++ 数字、string和char*的转换
- C++学习——centos7上部署C++开发环境
- C++学习——一步步学会写Makefile
- C++学习——临时对象的产生与优化
- C++学习——对象的引用的用法
- C++编程经验(6):使用C++风格的类型转换
- Won the CKA + CKS certificate with the highest gold content in kubernetes in 31 days!
- C + + number, string and char * conversion
- C + + Learning -- capacity() and resize() in C + +
- C + + Learning -- about code performance optimization
猜你喜欢
-
C + + programming experience (6): using C + + style type conversion
-
Latest party and government work report ppt - Park ppt
-
在线身份证号码提取生日工具
-
Online ID number extraction birthday tool
-
️野指针?悬空指针?️ 一文带你搞懂!
-
Field pointer? Dangling pointer? This article will help you understand!
-
HCNA Routing&Switching之GVRP
-
GVRP of hcna Routing & Switching
-
Seq2Seq实现闲聊机器人
-
【闲聊机器人】seq2seq模型的原理
随机推荐
- LeetCode 91. 解码方法
- Seq2seq implements chat robot
- [chat robot] principle of seq2seq model
- Leetcode 91. Decoding method
- HCNA Routing&Switching之GVRP
- GVRP of hcna Routing & Switching
- HDU7016 Random Walk 2
- [Code+#1]Yazid 的新生舞会
- CF1548C The Three Little Pigs
- HDU7033 Typing Contest
- HDU7016 Random Walk 2
- [code + 1] Yazid's freshman ball
- CF1548C The Three Little Pigs
- HDU7033 Typing Contest
- Qt Creator 自动补齐变慢的解决
- HALCON 20.11:如何处理标定助手品质问题
- HALCON 20.11:标定助手使用注意事项
- Solution of QT creator's automatic replenishment slowing down
- Halcon 20.11: how to deal with the quality problem of calibration assistant
- Halcon 20.11: precautions for use of calibration assistant
- “十大科学技术问题”揭晓!|青年科学家50²论坛
- "Top ten scientific and technological issues" announced| Young scientists 50 ² forum
- 求反转链表
- Reverse linked list
- js的数据类型
- JS data type
- 记一次文件读写遇到的bug
- Remember the bug encountered in reading and writing a file
- 单例模式
- Singleton mode
- 在这个 N 多编程语言争霸的世界,C++ 究竟还有没有未来?
- In this world of N programming languages, is there a future for C + +?
- es6模板字符
- js Promise
- js 数组方法 回顾
- ES6 template characters
- js Promise
- JS array method review
- 【Golang】️走进 Go 语言️ 第一课 Hello World
- [golang] go into go language lesson 1 Hello World