Redis 学习笔记(4)-Redis 指令

Redis 学习笔记(4)-Redis 指令

本文是有关 Redis 的学习笔记的一部分,相关目录请参考 Redis 学习笔记(1)-简介。

上一篇 Redis 学习笔记(3)-部署单机 Redis下一篇 Redis 学习笔记(5)-客户端整合 Redis

在上一篇的学习中,已启动 Redis 容器,接下来要使用该 Redis 容器学习 Redis 指令。

下列为指令验证的项目:

StringHashListSetSorted SetMULTIFunctionLua 脚本

1. String 型别指令

为最基本的型别,其简易的操作(查/写/读/删) 如下:

操作指令範例查找 KeyKEYS patternkeys *写入 KeySET key valueset key k1 v1读出 KeyGET keyget k1删除 KeyDEL keydel k1

除了基本的操作外,模拟了三个想像的业务需求,使用 Redis 的功能来达成指定的需求。

流水号产生即时资源使用量流量控管

下列则是这三个业务需求的实作。

1.1 流水号产生

需求: 产出订单流水号,不可重複,由 1001 开始

相关指令:

操作指令範例递增 KeyINCR keyincr orderId:generator实作指令:指令操作说明set orderId:generator 1000设定初始值incr orderId:generator每次呼叫递增后回传incr orderId:generator每次呼叫递增后回传

1.2 即时资源使用量

需求: 统计系统当下的连接量,可作为是否熔断的依据

相关指令:

操作指令範例递减 KeyDECR keydecr session:current:count实作指令:指令操作说明incr session:current:count每次连入总数加1decr session:current:count每次断线总数减1get session:current:count取得当下连入数

1.3 流量控管

需求: 针对每一用户在单位时间内仅能进行额度内操作,超过额度则禁止该操作

相关指令:

操作指令範例确认 Key 存在EXISTS keyexists sessCtrl:sessId:101:count设定 Key 值和效期SETEX key seconds valuesetex sessCtrl:sessId:101:count 60 1读取 Key 效期TTL keyttl sessCtrl:sessId:101:count当 Key 不存在时写入SETNX key valuesetnx sessCtrl:sessId:101:count 1设定 Key 效期EXPIRE key secondsexpire sessCtrl:sessId:101:count 60实作一指令:指令操作说明exists sessCtrl:sessId:101:count检查该用户计数是否存在incr sessCtrl:sessId:101:count若存在则直接加1setex sessCtrl:sessId:101:count 60 1若不存在则设定值和效期get sessCtrl:sessId:101:count检视计数是否超额ttl sessCtrl:sessId:101:count检视效期实作二指令:指令操作说明setnx sessCtrl:sessId:101:count 1试写入 Key (不存在Key才写入)expire sessCtrl:sessId:101:count 60若成功则设定效期incr sessCtrl:sessId:101:count若失败则直接加1get sessCtrl:sessId:101:count检视计数是否超额ttl sessCtrl:sessId:101:count检视效期

2. Hash 型别指令

若一个键内包含不是单一的值,而是多个栏位,则可以使用杂凑(Hash)。例如,订单资料内有多个栏位(订单编号、客户编号、...)。

模拟了二个想像的业务需求,使用 Redis 的功能来达成指定的需求。

物件资料额度控管

下列则是这二个业务需求的实作。

2.1 物件资料

需求: 将一个客户资料集成在一个 Key 内

相关指令:

操作指令範例设定 Key 栏位HGET key fieldhset customer:custId:101 custId 101设定 Key 多栏位HMSET key field1 value1 [field2 value2 ]hset customer:custId:101 name Bob sex M读出 Key 栏位HGET key fieldhget customer:custId:101 name读出 Key 全栏位HGETALL keyhgetall customer:custId:101实作指令:指令操作说明hset customer:custId:101 custId 101设定 Key 和一个栏位hset customer:custId:101 name Bob sex M设定 Key 和多个栏位hgetall customer:custId:101检视所有栏位hget customer:custId:101 name检视一个栏位

2.2 额度控管

需求: 针对每个客户给定帐本,可储值和扣款,额度足够时才可执行指定的操作

相关指令:

