mysql 与 redis 的选型问题实践
背景
当前系统当前使用 mysql+redis 的技术架构,现需要对该系统进行重构工作,其中就需要对数据存储技术进行选型。
业务场景如下:
该系统为油站价格优惠系统,涉及众多油站(1w+)、商品(92/95/柴油/天然气等)、渠道(端内各渠道/各大 C 共 1k+)维度,涉及价格数量共 1 亿(1wx10x1k)。
优惠策略除各维度价格之外,还存在城市、全国维度等非“单点”策略。
优惠策略存在周期、人群等复杂筛选条件。
具体情况如下:
运营在后台配置优惠策略,数据保存在 mysql 数据库;
C 端用户需要获取某一门店商品价格,需要根据条件查询 mysql 数据库,并对查询结果进行匹配过滤;
后因性能问题引入缓存,对各维度查询结果进行缓存;
后又因缓存穿透、性能问题,演变成:C 端不读区数据库只读区缓存,运营配置完成后进行一轮异步刷缓存。
选型需求如下:
以稳定性为主要目标,将系统拆分为配置域与执行域:配置域处理运营配置、数据刷新等复杂不可控的业务;
策略加工后输送到运行域(即 C 端),运行域仅读取加工完的数据,进行简单的计算处理;
现在需要找到一个合适的存储载体来存在这些加工好后的运营策略。系统当前运行状态下,对应的载体应该是 redis,但是作为数据存储载体,其是否合适需要进一步讨论。
分析
当前使用场景的要求
查询场景:查询门店 x 商品 x 渠道维度对应的策略
数据量:门店 x 商品 x 渠道,一共有 1 亿个渠道
qps:当前访问峰值需要支持 1wqps
redis 作为存储载体
首先,redis 作为存储载体,是最“平滑”的选择,因为系统的现状,当前位置用的就是 redis。
但是,随着系统进行拆分,以及任务加工逻辑的复杂,继续使用 redis 会面临以下问题:
在执行域系统,redis 将成为唯一数据存储载体,这与 redis 本身作为缓存的定位有些不符;
系统中的数据存储载体正常应该是基于磁盘的数据库,而非基于内存的缓存,虽然 redis 也支持持久化;
另外,redis 的数据查询能力较弱,无法支持 sql 查询,事务支持不完善。因为在当前场景无此要求,所以可以忽略。
当然,使用 redis 也有其优越性:
key-value 的结构天然适合当前场景的“定点”查询;
redis 基于内存,能够支持高速访问;但同时也让 redis 的成本较高(虽然在企业使用上没有这方面考量);可以水平扩展,所以容易支持大数据量,高 qps。
总结:
从硬性要求考虑,redis 可以满足;
但从功能定位上,把 redis 作为一个数据存储的载体,不怎么合适
mysql 作为存储载体
另外一个方案就是使用回 mysql。
一般来说,从 mysql 转到 redis 都是因为性能问题,对热点 key 进行缓存,以提升访问速度,同时降低数据库的磁盘压力。
而系统当前引入 redis 的初衷,并非是为了热点数据的性能考量,而是因为某些“不得已”的性能瓶颈。所以在 redis 中缓存的并非只是热点数据,而是全盘的 key-value。
关于“不得已”的性能瓶颈:
前面的业务介绍有提到,除了门店-商品-渠道维度,还有类似“城市”、“全国”等全局维度;
系统采取的方案是先查询当前维度的策略,然后再查一次城市维度的策略,最后合并到一起;
随着城市维度的策略增多,数据库的响应时间出现问题,于是就因为“性能”问题引入了 redis。
上面的“优化”手段,其实并没有解决实质问题,只是将 mysql 中的“大数据量”问题换了一种形式存在:redis 中的大 key 问题。
在这之后,我们也掉到了这个坑里面;因为运营配置了较多的城市维度策略,引发了 redis 大 key(1.8M)问题,带来了一个线上故障。这也是这次重构工作的重要背景。
补充一句,在缓存出了问题之后,我们其实还可以将问题再次转移,给系统“续命”:将缓存中的 key 在应用本地再缓存一次。但这样做只会跟上一次“优化”的结果一样,给系统再一次埋下定时炸弹。
回到存储方案选型上来,通过历史我们可以得知:这个系统其实并不一定需要缓存。从线上的监控来看,mysql 的响应速度不比 redis 差太多。
那么,mysql 是否可以在当前场景作为存储方案的产品呢?
优点就不说了,直接看最为关键的两个问题:
响应速度:公司内部使用了 dbproxy 优化,从监控结果上看,性能跟 redis 所差不多;
qps:需要添加备库以支撑 qps,当前公司可申请的最大规格无法满足 qps 要求,需要 dba 操作;
数据量:需要进行分库分表,开发/维护成本较高。
总结:
从硬性要求考虑,mysql 可以满足;
但分库分表开发维护成本较高
redis + mysql
先不管是否可能,顺着前面的方案,采取 redis+mysql 方案会怎么样?
首先,redis 的“定位”问题解决,因为有了 mysql 来作为数据存储载体;
然后,因为 redis 的缓存,qps 的问题可能会有一些缓解,但不一定能打包票,还要考虑命中率及缓存可用性的问题;
最后 mysql 的大数据量无法解决,还是需要分库分表。
一个 Redis 与 MySQL 之间的方案
前面我们说 redis 最大的问题是产品定位问题。那么,redis 为什么不适合作为存储载体?或者说,redis 为什么不能作为数据库替代 mysql?
我觉得核心问题是持久化能力。
redis 虽有 rdb、aof 两种持久化方式,但其本质是仍一个内存数据库,持久化只是用于辅助内存的读写,宕机仍存在丢失风险;
aof 持久化虽然可以做到跟内存同步,但也是以牺牲性能作为代价的。
所以我们要找的应该是可持久化的、方便水平扩展的数据库产品。
刚好内部自研的 fusion 可以满足这个要求:
其定位介于 Redis 与 MySQL 之间,是一款基于 rocksdb 的主存储数据库,又实现了 redis 协议;
部署在 ssd 之上,上层自带 cache 缓存;
可支持水平扩展;
内部有团队支持。
总结
数据结构:门店 x 商品 x 渠道
mysql:b+树索引组合查询可以支持,另外支持 sql 丰富的查询统计
redis:k-v 结构可以支持
qps:1wqps
mysql:需要添加备库以支撑 qps,当前公司可申请的最大规格无法满足 qps 要求,需要 dba 操作。redis:可以水平扩展
数据量
mysql:需要进行分库分表,开发/维护成本较高
redis:可以水平扩展
rt
mysql:公司内部使用了 dbproxy 优化,从监控结果上看,性能跟 redis 所差不多
redis:基于内存,快速
定位
mysql:基于磁盘,适合数据存储
redis:基于内存(有持久化做辅助),适合做缓存
结论
因为功能定位问题,不用 redis;
使用类似 mysql 的基于磁盘的数据产品,但需要解决 mysql 的两个问题;
使用 fusion,产品定位介于 mysql、redis 之间,是数据存储载体,又可以支持大数据量水平扩展、支持高 qps。
参考
redis 是否可以代替 mysql 进行数据存储?怎么样? https://www.ucloud.cn/yun/ask/77842.html
fusion https://www.dandelioncloud.cn/article/details/1496098463225057281
版权声明: 本文为 InfoQ 作者【苏格拉格拉】的原创文章。
原文链接:【http://xie.infoq.cn/article/69403d4e74e00871aef847ca2】。文章转载请联系作者。
评论