golang工程中间件——redis常用结构及应用(string, hash, list)

Redis

命令中心

【golang工程中间件------redisxxxxx】这些篇文章专门以应用为主,原理性的后续博主复习到的时候再详细阐述

string结构以及应用

字符数组,redis字符串是二进制安全字符串,可以存储图片等二进制数据,同时也可以存储经过 messagepack 或者 protobuffer 等工具压缩后的二进制数据; 内部实际存储根据 string 的数据特征可采用 int 、 embstr 、 raw 存储;(长度,是否能化为整数等条件,主要是长度)

基础命令

shell 复制代码
# 设置 key 的 value 值
SET key val
# 获取 key 的 value
GET key
# 执行原子加一的操作
INCR key
# 执行原子加一个整数的操作
INCRBY key increment
# 执行原子减一的操作
DECR key
# 执行原子减一个整数的操作
DECRBY key decrement
# 如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做
SETNX key value
# 删除 key val 键值对
DEL key
# 设置或者清空key的value(字符串)在offset处的bit值。
SETBIT key offset value
# 返回key对应的string在offset处的bit值
GETBIT key offset
# 统计字符串被设置为1的bit数.
BITCOUNT key

应用

对象存储

常用存储json字符串,或者protobuffer序列化二进制。

shell 复制代码
SET role:10001 '{["name"]:"mark",["sex"]:"male",["age"]:30}'
GET role:10001
# 固定不变或者几乎不会修改它采用

程序读取后反序列化为对象即可

累加器
shell 复制代码
# 统计访问数 累计加1
incr accessCount
# 累计加100
incrby accessCount 100
分布式锁
  • 排他性
  • 定义获取锁行为
  • 定义释放锁行为
shell 复制代码
# 加锁
setnx lock 1
# 释放锁
del lock
位运算
shell 复制代码
# 月签到功能 10001 用户id 202107 2021年7月份的签到 7月份的第1天
setbit sign:10001:202107 1 1
# 计算 2021年7月份 的签到情况
bitcount sign:10001:202107
# 获取 2021年7月份 第二天的签到情况 1 已签到 0 没有签到
getbit sign:10001:202107 2

list结构以及应用

首尾相接的双向链表,链表首尾操作时间复杂度为O(1) ;查找中间元素时间复杂度为O(n) ;

列表中数据可能会被压缩:

  • 元素长度小于 48,不压缩;
  • 元素压缩前后长度差不超过 8,不压缩;

基础命令

shell 复制代码
# 从队列的左侧入队一个或多个元素
LPUSH key value [value ...]
# 从队列的左侧弹出一个元素
LPOP key
# 从队列的右侧入队一个或多个元素
RPUSH key value [value ...]
# 从队列的右侧弹出一个元素
RPOP key
# 返回从队列的 start 和 end 之间的元素 0, 1 2
LRANGE key start end
# 从存于 key 的列表里移除前 count 次出现的值为 value 的元素
LREM key count value
# 它是 RPOP 的阻塞版本,因为这个命令会在给定list无法弹出任何元素的时候阻塞连接
# block right pop
BRPOP key timeout #

应用

sh 复制代码
LPUSH + LPOP
# 或者
RPUSH + RPOP
队列
sh 复制代码
LPUSH + RPOP
# 或者
RPUSH + LPOP
异步队列
redis队列存储应用日志,再落盘到ES索引中

类似异步队列,一些后台服务往redis队列里写日志,专门起日志落盘程序,读取redis队列中的日志json字符串(对象),格式化落盘到ES索引中。

阻塞队列(blocking queue)
sh 复制代码
LPUSH + BRPOP
# 或者
RPUSH + BLPOP

实际应用过程中,需要保证命令的原子性,所以需要使用 lua 脚本或者使用 pipeline 命令 + 事 务;