操作指令範例对栏位作增减HINCRBY key field incrementhincrby customer:custId:101 points -30实作指令:指令操作说明hincrby customer:custId:101 points -30对帐本扣款30,若回传负值则表余额不足hincrby customer:custId:101 points 30若余额不足,作补偿操作hincrby customer:custId:101 points 100执行储值 100hincrby customer:custId:101 points -30对帐本扣款30,若回传正值则表余额足够hgetall customer:custId:101检视所有栏位

3. List 型别指令

列表添加元素时,可由表头添加、表尾添加或是插入指定位置。弹出列元素时,可由表头弹出也可由表尾弹出。可以使用这些功能模拟 Queue(FIFO) 或是 Stack (LIFO)。
当有多路资料滙总,依序由特定程序处理时,可使用由 List 模拟的 Queue 来完成。

模拟了一个想像的业务需求,使用 Redis 的功能来达成指定的需求。

任务待处理伫列

下列则是这一个业务需求的实作。

3.1 任务待处理伫列

需求: 多点可请求发送简讯,只需将简讯请求推入 Queue 即可,简讯发送程式(一个或多个)可由 Queue 取出请求并执行发送。

相关指令:

操作指令範例将元素由右侧(尾)推入RPUSH key value1 [value2]rpush task:reqQue "mdn:123,to:456,msg:msg01"将元素由左侧(头)取出RPOP keylpop task:reqQue以阻塞模式,将元素由左侧(头)取出BLPOP key1 [key2 ] timeoutblpop task:reqQue 30实作指令:指令操作说明rpush task:reqQue "mdn:123,to:456,msg:msg01"将请求推入 Queue 中rpush task:reqQue "mdn:123,to:456,msg:msg02"将请求推入 Queue 中rpush task:reqQue "mdn:123,to:456,msg:msg03"将请求推入 Queue 中lpop task:reqQue以非阻塞方式取出元表blpop task:reqQue 30以阻塞方式取出元表

4. Set 型别指令

集合内的元素不可重覆,故可用来作去重功能。集合可以差集、交集、联集,可用来达成共同朋友功能,或黑名单功能。

模拟了一个想像的业务需求,使用 Redis 的功能来达成指定的需求。

每日使用者访问数

下列则是这一个业务需求的实作。

4.1 每日使用者访问数

需求: 同一使用者每日访问可能多次,要求统计每日不同使用者访问总数

相关指令:

操作指令範例将元素加入SADD key member1 [member2]sadd visit:20220101 userId:101查元素总数SCARD keyscard visit:20220101查全部元素SMEMBERS keysmembers visit:20220101检查是否为集合元素SISMEMBER key membersismember visit:20220101 userId:101二集合交集SINTER key1 [key2]sinter visit:20220101 visit:20220102二集合联集并储存结果SUNIONSTORE destination key1 [key2]sunionstore visit:tmp01 visit:20220101 visit:20220102实作指令:指令操作说明sadd visit:20220101 userId:101记录 1/1 userId:101 访问sadd visit:20220101 userId:102记录 1/1 userId:102 访问sadd visit:20220101 userId:101记录 1/1 userId:101 访问sadd visit:20220101 userId:101记录 1/1 userId:101 访问scard visit:20220101查询 1/1 访问的使用者数目smembers visit:20220101查询 1/1 访问的使用者sismember visit:20220101 userId:103查询 userId:103 在 1/1 是否访问sadd visit:20220102 userId:101记录 1/2 userId:101 访问sadd visit:20220102 userId:103记录 1/2 userId:103 访问sinter visit:20220101 visit:202201021/1 & 1/2 皆有访问的使用者sunionstore visit:tmp01 visit:20220101 visit:202201021/1 & 1/2 皆有访问的使用者expire visit:tmp01 60对暂时的 Key 设效期scard visit:tmp01查询 1/1 ~ 1/2 访问的使用者数目

5. Sorted Set 型别指令

集合内的元素不可重覆,但每个元素可以有分数,元素会依分数由小到大在集合内排序。该特性可以用来实作排行榜、时效任务管理和权重任务管理。

模拟了一个想像的业务需求,使用 Redis 的功能来达成指定的需求。

活跃使用者排行榜

下列则是这一个业务需求的实作。

5.1 活跃使用者排行榜

