Redis 学习笔记(4)-Redis 指令
本文是有关 Redis 的学习笔记的一部分,相关目录请参考 Redis 学习笔记(1)-简介。
上一篇 Redis 学习笔记(3)-部署单机 Redis下一篇 Redis 学习笔记(5)-客户端整合 Redis
在上一篇的学习中,已启动 Redis 容器,接下来要使用该 Redis 容器学习 Redis 指令。
下列为指令验证的项目:
StringHashListSetSorted SetMULTIFunctionLua 脚本1. String 型别指令
为最基本的型别,其简易的操作(查/写/读/删) 如下:
除了基本的操作外,模拟了三个想像的业务需求,使用 Redis 的功能来达成指定的需求。
流水号产生即时资源使用量流量控管下列则是这三个业务需求的实作。
1.1 流水号产生
需求: 产出订单流水号,不可重複,由 1001 开始
相关指令:
1.2 即时资源使用量
需求: 统计系统当下的连接量,可作为是否熔断的依据
相关指令:
1.3 流量控管
需求: 针对每一用户在单位时间内仅能进行额度内操作,超过额度则禁止该操作
相关指令:
2. Hash 型别指令
若一个键内包含不是单一的值,而是多个栏位,则可以使用杂凑(Hash)。例如,订单资料内有多个栏位(订单编号、客户编号、...)。
模拟了二个想像的业务需求,使用 Redis 的功能来达成指定的需求。
物件资料额度控管下列则是这二个业务需求的实作。
2.1 物件资料
需求: 将一个客户资料集成在一个 Key 内
相关指令:
2.2 额度控管
需求: 针对每个客户给定帐本,可储值和扣款,额度足够时才可执行指定的操作
相关指令:
3. List 型别指令
列表添加元素时,可由表头添加、表尾添加或是插入指定位置。弹出列元素时,可由表头弹出也可由表尾弹出。可以使用这些功能模拟 Queue(FIFO) 或是 Stack (LIFO)。
当有多路资料滙总,依序由特定程序处理时,可使用由 List 模拟的 Queue 来完成。
模拟了一个想像的业务需求,使用 Redis 的功能来达成指定的需求。
任务待处理伫列下列则是这一个业务需求的实作。
3.1 任务待处理伫列
需求: 多点可请求发送简讯,只需将简讯请求推入 Queue 即可,简讯发送程式(一个或多个)可由 Queue 取出请求并执行发送。
相关指令:
4. Set 型别指令
集合内的元素不可重覆,故可用来作去重功能。集合可以差集、交集、联集,可用来达成共同朋友功能,或黑名单功能。
模拟了一个想像的业务需求,使用 Redis 的功能来达成指定的需求。
每日使用者访问数下列则是这一个业务需求的实作。
4.1 每日使用者访问数
需求: 同一使用者每日访问可能多次,要求统计每日不同使用者访问总数
相关指令:
5. Sorted Set 型别指令
集合内的元素不可重覆,但每个元素可以有分数,元素会依分数由小到大在集合内排序。该特性可以用来实作排行榜、时效任务管理和权重任务管理。
模拟了一个想像的业务需求,使用 Redis 的功能来达成指定的需求。
活跃使用者排行榜下列则是这一个业务需求的实作。
5.1 活跃使用者排行榜
需求: 依使用者的访问次数,列出前三名,榜单10分钟更新。相关指令: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):
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个注意要点