闲鱼技术2022年度白皮书-服务端主题-闲鱼如何计算实时优惠:兼顾可扩展、高并发与数据一致性(上)

简介: 闲鱼技术2022年度白皮书-服务端主题-闲鱼如何计算实时优惠:兼顾可扩展、高并发与数据一致性

 

作者:泊垚

 

一、 问题与挑战

 

如何描述、存储和计算优惠并提供较好的业务可扩展性

如何保障大流量下优惠实时计算的性能

为优惠查询加速做的数据同步如何实现一致性

 

本文的方案经过线上系统验证,对于优惠系统设计的场景和数据同步的场景可做相应的参考。

 

二、 背景

 

在我们日常生活中,常常会遇到下面这样的场景:

 

image.png


在闲鱼上,针对闲鱼交易中的粉丝购买和粉丝回购的优惠促销场景,提供了一种定向一口价的优惠能力:

 

卖家可以按商品分别面向全部粉丝、老粉、已购粉设置不同的优惠价格。

买家在导购、下单等场景可以实时看到自己能够享受的最低优惠价格。

 

三、 技术实现

 

我们通过三个步骤来实现

 

分解优惠的基本要素,实现优惠的基本表达和计算

为了保障大流量下的优惠查询下性能和业务的可扩展性,对优惠对象的判定过程进行抽象和加速

在优惠对象制备的过程中,通过离线+实时的方式同步数据,保障数据一致性

 

1. 优惠的描述、存储与计算

 

一个优惠主要描述了“谁对哪个商品享受什么优惠”,拆解为三个要素就是:【优惠对象】+【优惠商品】+【优惠价格】。

 

image.png


在这个规则中,主要是要解决如何描述优惠对象:在粉丝优惠的场景下,优惠对象是指卖家的粉丝、卖家的已购粉丝等,在存储一条优惠时,一个卖家的粉丝可以被描述为卖家ID_all_fans的符号(同理,已购粉丝是“卖家ID_buy_fans”)。这样我们可以得到一个优惠规则的描述大致如下:

 

【卖家A_all_fans】+【商品1234】+【18.88元】,对应的业务语义是:卖家A的所有粉丝,对于(卖家A的)商品1234,可以以18.88元的优惠价格成交。

 

以这条优惠为例,当买家B访问商品1234时,我们会执行这样的一个过程

 

查询商品1234上的优惠规则,发现一条【卖家A_all_fans】+【商品1234】+【18.88元】的规则

分析【卖家A_all_fans】表达的含义,表示的是卖家A的全部粉丝可以享受优惠

确定买家B是否是卖家A的粉丝,如果是,则以18.88元的价格展示优惠或者成交

 

这样,我们就实现了优惠设置和计算的能力,这个时候,我们只需要这样一个架构就可以实现:

 

image.png

 

2. 优惠对象判定的抽象和加速——人群

 

但这样的架构存在两个问题:

 

优惠计算过程需要解析【优惠对象】这个符号背后所包含的业务语义,再由系统进行判断买家是否符合条件,随着业务规则的升级,系统的会变的非常复杂,可扩展性差。

 

每一次优惠查询,都需要访问用户的关注关系、购买关系,这整个查询过程非常长,性能低下,当面对大流量时,系统会陷入瘫痪。

 

为了解决这两个问题,我们希望优惠计算过程不再需要理解【优惠对象】的语义,判定过程中也不要再去查询各个业务系统。

 

我们发现,优惠对象的判定过程,都是在回答“用户是否属于某个群体”,我们可以将这个关系进行抽象,提前制备并存储起来。在我们常见的技术手段中,表达一个用户是否属于某个群体有两种实现:

 

在用户对象上打上一个标记。

创建一个“人群”对象,将用户关联到人群。

 

一般情况下,第一种方式使用于群体较少可枚举的情况,第二种方案适用于群体较多的情况。在我们的实现中,使用了第二种方案。

 

我们将用于描述优惠对象的符号(例如“卖家A_all_fans”)作为人群的名称去定义一个人群,按照这个规则,我们为每个卖家的不同分组各定义这样一个人群(这里人群作为一个符号,这里不需要实际被“创建”)。

 

人群和用户的关系存储可以通过redis实现,我们设计一个类似:{user_A}_{user_A}_{user_A}_{crowd_B}的key写入redis。在查询时,查询{user_A}_{user_A}_{user_A}_{crowd_B}这个key是否存在,就可以判定user_A是否属于crowd_B。(当然这是一种比较简易的实现,实际设计中需要根据数据特性进行优化)。就这样,我们定义了人群的概念,并提供了一种实现人群的技术方案,这个架构中,人群在同时充当了“协议”和“缓存”的作用。

 