需求: 依使用者的访问次数,列出前三名,榜单10分钟更新。相关指令:操作指令範例对元素分数作递增ZINCRBY key increment memberzincrby visit:active 1 userId:101以大到小排序,返回索引区间ZREVRANGE key start stop [WITHSCORES]zrevrange visit:active 0 100 withscores实作指令:指令操作说明zincrby visit:active 1 userId:101记录 userId:101 访问zincrby visit:active 1 userId:102记录 userId:102 访问zincrby visit:active 1 userId:103记录 userId:103 访问zincrby visit:active 1 userId:104记录 userId:104 访问zrevrange visit:active 0 2返回由大到小前三名zrevrange visit:active 0 2 withscores返回由大到小前三名(含分数)

6. MULTI 型别指令

MULTI 可以将数个指令先上传到伺服器上的队列中,此时尚未执行,待客户端发送 EXEC 指令时,伺服器上队列内的指令会一起执行。此时的执行也保证原子性操作。

7 Function 指令

Redis 7 以后支援 functions,在 Redis 7 以前是使用 LUA Script。相比 LUA Script 使用 function 更符合开发者习惯性。
使用 function 可将数个 redis 指令整合成为一个 redis function,执行时该 function 在 redis servier 端执行,不但整个 function 为一个原子性操作,所有的资料也由本地端读取不经由网路。

模拟了一个想像的业务需求,使用 Redis 的功能来达成指定的需求。

秒杀功能

下列则是这一个业务需求的实作。

7.1 秒杀功能

需求: 有产品三件,让大量客户端抢购,仅有最早三个请求可以成功。

实作指令(建立 function): 进入 Redis 容器后,在 Shell 中执行下列指令

cat << EOF | redis-cli -a 'mypwd' -x FUNCTION LOAD REPLACE#!lua name=mylib  local function flash_sale_top3(keys, args)  local myZset = keys[1] -- 取得 KEY 名称  local myZval = args[1] -- 取得 VALUE  local keyType = redis.call('type', myZset) -- 取得 KEY 的型别  keyType = keyType.ok or keyType -- 由 TABLE 取出结果  -- redis.log(redis.LOG_NOTICE, 'KeyType:'..keyType) -- 在日誌中印出  -- 若 KEY 存在但不为 ZSET 则回应错误  if keyType ~= 'none' and keyType ~= 'zset' then     return redis.error_reply('输入的 KEY 应为 zset 或不存在')  end  -- 若 VALUE 不存在 则回应错误  if myZval == nil then    return redis.error_reply('未输入 '..myZset..' 对应的值')  end  -- 若 VALUE 已加入 KEY 中,则回应重覆  local myScore = redis.call('zscore',myZset,myZval)  if myScore then    return redis.status_reply('duplicated-'..myScore)  end  -- 若 KEY 内的数目已达 3 则回应无额度  local count = redis.call('zcard', myZset)  if count >= 3 then    return redis.status_reply('no quota')  end  -- 将 VALUE 加入 KEY 中,并回应其排名  redis.call('zadd',myZset,count+1, myZval)  -- return redis.status_reply(tostring(count+1))  return redis.status_reply('success-'..(count+1))endredis.register_function('flash_sale_top3', flash_sale_top3)EOF

实作指令(执行 function):

指令操作说明fcall flash_sale_top3 1 myZset user01模拟 user01 抢购,成功fcall flash_sale_top3 1 myZset user02模拟 user02 抢购,成功fcall flash_sale_top3 1 myZset user03模拟 user03 抢购,成功fcall flash_sale_top3 1 myZset user04模拟 user04 抢购,失败fcall flash_sale_top3 1 myZset user05模拟 user05 抢购,失败fcall flash_sale_top3 1 myZset user02模拟 user02 重覆抢购,失败

8. Lua 脚本

若无法使用 Function 功能的情况下,也可以使用 LUA 脚本。LUA 脚本有二种使用方式如下:

使用 EVAL 指令: 将脚本内容上传,并直接执行使用 SCRIPT LOAD 指令将脚本内容上传,使用 EVALSHA 执行脚本

模拟了一个想像的业务需求,使用 Redis 的功能来达成指定的需求。

秒杀功能

下列则是这一个业务需求分别使用 EVAL 和 SCRIPT LOAD 的实作。

8.1 使用 EVAL 方式实作

