首先,写在前面:
在整个供应链系统中,会出现多种单据(采购订单、入库单、到货单、运单等)。).当涉及到写入文档数据的接口(增加、删除、修改操作)时,即使前端做了相关限制,也有可能因为网络或者异常操作,同样的文档会被同样的处理。
为了防止这种情况对系统造成异常影响,我们通过Redis实现了一个简单的文档锁。每个请求都需要在执行业务逻辑之前获取锁,然后在执行之后释放锁;它保证同一文档只有一个并发重复操作的请求才能获得锁(单线程依赖Redis),是悲观锁设计;
注意:我们系统中的Redis锁一般只用于解决并发重复请求的情况。对于非并发的重复请求,数据库或日志通常用于检查数据状态。两种机制的结合可以保证整个环节的可靠性。
二、锁定机构:
主要依靠Redis setnx指令来实现:
但是使用setnx有一个问题,就是setnx指令不支持设置到期时间,需要使用expire指令为另一个键设置超时时间,所以整个锁定操作不是原子操作。有可能setnx锁定成功,但由于程序异常退出导致超时时间设置不成功,如果不及时解锁可能会导致死锁(即使业务场景中没有死锁,也始终没有设计好无用的密钥);
在这种情况下,可以使用Redis事务来解决问题,setnx和expire指令作为原子操作执行,但是这样做相对比较麻烦。幸运的是,在Redis 2.6.12之后的版本中,Redis指令集支持nx和ex两种模式,并且支持自动设置到期时间:
第三,锁的实现(完整的测试代码会贴在最后):
/* * *添加文档锁* @ param int $ intOrderId文档Id * @ param int $ int expiration entime锁到期时间(秒)* @return bool|int lock成功返回唯一锁ID。锁定失败返回false */public static函数add lock ($ int orderid,$ int expire time=self : redis _ lock _ default _ expire _ time){//参数check if(empty($ int orderid)| $ int expire time=0){ return false;}//获取Redis连接$ objredisconn=self :3360 getredisconn();//生成唯一的锁ID,解锁时需要持有$ INTUNIQUELOCKID=SELF :3360 Generate euniqueockid();//根据模板和单据ID,生成唯一的Redis密钥(一般来说,单据ID在业务系统中是唯一的)$ strkey=sprintf(self :3360 Redis _ lock _ key _ template,$ intoorderid);//锁定(通过Redis setnx指令实现,从Redis 2.6.12开始,setnx也可以通过set指令的可选参数实现,超时时间可以原子设置)$ BOLRES=$ objredisconn-SET($ strkey,$ intuniquelockid,['NX ',' ex '=$ intexpiretime]//锁定成功返回lockid,锁定失败返回false返回$ BOLRES?$ intUniqueLockId : $ bolRes}四。解锁机制:
解锁是指锁定时比较唯一的锁id,如果比较成功,删除钥匙;需要注意的是,解锁的整个过程还需要保证原子性,这取决于redis的观察和事务实现;
WATCH命令可以监视一个或多个键,一旦其中一个键被修改(或删除),后续的事务就不会被执行。监视一直持续到exec命令(事务中的命令在EXEC之后被执行,因此WATCH监视的键值可以在MULTI命令之后被修改)
动词(verb的缩写)解锁实现(完整的测试代码将在最后公布):
/**沙吾提* @ param int $ intorderid唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟id * @param int $intlockid苏秦(音译)id * @ return bool */public static函数rela sellock($ intockid){//你好if(empty($ intock)| | empty($ intock id)){ 0返回false} //阿宽再说一遍你好$ objredicon=self 3: getremotion();//阿金redis key $ strkey=sprint(self 3: redis _ lock _ key _ template,$ intordid);//你好吗重定向关键点日积月累【中文】云娥锁定id]-什么【中文】绿筠小姐【中文】阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金,哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥,哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥$ objredicon-watch($ str key);if($ intock id==$ objrediscon-get($ str key)){ $ objrediscon-multi()-del($ str key)-exec();返回true } $ objredicon-unwatch();返回false}菲儿~我爱你~阿云阿云阿云阿云(鲁仲尼鲁仲尼鲁仲尼)
?php/** *类锁定服务唉呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀*/class Lock_Service{ /**唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟重定向关键点昂儒昂*/const redis _ lock _ key _ template=' order _ lock _ % s ';/**哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟(哈哈哈)*/const redis _ lock _ default _ expire _ time=86400;/**唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟* @ param int $ intorderid唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟ID * @param int $intExpireTime朱曼丹曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔曼努埃尔(哈哈哈)* @返回bool|int唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔唔身份号,哎哎哎哎哎哎哎false */公共静态函数add lock($ intordid ),$ intexpiretime=self 3: redis _ lock _ default _ expire _ time){//你好if(empty($ intyrid)| | $ intexpiretime=0){ 0返回false} //阿宽再说一遍你好$ objredicon=self 3: getremotion();//安庆安庆安庆安庆身份号,绿筠小姐id $ intuniquelockd=self 3: generated uniqueid();//阿金,你好身份号,安其林安其林重定向键(何如,唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟身份号唉呀呀呀呀呀呀呀呀呀呀呀呀呀呀呀)$ strkey=sprint(self 3: redis _ lock _ key _ template、$ intordid);//哎哎(阿久瑞斯赛克斯吴亚玲呀,云娥第2.6.12条你好,阿久集合(设置)赛克斯,吴亚玲吴亚玲吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲,吴亚玲)$bolres=$objredisconn集($strKey、$intuniquelockid、['nx ',' ex '=$ int expirtime ");//唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟身份号,哎哎哎哎哎哎哎假回报$bolRes?插管固定3360美元;} /**沙吾提* @ param int $ intorderid唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟id * @param int $intlockid苏秦(音译)id * @ return bool */public static函数rela sellock($ intockid){//你好if(empty($ intock)| | empty($ intock id)){ 0返回false} //阿宽再说一遍你好$ objredicon=self 3: getremotion();//阿金redis key $ strkey=sprint(self 3: redis _ lock _ key _ template,$ intordid);//你好吗重定向关键点日积月累【中文】云娥锁定id]-什么【中文】绿筠小姐【中文】阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金阿金,哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥,哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥哥$ objredicon-watch($ str key);if($ intock id==$ objrediscon-get($ str key)){ $ objrediscon-multi()-del($ str key)-exec();返回true } $ objredicon-unwatch();返回false} /** *重复我的天:IP */const redis _ config _ host=' 127。0 .0 .1 ';/** *重复我的天:你好*/const REDIS _ CONFIG _ PORT=6379:/* *阿宽再说一遍你好(吴登盛,吴登盛(音译))* @ param string $ strip IP * @ param int $ int port你好* @返回对象Redis(返回对象重定向器)你好*/public static函数get disk($ strip=self 3: redis _ config _ host)、$ int port=self 3: redis _ config _ port){ $ objredis=new redis();$星期四-连接($ strip、$ intport);星期三返回$美元;} /**王世英王世英王世英身份号什么事redis密钥*/const redis _ lock _ unique _ id=lock _ unique _ id;/**钟其文钟其文ID(阿久Redis incr(增量)阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔阿叔,吴亚玲安亚玲安亚玲安亚玲安亚玲安亚玲安亚玲安亚玲安亚玲安亚玲安亚玲安亚玲安亚玲~我爱你~阿云哥~我爱你~哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟~我爱你~你好~我爱你~吴亚玲吴亚玲,安其林安其林安其林安其林id)* @ return mixed */public static函数generate dunique lock id(){ return self 3: getregyptonin()-incr(self 3: redis _ lock _ unique _ id _ key);}//test $ RES 1=lock _ service 3: addlock(' 66666 ');var _ dump(RES 1);//阿忠锁定身份证、吴亚玲(音似"美"音似"音似"音似"音似"音似"音似"音似"音似"音似"音似"音似"音似"音似"音似"音似"音似"音似" $ RES 2=lock _ service 3:添加锁(' 6666 ');var _ dump(R2);//false,唉哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟哟$ R3=lock _ service 3:释放锁(' 6666 ',$ RES 1);var _ dump($ R3);//真,绿筠小姐$ RES 4=lock _ service 3:释放锁(' 6666 ',$ RES 1);var _ dump(R4);//false,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,你看,云娥云娥。