快速成长期应用架构实践 (1):关键业务需求

叁叁肆2018-11-14 18:15

欢迎访问网易云社区,了解更多网易技术产品运营经验。


在经过了最初的业务原型验证和上线运行期之后,用户业务进入了高速成长阶段。在 这一阶段,业务重点不再是方向上的调整,而是在原来基础上的不断深挖、扩展;开发不 仅是功能的实现,还需要兼顾成本和性能;系统不再是单体架构,还会涉及系统的扩展和 多系统之间的通信;高可用也不仅是服务自动拉起或者并行扩展,还需要考虑数据可靠、 对用户影响,以及服务等级协议(SLA)。
本章我们将以上述挑战为出发点,介绍如何通过引入新的工具、新的架构,对原有系 统进行升级和优化,来更好满足这一阶段需求,并为产品的进一步发展打下基础。


4.1 关键业务需求


随着用户业务的发展,原来的功能已经无法满足要求,需要增强或者增加新的功能。 在用户数和访问量达到一定规模后,原先单体架构下的简单功能,如计数和排序,将变得 复杂;随着业务深入,定期举行的秒杀、促销等活动,给系统带了巨大的压力;由于数据 量的飞速增长,单纯的数据库或者内存检索已经无法满足不断增加的各种查询需求;随着 业务数据量的增加,产品价值的提高,如何收集系统运行数据,分析业务运行状态也成了 基本需求。接下来我们聚焦这一阶段的关键业务需求,并给出相应的解决方案。


4.1.1 计数与排序


在单体架构下,通过简单的内存数据和对应算法就可以实现计数和排序功能。但是在 大量数据和多节点协作的环境下,基于单点内存操作的实现会遇到高并发、数据同步、实 时获取等问题。在这一阶段,通用方法是使用 Redis 的原生命令来实现计数和排序。 


计数

在 Redis 中可用于计数的对象有字符串(string)、哈希表(hash)和有序集合(zset)3 种,对应的命令分别是 incr/incrby、hincrby 和 zincrby。


网站可以从用户的访问、交互中收集到有价值的信息。通过记录各个页面的被访问次 数,我们可以根据基本的访问计数信息来决定如何缓存页面,从而减少页面载入时间并提 升页面的响应速度,优化用户体验。


计数器

要实现网页点击量统计,需要设计一个时间序列计数器,对网页的点击量按不同的时 间精度(1s、5s、1min、5min、1h、5h、1d 等)计数,以对网站和网页监视和分析。


数据建模以网页的地址作为 KEY,定义一个有序集合(zset),内部各成员(member) 分别由计数器的精度和计数器的名字组成,所有成员的分值(score)都是 0。这样所有精 度的计数器都保存在了这个有序集合里,不包含任何重复元素,并且能够允许一个接一个 地遍历所有元素。


对于每个计数器及每种精度,如网页的点击量计数器和 5s,设计使用一个哈希表 (hash)对象来存储网页在每 5s 时间片之内获得的点击量。其中,哈希表的每个原生的 field 都是某个时间片的开始时间,而原生的 field 对应的值则存储了网页在该时间片内获得的点 击量。如图 4-1 所示。


更新计数器信息示例代码。



图 4-1 网页点击计数器的实现


获取计数器信息示例代码。 


当然,这里只介绍了网页点击量数据的存储模型,如果我们一味地对计数器进行更新 而不执行任何清理操作的话,那么程序最终将会因为存储了过多的数据而导致内存不足, 由于我们事先已经将所有已知的计数器都记录到一个有序集合里面,所以对计数器进行清 理只需要遍历这个有序集合,并删除其中的旧计数器即可。

排序 

在 Redis 中可用于排序的有天然有序的有序集合(zset)和键(keys)类型中的 SORT 命令,其中 SORT 命令的功能非常强大,不仅可以对列表(list)、集合(set)和有序集合 (zset)进行排序,还可以完成与关系型数据库中的连接查询相类似的任务,下面分别以两 个例子来介绍各自的应用。


帖子排序 

