某队列积压问题分析解决

Posted sealake

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了某队列积压问题分析解决相关的知识,希望对你有一定的参考价值。

07.29 最高到了115w积压, 07.30也有持续几分钟上万的走势

2017.07.29队列走势

技术分享图片

2017.07.30 队列走势

技术分享图片

分析

对update_betting_offer的处理包括2部分,Handler_Filter和Handler_Trigger, 性能问题发生在Handler_Filter阶段

Handler_Filter各个子阶段在07.29日大于1s的个数如下:

阶段名大于1s个数
odds_source_save 12233
odds_match_map 423910
odds_get_event 4798
odds_source_filter 12513
odds_cache_save 179

 

08.11 开始新的统计各阶段大于1s次数

阶段名08.1108.1208.13
odds_source_save 92218 85183 26141
odds_match_map 45237 24560 8566
odds_get_event 79546 83638 25125
odds_source_filter 125564 124135 35280
odds_cache_save 968 1960 687
odds_risk_control 113275 111902 33028

主要问题点:

odds_match_map  将赔率源的比赛转化为内部的比赛信息

gearman同步调用,发送赔率信息到某队列
1.1 结构问题: 同一个时刻会收到大量同一场比赛的赔率信息, 这些相同比赛的赔率都会进行findMatch处理(其实没必要),只要该比赛的处理慢,那都会慢,导致队列堆积

      加锁 锁名称 : lock:update_betting_offer:match_map:赔率源:源sport_id:源match_id      缓存名称: update_betting_offer:match_map:赔率源:源sport_id:源match_id ,   存储匹配后的match_id, 值为0表示无法匹配。 过期时间 3分钟
1.2 程序问题

  • 在findMatch最后,不管匹配数据是否发生变化,都会向map_soccer_match更新数据,再加上并发问题,导致大量无谓的更新操作。下图中的count表示数据更新的次数。 (对数据进行了判断,数据变化的时候才去更新)

技术分享图片

  • 当模糊匹配时,需要匹配的比赛多是,性能下降。 经测试,SQL框架比直接连接mysql查询要慢。 277场比赛慢2s,框架2.6s,直连0.6s
  • addMapMatchLog函数中sql没有索引,影响大。表是map_sort_match_log,可以对ms_id加索引,同时因为并发问题,导致很多重复数据   (08.07添加的索引). 并改成异步写
  • findMatchByTeamId函数中sql语句优化, 里面有联表查询

