记一个redis并发处理问题1.场景分析所在公司是一家IoT公司,涉及到向设备发送指令。目前问题是某产品线接入天猫语音发现开启组合指令模式的bug,比如场景模式,1.打开卧室灯,2.打开楼道灯,3.打开客厅的灯。上面三个灯是我们产品的一个开关面板,面板是一个三路Switch,也就是每个灯对应一个开关,如下图:天猫精灵发送控制命令,我在发送对设备的命令。流程图如下2.流程分析上面可以看到整个流程,包括天猫语音触发将云端应用到下发控制指令的过程中,重点可以锁定在发送参数上。1.发送二进制指令,现在有3个面板对应111个二进制数,公司最多有8个开关,如上流程所示,控制客厅时,参数00000发出1002。上面也看到了缓存机制,下发控制命令时也会带上其他开关位置的状态。那么在实际控制的时候,我们会查看其他开关位置的状态。然后缓存状态由设备更新。看这里,几乎不会有问题。接下来,我们将面对真正的问题。3.并发问题分析以上例子都是单次请求,因为虽然同一个面板的不同开关位置会依赖设备更新缓存,但是单次控制耗时还是没有问题的,即当用户触发天猫控灯。都是一一控制的,但是现在引入场景,就是天猫精灵上有一个场景模式,比如你设置场景回家,那么三路开关上的所有灯都必须被打开!而且天猫精灵是并发处理的,即会同时向应用云发送三个请求,然后由应用云下发控制指令。问题分析那么问题来了,三个请求之间存在依赖关系。比如打开客厅灯,会查询走廊灯的状态,卧室灯的状态会查询缓存,三个控制命令同时到达。那么设备还没有更新缓存。比如卧室灯(00000100)和楼道灯(00000101)已经打开了,但是客厅灯最后进来发现其他灯的缓存都关闭了,所以将0取下来((00000010)),然后导致灯的前两次打开和关闭。四、优化方案1、不依赖设备更新缓存,使用临时缓存,即不需要查询设备的缓存,直接使用临时缓存,时间为1-2s,当三个并发请求同时到达,直接存储一个临时值,告诉其他请求还有其他面板需要控制的开关。controlserver采用的sowole进程模型//采用hash结构,每个case对应一个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()维护原子操作,目前效果较好
