前言在使用Redis的过程中,我们难免会遇到并发访问和数据更新的问题。但是很多场景对数据的并发修改非常敏感。例如,如果库存数据的并发读取和更新控制不当,将会导致严重的业务问题。今天我们就来说说如何做好并发访问和数据更新。哪些场景需要控制并发访问?需要控制并发访问,说明这些并发访问可能会影响其他访问。比如上面提到的库存问题,如果多个客户端同时访问商品A的库存数据,并且可能需要更新库存数据,那么就需要控制并发访问。毕竟并发访问需要控制的是对数据的更新动作。一般来说,当客户端要更新数据时,可以分为两步:客户端读取Redis数据到本地。确认数据后,修改Redis数据。从单次访问来看,这个过程没有任何问题。但是,如果并发过多,一分为二的进程会导致数据错误。这里还是以库存为例:时间a::客户端1读取库存=10,我们需要对库存+1=11进行操作。时间b:客户端2读取的库存也是10,此时库存-1=9需要操作。c时刻:客户端1将+1后的值11写回Redis。d时刻:Client2将-1后的值9写回Redis。这样一来,很明显库存数据是错误的。10+1-1=10,正确的存货是10,上面的场景最后是9。可见一分为二的操作不是原子的,所以产生了错误的结果。这种场景有很多,所以我们需要对这些并发访问场景进行控制。并发访问控制方法Redis并发访问控制一般有两种方法。他们正在添加一种锁定机制,并使一系列操作成为原子操作。1、加锁机制首先,加锁机制是一种很常见的方案。简单来说,客户端在访问数据之前,首先要获取锁,数据操作完成后,再解锁。在客户端拥有锁的同时,如果其他客户端也想访问和修改数据,则必须等待锁释放后才能获取锁。加锁方案可以解决并发访问的数据准确性问题,但是在redis场景下效果不是很好。首先,作为缓存,Redis本身是有很多并发访问的。频繁加锁和解锁会大大降低Redis的访问性能;那么,当Redis客户端需要加锁的时候,就需要使用分布式锁。我们必须使用额外的能量来维护这个分布式锁。2.操作的原子化操作的原子化意味着要执行的一系列动作保持原子性。它的优点是不需要额外的锁定机制。做到了并发数据的准确性,不会对Redis的性能造成太大的影响。在Redis中实现原子操作,总结起来有两种方式:单命令操作:即Redis中的INCR、HINCRBY等命令,直接将简单的加减运算组合成一条命令执行;Lua脚本:借助Lua脚本,多个操作在Lua脚本上实现原子操作。1、单命令操作首先,单命令操作是直接用Redis命令执行值的加减操作。字符串的加减法可以通过INCR和DECR来完成,哈希列表字段的加减法可以通过HINCRBY来完成。比如下面截图中,两个client在不同时间读取到的linux_pidsa值为4,+1和-1后a值分别为4。结果是正确的。可见使用Redis的INCR、DECR等命令可以解决简单的增减值的并发场景。但是如果我们的数据更新不是简单的加减运算,Redis的这些命令就无能为力了。此时我们可以考虑另一种解决方案:Lua脚本。2、Lua脚本Lua语言是C语言编写的,所以支持多平台多系统。从Redis2.6开始,Redis内置了Lua解释器,我们可以直接使用Redis客户端来执行lua脚本。我们可以将一系列需要执行的操作写在Lua脚本中,然后使用Redis来执行。Lua脚本方式之所以能保证原子操作,是因为Redis会对Lua脚本执行一次,也就是说执行Lua脚本是0-1的操作,要么成功,要么失败。可以理解为MySQL的事务特性。Redis使用lua脚本有两种方式:在客户端使用:使用script加载脚本内容,evalsha等命令直接执行lua脚本。我们一般采用第二种方式来执行。客户端使用方法:先使用scriptload加载脚本命令,然后使用evalsha执行加载的sha1值。127.0.0.1:6379>scriptload"return'hello'""1b936e3fe509bcbc9cd0664897bbe8fd0cac101b"127.0.0.1:6379>evalsha"1b936e3fe509bcbc9cd0664897bbe8fd0cac101b"0"hello"再来看看Redis使用Lua脚本的语法:redis-cli--eval{lua_path}KEYS[1]KEYS[2]...,ARGV[1]ARGV[2]...--eval:执行lua脚本的命令{lua_path}:lua脚本路径KEYS[1]KEYS[2]:lua脚本中要操作的rediskey,我们可以在lua脚本中使用KEYS[1]、KEYS[2]、KEYS[3]指定多个ARGV[1]ARGV[2]:传递给lua脚本的参数,在脚本中使用ARGV[1],ARGV[2]...获取。Redis使用lua脚本的场景有很多。最经典的案例就是用lua来控制某个IP的访问频率。例如,为了防止恶意访问网站,我们规定1分钟内的访问次数不得超过30次。实现方式有很多种,比如漏桶方案和令牌桶方案,但应用最广泛的是Redis+lua分布式限流方案。我们使用lua脚本(test_lua.script)简单实现以上功能,即1分钟内访问次数超过30则直接拦截,否则访问次数+1:--current-limitedkeylocallimit_key=KEYS[1]--limit本地流的数量limit_nums=30--当前访问的数量localcurrent_num=tonumber(redis.call('get',limit_key)or0)--访问的数量timesthecurrentlimitexceededifcurrent_num+1>limit_numthenreturn'exceededthenumberofvisits'--如果没有超过当前限制,则访问次数+1elseredis.call("INCRBY",limit_key,"1")--第一次访问,设置过期时间ifcurrent_num==0thenredis.call("expire",limit_key,"60")returncurrent+1end用Redis执行,命令如下:redis-cli--evaltest_lua.scriptlimit_key总结本文介绍了Redis并发访问的控制以及如何保证并发操作的原子性。原子操作可以通过单命令操作和Lua脚本来实现。我们在处理相关问题的时候,可以根据自己的需要选择相应的方案来解决。至此,这篇关于Redis并发访问问题详解的文章就介绍到这里了。更多Redis并发访问相关内容,请搜索服务器之家往期文章或继续浏览下方相关文章。希望大家以后多多支持服务器之家!原文链接:https://blog.csdn.net/m0_71777195/article/details/128094212
