某队列积压问题分析、解决

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

分析

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

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

阶段名大于1s个数
odds_source_save12233
odds_match_map423910
odds_get_event4798
odds_source_filter12513
odds_cache_save179

 

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

阶段名08.1108.1208.13
odds_source_save922188518326141
odds_match_map45237245608566
odds_get_event795468363825125
odds_source_filter12556412413535280
odds_cache_save9681960687
odds_risk_control11327511190233028

主要问题点:

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.youkuaiyun.com/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对应的值 ];  按正常流程处理即可

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

通过上面优化的结果:高峰期的队列无积压。

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

1. 大量无谓的重复查询

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

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

4. 并发冲突,系统无法横向扩展

转载于:https://www.cnblogs.com/sealake/p/8118568.html

<think>我们正在解决RocketMQ消息积压问题。根据引用内容,我们可以总结出以下解决方案:1.增加消费者实例:通过增加消费者数量来提高消费能力(水平扩展)[^1][^3]。2.提高消费并行度:调整消费者的线程数或进程数,提高单个消费者的处理能力[^3]。3.优化消费逻辑:检查消费者代码,优化处理逻辑,提高消费速度[^4]。4.设置合理的消费位点:对于积压的消息,可以启动新的消费者组从合适的位点重新消费[^3]。5.扩容消息队列:如果消息队列容量不足,需要扩容[^4]。6.制定应急预案:提前准备预案,包括临时扩容、数据迁移等[^2]。7.监控和告警:实时监控消息积压情况,及时发现问题并处理[^2]。此外,引用中还提到消息积压的原因,包括消费者处理速度慢、消息消费失败和配置不合理[^4]。因此,在解决积压问题时,需要先分析具体原因,再采取针对性措施。下面我们将详细展开解决方案,并注意按照要求使用LaTeX格式。</think>### RocketMQ消息积压解决方案 RocketMQ消息积压是指消息在队列中堆积过多,导致消费延迟和系统性能下降的问题。以下是系统性的解决方案: #### 一、紧急处理措施 1. **增加消费者实例** 通过水平扩展消费者组规模提升消费能力,这是应对突发流量最直接有效的方式[^1]。计算公式: $$ \text{所需消费者数} = \frac{\text{消息生产速率}}{\text{单个消费者处理能力}} \times \text{安全系数} $$ 2. **动态调整消费位点** 对于非关键消息,可将消费位点重置到最新位置,跳过积压消息: ```shell ./mqadmin resetOffsetByTime -n localhost:9876 -g consumer_group -t topic_name -s now ``` #### 二、消费能力优化 1. **提升消费并行度** - 增加消费者线程数:修改`consumeThreadMin`和`consumeThreadMax`参数 - 分区扩容:对Topic进行队列扩容(需重启Broker) $$ \text{理论吞吐} = \text{队列数} \times \text{单队列消费速度} $$ 2. **优化消费逻辑**[^3] - 批处理消息:使用`ConsumeMessageBatchMaxSize`参数 - 异步处理:将耗时操作异步化 - 避免阻塞操作:如同步数据库调用 #### 三、预防性措施 1. **容量规划** 根据业务峰值设定队列数和分区数: ```java // 创建Topic时指定队列数 admin.createTopic("TOPIC_NAME", "TOPIC_CODE", queueNum); ``` 2. **监控告警体系**[^2] 监控关键指标: - `Diff`(未消费消息数) - `BrokerPutNums`(消息生产速率) - `GetMessageTotalTime`(消费耗时) 3. **熔断降级机制** 当积压超过阈值时自动触发: ```mermaid graph LR A[积压量>10万] --> B[触发限流] B --> C[降级非核心业务] C --> D[报警通知] ``` #### 四、积压消息处理 1. **历史消息回放** 新建临时消费者组,从指定时间点重消费: ```java consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP); consumer.setConsumeTimestamp("20240101000000"); ``` 2. **消息转储** 将积压消息导出到其他存储系统(如HBase)进行离线处理[^3]。 #### 五、根因分析[^4] | 原因类型 | 检测方法 | 解决方案 | |---------|---------|---------| | 消费逻辑阻塞 | 线程堆栈分析 | 优化慢查询/远程调用 | | 资源不足 | 监控CPU/内存 | 扩容消费者实例 | | 配置错误 | 检查`pullBatchSize` | 调整批次大小 | | 消息倾斜 | 查看队列分布 | 重分配队列 | > **最佳实践**:某电商平台通过"队列扩容+消费批处理+动态线程池"方案,将峰值10万/s的积压消息在5分钟内降至安全水位[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值