`

用户积分功能的设计

阅读更多

有一个SNS应用,用户在使用的过程中积累积分,例如登陆+3点,个人空间每次浏览+1点,结交每个朋友+5点等等。同时,很重要的一点是,用户需要看到自己的积分累计有多少,能够根据积分划分用户等级,在自己的空间展示积分。

在用户量比较大的情况下(例如超过三千万),这是一个比较典型的读写都很频繁的问题,而且写入的次数可能和读取的次数差别不大(大多数SNS应用中,读次数远超写次数的场景居多,例如用户的状态信息,更新一次以后有成千上万的访问)。

这实际是一个简单,但是典型的功能。试想,给文章投票(例如“顶”一下),给微博统计访问次数,给媒体打分……这些都是非常类似的功能。对于这样问题的思考和设计,考虑到典型性和推广性,是很有价值的。

需要准确的数据?

在常规情况下,我们可以自问自答这样几个关于产品设计约束的问题。例如:

  1. 用户需要看到准确的积分吗?
    1. 如果是别的用户的操作引发了用户积分的增加,允许一定的延时;如果是用户自己的操作,必须实时看到积分变化。例如:有用户关注了我,无论我是否马上知道,理论上我应当立即获得4点的积分,但是这个操作如果不实时反映到用户的积分展示上,对我影响不大;但是如果我发起结交了一位朋友,我希望马上看到我的积分增加了5点。
    2. 在我积分少的时候,我希望看到积分的实时变化;如果积分超过了一定数量(例如10000分),积分的变化实时性变得反而没那么重要。
  2. 用户需要看到准确的积分排名吗?
    1. 我是这个应用的粉丝,我的排名在1000名以内,积分排名非常重要,实时性要求高,当我的积分超过我的竞争对手的时候,我希望看到我的排名立即获得上升;
    2. 我的排名在千里之外,把我排在第9876543名还是8765432名其实对我来说并不重要,兴许,你只需要告诉我我排在十万名之外就行了,我不需要那么高的实时性。
  3. 用户需要知道自己的准确平均分(这种分数是另一种类型,重要的是平均分而不是总分,例如用户可以给彼此打分,再如淘宝网站销售记录里的平均得分等等)吗?
    1. 这个问题也类似,在用户被打分次数比较小的时候,任何一次给用户的打分都会影响到平均分;
    2. 但是在用户被评了几万次几十万次的时候,几十、几百个用户打分都不会对展示的平均分有一丝影响,这时候实时性就没有那么高的要求了。

这些问题,都是需要在产品设计阶段考虑清楚的。当然,从技术实现的角度来说,对于这种大用户量的积分功能的设计,实时性要求越低,越容易实现。我们可以把用户分为多个级别,如果只有那些top用户的排名才显得很重要,那么可以区分对待,例如对于100名以内的用户排名需要实时计算,那么实际可以实时计算200名以内的用户排名(其中的后100名用户视作缓冲,可能会和前100名用户排名发生交换)。

数据结构设计

这里谈论数据结构的设计主要是考虑到在如此高的读写频率下,我们需要在内存里面存放一部分或全部的用户积分信息,以减少对数据库或者文件存储系统的压力。

map

最常见的一种数据结构就是使用一个LRU的map,需要获知积分数据的时候,先根据用户的id去这个map里寻找积分数据。排名的问题要麻烦一些,如果非实时要求,可以定期、单独计算排名,例如每天凌晨1点算一次排名。对于那些top用户,他们的排名信息也和积分数据一样,使用一个LRU的map缓存在内存里面。在数据量不是非常大的情况下,所有的积分、排名信息都可以存储在内存中。

这个map如果对并发性能要求高,可以自己设计读写算法,也可以寻找开源实现。对于JDK中的ConcurrentHashMap,我曾经测试过,它的写入速度要略好于没有加同步保护的HashMap,但是读取速度大大低于没有同步保护的HashMap(大约只有一半)。

数组

使用数组也是适合的,特别是在用户id分布规律的时候(例如从1到999999),只需要一个大小为1000000的数组,由于访问是随机访问,非常快。在设计排名的时候,数组有一个妙用:用户积分的变化往往不大,例如从1024分变到1026分,但是如果要考虑排名实时性,就不得不把整个用户的排名重新计算一遍,这显得杀鸡用牛刀。如果把数组设计以存储排名信息,但是数组的下标不是表示用户id,而是表示积分:

用户积分功能的设计

例如上图,左侧是数组下标,右侧的中括号里表示的是排名,右侧逗号分隔的数表示的是相应的用户id。现在id为50的用户积分1024点,因为某个操作而增加了两点,变成了1026点,他的排名只需要计算上述三个蓝色表示的数组元素所存储的排名就够了,也就是说,积分少量的变化不需要进行整个数组排名的重计算。

树是一种有趣的办法,它的好处在于通过节点的分裂和合并,使得其的结构和节点内元素数量总是处在一个合理的位置:

用户积分功能的设计

每一个非叶子节点(实线)都分裂出若干个节点,直到叶子节点(虚线)为止。每个叶子节点关联若干个用户id,例如上图中,积分为100077的用户包括了403、7886和36651三位用户。有很多构建和调整这棵树的策略,例如:

  1. 同层节点是有序的,当用户的积分改变时,该用户id在树中的位置就会发生变化。
  2. 同时,每一个非叶子节点也记录了该节点下递归包含的所有用户id数量p,对于每个非叶子节点x有一个包含所有用户数量的上限f(n),以及下限g(n),其中n为该节点的深度:
    1. 当数量超过f(n)的时候,该节点x会分裂出一个新的子节点来;
    2. 当数量小于g(n)的时候,该节点会和兄弟节点合并。
  3. 可以通过树的节点调整和转换,让树的深度和节点的最大子节点树保持在一个合理的范围内。

我们当然也可以参照一些经典的关于树的数据结构的方案来思考,但总的来说,实现略有复杂,但是这种设计方法有较好的适应性,对于不同的积分-用户分布的情况,同一深度的节点所对应的积分区间各不相同,但总是让每个节点下的用户节点的数量保持在一个可接受的范围内。

如果要获知用户所在的排名,就需要遍历树的节点:找到该用户所在节点所有左侧的兄弟节点和所有递归父节点的所有左侧兄弟节点下所有的用户数量(下图中红色叶子节点存储了需要统计的用户id,蓝色节点所包含的所有用户id数量应当被统计进去):

用户积分功能的设计

数据库的选择和设计

数据库(甚至使用某种文件系统)是必不可少的,以持久化积分或排名的数据。通常情况下,这张表的读取和写入频率都很高,考虑一些常见的优化方式:

  1. 用户积分的表是一张大表,因为许多数据库都是按块读取数据的,尽可能减少每一行的大小可以提高数据库表访问的性能,所以,一定要把这张表独立出来(例如,userId是主键,也关联到用户表的外键,还有一个字段是mark),不能给用户表添加一个mark字段了事;
  2. 数据库sharding,把这张表的userId做散列,分布到不同的服务器上,以减轻服务器压力;
  3. 上面说的是水平切分,也可以考虑垂直切分,我们可以考虑把单独的用户积分表放到单独的服务器上;
  4. 把统计和映像功能独立到其他的服务器上去,尽可能让这些异步的辅助功能不影响主功能的运行;
  5. 如果要选用NoSQL的数据库,根据CAP原理,可用性肯定不能牺牲,分区容忍性也显得相对比较重要,只能牺牲一致性了,比如Dynamo、Cassandra等等。

稍微多说一下关于功能独立的问题,把统计和映像功能独立到其它的服务器上去:

用户积分功能的设计

snapshot的作用仅仅是给当前数据做镜像,在很多情况下,用户的积分变更记录我们需要获知,但是也许不需要获取每次的变更,只需要定期取得这样一个镜像(包括数据库的状态)就可以了;而statistics是进行数据统计挖掘的服务器,数据尽可能从snapshot中获取,以免对主数据库造成影响。

批量和异步

批量和异步都是牺牲实时性和一致性来换取性能的做法。对于用户的评分,不需要每次都实时写入数据库,完全可以积攒到一定的程度批量写入,而且,数据库关心的应当是写入请求提供的增量数据,如“用户xxx评分增加15点”,而不是“应将总点数66553写入数据库”:

用户积分功能的设计

如上例,多台server中,每台机器都会触发事件将用户的积分信息批量写入数据库,一旦满足:

  1. 时间超过t而未写入数据库;
  2. 或内存中待写入的用户数量超过100000
  3. 或容器重启。

在读取环节,选用适当的缓存框架(特别是分布式和多层的缓存框架),可以帮助提高读取性能。需要注意的是缓存数据的过期条件,尤其是在集群环境中。有这样一个常见的场景,用户一个登陆操作增加了自己的积分,也许对于其他用户来说也许实时性并不重要,但是你肯定希望用户自己能立刻发现自己的积分上涨了。这时,积分的增加至少要在当前用户会话所在的服务器机器上体现出来,其他集群机器则可以延迟一些。

文章系本人原创,转载请保持完整性并注明出自《四火的唠叨》

5
1
分享到:
评论
3 楼 mowengaobo 2013-07-09  
好文章,面面俱到。
2 楼 hot002 2013-06-18  
1 楼 itway 2013-06-18  

相关推荐

    积分系统开发总结及设计原则.pdf

    例如,设置不同级别的称号(如“翻样达人”、“大财主”),根据用户积分的积累来晋升等级,激励用户持续参与。 在架构设计上,积分系统需考虑系统级复用,支持异构产品的用户行为数据采集,并提供灵活的积分计算...

    怎样设计社区积分系统

    设计积分系统的主要目的是通过一系列规则和奖励措施,激励用户积极参与社区活动,从而达到以下目的: - **提升用户活跃度**:通过积分奖励,鼓励用户发布内容、参与讨论等活动。 - **增加APP停留时间**:积分可以...

    积分数据库设计.7z

    通过以上分析,我们可以看出这个积分管理系统涵盖了积分的获取、消耗、存储、查询等核心功能,而PowerDesigner和相应的Java实体类则为其实现提供了坚实的基础。这样的设计使得系统具备良好的可维护性和扩展性,能够...

    积分制BBS功能和页面设计

    本篇文章将深入探讨积分制BBS的功能设计与页面布局规划,以CSDN为例,它是中国知名的开发者社区,采用积分制BBS模式,旨在提升用户互动和社区建设。 首先,积分制BBS的核心功能设计包括以下几点: 1. **用户注册与...

    超市积分系统程序设计书

    【超市积分系统程序设计书】是一份详细描述如何构建一个超市...综上所述,这份设计书全面阐述了一个超市积分管理系统的设计思路、功能要求和实施步骤,为开发团队提供了明确的指导,同时也为教师评估项目提供了依据。

    积分系统开发总结及设计原则

    ### 积分系统开发总结及设计原则 #### 一、积分系统概述 积分系统作为一种常见的用户激励机制...通过综合运用上述设计原则和技术手段,可以构建出高效稳定的积分系统,从而有效地提升用户体验,促进产品的长期发展。

    基于c、c语言的客户消费积分管理系统的设计与实现.doc

    该系统的设计和实现是基于C/C++语言的,具有添加用户、查找用户、修改用户、删除用户、统计客户数量、按一定顺序显示客户信息、清空用户信息和永久保存用户信息等功能。 系统的设计目的: 1. 实现客户消费积分的...

    软件设计模式作业实验代码+idea+Java 1. 某网上书店,用户购买图书可以积分,积分达到一定值后可以升级为银会员、黄金会员

    1. 某网上书店,用户购买图书可以积分,积分达到一定值后可以升级为银会员、黄金会员等(用户等级有普通会员、银会员、黄金会员、钻石会员等),不同级别的会员购买图书有不同的优惠。同时用户也可以用积分在网上...

    直流双积分电压表设计

    本文将详细讨论基于89C51单片机的双积分型A/D转换器在设计4位半积分式数字直流电压表过程中的关键技术和实现功能。 首先,89C51单片机是微控制器的一种,具有高速处理能力和丰富的I/O端口,是构建电子设备的核心...

    积分商城demo

    5. **界面设计**:一个良好的积分商城demo应有清晰、友好的用户界面,包括商品展示、分类导航、搜索功能等,使用户能快速找到所需商品。此外,界面设计还需考虑移动设备的适配,以适应不同用户群体的使用习惯。 6. ...

    电子商务会员与积分系统设计.docx

    《电子商务会员与积分系统设计》文档详尽地阐述了一个针对电子商务平台的会员管理和积分系统的设计方案,旨在提升用户购物体验并增强用户粘性。该系统的设计涵盖了从需求分析到实现的全过程,包括系统概述、总体设计...

    会员消费积分管理系统课程设计

    课程设计的分析报告会详细阐述系统的设计思路、功能模块划分以及实现方法。通常包括以下几个部分: 1. 系统需求分析:明确系统的目标和预期功能,如会员注册、消费记录录入、积分查询、积分兑换等。 2. 系统架构...

    基于Java的超市积分管理系统的设计与实现

    超市积分管理系统采用了Model-View-Controller(MVC)设计模式,这是一种常见的软件架构模式,用于分离业务逻辑、数据模型和用户界面。Model负责数据的处理和存储,View负责显示数据,Controller处理用户的输入并...

    电子商务会员及积分系统设计.doc

    总结,电子商务会员及积分系统设计是一个涉及多方面考虑的复杂过程,从用户需求分析到系统架构设计,再到功能实现和后期维护,都需要精确规划和细致实施。一个成功的会员积分系统不仅能提升用户体验,更能为企业带来...

    ecshop小京东 积分兑换优惠券功能

    4. **后台用户积分兑换管理**:管理员在后台可以查看用户积分兑换记录,包括兑换时间、用户ID、兑换的优惠券类型等,以便分析用户行为和调整策略。 三、技术实现细节 1. **数据库设计**:需要新增积分兑换表,记录...

    客户用户积分管理(带简易QT界面)

    总之,“客户用户积分管理系统”结合了QT的图形界面设计、C++的编程能力和链表的数据结构,构建了一个功能完善的积分管理工具。通过对客户消费行为的精细化管理,企业可以更好地激励客户消费,提高客户忠诚度,从而...

    ecshop小京东移动端积分兑换优惠券功能

    后台的用户积分兑换管理页面则是商家或管理员监控和管理积分兑换活动的地方。在这里,管理员可以看到积分兑换的统计信息,如每日兑换次数、最热门的优惠券等,以便调整积分策略。此外,管理员还可以处理异常兑换请求...

    积分商城购物车功能效果实现项目教学设计.docx

    难点在于如何综合设计积分商城购物车的功能效果,包括但不限于购物车页面的布局、交互逻辑、用户体验优化等。 为了支持学习,选择了机房网络作为学习环境,利用互联网资源,如教材、多媒体课件、案例库和优慕课平台...

    签到送积分

    3. **数据库设计**:数据库中至少需要两个关键表,一是用户表,存储用户的基本信息和积分余额;二是签到记录表,记录用户的每日签到情况,包括签到日期、连续签到天数等。 4. **时间管理**:系统需要能够正确处理...

Global site tag (gtag.js) - Google Analytics