Redis五种基本数据类型的用法和常见命令的使用
Redis数据类型和应用场景
Redis是一个Key-Value的存储系统,使用ANSI C语言编写。
key的类型是字符串。
value的数据类型有:
常用的:string字符串类型、list列表类型、set集合类型、sortedset(zset)有序集合类型、hash类型。
不常见的:bitmap位图类型、geo地理位置类型。
Redis5.0新增一种:stream类型
注意:Redis中命令是忽略大小写,(set SET),key是不忽略大小写的 (NAME name)
Redis的Key的设计
- 用:分割
- 把表名转换为key前缀, 比如: user:
- 第二段放置主键值
- 第三段放置列名
比如:用户表user, 转换为redis的key-value存储
username 的 key: user:9:username
{userid:9,username:zhangf}
email的key user:9:email
表示明确:看key知道意思
不易被覆盖
string字符串类型
Redis的String能表达3种值的类型:字符串、整数、浮点数 100.01 是个六位的串
应用场景:
1、key和命令是字符串
2、普通的赋值
3、incr用于乐观锁
incr:递增数字,可用于实现乐观锁 watch(事务)
4、setnx用于分布式锁
当value不存在时采用赋值,可用于实现分布式锁
list列表类型
应用场景:
1、作为栈或队列使用
列表有序可以作为栈和队列使用
2、可用于各种列表,比如用户列表、商品列表、评论列表等。
set集合类型
Set:无序、唯一元素
集合中最大的成员数为 2^32 - 1
应用场景:
适用于不能重复的且不需要顺序的数据结构
比如:关注的用户,还可以通过spop进行随机抽奖
sortedset有序集合类型
SortedSet(ZSet) 有序集合: 元素本身是无序不重复的
每个元素关联一个分数(score) 可按分数排序,分数可重复
应用场景:
由于可以按照分值排序,所以适用于各种排行榜。比如:点击排行榜、销量排行榜、关注排行榜等。
hash类型(散列表)
Redis hash 是一个 string 类型的 field 和 value 的映射表,它提供了字段和字段值的映射。
每个 hash 可以存储 2^32 - 1 键值对(40多亿)。
应用场景:
对象的存储 ,表数据的映射
bitmap位图类型
bitmap是进行位操作的
通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。
bitmap本身会极大的节省储存空间。
应用场景:
1、用户每月签到,用户id为key , 日期作为偏移量 1表示签到
2、统计活跃用户, 日期为key,用户id为偏移量 1表示活跃
3、查询用户在线状态, 日期为key,用户id为偏移量 1表示在线
geo地理位置类型
应用场景:
1、记录地理位置
2、计算距离
3、查找”附近的人”
stream数据流类型
stream是Redis5.0后新增的数据结构,用于可持久化的消息队列。几乎满足了消息队列具备的全部内容,包括:
消息ID的序列化生成
消息遍历
消息的阻塞和非阻塞读取
消息的分组消费
未完成消息的处理
消息队列监控
每个Stream都有唯一的名称,它就是Redis的key,首次使用 xadd 指令追加消息时自动创建。
底层数据结构
Redis没有表的概念,Redis实例所对应的db以编号区分,db本身就是key的命名空间。
比如:user:1000作为key值,表示在user这个命名空间下id为1000的元素,类似于user表的id=1000的行。
RedisDB结构
Redis中存在“数据库”的概念,该结构由redis.h中的redisDb定义。
当redis 服务器初始化时,会预先分配 16 个数据库
所有数据库保存到结构 redisServer 的一个成员 redisServer.db 数组中
redisClient中存在一个名叫db的指针指向当前使用的数据库
1 | typedef struct redisDb { |
RedisObject结构
Value是一个对象
包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象
结构信息概览
1 | typedef struct redisObject { |
- 4位type
- 4位encodin
- 24位LRU
lru 记录的是对象最后一次被命令程序访问的时间,( 4.0 版本占 24 位,2.6 版本占 22 位)。
高16位存储一个分钟数级别的时间戳,低8位存储访问计数(lfu : 最近访问次数)
lru—-> 高16位: 最后被访问的时间
lfu—–>低8位:最近访问次数
- refcount
refcount 记录的是该对象被引用的次数,类型为整型。
refcount 的作用,主要在于对象的引用计数和内存回收。
当对象的refcount>1时,称为共享对象
Redis 为了节省内存,当有一些对象重复出现时,新的程序不会创建新的对象,而是仍然使用原来的对象。
- ptr
ptr 指针指向具体的数据,比如:set hello world,ptr 指向包含字符串 world 的 SDS。
type
- 字符串对象
- 跳跃表
跳跃表是有序集合(sorted-set)的底层实现,效率高,实现简单。
跳跃表的基本思想:
将有序链表中的部分节点分层,每一层都是一个有序链表。
- 数组
- Hash表
- dict字典
- 压缩列表
- 快速列表
- 双向列表
- …
缓存过期和淘汰策略
Redis性能高:
官方数据
读:110000次/s
写:81000次/s
长期使用,key会不断增加,Redis作为缓存使用,物理内存也会满
内存与硬盘交换(swap) 虚拟内存 ,频繁IO 性能急剧下降
maxmemory
不设置的场景
Redis的key是固定的,不会增加
Redis作为DB使用,保证数据的完整性,不能淘汰 , 可以做集群,横向扩展
缓存淘汰策略:禁止驱逐 (默认)
设置的场景
Redis是作为缓存使用,不断增加Key
maxmemory : 默认为0 不限制
问题:达到物理内存后性能急剧下架,甚至崩溃
内存与硬盘交换(swap) 虚拟内存 ,频繁IO 性能急剧下降
设置多少与业务有关
1个Redis实例,保证系统运行 1 G ,剩下的就都可以设置Redis
一般情况设置为物理内存的3/4
slaver : 留出一定的内存
在redis.conf中 : maxmemory 1024mb
命令: 获得maxmemory数
1 | CONFIG GET maxmemory |
设置maxmemory后,当趋近maxmemory时,通过缓存淘汰策略,从内存中删除对象
不设置maxmemory 无最大内存限制 maxmemory-policy noeviction (禁止驱逐) 不淘汰
设置maxmemory maxmemory-policy 要配置
expire
在Redis中可以使用expire命令设置一个键的存活时间(ttl: time to live),过了这段时间,该键就会自动被删除。
删除策略
Redis的数据删除有定时删除、惰性删除和主动删除三种方式。
Redis目前采用惰性删除+主动删除的方式。
- 定时删除
在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除
操作。
需要创建定时器,而且消耗CPU,一般不推荐使用。
- 惰性删除
在key被访问时如果发现它已经失效,那么就删除它。
调用expireIfNeeded函数,该函数的意义是:读取数据之前先检查一下它有没有失效,如果失效了就删除它。
- 主动删除
在redis.conf文件中可以配置主动删除策略,默认是no-enviction(不删除)
- LRU
LRU (Least recently used) 最近最少使用,算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:
- 新数据插入到链表头部;
- 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
- 当链表满的时候,将链表尾部的数据丢弃。
- 在Java中可以使用LinkHashMap(哈希链表)去实现LRU
Redis的LRU 数据淘汰机制
在服务器配置中保存了 lru 计数器 server.lrulock,会定时(redis 定时程序 serverCorn())更新,server.lrulock 的值是根据 server.unixtime 计算出来的。
另外,从 struct redisObject 中可以发现,每一个 redis 对象都会设置相应的 lru。可以想象的是,每一次访问数据的时候,会更新 redisObject.lru。
LRU 数据淘汰机制:在数据集中随机挑选几个键值对,取出其中 lru 最大的键值对淘汰。
不可能遍历key 用当前时间-最近访问 越大 说明 访问间隔时间越长
volatile-lru
从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
allkeys-lru
从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- LFU
LFU (Least frequently used) 最不经常使用,如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小。
- 随机
volatile-random
从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-random
从数据集(server.db[i].dict)中任意选择数据淘汰
- ttl
volatile-ttl
从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
redis 数据集数据结构中保存了键值对过期时间的表,即 redisDb.expires。
TTL 数据淘汰机制:从过期时间的表中随机挑选几个键值对,取出其中 ttl 最小的键值对淘汰。
- noenviction
禁止驱逐数据,不删除 默认
缓存淘汰策略的选择
allkeys-lru : 在不确定时一般采用策略。 冷热数据交换
volatile-lru : 比allkeys-lru性能差 存 : 过期时间
allkeys-random : 希望请求符合平均分布(每个元素以相同的概率被访问)
案例分享:字典库失效
key-Value 业务表存 code 显示 文字
将字典库保存到redis中,设置了maxmemory,并设置缓存淘汰策略为allkeys-lru
结果造成字典库某些字段失效,缓存击穿 , DB压力剧增,差点宕机。
分析:
字典库 : Redis做DB使用,要保证数据的完整性
maxmemory设置较小,采用allkeys-lru,会对没有经常访问的字典库随机淘汰
当再次访问时会缓存击穿,请求会打到DB上。
解决方案:
1、不设置maxmemory
2、使用noenviction策略
Redis是作为DB使用的,要保证数据的完整性,所以不能删除数据。
可以将原始数据源(XML)在系统启动时一次性加载到Redis中。
Redis做主从+哨兵 保证高可用。