记一次redis并发处理问题

记一次redis并发处理问题

1、场景分析

所在的公司是物联网公司,涉及到向设备发送指令,如今的问题是在和天猫语音对接的一个产品线上出现了一个bugphp

启用组合指令模式,例如一个情景模式,一、开卧室灯、2开走廊灯、3开客厅灯
  • 上面的三个灯对于咱们的产品来讲就是一个开关面板,面板上面是一个三路开关,也就是每个灯对应一个开关,以下图:

image

当天猫精灵一条控制指令发送过来,我在将指令发送给设备,流程图以下

image

2、流程分析

上面能够看到整个流程,包括天猫语音触发应用云到下发控制指令的过程,
那几乎能够把重点锁定在下发参数上面redis

1.下发二进制指令

  • 如今有三路面板 对应的是 1 1 1 二进制位,公司最可能是8路开关
  • 则如上面的流程所示,当控制客厅的时候 下发参数 0 0 0 0 0 1 0 0

2.缓存机制

上面也看到了,在下发控制指令的时候会将 其余开关位的状态也带上。缓存

  • 那么实际在控制的时候,会去查下其余开关位的状态
  • 那么这个缓存状态是由设备来更新的
到这里来看,几乎也不会出现啥问题,接下来咱们就要直面真正遇到的问题

3、并发问题分析

上面的例子都是单次请求,由于同一个面板的不一样开关位虽然会依赖设备更新缓存,可是单次控制占时仍是没有问题的,也就是用户触发天猫控制灯的时候都是一个一个控制的并发

可是如今引入了场景,也就是天猫精灵上面有场景模式,例如设置场景 回家了,那么就是三路开关上全部的灯都要打开全部的灯,!并且天猫精灵是并发处理的,也就是会把三个请求同时发送到应用云上,而后应用云在下发控制指令。

问题分析

那么问题来了,三次请求之间都是有依赖关系的
  • 列如开客厅灯会查询走廊灯的状态,还有卧室灯的状态,都会去查询缓存,而三条控制指令都是同时到达,那么设备尚未更新缓存
  • 好比卧室灯(00000100) 走廊灯(00000101)已经开了,可是客厅灯最后进来,发现其余灯的缓存都是关的,那么会将0带下去((00000010)),而后致使前两次的灯开了又关。

4、优化处理方案

1.不依赖设备更新缓存,采用临时缓存

也就是不用去查询设备的缓存,直接采用临时缓存 ,时间1-2s,当并发的三个请求同时到来时,直接存储一个临时值,告诉其余请求针对要控制的面板有其余开关在控制
服务端采用的sowole进程模型
//采用hash结构,每个案件对应一个key值,
//每个按键的请求到来时更新hashkey时间为1s
$redis->hset("concurrent_control_hash_off".$devSn,$keynum,1);
$redis->expire("concurrent_control_hash_off".$devSn,1);
//直接从临时缓存里获取,若是没有在去设备缓存里获取
$res = $redis->hGetAll("concurrent_control_hash_off".$devSn);
  • 上诉的方法的方法在高并发下仍是有问题!

2.保证单次请求的redis操做原子性

也就是当并发请求的时候每条redis指令没有顺序
$redis->multi();
$redis->hset("concurrent_control_hash_on".$devSn,$keynum,1);
$redis->expire("concurrent_control_hash_on".$devSn,1);
$redis->hGetAll("concurrent_control_hash_on".$devSn);
$res = $redis->exec();
$concurrentArr = $res[2];
  • @multi()
  • @exec()

保持原子操做,效果到如今为止要好了些高并发

总结

即便上面根据流程作了屡次修改,已经对接流程的修改,总之没有绝对的状况,都须要进行优化,并发测试测试

相关文章
相关标签/搜索