100万用户量“排行榜列表”和“我的排名”功能优化记录
- 前言背景:
- 难点:
- 解决过程:
- mongodb小坑:
- 总结优化过程:
- 最后:
- 参考:
前言背景:
最近做一个预算100万用户量的项目,需要优化,尤其是“排行榜列表”和“我的排名”功能。比较判定的逻辑是:
“排行榜列表”:
分数倒序,用时升序,用户ID升序
“我的排名”:(
(分数 < 我的分数)
|| (分数 = 我的分数 && 时间 < 我的时间)
|| (分数 = 我的分数 && 时间 = 我的时间 && 用户ID < 我的ID)
)
难点:
"or"查询无法触发索引
解决过程:
一开始尝试的数据库索引优化,结果效果不理想。
然后尝试把数据取出到php处理,结果内存爆满(其实该方法只适合数据计算较复杂的逻辑,数据量大的情况不合适);
然后采用redis加锁查数据库存缓存定时过期刷新的方法,结果客户说要即时的(而且这个方法也优化不了“我的排名”,只能优化“排行榜列表”);
然后采用mongodb,芒果有提供类似mysql一样的排序查询语句,可以直接在数据库里面处理,而芒果数据是在缓存里面的,所以可以直接在缓存里面执行语句,速度嘎嘎快,解决了“排行榜列表”的即时需求和性能问题,但是“我的排名”消耗依然高,因为不管是mysql还是mongodb,“or”都是不能触发索引的,哪怕芒果数据是在缓存里,100万数据不用索引也够呛。
然后采用分表的方法,由于游戏比较简单,所以估计80-100分的人居多,所以按这样分表(0-80,80-85,85-90,90-95,95-100),在设个冗余表,记录每个分表的总数。插入数据时,根据分数范围插入对应表和在对应分表总数加一,查询“我的排名”时,根据我的分数查找对应分表,计算我在该分表的排名,加上前面分表的总数,算出我的总排名。大功告成!
mongodb小坑:
用php这种弱类型语言插入数字很可能被识别为字符串,从而排序出现问题
总结优化过程:
索引 -> php代替mysql计算 -> redis -> mongodb -> 分表+冗余表
最后:
分表的优化操作有点麻烦,就去磨了一下产品,砍掉了“我的排名”需求哈哈哈,所以这个分表操作最终没有落地,但理论上应该是可行的。所以最终是用mongodb实现了“排行榜列表”的优化,用砍产品的方式实现了“我的排名”优化哈哈。
一直以为redis已经能处理大部分的单机优化了,直到遇到了100万用户量的排行榜和我的排名这样的需求,数据库排序消耗太大,加索引也不行,取到php处理,php内存消耗也大,redis不能做多条件的排序(比如分数倒序,时间升序,id升序按这三个条件来排,redis就做不到),最后就用上了mongodb,这个以为已经被淘汰的工具。
参考:
https://www.php.net/manual/zh/book.mongodb.php php的mongodb原生驱动
https://www.mongodb.com/docs/v4.2/crud/ mongodb官方文档
https://www.yisu.com/zixun/39612.html php封装mongodb
https://www.php.cn/php-weizijiaocheng-405982.html php的mongodb操作
https://www.kancloud.cn/noahs/linux/1425616 用户权限操作