CA,给了数据库,给了机器,为啥也扩不了容?
随着业务越来越复杂,数据量越来越大,并发量越来越大,数据库的性能越来越低。好不容易找运维申请了两台机器,让DBA部署了几个实例,想把一些业务库拆分出来,却发现拆不出来,扩不了容,尴尬!
因为数据库强关联在一起,无法通过增加数据库实例扩容,就是一个耦合的典型案例。
场景还原
有一个公共用户数据库DB_USER,里面table_user存放了通用的用户数据:
table_user (uid, name, passwd, …)
在数据量比较小,并发量比较小,业务还没有这么复杂的时候,为了提高资源利用率(程序员才没有考虑什么资源利用率,更多的是图方便),业务A把用户个性化的数据也放在这个库里:
table_A(uid, A业务的个性化属性)
业务A有一个需求,即要展现用户公共属性,又要展现业务A个性化属性,程序员经常这么实现的:
select * from table_user, table_A
where table_user.uid = table_A.uid
and table_user.uid = $uid
初期关联查询没有任何问题,单条记录访问,命中索引,一次查询所有数据,简单高效。
如何产生各业务数据耦合?
通过join实现业务,导致通用表table_user和业务表table_A必须存在于一个数据库实例里。
如果业务B也这么做,业务C也这么做,会导致公用业务,业务A,业务B,业务C都必须存在于一个数据库实例里。
会产生什么潜在问题呢?
假如A业务线上线了一个新功能,不小心进行了全表扫描,导致数据库CPU100%,数据库实例性能下降,由于实例共用,通用业务,业务B和业务C都会受影响。
即某个业务线的数据库性能急剧下降导致所有业务都受影响,这种耦合,历史总是惊人的相似:
- 业务B的大boss在群里首先发飙:“技术都干啥了,怎么系统挂了”
- 业务B的rd一脸无辜:“业务A上线了,所以我们挂了”
额,然而,这个理由,好像在大boss那解释不通…
- 业务B的大boss:“赶紧加几台机器,拆分开”
- 业务B的rd一脸无奈:“加机器加实例也扩容不了”
- 业务B的大boss对业务2的rd吼道“还想甩锅,拖出去祭天”
...
唉,加了几台机器,加了几个实例,然而并没有什么卵用,都耦合在一个实例里,完全扩不了容。
那,如何解除公共数据库与业务数据库的耦合?
第一步:公共数据访问下沉服务化
还是上面的例子,当公共的user数据访问服务化之后,依据服务化的原则:
- 业务层只能通过服务RPC接口访问数据
- 底层user库属于user服务私有
- 任何上游不允许跨过服务访问底层的user库
第二步:垂直拆分,个性化数据访问上浮
原来业务方:
- 通过join一次性获取通用的数据和个性化的业务数据数据
服务化+垂直拆分后,变成两次访问:
- 一次取得业务数据(业务可以直接调用自己的数据库,也可以自己做业务服务调用RPC接口)
- 一次取得共性数据(调用通用的RPC接口)
两种方式相比:
- 之前的方式其实业务代码可能会更简单一些,因为它是将这个业务逻辑放在了SQL语句中,但是导致数据库耦合在了一起
- 后面这种方式就是业务的代码会更复杂,会变成多次访问,将原来在SQL中进行的逻辑计算变成业务代码中的逻辑计算,但是数据库解耦了
业务复杂,数据量大,并发老大,对扩展性要求更高的架构,一定是后者。
此时各业务有自己的库,公共有公共的库:
- 早期:可以放在一个数据库实例里
- 后期:可以很容易地通过新增数据库实例,把user库或者业务A/B/C的库拆分出来,实现增加机器增加实例就实现扩容
个性业务数据访问垂直拆分,共性数据访问服务化下沉,只是一个很小的优化点,但对于数据库解耦却是非常的有效。
- WebSocket刨根问底(三)之群聊
- SDNLAB群分享(四):利用ODL下发流表创建VxLAN网络
- 一个简单的案例带你入门Dubbo分布式框架
- Ajax上传图片以及上传之前先预览
- Spring Cloud中Hystrix的服务降级与异常处理
- Open vSwitch源码解析之基于VxLAN实现NSH解析功能
- Spring Cloud自定义Hystrix请求命令
- JavaScript面试问题:事件委托和this
- Spring Cloud中的断路器Hystrix
- js的隐含参数(arguments,callee,caller)使用方法
- Spring Cloud中的负载均衡策略
- Spring Cloud中负载均衡器概览
- 没有event loop的PHP
- RestTemplate的逆袭之路,从发送请求到负载均衡
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Nginx部署Vue项目以及解决刷新页面404
- Kafka核心API——Stream API
- Kafka核心API——Consumer消费者
- Kafka核心API——Producer生产者
- Linux Lab v0.5 正式发布,功能强大,用法简单
- Kafka核心API——AdminClient API
- PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
- Kafka的安装与配置
- PyQt5 技术篇-如何彻底删除控件?布局移除控件方法。
- PyQt5 技术篇-设置滚动条拉动位置,scrollArea滚动条位置设置方法。
- CentOS8更换yum源后出现同步仓库缓存失败的问题
- log4j配置方式
- 基于MHA搭建MySQL Replication集群高可用架构
- PyQt5 技巧篇-解决相对路径无法加载图片问题,styleSheet通过"相对"路径加载图片,python获取当前运行文件的绝对路径。
- 基于MMM搭建MySQL Replication集群高可用架构