从读写分离到 CQRS,张大胖是如何解决性能问题的?

转自:https://mp.weixin.qq.com/s/rpiYZkxiLKa77OFw8XaBwA

不堪重负的数据库

张大胖公司的数据库已经不堪重负了。

这个系统最早是两个实习生写的, 按照最初的设计,只是内部用户玩的, 大家可以把一些闲置不用的东西放在上面做交换,  仅此而已,后来为了在互联网的大潮中赚点钱,又包裹上了一层Web的外衣, 让外界也可以访问。

大家没有想到互联网威力如此巨大, 用户量会如此之多, 他们系统使用的Mysql数据库很快就撑不住了。

作为技术负责人的张大胖早已经向老大申请了一笔费用, 专门买了一个高性能的服务器来应对, 但是汹涌而来的用户很快就把高性能给吃得连渣都不剩。

张大胖忧心忡忡: “老大,怎么办? ”

老大也是技术出身,反问道: “你分析过为什么数据库压力这么大吗? ”

“无非就是读写量太大了,尤其是有一些非常复杂的查询, 比如最近24小时最热门的物品之类,需要写很复杂的SQL, 运行起来实在太慢了。”

“我记得咱们俩聊过读写分离啊, 怎么不试一试?”

“老大啊, 你不知道,这实在是不好弄啊, 为了实现读写分离, 得把数据库拆分成master库和slave库, 还比较简单, 但是我们的系统代码也得改啊, 写数据的时候用master 库, 读数据的时候用slave库, 你知道我们这是上个世纪开发的系统,典型的遗留代码,  改动起来太麻烦了。”

老大说:“那也得改啊, 你要知道现在这个系统可是咱们公司最大的收入来源了。 你们要是不想改,就退下来,我只好去找李小疯去做了”

张大胖向来瞧不起马屁精李小疯,技术不咋地,升的到挺快,一起进公司的, 现在已经比自己高一级了。

张大胖赶紧说:“ 别别, 还是我来”

张大胖带着几个弟兄和遗留代码奋战了几个月,  工作量不亚于一次重写。 张大胖深深地体会到,别看现有代码很烂, 但是经过无数人的修补,勉强能工作。 现在自己从头写一遍,出的问题更多,很多小细节考虑不到,被测出了无数Bug。

不过好处也是巨大的,这次重写,理清了业务, 实现了读写分离,还把缓存也用上了, 最后熬了两天两夜,新系统终于上线了。

张大胖想着好日子就要开始了,崭新的代码, 崭新的系统,应该可以撑一段时间。

2复杂的查询

可是新系统上线了一周后,问题又出现了,这次的问题主要集中在一些复杂的SQL查询上,这些SQL查询最要命的得有几十行! 严重地拖累了数据库 !

张大胖找来DBA 小梁过来做优化,小梁看了半天说: “没辙, 你们的业务太复杂了, 你看看有这么多表在做Join,怎么可能快呢?”

张大胖说:“这没办法啊,数据库就是这么设计的啊, 你懂的,无论如何也得满足第一范式吧。 要不这样,你给我们创建一个视图(View) 吧, 把这个复杂的查询给封装起来, 这样我们使用起来就简单了”

“那也是换汤不换药啊, 实际的查询还在, 没有本质的改变,  照样还是慢。”

“唉,这可怎么办, 我们有20多个复杂查询,怎么才能提高速度呢?”

小梁说: “你看看这个超级复杂的查询, 不就是为了获得过去24小时的热门产品吗,要是有个表单独存放就好了 hot_products(id, name, desc, total_sold) , 这样以来一条简单的SQL就搞定”

小梁的话启发了张大胖: 实际上,一套单一的数据库表 对于报表、搜索、事务等不同的行为是不适当的 !

现在复杂的数据查询和简单的数据修改利用的就是同一套领域模型和数据库表, 现在的数据库表主要是为了新增、修改数据而设计的, 对于复杂的查询并不友好。  我们能不能单独的建一套数据库,专门应对查询呢?