odds_source_save  保存赔率源数据

  • 将saveOdds函数中想mongo写数据的操作改成异步
  • saveOdds对mysql 跟上面的写mongo一起做成异步 
  • $sql = "SELECT last_updated FROM `{$tbl}` WHERE `match_id` = ‘{$data[‘match_id‘]}‘ AND `provider_id` = ‘{$data[‘provider_id‘]}‘ AND `betting_type_id` = ‘{$data[‘betting_type_id‘]}‘ AND `boundary` = ‘{$data[‘boundary‘]}‘ LIMIT 1";    这条语句时不时超过1s。 
    1: where后面的字段都是整型, 变量都赋成了字符串。 要让数据库减少这种类型转换
    2: where后面的字段被设置成了 PRIMARY KEY (`match_id`,`provider_id`,`betting_type_id`,`boundary`),   而offer表 插入更新是非常频繁的,这4个字段的主键会造成过大的主键索引和IO操作。 需要新增一个自增字段作为主键,  为match_id`,`provider_id`,`betting_type_id`,`boundary`创建唯一联合索引

    主键的选择:
    主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和内存的使用
    主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类型可以有效的减少索引的磁盘空间,提高索引的缓存效率
    http://blog.csdn.net/jhgdike/article/details/60579883

    3. offer表定期清理数据,根据last_updated保留近半个月数据。大概400w条
    4. 分表数据分布不均衡,目前写操作集中到了同一张表,修改分表算法
    5. 改成读从库

odds_check_league 检查联赛是否有效

    加锁 锁名称 : lock:update_betting_offer:check_league:match_id      缓存名称: update_betting_offer:check_league:match_id ,  存储匹配后的联赛id以及是否有效,格式(1有效,0无效): league_id:1,  过期时间 60分钟      

odds_get_providerid 获取赔率公司id

  •  大量并发查询相同公司id, 如果查不到还会大量插入更新。 加分布式锁(赔率源名称+provider_id)只查一次。
      加锁 锁名称 : lock:update_betting_offer:get_provider_id:赔率源:provider_id      缓存名称: update_betting_offer:get_providerid:赔率源:provider_id ,  存储匹配后的provider_id, 值为0表示无法匹配,  过期时间 60分钟
  •  可以将这一步检测提前到程序开始处,查不到的就直接返回了
  •  写 表map_soccer_provider的操作改成异步

odds_get_event 获取eventid

odds_source_filter 

odds_risk_control  赔率风控

  • 调用http接口获取99家平均赔率时间长

    2017-08-18 23:55:07  2600723 1.0097689628601 msg=
    2017-08-18 23:55:09  2602205 3.0126769542694 msg=
    2017-08-18 23:55:09  2601298 3.0131931304932 msg=
    2017-08-18 23:55:11  2601881 1.0082378387451 msg=
    2017-08-18 23:55:11  2601235 1.0080330371857 msg=
    2017-08-18 23:55:11  2601259 1.0102519989014 msg=
    2017-08-18 23:55:12  2602482 1.0055501461029 msg=
    2017-08-18 23:55:12  2601226 1.0132040977478 msg=
    2017-08-18 23:55:14  2600658 1.0105800628662 msg=
    2017-08-18 23:55:14  2599056 1.0061559677124 msg=
    2017-08-18 23:55:14  2601298 1.0084340572357 msg=
    2017-08-18 23:55:15  2601300 1.0130741596222 msg=
    2017-08-18 23:55:23  2602194 1.0079371929169 msg=
    2017-08-18 23:55:26  2601221 1.0092370510101 msg=
    2017-08-18 23:55:28  2600658 1.0096790790558 msg=
    2017-08-18 23:55:28  2600575 1.0079090595245 msg=
    2017-08-18 23:55:31  2600710 1.012079000473 msg=
    2017-08-18 23:55:34  2599398 1.0108640193939 msg=
    2017-08-18 23:55:39  2601414 1.0104100704193 msg=
    2017-08-18 23:55:39  2600723 1.0120589733124 msg=
    2017-08-18 23:55:39  2601519 1.0102570056915 msg=
    2017-08-18 23:55:46  2602127 3.0110030174255 msg=
    2017-08-18 23:55:52  2601759 1.0077710151672 msg=
    2017-08-18 23:55:52  2601060 1.0113618373871 msg=
    2017-08-18 23:55:55  2600575 1.0087251663208 msg=
    2017-08-18 23:55:55  2601414 1.016450881958 msg=
    2017-08-18 23:55:57  2602255 1.0063278675079 msg=
    2017-08-18 23:56:00  2599400 1.0102560520172 msg=
    2017-08-18 23:56:00  2600574 1.010486125946 msg=
    2017-08-18 23:56:00  2601304 1.0060698986053 msg=

     

  • 99家平均由http调用改成直接读取数据库。 (只有一台http机器调用超时严重)17.11.30
  • 调用gearman报错

    [18-Aug-2017 13:19:50 Asia/Shanghai] php Warning:  GearmanClient::doNormal(): _client_do(GEARMAN_NO_ACTIVE_FDS) occured during gearman_client_run_tasks()
    
    [18-Aug-2017 13:19:51 Asia/Shanghai] PHP Warning:  GearmanClient::doNormal(): _client_do(GEARMAN_TIMEOUT) occured during gearman_client_run_tasks() -> libg

     

关于锁的操作

  1.  redislock 的lock函数增加参数 cache_key, 判断是否lock成功根据wait_time和cache_key一起,  当wait_time时间未到但cache_key存在时返回 [ ‘cache_key‘ => cache_key, ‘cache_value‘ => cache_key对应的值 ];
  2. 调用lock函数设置锁的expire_time要比wait_time长,  当超时返回lock失败后, 调用对应接口
  3. 当lock成功后, 要设置缓存以及过期时间,最后释放锁
  4. 当返回的是[ ‘cache_key‘ => cache_key, ‘cache_value‘ => cache_key对应的值 ];  按正常流程处理即可

--------------

通过上面优化的结果:高峰期的队列任务最高100左右。

这里面最主要问题:开发人员没有性能意识,自己给自己挖坑,人为增加系统压力

1. 大量无谓的重复查询

2. 大量无谓的写操作以及没有分散写

3. 主次不分, 让边缘功能影响了核心功能

4. 单点问题,系统无法横向扩展

以上是关于某队列积压问题分析解决的主要内容,如果未能解决你的问题,请参考以下文章

三招!解决消息队列的数据积压问题

系统学习消息队列分享(八) 息积压了该如何处理?

完了!生产事故!几百万消息在消息队列里积压了几个小时!

完了!生产事故!几百万消息在消息队列里积压了几个小时!

消息队列漫谈:消息丢失,消息重复,消息积压一些处理方式

生产环境消息队列ActiveMQ的数据积压优化过程