需求: 有产品三件,让大量客户端抢购,仅有最早三个请求可以成功。

实作指令档

cat << EOF > myfunc.lua  local myZset = KEYS[1] -- 取得 KEY 名称  local myZval = ARGV[1] -- 取得 VALUE  local keyType = redis.call('type', myZset) -- 取得 KEY 的型别  keyType = keyType.ok or keyType -- 由 TABLE 取出结果  -- redis.log(redis.LOG_NOTICE, 'KeyType:'..keyType..',myZset:'..myZset) -- 在日誌中印出  -- 若 KEY 存在但不为 ZSET 则回应错误  if keyType ~= 'none' and keyType ~= 'zset' then     error( '输入的 KEY 应为 zset 或不存在' )  end  -- 若 VALUE 不存在 则回应错误  if myZval == nil then    error('未输入 '..myZset..' 对应的值')  end  -- 若 VALUE 已加入 KEY 中,则回应重覆  local myScore = redis.call('zscore',myZset,myZval)  if myScore then    return 'duplicated-'..myScore  end  -- 若 KEY 内的数目已达 3 则回应无额度  local count = redis.call('zcard', myZset)  if count >= 3 then    return 'no quota'  end  -- 将 VALUE 加入 KEY 中,并回应其排名  redis.call('zadd',myZset,count+1, myZval)  return 'success-'..(count+1)EOF

执行指令:

## 第一次抢购redis-cli -a 'mypwd' --eval myfunc.lua k1 , v1## 第二次抢购redis-cli -a 'mypwd' --eval myfunc.lua k1 , v2## 第三次抢购redis-cli -a 'mypwd' --eval myfunc.lua k1 , v3## 第四次抢购redis-cli -a 'mypwd' --eval myfunc.lua k1 , v4

8.2 使用 SCRIPT LOAD 方式实作

需求: 有产品三件,让大量客户端抢购,仅有最早三个请求可以成功。

实作指令档

cat << EOF | redis-cli -a 'mypwd' -x script load  local myZset = KEYS[1] -- 取得 KEY 名称  local myZval = ARGV[1] -- 取得 VALUE  local keyType = redis.call('type', myZset) -- 取得 KEY 的型别  keyType = keyType.ok or keyType -- 由 TABLE 取出结果  -- redis.log(redis.LOG_NOTICE, 'KeyType:'..keyType..',myZset:'..myZset) -- 在日誌中印出  -- 若 KEY 存在但不为 ZSET 则回应错误  if keyType ~= 'none' and keyType ~= 'zset' then     error( '输入的 KEY 应为 zset 或不存在' )  end  -- 若 VALUE 不存在 则回应错误  if myZval == nil then    error('未输入 '..myZset..' 对应的值')  end  -- 若 VALUE 已加入 KEY 中,则回应重覆  local myScore = redis.call('zscore',myZset,myZval)  if myScore then    return 'duplicated-'..myScore  end  -- 若 KEY 内的数目已达 3 则回应无额度  local count = redis.call('zcard', myZset)  if count >= 3 then    return 'no quota'  end  -- 将 VALUE 加入 KEY 中,并回应其排名  redis.call('zadd',myZset,count+1, myZval)  return 'success-'..(count+1)EOF

执行成功后会得到一个识别字串(如:28a7bad2bba6b34a220eaaf3cd6da48d4be22bd9),可用该字串来指称上传的 SCRIPT

执行指令:

## 第一次抢购evalsha 28a7bad2bba6b34a220eaaf3cd6da48d4be22bd9 1 k2 v1## 第二次抢购evalsha 28a7bad2bba6b34a220eaaf3cd6da48d4be22bd9 1 k2 v2## 第三次抢购evalsha 28a7bad2bba6b34a220eaaf3cd6da48d4be22bd9 1 k2 v3## 第四次抢购evalsha 28a7bad2bba6b34a220eaaf3cd6da48d4be22bd9 1 k2 v4

9. 结论

经过这些指令的学习,可以了解 Redis 特色功能,接下来就是整合到 Java 客户端程式中了。


参考资料:

Redis TutorialRedis教学Redis资料型别的应用业务场景分析Redis之五种基本类型及其应用场景举例使用Redis,你必须知道的21个注意要点

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章