有了这个专门的查询库, 用户在界面上发起查询的时候处理起来非常简单, 一条SQL就搞定,甚至都不用通过业务领域层,换句话说数据库模型和展示层是对应的!  再也不用像原来那样从原始数据库表中得到数据,转化成领域对象, 然后再转化成展示层对象, 实在是太麻烦了 !

但是这个专门的查询库该如何更新呢? 更重要的是能不能忍受数据的延迟呢?

3CQRS

张大胖把自己的想法和苦恼给老大讲了下。

老大拍了拍他的肩膀: “看来你小子开窍了啊, 想得挺深入的, 从业务上看数据的延迟可以忍受,比如过去24小时的热门产品,一点点过时的数据对用户不会产生重大的影响。只要你能达到最终一致就可以了。”

“那我们该怎么更新这个专门的查询库呢?”

“我最近在看一个叫做CQRS的东西”  老大说  “ 你遇到的这个问题可以用同样的思路来解决下”

“什么是CQRS ? ”

"Command Query Responsibility Segregation,就是命令(增删改)和查询的责任分离, 你看看这个图"

“这和我刚才的图差不多啊” 张大胖说

“所以说思路是一致的嘛, 在CQRS中, 强调的是读(Query)和写(Command) 的分离 ,  它背后的理念是用户读到的数据通常是过时的,比如过去24小时最火的产品, 既然如此, 为什么还要从数据库中读取一遍,转化为领域模型,DTO, VO, 最后在UI层展示呢? 何不直接一点,干脆为‘读’专门建立一个直接的数据源呢? 这新的数据源不一定是关系数据库,可以是Cache ,可以直接存储为xml/json数据, 只要界面查询起来方便即可。 ”

“是,最早我也是这么想的,那这个Event是怎么回事?”

“Event 就是事件喽,例如有人下了一个订单, 导致某个产品已经卖出, 这个时候就可以发布一个产品已经卖出(ProductSold)的事件 , 其中包含产品的ID, 价格,卖出时间等属性, 这样的事件被处理以后,可以变成任意的Read Model,例如过去24小时最火的产品 。”

“奥,原来是这么玩的啊, 通过事件机制把同步变成异步 ”  张大胖说 “ 还有一个问题,如果我们用CQRS, 难道我们的应用需要把所有的Command 和Query完全分开吗, 查询都通过新的数据源?  可是很多查询很简单,直接使用关系数据库就够了啊。 ”

“不,不要把摊子铺得太大, 引入一种新的技术也是需要付出代价的,我们把同步操作变成了异步的操作, 得有良好的事件处理机制才可以。 所以先用这种思路把你的当前问题,也就是复杂查询的问题解决掉吧!” 老大最后拍了板。

(完)

时间: 2024-12-30 04:45:57

从读写分离到 CQRS,张大胖是如何解决性能问题的?的相关文章

数据库(七),读写分离到CQRS

读写分离 当一个公司业务不断扩展,用户量大量增加,原来使用的数据库很可能就撑不住了.那么可以 Scale-in,扩充硬件的性能,但是很可能用户量继续增长,增加的性能很快就吃光了. 读写分离:数据库撑不住了,无非就是读写量过大,特别是有一些复杂的查询比如最近24小时最热门的产品等.需要很复杂的SQL语句,运行起来当然是慢. 但是为了读写分离,需要把数据库拆分为master库和Slave库, 市面上主要的关系数据库都支持数据复制功能,所以可以把一个数据库拆分为Master和Slave两种角色,写操作

java实现mysql数据库读写分离之定义多数据源方式

该示例是基于spring提供的AbstractRoutingDataSource,实现了一个动态数据源的功能,在spring配置中定义多个数据库分为主.从数据库,实现效果为当进行保存和修改记录时则对主表操作,查询则对从表进行操作,从而实现对数据库表的读写分离.这样做有利于提高网站的性能,特别是在数据库这一层.因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.我们通常的做法就是把查询从主库中

Django配置数据库读写分离

