1.String(基于SDS)
应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
简单动态字符串(SDS)与原生字符串的区别:
- SDS 不仅可以保存文本数据,还可以保存二进制数据。因为SDS 使用 len 属性的值而不是空字符来判断字符串是否结束,并且 SDS 的所有 API 都会以处理二进制的方式来处理 SDS 存放在 buf[] 数组里的数据;
- Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出。 SDS 在拼接字符串之前会检查 SDS 空间是否满足要求,如果空间不够会自动扩容;
- 取字符串长度的时间复杂度是 O(1)。
1.1 String的三种编码格式
embstr和raw编码都会使用SDS来保存值,但不同之处在于embstr会通过一次内存分配函数来分配一块连续的内存空间来保存redisObject和SDS,而raw编码会通过调用两次内存分配函数来分别分配两块空间来保存redisObject和SDS。
1.2 基本使用
127.0.0.1:6379> set name dx
OK
127.0.0.1:6379> get name
"dx"
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> strlen name
(integer) 2
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
1.3 存储webp格式的文件
var rdb *redis.Client
func initClient() (err error) {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // 密码
DB: 0, // 数据库
PoolSize: 20, // 连接池大小
})
_, err = rdb.Ping().Result()
return err
}
// 缓存图片
func CachePicWithRedis() {
// 打开图片文件
data, err := os.ReadFile("D:/Projects/go_-projects/gin_test/redisdemo/img.webp")
if err != nil {
panic(err)
}
// 将图片数据写入Redis
err = rdb.Set("image", data, 0).Err()
if err != nil {
panic(err)
}
fmt.Println("Image cached in Redis.")
}
func RedisDemo() {
err := initClient()
if err != nil {
fmt.Printf("redis init failed, err:%v\n", err)
return
}
fmt.Printf("redis init success...\n")
defer rdb.Close()
CachePicWithRedis()
}
执行
127.0.0.1:6379> get image
后的部分结果
...4\xd7BM\xe4cK\xc3\xb3\x969\xe2\x01\x95\x9c\xb6EVI\xb31\xc7\xa3\xaa\x02DN\xff\xef\xfe<\x91\x029k\b\xcd\x0e:\x061\x8f}\xd1_\x19\xbb\x82\x84.\x05\xb3\xb1\n\xc0KU/\x85\x00\x83\x01$\xb3.\x99\xcc\xa0\x13>W\xc0\x84\xed,^\t\x02\xc9=t\x0c\x90\x80\xf5\xe6\x94M\x91\x03,y\x8a\x8d\xdb\x1a\xaeB\xb6\xf8\xd2Fg\...
1.4 应用
1.4.1缓存对象(见1.2和1.3)
1.4.2常规计数
127.0.0.1:6379> SET aritcle:readcount:1001 0
OK
127.0.0.1:6379> INCR aritcle:readcount:1001
(integer) 1
127.0.0.1:6379> INCR aritcle:readcount:1001
(integer) 2
127.0.0.1:6379> DECR aritcle:readcount:1001
(integer) 1
127.0.0.1:6379> GET aritcle:readcount:1001
"1"
1.4.3分布式锁
实现原理:SET 命令有个 NX 参数可以实现「key不存在才插入」,可以用它来实现分布式锁,如果 key 不存在,则显示插入成功,可以用来表示加锁成功;如果 key 存在,则会显示插入失败,可以用来表示加锁失败。
设置锁
//unique_value用于标识加锁的客户端,只有加锁的客户端才能解锁;
//NX代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
//PX 10000 表示设置 lock_key 的过期时间为 1000s,这是为了避免客户端发生异常而无法释放锁。
127.0.0.1:6379> SET lock_key unique_value NX PX 1000000
OK
解锁时,先获取值,判断值与当前解锁的客户端是否一致,一致就删除key,解锁成功,否则解锁失败。
1.4.4共享session信息
通常我们在开发后台管理系统时,会使用 Session 来保存用户的会话(登录)状态,这些 Session 信息会被保存在服务器端,但这只适用于单系统应用,如果是分布式系统此模式将不再适用。
例如用户一的 Session 信息被存储在服务器一,但第二次访问时用户一被分配到服务器二,这个时候服务器并没有用户一的 Session 信息,就会出现需要重复登录的问题,问题在于分布式系统每次会把请求随机分配到不同的服务器。
因此,我们需要借助 Redis 对这些 Session 信息进行统一的存储和管理,这样无论请求发送到那台服务器,服务器都会去同一个 Redis 获取相关的 Session 信息,这样就解决了分布式系统下 Session 存储的问题。
List(基于quicklist)
应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。
注:5.0 版新增Stream,用于做消息队列,相比于基于 List 类型实现的消息队列,可以自动生成全局唯一消息ID,支持以消费组形式消费数据。
Hash(Redis7基于listpack)
应用场景:缓存对象、购物车等。
Set(哈希表或整数集合)
应用场景:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
ZSet(Redis7基于listpack)
应用场景:排序场景,比如排行榜、电话和姓名排序等。