共计 4944 个字符,预计需要花费 13 分钟才能阅读完成。
作者:极光高级工程师—包利
摘要
极光推送后盾标签 / 别名零碎存储超过百亿条数据, 高峰期 QPS 超过 50 万, 且随着业务的倒退,存储容量和访问量还在一直减少。之前零碎存在的一些瓶颈也逐步浮现,所以近一两年继续做了很多的优化工作,最终达到十分不错的成果。近期,通过积攒和积淀后,将这一部分的工作进行总结。
背景
以后的旧零碎中次要存储标签 / 别名与注册 ID 的互相映射关系, 应用 Key-Value 构造存储, 思考到一个注册 ID 可能有多个标签, 同时一个标签也存在多个不同的注册 ID, 这部分数据应用 Redis 存储中的 Set 数据结构; 而一个注册 ID 只有一个别名, 同时一个别名也存在多个不同的注册 ID, 这部分数据应用 String/Set 数据结构。因为此局部数据量过大, 思考到存储老本, Redis 应用的单 Master 模式, 而最终数据的落地应用 Pika 存储(一品种 Redis 的文件存储)。Pika 与 Redis 中的数据由业务方保持一致, Redis 失常可用时, 读 Redis; 当 Redis 不可用时读 Pika, Redis 复原可用后, 从 Pika 复原数据到 Redis, 从新读 Redis。旧零碎的存储架构如下:
从下面的架构图能够看到, Redis/Pika 均采纳主从模式, 其中 Redis 只有 Master, 配置管理模块用来保护 Redis/Pika 集群的主从关系, 配置写入 ZooKeeper 中, 业务模块从 ZooKeeper 中读取配置, 不做配置变更。所有的配置变更由配置管理模块负责. 当须要迁徙, 扩容, 缩容的时候, 必须通过配置管理模块操作。这个旧零碎的优缺点如下:
长处:
配置集中管理, 业务模块不须要离开独自配置
读取 Redis 中数据, 保障了高并发查问效率
Pika 主从模式, 保障了数据落地, 不失落
配置管理模块保护分片 slot 与实例的映射关系, 依据 Key 的 slot 值路由到指定的实例
毛病:
Redis 与 Pika 中存储的数据结构不统一, 减少了代码复杂度
Redis 单 Master 模式, Redis 某个节点不可用时, 读申请穿透到 Pika, 而 Pika 不能保障查问效率, 会造成读申请耗时减少甚至超时
Redis 故障复原后, 须要从 Pika 从新同步数据, 减少了零碎不可用持续时间, 且数据一致性须要更加简单的计算来保障
当迁徙 / 扩容 / 缩容时须要手动操作配置管理模块, 步骤繁琐且容易出错
Redis 中存储了与 Pika 同样多的数据, 占用了大量的内存存储空间, 资源老本很高
整个零碎的可用性还有晋升空间, 故障复原工夫能够尽量缩短
配置管理模块为单点, 非高可用, 当此服务 down 掉时, 整个集群不是高可用, 无奈感知 Redis/Pika 的心跳状态
超大 Key 打散操作须要手动触发. 零碎中存在个别标签下的注册 ID 过多, 存储在同一个实例上, 容易超过实例的存储下限, 而且单个实例限度了该 Key 的读性能
旧零碎毛病剖析
思考到旧零碎存在以上的毛病, 次要从以下几个方向解决:
Redis 与 Pika 中存储的数据结构不统一, 减少了代码复杂度
剖析: 旧零碎中 Redis 与 Pika 数据不统一次要是 Pika 晚期版本 Set 数据结构操作效率不高, String 数据结构操作的效率比拟高, 但获取标签 / 别名下的所有注册 ID 时须要遍历所有 Pika 实例, 这个操作十分耗时, 思考到最新版本 Pika 曾经优化 Set 数据结构, 进步了 Set 数据结构的读写性能, 应该放弃 Redis 与 Pika 数据结构的一致性。
Redis 单 Master 模式, Redis 某个节点不可用时, 读申请穿透到 Pika, 而 Pika 不能保障查问效率, 会造成读申请耗时减少甚至超时
剖析: Redis 单 Master 模式危险极大。须要优化为主从模式, 这样可能在某个 Master 故障时可能进行主从切换, 不再从 Pika 中复原数据, 缩小故障复原工夫, 缩小数据不统一的可能性。
Redis 故障复原后, 须要从 Pika 从新同步数据, 减少了零碎不可用持续时间, 且数据一致性须要更加简单的计算来保障
剖析: 这个零碎复原工夫过长是因为 Redis 是单 Master 模式, 且没有长久化, 须要把 Redis 优化成主从模式且必须开启长久化, 从而简直不须要从 Pika 复原数据了, 除非 Redis 的主从实例全副同时不可用。不须要从 Pika 复原数据后, 那么 Redis 中的数据在 Redis 主从实例产生故障时, 就和 Pika 中的数据统一了。
当迁徙 / 扩容 / 缩容时须要手动操作配置管理模块, 步骤繁琐且容易出错
剖析: 配置管理模块手动干涉操作过多, 非常容易出错, 这部分应尽量减少手动操作, 思考引入 Redis 哨兵, 可能替换大部分的手动操作步骤。
Redis 中存储了与 Pika 同样多的数据, 占用了大量的内存存储空间, 资源老本很高
剖析: 通过对 Redis 中的各个不同维度数据进行数据量和访问量以及拜访起源剖析(如下图)。内部申请量(估算) 这栏的数据反馈了各个不同 Key 的单位工夫内访问量状况。
Redis 的存储数据次要分为标签 / 别名到注册 ID 和注册 ID 到标签 / 别名两局部数据. 通过剖析得悉, 标签 / 别名到注册 ID 的数据约占 1/3 左右的存储空间, 访问量占到 80%; 注册 ID 到标签 / 别名的数据约占 2/3 左右的存储空间, 访问量占到 20%。能够看到, 红色数字局部为拜访的 Pika, 彩色局部拜访的 Redis, 3.7% 这项的数据能够优化成拜访 Redis, 那么能够得出结论, 红色的数据在 Redis 中是永远拜访不到的。所以能够思考将 Redis 中注册 ID 到标签 / 别名这部分数据删掉, 拜访此局部数据申请到 Pika, 可能节俭约 2/3 的存储空间, 同时还能保障整个零碎的读性能。
整个零碎的可用性还有晋升空间, 故障复原工夫能够尽量缩短
剖析: 这部分次要因为其中一项服务为非高可用, 而且整个零碎架构的复杂性较高, 以及数据一致性绝对比拟难保障, 导致故障复原工夫长, 思考应将所有服务均优化为高可用, 同时简化整个零碎的架构。
配置管理模块为单点, 非高可用, 当此服务 down 掉时, 整个集群不是高可用, 无奈感知 Redis/Pika 的心跳状态
剖析: 配置手动治理危险也十分大, Pika 主从关系通过配置文件手动指定, 配错后将导致数据错乱, 产生脏数据. 须要应用 Redis 哨兵模式, 用哨兵治理 Redis/Pika, 配置管理模块不再间接治理所有 Redis/Pika 节点, 而是通过哨兵治理, 同时再产生主从切换或者节点故障时告诉配置管理模块, 自动更新配置到 Zookeeper 中, 迁徙 / 扩容 / 缩容时也根本不必手动干涉。
超大 Key 打散操作须要手动触发。零碎中存在个别标签下的注册 ID 过多, 存储在同一个实例上, 容易超过实例的存储下限, 而且单个实例限度了该 Key 的读性能
剖析: 这部分手动操作, 应该优化为主动触发, 主动实现迁徙, 缩小人工干预, 节俭人力老本。
Redis 哨兵模式
Redis 哨兵为 Redis/Pika 提供了高可用性, 能够在无需人工干预的状况下抵制某些类型的故障, 还反对监督, 告诉, 主动故障转移, 配置管理等性能:
监督: 哨兵会一直查看主实例和从实例是否按预期工作
告诉: 哨兵能够将呈现问题的实例以 Redis 的 Pub/Sub 形式告诉到应用程序
主动故障转移: 如果主实例呈现问题, 能够启动故障转移, 将其中一个从实例降级为主, 并将其余从实例重新配置为新主实例的从实例, 并告诉应用程序要应用新的主实例
配置管理: 创立新的从实例或者主实例不可用时等都会告诉给应用程序
同时, 哨兵还具备分布式性质, 哨兵自身被设计为能够多个哨兵过程协同工作, 当多个哨兵就给定的主机不再可用这一事实达成共识时, 将执行故障检测, 这升高了误报的可能性。即便不是所有的哨兵过程都在工作, 哨兵仍能失常工作, 从而使零碎可能应答故障。
Redis 哨兵 + 主从模式可能在 Redis/Pika 产生故障时及时反馈实例的衰弱状态, 并在必要时进行主动主从切换, 同时会通过 Redis 的 sub/pub 机制告诉到订阅此音讯的应用程序。从而应用程序感知这个主从切换, 可能短时间将链接切换到衰弱的实例, 保障服务的失常运行。
没有采纳 Redis 的集群模式, 次要有以下几点起因:
以后的存储规模较大, 集群模式在故障时, 复原工夫可能很长
集群模式的主从复制通过异步形式, 故障复原期间不保证数据的一致性
集群模式中从实例不能对外提供查问, 只是主实例的备份
无奈全局含糊匹配 Key, 即无奈遍历所有实例来查问一个含糊匹配的 Key
最终解决方案
综上, 为了保障整个存储集群的高可用, 缩小故障复原的工夫, 甚至做到故障时对局部业务零影响, 咱们采纳了 Redis 哨兵 +Redis/Pika 主从的模式, Redis 哨兵保障整个存储集群的高可用, Redis 主从用来提供查问标签 / 别名到注册 ID 的数据, Pika 主从用来存储全量数据和一些注册 ID 到标签 / 别名数据的查问。须要业务保障所有 Redis 与 Pika 数据的全量同步。新计划架构如下:
从下面架构图来看, 以后 Redis/Pika 都是多主从模式, 同时别离由不同的多个哨兵服务监督, 只有主从实例中任一个实例可用, 整个集群就是可用的。Redis/Pika 集群内蕴含多个主从实例, 由业务方依据 Key 计算 slot 值, 每个 slot 依据配置管理模块指定的 slot 与实例映射关系。如果某个 slot 对应的 Redis 主从实例均不可用, 会查问对应的 Pika, 这样就保障整个零碎读申请的高可用。这个新计划的优缺点如下:
长处:
整个零碎所有服务, 所有存储组件均为高可用, 整个零碎可用性十分高
故障复原工夫快, 实践受骗有 Redis/Pika 主实例故障, 只会影响写入申请, 故障工夫是哨兵检测的间隔时间; 当 Redis/Pika 从实例故障, 读写申请都不受影响, 读服务能够主动切换到主实例, 故障工夫为零, 当从实例复原后主动切换回从实例
标签 / 别名存储隔离, 读写隔离, 不同业务隔离, 做到互不烦扰, 依据不同场景扩缩容
缩小 Redis 的内存占用 2/3 左右, 只应用原有存储空间的 1/3, 缩小资源老本
配置管理模块高可用, 其中一个服务 down 掉, 不影响整个集群的高可用, 只有其中一台服务可用, 那么整个零碎就是可用
能够平滑迁徙 / 扩容 / 缩容, 能够做到迁徙时无需业务方操作, 无需手动干涉, 主动实现; 扩容 / 缩容时运维同步好数据, 批改配置管理模块配置而后重启服务即可
超大 Key 打散操作主动触发, 整个操作对业务方无感知, 缩小运维老本
毛病:
Redis 主从实例均可不必时, 整个零碎写入这个实例对应 slot 的数据均失败, 思考到从 Pika 实时同步 Redis 数据的难度, 并且主从实例均不可用的概率非常低, 抉择容忍这种状况
哨兵治理减少零碎了的复杂度, 当 Redis/Pika 实例主从切换时告诉业务模块解决容易出错, 这部分性能曾经过严格的测试以及线上长时间的性能测验
其余优化
除了通过以上架构优化, 本次优化还包含以下方面:
通过 IO 复用, 由原来的每个线程一个实例链接, 改为在同一个线程同时治理多个链接, 进步了服务的 QPS, 升高高峰期的资源使用率 (如负载等)
之前的旧零碎存在多个模块相互调用状况, 缩小模块间耦合, 缩小部署及运维老本
之前的旧零碎模块间应用 MQ 交互, 效率较低, 改为 RPC 调用, 进步了调用效率, 保障调用的成功率, 缩小数据不统一, 不便定位问题
不再应用 Redis/Pika 定制化版本, 可依据须要, 降级到 Redis/Pika 官网的最新稳固版本
查问模块在查问大 Key 时减少缓存, 缓存查问后果, 此缓存过期前不再查问 Redis/Pika, 进步了查问效率
瞻望
将来此零碎还能够从以下几个方面持续改良和优化:
大 Key 存储状态更加智能化治理, 减少设置时的大 Key 自动化迁徙, 使存储更加平衡
制订正当的 Redis 数据过期机制, 升高 Redis 的存储量, 缩小服务存储老本
减少汇合操作服务, 实现跨 Redis/Pika 实例的交加 / 并集等操作, 并增加缓存机制, 进步上游服务拜访效率, 进步推送音讯下发效率
总结
本次系统优化在原有存储组件的根底上, 依据服务和数据的特点, 正当优化服务间调用形式, 优化数据存储的空间, 将 Redis 当作缓存, 只存储访问量较大的数据, 升高了资源使用率。Pika 作为数据落地并承载访问量较小的申请, 依据不同存储组件的优缺点, 正当调配申请形式。同时将所有服务设计为高可用, 进步了零碎可用性, 稳定性。最初通过减少缓存等设计, 进步了高峰期的查问 QPS, 在不扩容的前提下, 保证系统高峰期的响应速度。