论坛中的帖子通常会有各种排序方式方便用户查看,比如按发帖时间排序、按回复时 间排序、按回复数量排序、按阅读量排序等,这些 TOP N 场景对响应时间要求比较高,非 常适宜用有序集合(zset)来缓存排序信息,其中排序字段即为分值(score)字段。


例子 


SORT 命令 


SORT 命令提供了多种参数,可以对列表,集合和有序集合进行排序,此外还可以根 据降序升序来对元素进行排序(DESC、ASC);将元素看作是数字还是二进制字符串来进 行排序(ALPHA);使用排序元素之外的其他值作为权重来进行排序(BY pattern)。
下面代码清单展示了 SORT 命令的具体功能使用。


对列表(list)进行排序

1. 顺序 

2. 逆序 

3. 使用 alpha 修饰符对字符串进行排序 


使用 limit 修饰符限制返回结果 


使用外部 key 进行排序 

可以使用外部 key 的数据作为权重,代替默认的直接对比键值的方式来进行排序。假 设现在有用户数据如表 4-1 所示。
表 4-1 用户数据示例

以下将哈希表(hash)作为 by 和 get 的参数,by 和 get 选项都可以用 key->field 的格 式来获取哈希表中的域的值,其中 key 表示哈希表键,而 field 则表示哈希表的域。

1. 数据输入到 Redis 中 


2. by选项 

通过使用 by 选项,让 uid 按其他键的元素来排序。

例如以下代码让 uid 键按照 user_info_*->level 的大小来排序。 


3. get 选项 

使用 get 选项,可以根据排序的结果来取出相应的键值。 

例如以下代码先让 uid 键按照 user_info_*->level 的大小来排序,然后再取出 user_info_ *->name 的值。 


现在的排序结果要比只使用 by选项要直观得多。 


4. 排序获取多个外部 key
可以同时使用多个 get 选项,获取多个外部键的值。

5. 不排序获取多个外部 key 



保存排序结果 

SORT 命令的时间复杂度用公式表示为 O(N+M*log(M)),其中 N 为要排序的列表或集 合内的元素数量,M 为要返回的元素数量。如果只是使用 SORT 命令的 get 选项获取数据 而没有进行排序,时间复杂度为 O(N)。


云环境下的实践 

在云服务中实现计数和排序,可以自己使用云主机搭建 Redis 服务,也可以使用云计 算服务商提供的 Redis 服务。
对于高可用和性能有要求的场景,建议使用云计算服务商提供的 Redis 服务。专业的

服务商会从底层到应用本身进行良好的优化,可用率、性能指标也远高于自己搭建的 Redis 实例。同时,由于服务商提供了各种工具,开发运维成本也更低。


以网易云为例,网易云基础服务提供了名为 NCR(Netease Cloud Redis)的缓存服务, 兼容开源 Redis 协议。并根据用户具体使用需求和场景,提供了主从版本和分布式集群版 本两种架构。


主从服务版 

如图 4-2 所示,主从版本实例都提供一主一从两个 Redis 实例,分别部署在不同可用域 的节点上,以确保服务安全可靠。在单点故障时,主从服务通过主备切换来实现高可用。
主从版本使用较低的成本提供了高可用服务,但是也存在无法并行扩展等问题,因此 适合数据量有限、对高可用有要求的产品使用。 

图 4-2 主从服务架构


分布式集群 

分布式集群采用官方 Redis 集群方案,gossip/p2p 的无中心节点设计实现,无代理设计 客户端直接与 Redis 集群的每个节点连接,计算出 Key 所在节点直接在对应的 Redis 节点 上执行命令,如图 4-3 所示,详细的过程请参考后续 Redis Cluster 的相关介绍。 


分布式集群采用多活模式,支持并行扩展,因此在性能、可用率方面有明显优势。但 是由于分布式集群最少需要 3 个节点,因此成本会较高,适合对可用率、性能有较高要求的用户使用。


图 4-3 分布式集群架构 


文章节选自《云原生应用架构实践》 网易云基础服务架构团队 著 


网易云计算基础服务深度整合了 IaaSPaaS 及容器技术,提供弹性计算、DevOps 工具链及微服务基础设施等服务,帮助企业解决 IT、架构及运维等问题,使企业更聚焦于业务,是新一代的云计算平台。点击可免费试用