✅全国的酒店价格(千万级)需要在某个瞬间比如7点发生变动,怎样高性能准点去进行变更
典型回答
问题中提到的其实是一个千万级数据的秒级变更的设计方案。其实这个方案很难,因为任何一种分布式任务、分布式更新的方案,都很难做到秒级更新千万级数据,那么,如果要保证7点时间一到,价格立即更新,可以考虑以下几个方案。
前提、预计算
想要实现高性能的准点价格更新,基本上不太可能在7点这一刻进行价格的计算,所以价格一定是提前就计算好了的,这样不管用什么样的方案,在7点需要更新价格的时候,都可以避免实时计算,可以大大的降低更新过程的耗时。
所以,价格的预计算,是这个问题的解决方案的前提。
方案一、多版本价格
有一个方案是我们在用的,我之前做过一个金融的定价中心,我们的价格就有经常的变动,我们的做法是不会针对价格配置做更新的,只会新增一条新的价格。比如以下是我的价格表的关键几个字段:
| 字段名 | 解释 |
|---|---|
| price | 价格 |
| strat_time | 生效开始时间 |
| end_time | 生效结束时间 |
| code | 价格定义的唯一 code |
| condition | 价格生效条件 |
| product_id | 价格所属的产品 |
比如当前有一条定价,是A 酒店的 X 房型,199每晚,从24年1月1日开始有效。那么定价配置如下:
| 字段名 | 解释 |
|---|---|
| id | 1000 |
| price | 199 |
| strat_time | 2024-01-01 00:00:00 |
| end_time | null |
| code | A_X_after_20240101 |
那么,如果从4.1日的晚7点开始调价,299生效,则配置内容如下:
| 字段名 | 解释 |
|---|---|
| id | 1000 |
| price | 199 |
| strat_time | 2024-01-01 00:00:00 |
| end_time | 2024-04-01 19:00:00 |
| code | A_X_after_20240101 |
| 字段名 | 解释 |
|---|---|
| id | 1001 |
| price | 299 |
| strat_time | 2024-04-01 19:00:00 |
| end_time | null |
| code | A_X_after_20240401 |
那么就相当于把之前的定价的结束时间设置为2024-04-01 19:00:00,新插入一条新的定价,生效时间为2024-04-01 19:00:00。这样用户在查询价格的时候,只需要我们计算下当前时间,去数据库查询的时候再 where 中加上start_time <= now() and end_time > now()即可。
这样,当时间变成了 2024-04-01 19:00:00的时候,查询到的价格就都是299了。
后面如果再调整价格,还是把之前生效的定价截止时间设置上, 然后再创建一条新的定价。
以上就是大致思路,但是实际上我们做的要比这个复杂的多,我们可能还有价格的优先级、分层定价、价格的叠加互斥等逻辑,总之我们的调价,都是新增定价配置的方式来做的,即提前算好价格和生效时间,提前配置进去。
缓存失效方案
如果你的价格是存储在缓存中的,想要在19点更新,那么我么可以把这个缓存的价格的失效时间设置为19点,这样在到达这一时刻的时候,key 就会失效了。那么下次用户在查询的时候,就会去到数据库查询最新的价格了,前提是最新的价格数据库已经更新好了,当然,这个过程可以提前做批量更新,这时候对性能要求就没有这么高了。
但是,这个方案可能会导致缓存击穿,所以需要加个分布式锁,防止缓存击穿带来的数据库压力。
分布式方案
以上方案都是说提前可以算好价格,然后在7点的时候去读取最新的配置。但是如果不能这么干呢,就要你在数据中做更新咋办呢?
首先,其实这个问题并不难,只需要定时任务处理就行了,而且千万级数据更新价格,虽然数据量看上去挺大的,但是并不存在热点更新和锁争抢的问题。
基于以上判断,我们需要做的就是定时任务在晚上7点开始执行,然后利用分布式任务的方式,让集群中的更多的服务器同时进行价格更新(如 schedulerx 的 mapReduce 任务、xxl-job 中的网格任务等),同时千万级数据的变动操作需要考虑数据库的承载能力。可以通过分库分表来减小单个数据库实例的压力。分库可以按地域、时间等维度划分,使得单个数据库的负载降低。
同时,在更新的时候也可以考虑一些批量更新的方案,在一个 SQL 中更新一批同类的价格配置,减少整体的数据库的连接的并发数。
总之就是想想办法提升整体的价格更新的并发度,分布式任务、多线程执行、分库分表等。