对网站的数据库作读写分离(Read/Write Splitting)可以提高性能,在Django中对此提供了支持,下面我们来简单看一下.注意,还需要运维人员作数据库的读写分离和数据同步. 配置数据库 我们知道在Django项目的settings中,可以配置数据库,除了默认的数据库,我在下面又加了一个db2.因为是演示,我这里用的是默认的SQLite,如果希望用MySQL,看这里 . DATABASES = { 'default': { 'ENGINE': 'django.db.backends.

浅谈高性能数据库集群——读写分离

本文主要介绍高性能数据库集群读写分离相关理论,基本架构,涉及的复杂度问题以及常见解决方案. 1 读写分离概述 基本架构图: 2 适用场景 读写分离不是银弹,并不是一有性能问题就上读写分离,而是应该先优化,例如优化慢查询,调整不合理的业务逻辑,引入缓存查询等只有确定系统没有优化空间后才考虑读写分离集群 3 引入的系统复杂度问题 问题一 主从复制延迟 问题二 分配机制 如何将读写操作区分开来,然后访问不同的数据库服务器? 解决方案1 客户端程序代码封装实现 基本架构图 业界开源实现 Sharding

Akka-CQRS(0)- 基于akka-cluster的读写分离框架,构建gRPC移动应用后端架构

上一篇我们讨论了akka-cluster的分片(sharding)技术.在提供的例子中感觉到akka这样的分布式系统工具特别适合支持大量的带有内置状态的,相对独立完整的程序在集群节点上分布运算.这里重点要关注这些程序的内部状态,它们会占用系统资源包括内存.把状态保存在内存里相对存放在数据库里能显著提高程序运算效率.在系统出现各种情况下对这些非持久化的程序状态的管理自然就成为了需要考虑的问题,此其一.在一个多用户.高并发的大型分布式系统里往往数据库数据使用会产生大量的冲突影响系统性能.如果能够把数

重新学习Mysql数据13:Mysql主从复制,读写分离,分表分库策略与实践

一.MySQL扩展具体的实现方式 随着业务规模的不断扩大,需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量. 关于数据库的扩展主要包括:业务拆分.主从复制.读写分离.数据库分库与分表等.这篇文章主要讲述数据库分库与分表 (1)业务拆分 在?大型网站应用之海量数据和高并发解决方案总结一二?一篇文章中也具体讲述了为什么要对业务进行拆分. 业务起步初始,为了加快应用上线和快速迭代,很多应用都采用集中式的架构.随着业务系统的扩大,系统变得越来越复杂,越来越难以维护,开发效率变得越

mysql读写分离的三种实现方式

1 程序修改mysql操作类可以参考PHP实现的Mysql读写分离,阿权开始的本项目,以php程序解决此需求.优点:直接和数据库通信,简单快捷的读写分离和随机的方式实现的负载均衡,权限独立分配缺点:自己维护更新,增减服务器在代码处理 2 amoeba参考官网:http://amoeba.meidusa.com/优点:直接实现读写分离和负载均衡,不用修改代码,有很灵活的数据解决方案缺点:自己分配账户,和后端数据库权限管理独立,权限处理不够灵活 3 mysql-proxy参考 mysql-proxy

mysql主从复制与读写分离

MySQL主从复制与读写分离 MySQL主从复制(Master-Slave)与读写分离(MySQL-Proxy)实践 Mysql作为目前世界上使用最广泛的免费数据库,相信所有从事系统运维的工程师都一定接触过.但在实际的生产环境中,由单台Mysql作为独立的数据库是完全不能满足实际需求的,无论是在安全性,高可用性以及高并发等各个方面. 因此,一般来说都是通过 主从复制(Master-Slave)的方式来同步数据,再通过读写分离(MySQL-Proxy)来提升数据库的并发负载能力 这样的方案来进行部

mysql读写分离

mysql读写分离  静态分离:直接将服务器地址写入程序  动态分离:通过代理服务器对数据进行读写操作,由代理服务器判定读写操作,在主服务器上写数据,在          从服务器上读数据.    1.使用mysql-proxy实现读写分离  # ./mysql-proxy --proxy-backend-addresses=10.0.5.150:3306 --proxy-read-only-backend-addresses=10.0.5.151:3306 --proxy-lua-script