这时我们的得到的整体架构是这样的(顺带缓存了一下优惠数据):

 

image.png

 

事实上,在我们基于中台的解决方案中,从一开始面临的就是这样的架构(实际中台的架构比这个会更复杂一些)。这里我们尝试从头演进了这个系统,也得到这样的一个方案。

 

在实际落地的过程中,我们核心要解决的问题,是如何将业务系统中的关注和购买关系同步到人群中,并保证数据的一致性。

 

接下篇:https://developer.aliyun.com/article/1225757?spm=a2c6h.13148508.setting.14.3ffe4f0eTljMms

相关文章
|
14天前
|
缓存 监控 安全
揭秘高并发神话背后:打造坚不可摧的秒杀系统,技术大牛必修课!
【8月更文挑战第29天】在设计高并发、高可用的分布式秒杀系统时,需关注系统架构、数据库设计、缓存策略、并发控制、降级限流及安全防护。采用微服务架构并通过API网关和负载均衡器通信;数据库设计需考虑分库分表与读写分离;利用Redis缓存热点数据;采用限流算法和排队机制控制并发;实施IP限流和验证码验证保障安全。以下为简化代码示例,展示如何在秒杀服务中实现预扣减库存和订单创建逻辑。此外,还需进行性能测试与优化,并设置监控和日志记录机制,确保系统稳定可靠。
26 1
|
22天前
|
存储 监控 固态存储
【性能突破】揭秘!如何让您的数据库在高并发风暴中稳如磐石——一场关于WAL写入性能优化的实战之旅,不容错过的技术盛宴!
【8月更文挑战第21天】在高并发环境下,数据库面临极大挑战,特别是采用Write-Ahead Logging (WAL)的日志机制。本文通过一个在线交易系统的案例,分析了WAL写入性能瓶颈,并提出优化方案:理解WAL流程;分析磁盘I/O瓶颈、缓冲区设置与同步策略;通过增大WAL缓冲区、使用SSD及调整同步策略来优化;最后通过测试验证改进效果,总结出一套综合优化方法。
38 0
|
2月前
|
设计模式 安全 Java
Java面试题:如何实现一个线程安全的单例模式,并确保其在高并发环境下的内存管理效率?如何使用CyclicBarrier来实现一个多阶段的数据处理任务,确保所有阶段的数据一致性?
Java面试题:如何实现一个线程安全的单例模式,并确保其在高并发环境下的内存管理效率?如何使用CyclicBarrier来实现一个多阶段的数据处理任务,确保所有阶段的数据一致性?
36 0
|
2月前
|
设计模式 存储 缓存
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
41 0
|
3月前
|
关系型数据库 MySQL 调度
OceanBase 高并发场景技术解读
OceanBase 高并发场景技术解读
|
4月前
|
监控 关系型数据库 分布式数据库
【PolarDB开源】PolarDB在电商场景的应用:应对高并发与数据一致性挑战
【5月更文挑战第26天】阿里云PolarDB是为电商解决高并发和数据一致性问题的云原生数据库。它采用读写分离、弹性扩展和分布式缓存策略应对高并发,通过全局时钟、分布式事务和数据复制保证数据一致性。在大型促销活动中,电商平台可提前扩容、启用读写分离、优化索引并设置监控告警来应对挑战。PolarDB助力电商构建高性能、高可用的数据处理系统,赢得市场优势。
147 1
|
4月前
|
关系型数据库 MySQL 数据库连接
Python+SQLite数据库实现服务端高并发写入
Python中使用SQLite内存模式实现高并发写入:创建内存数据库连接,建立表格,通过多线程并发写入数据。虽然能避免数据竞争,但由于SQLite内存模式采用锁机制,可能在高并发时引发性能瓶颈。若需更高性能,可选择MySQL或PostgreSQL。
230 0
|
4月前
|
消息中间件 存储 NoSQL
【Redis项目实战】使用Springcloud整合Redis分布式锁+RabbitMQ技术实现高并发预约管理处理系统
【Redis项目实战】使用Springcloud整合Redis分布式锁+RabbitMQ技术实现高并发预约管理处理系统
|
4月前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
|
3月前
|
缓存 NoSQL Java
Java高并发实战:利用线程池和Redis实现高效数据入库
Java高并发实战:利用线程池和Redis实现高效数据入库
308 0