mysql消息中心_消息中心开发过程中踩的几个(常识)坑

3. 锁超时

消息中心上线后,PM提出要将原有社区的消息全部迁移到消息中心,当时用了一个数量为24的线程池并发做数据迁移,结果测试时后台JDBC不停地报INSERT INTO ... ON DUPLICATE KEY UPDATE socket超时,而这句SQL是整个事务调用的第一条语句。

一开始怀疑是网络问题,于是把JDBC连接字符串中的socketTimeout参数调高到10s,然而并没有什么卵用。考虑到只有在数据迁移这样的并发场景下才会出现问题,很自然地想到会不会是锁超时。回顾发布消息的流程,第一步是更新消息所属Session,如果测试数据中有一批连续的消息发送给同一个人,且对应的聚合消息为同一条,那么极端情况下会出现24个线程争用同一把锁的情况。

但即使如此,如果每个事务的时间都比较短,对最后一个获取锁的事务而言10s也应该够了,除非事务里做了什么耗时的动作。按这个思路用 spring 提供的 StopWatch 分别对数据库操作、模板渲染&保存到Tair两个阶段计时,发现前者平均30ms,后者则高达300ms,问题应该就出在这里。

修复方法很简单:

降低线程数量,24–>12;

将模板渲染 & Tair存取的动作提到事务外面,降低事务耗时。

但这样一来我们就无法利用事务的原子性了,假如模板渲染或Tair失败,事务并不会回滚,用户会看到title或content为空的消息。考虑到真实业务中并不会出现同时给一个人发送大量消息的场景,相对并发程度而言,线上的数据完整性更重要,因此只在数据迁移时用了上面的方案。假如哪天既要保证并发度,又要保证数据完整性,可以使用上述方案,并在第二步失败时手动对数据订正,将脏数据还原或删除;用户在一个极短的时间窗口内是可能看到脏数据的,但这并没有太大影响。

最后还有一个问题,明明是锁的问题,为啥报 socket 超时呢?这是因为 InnoDB 锁超时时间由参数 innodb_lock_wait_timeout决定,默认值为50s,测试数据库并没有修改,因此 JDBC 只会发现 socket 读超时,而无法感知到锁超时。

经验&教训:

耗时的动作不在事务里做,尽量减少锁的持有时间;

根据具体业务的特点和需求,在事务的ACID与并发性之间做tradeoff,必要情况下可以允许短暂的脏数据,事后再进行数据订正;

活用 spring / apache.commons 提供的 StopWatch 分析任务耗时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值