起因
在接口设计之初,没有考虑到用户访问量的问题,造成在大量并发请求的情况下,造成数据库的负载压力过于庞大(接口每2分钟上报一次,且活跃用户有20W以上),有造成数据库瓶颈的危险
目标
在保证原有接口正常的情况下,尽可能的减少数据库查询、更新、插入的SQL运行次数,以减轻数据库负载
设计
日志表
原有日志表采用请求一次就插入一条数据,现改成队列的形式,达到插入条件后,则采用批量插入的形式,提高执行效率
数据统计表
原:
请求一次,判断是否为已存在数据,若存在则进行更新操作,若不存在则进行插入操作
现:
1、判断是否存在此数据;查询缓存,若缓存不存在,则进行数据库查询,若数据库不存在,则写入插入队列,并将此信息存储到Redis的集合中,实现去重操作,下一次若有一样的数据,则添加到更新队列中
2、先执行批量插入队列,执行成功后,将Redis中的相关集合清空,并且执行更新操作,更新完成则清除队列数据【此处有两种实现方式,会写入到踩过的坑中说明】
相关查询表(用户信息、关联ID信息等等)
原:
每次请求都去查询一次数据库表中的数据
现:
引入Redis缓存,因此处只需要判断是否存在,所以采用Redis的位图bitMap数据结构,存储相关数据
【位图比string数据结构更加节约资源,相关的位图介绍,可以点击此链接查看:位图介绍】
测试
用自带的ab进行压测,具体相关的教程可以点击此处
最终结果
在并发1000、请求1000的情况下,SQL执行条数减少了四分之三
踩过的坑
1、批量更新使用update when then 语句时,一定要注意更新的数据是是否正常
##例如:使用下面的语句,会有怎样的情况发生呢?
UPDATE table_name SET a=CASE id WHEN 797 THEN 123 WHEN 273 THEN 456 END,b=CASE id WHEN 797 THEN b+138 END,c=CASE id WHEN 273 THEN c+123 END WHERE id IN ('797','273')
##执行这条SQL的时候,会出现其他的字段被置为0,例如:表里面还有d、e、f,而且里面是有相关数据的,用了上面的SQL后,会造成其他的d、e、f的数据被清空
2、当高并发场景下时,一定要注意原子性
(1)加分布式锁,用Redis实现
(2)队列数据取出的同时,马上删除此部分数据,若执行成功,则丢弃此数据集,若执行失败,则用代码实现将数据回滚回原队列中
3、无论是什么缓存,都必须加上过期时间,防止不消费的情况下,缓存中的数据一直保留,占用服务器资源
4、使用Redis的scan方法的同时,必须注意游标的问题,详情可点击此处
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
5、做相关优化的时候,一定要多重测试;例如:自测、测试服测、上线前奏测、线上测等等
更多内容请去公众号【老罗分享】获取