知识卡片
预建索引替代扫描列表的性能优化
内容
雪球在2015年A股剧烈波动期间遇到过两类典型的性能瓶颈,都是靠”把线性扫描换成预建索引/独立存储”这个共同思路解决的。第一个是股价提醒功能——用户可以关注某只股票,设置涨跌幅超过某个值(默认7%)时提醒自己,雪球热门股票(如招商银行、苏宁云商)粉丝数超过50万。原来的做法是:股票涨跌达到某个值时,扫一遍全部粉丝列表,过滤出符合条件的粉丝再推送——这种做法的开销随粉丝规模和触发频率线性增长,在连续多日大量个股跌停、又连续多日涨停的极端行情下(曾出现过连续3天每天超过1000股跌停,证监会开会后又连续2天超过1000股涨停),这种线性扫描策略难以承受。新做法是预先建立索引、开盘期间载入内存——按不同涨跌幅阈值分桶维护粉丝ID列表(比如”1%档位对应哪些用户,2%档位对应哪些用户”),触发时直接查对应阈值桶,不再需要扫描全部粉丝。优化后线上4台机器能做到99%的单条消息延时小于30秒,下一步目标是压到10秒以内;但这个方案本身也带来一个新问题——过于及时反而会导致频繁提醒:”打开跌停、再跌停、再打开”这种股价反复穿越阈值的场景会触发多次重复提醒。第二个类似的优化是Quote Server(提供开盘期间每秒查询一次股价的服务):最初这个接口只是部署在大一统服务Snowball里的普通接口,股价数据实时写入Redis、读取时也从Redis读,A股大涨、访问量剧增后Snowball扛不住了,于是团队做了一个典型优化——把这部分逻辑拆成独立Server、用本地内存存储股价数据,数据更新时由专门的数据接收组件主动更新到Quote Server内存中,绕开了共享存储(Redis)在高并发读场景下的瓶颈。这两个案例共同的方法论是:当某个查询/匹配操作的开销随数据规模线性增长、且这个操作在高峰期会被高频触发时,用”提前建好索引/把热数据放到离计算最近的地方(本地内存)”替代”每次都从头扫描/从远端存储读取”,能把开销从O(n)降到接近O(1)。