文章目录
- Redis1------基本命令及原理
-
- [1. Redis原理](#1. Redis原理)
-
- [1.1 特点](#1.1 特点)
- [1.2 数据类型及其存储方式](#1.2 数据类型及其存储方式)
-
- [1.2.1 **string** 字符串](#1.2.1 string 字符串)
- [1.2.2 **list** 列表](#1.2.2 list 列表)
- [1.2.3 **hash** 哈希表](#1.2.3 hash 哈希表)
- [1.2.4 **set** 集合](#1.2.4 set 集合)
- [1.2.5 **zset** 有序集合](#1.2.5 zset 有序集合)
- [2. 基本命令及应用场景:](#2. 基本命令及应用场景:)
-
- [2.1 Redis应用场景](#2.1 Redis应用场景)
- [2.2 string------sds动态字符串](#2.2 string——sds动态字符串)
- [2.3 list------双向循环链表](#2.3 list——双向循环链表)
- [2.4 hash------散列表](#2.4 hash——散列表)
- [2.5 set------无序集合](#2.5 set——无序集合)
- [2.6 zset------有序集合](#2.6 zset——有序集合)
- [2. 如何设计kv](#2. 如何设计kv)
-
- [2.1 如何设计key](#2.1 如何设计key)
- 学习参考
Redis1------基本命令及原理
1. Redis原理
Redis是Re mote D ictionary Service的简称,及远程字典服务。Redis是一个基于内存的存储键值对的非关系型数据库。
1.1 特点
- 没有显式地创建和和删除数据结构地语句,当添加元素地时候自动创建,当删除元素时,如果集合为空,则自动删除数据结构。
- 数据量少的时候,以存储效率高为主(ziplist),数据量大的时候以查询效率高为主(skiplist,dict)
1.2 数据类型及其存储方式
主要是指value指的数据结构:
1.2.1 string 字符串
存储结构:如果字符串可以表示为整数,且范围在64位二进制以内,则存储为整数,否则使用sds存储。
sds是一个能够动态扩容的内存安全的数组结构,当字符串长度不超过512M时使用它来存储。
如果字符串长度小于44字节,使用embstr编码(压缩编码),将redis对象结构与SDS分配在连续的内存中,减少内存碎片。
否则采用raw编码(普通编码),将redis对象结构与SDS分开存储,因此需要更多的内存分配和释放操作。
注意 :redis的string
是一个安全的二进制字符串。二进制指可以不存储为字符编码,安全是指保存了字符串的长度信息。
1.2.2 list 列表
存储结构:当节点总数小于等于512或者元素长度都不超过64字节时采用ziplist压缩列表。否则采用quicklist双向循环链表。
注意:保证插入有序。
1.2.3 hash 哈希表
存储结构:节点数量大于512(hash-max-ziplist-entries)或所有字符串长度大于64(hash-max-ziplist-value),则使用dict实现。
节点数量小于等于512且有一个字符串长度小于64,则使用ziplist实现。
注意:保存的元素为键值对,能够去重。
1.2.4 set 集合
存储结构:元素都为整数且元素数量小于等于512(set-max-intset-entries),则使用整数数组存储。否则使用dict。
注意:保存的元素为值,能够去重。
1.2.5 zset 有序集合
存储结构:节点数量大于128或者有一个字符串长度大于64,则使用跳表skiplist,否则使用压缩列表ziplist。
2. 基本命令及应用场景:
2.1 Redis应用场景
用于各种需要高效的临时存储和访问数据的场景。例如用作缓存中间件:缓存热点数据;用户标识信息等。
2.2 string------sds动态字符串
扩容策略:当长度小于1M时,加倍扩容;超过1M每次只扩容1M;字符串最大为512M。
存储内容:既可以存储字符串,也可以存储其它二进制数据,例如二进制协议数据protobuf等。
基本操作:
shell
# 赋值与查询
SET key val
SETNX key val # 如果不存在才赋值
GET key
# 增加
INCR key # 累加1
INCRBY key increment
# 减少
DECR key
DECRBY key decrement
# 删除各种类型的kv对
DEL key
# 查看值得数据类型
TYPE key
应用
作为位图使用
shell
# 将其作为二进制数据,位图
SETBIT key offset value
BITCOUNT key start end
GETBIT key offset
对象存储
shell
SET key '{"name":["li", "lou", "la"], "age": [21, 33, 12]}'
累加器
shell
incr num
incrby num 100
分布式锁
shell
# 获取锁
SET lock_key uuid NX EX 10
释放锁:lua脚本,保证原子性
lua
if redis.call("GET", lock_key) == uuid then
-- 成功释放
return redis.call("DEL", KEYS[1])
else return 0
end
2.3 list------双向循环链表
列表首尾操作时间复杂度为O(1),中间元素操作时间复杂度为O(n)。
基本操作
shell
LPUSH key e1 e2 ... en # 插入左侧
LPOP key [count] # 弹出左侧
RPUSH key e1 e2 ... en # 掺入右侧
RPOP key [count]
# 查询
LRANGE key start end
# 删除
LREM key count element
# 阻塞弹出,如果列表为空就等待
BRPOP k1 k2 ... kn timout
# 裁剪数据,只保留特定范围的数据
ltrim key start end
应用
栈(先进后出)
shell
LPUSH + LPOP
# 或者
RPUSH + RPOP
队列(先进先出)
shell
LPUSH + RPOP
# 或者
RPUSH + LPOP
阻塞队列
shell
LPUSH + BRPOP
# 或者
RPUSH + BLPOP
异步消息队列
shell
# 同队列操作,只是在不同的系统之间
获取固定窗口的数据记录
shell
# 获取最近50条记录
LTRIM key 0 49
2.4 hash------散列表
数据类型为散列表,保存多个键值对,可以方便修改某个字段的值。
基本命令
shell
# 赋值与查询
hset key f1 v1 f2 v2 ... fn vn
hmset key f1 v1 f2 v2 ... fn vn
hgetall key
hget key field
# 递增
hincrby key field increment
# 查看键值对个数
hlen key
# 删除
hdel key field
应用
通常将hash与其它数据结构组合,完成业务功能
shell
2.5 set------无序集合
基本命令
shell
# 添加元素
SADD key m1 m2 ... mn
# 查看集合所有元素
SMEMBERS key
# 查询元素个数
SCARD key
# 查询集合中是否存在某个元素
SISMEMBER key member
# 从集合中随机返回一个或多个元素,但不删除
SRANDMEMBER key [count]
# 从集合中随机删除一个或多个元素
SPOP key [count]
# 做差集
SDIFF key [key ...]
# 做交集
SINTER key [key ...]
# 做并集
SUNION key [key ...]
应用
抽奖
shell
srandmember key 10
共同关注的人
shell
sinter key1 key2
推荐好友
shell
sdiff john tom # 将john的好友推荐给tom
2.6 zset------有序集合
基础命令
shell
# 插入元素
ZADD key [NX|XX] [CH] [INCR] s1 m1 s2 m2 ... sn mn
# 删除元素
ZREM key m1 m2 ... mn
ZREMRANGEBYSCORE key min max
# 返回score
ZSCORE key member
# 修改score
ZINCRBY key increment member
# 返回元素个数
ZCARD key
# 返回排名
ZRANK key member
# 返回排名范围的member,默认时从小到大排序
ZRANGE key start stop [WITHSCORES]
# 逆序返回
ZREVRANGE key start stop [WITHSOCRES]
应用
排行榜
shell
ZINCRBY players 13 adam
# 返回分数最大前10位player
ZREVRANGE players 0 9 withscores
延时队列
使用zset保存消息,其score为到期时间,这样可以轮询zset将到期的消息取出处理。
python
def delay(msg):
msg.id = str(uuid.uuid4())
value = json.dumps(msg)
retry_ts = time.time() + 5
redis.zadd("delay-queue", retry_ts, value)
def loop():
while True:
values = redis.zrangebyscore("delay-queue", 0, time.time(), start=0, num=1)
if not values:
time.sleep(1)
continue
value = values[0]
success = redis.zrem("delay-queue", value)
# 保证原子性
if success:
msg = json.loads(value)
handle_msg(msg)
分布式定时器
生产者将定时任务利用hash分配到不同的redis中,每个redis有一个dispather进程,不断从中获取到期的定时任务并发布到不同的消费者中。
时间窗口限流
限定用户的某个行为在指定的可动态变化的时间范围内最多发生N次
lua
local function is_action_allowed(res, userid, action, period, max_count)
local key = tab_concat({"hist", userid, action}, ":")
local now = zv.time()
red:init_pipeline()
-- 记录行为
red:zadd(key, now, now)
-- 移除时间窗口外的行为
red:zremrangebyscore(key, 0, now - period * 100)
-- 获取时间窗口内的行为数量
red:zcard(key)
-- 设置过期时间
red:expire(key, period + 1)
local res = red:commit_pipeline()
return res[3] <= max_count
end
2. 如何设计kv
2.1 如何设计key
对于单个功能的单个key,使用有意义的名字
shell
SET name lee
对于相同功能的多个key,使用有意义的字段并用冒号分隔。
shell
SETBIT sign:lee:202410 12 1
学习参考
学习更多相关知识请参考零声 github。