sh 复制代码
# 在某些业务场景下,需要获取固定数量的记录;比如获取最近50条战绩;这些记录需要按照插入的先
后顺序返回;
lpush says '{["name"]:"狐金道长", ["text"]:"镇山河!",
["picture"]:["url://image-20230601232741434.jpg", "url://image202306asxx741435.jpg"], timestamp = 1699241288}'
lpush says '{["name"]:"六红军爷", ["text"]:"任驰骋!",
["picture"]:["url://image-20230601134742434.jpg", "url://image20230asff72741436.jpg"], timestamp = 1699241288}'
lpush says '{["name"]:"蹩脚毒萝", ["text"]:"化蝶!",
["picture"]:["url://image-20230601172543434.jpg", "url://image2021060123a41437.jpg"], timestamp = 1699241288}'
lpush says '{["name"]:"sb弓", ["text"]:"我是傻狗",
["picture"]:["url://image-20230601172744678.jpg", "url://image20230601146272741438.jpg"], timestamp = 1699241288}'
# 裁剪最近5条记录 例如聊天记录 
ltrim says 0 4
# 获取队列所有内容
lrange says 0 -1

问题

可能会有很多连接去对list进行操作。但是redis是单线程的,只会处理一个命令。但是在这两条命令之间,可能会有别的命令对list进行操作。所以需要保证这两个命令一起执行。

# 裁剪最近5条记录 例如聊天记录 
ltrim says 0 4
# 获取队列所有内容
lrange says 0 -1

实际应用过程中,需要保证命令的原子性,所以需要使用 lua 脚本或者使用 pipeline 命令 + 事 务

hash结构以及应用

字典结构,通过 hash 函数来确定节点的位置,很多高级语言包含 这个数据结构,例如 c++ 中 unordered_map,go 语言当中的 map 结构;

基础命令

shell 复制代码
# 获取 key 对应 hash 中的 field 对应的值
HGET key field
# 设置 key 对应 hash 中的 field 对应的值
HSET key field value
# 设置多个hash键值对
HMSET key field1 value1 field2 value2 ... fieldn valuen
# 获取多个field的值
HMGET key field1 field2 ... fieldn
# 给 key 对应 hash 中的 field 对应的值加一个整数值
HINCRBY key field increment
# 获取 key 对应的 hash 有多少个键值对
HLEN key
# 删除 key 对应的 hash 的键值对,该键为field
HDEL key field

存储结构

当节点数量少的时候且字符串长度小的时候,内部采用压缩列表存储,否则采用字典实现;

sh 复制代码
10.62.122.23:7002> object encoding MyCart:10001
"ziplist"

应用

存储对象
sh 复制代码
hmset hash:10001 name qudaodao age 18 sex male
# 与 string 比较
set hash:10001 '{["name"]:"qudaodao",["sex"]:"male",["age"]:18,
["money"]:1000000}'
# 假设现在修改qudaodao的年龄为19岁
# hash:
hset hash:10001 age 19
# string:
get role:10001
# 将得到的字符串调用json.decode解密,取出字段,修改 age 值
# 再调用json加密
set role:10001 '{["name"]:"qudaodao",["sex"]:"male",["age"]:19}'
购物车
sh 复制代码
# 将用户id作为 key
# 商品id作为 field
# 商品数量作为 value
# 注意:这些物品是按照我们添加顺序来显示的;
# 添加商品:
hset MyCart:10001 40001 1
lpush MyItem:10001 40001
# 增加数量:
hincrby MyCart:10001 40001 1
hincrby MyCart:10001 40001 -1 // 减少数量1
# 显示所有物品数量:
hlen MyCart:10001
# 删除商品:
hdel MyCart:10001 40001
lrem MyItem:10001 1 40001
# 获取所有物品:
lrange MyItem:10001
# 40001 40002 40003
hget MyCart:10001 40001
hget MyCart:10001 40002
hget MyCart:10001 40003
相关推荐
fpcc4 小时前
redis6.0之后的多线程版本的问题
c++·redis
刘九灵4 小时前
Redis ⽀持哪⼏种数据类型?适⽤场景,底层结构
redis·缓存
登云时刻7 小时前
Kubernetes集群外连接redis集群和使用redis-shake工具迁移数据(一)
redis·kubernetes·bootstrap
hummhumm7 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
hummhumm7 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
YMWM_8 小时前
第一章 Go语言简介
开发语言·后端·golang
hummhumm9 小时前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
好奇的菜鸟9 小时前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.09 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
隔着天花板看星星10 小时前
Kafka-Consumer理论知识
大数据·分布式·中间件·kafka