为什么使用 Redis
- 解决 CPU 和内存压力
- 减少 IO 操作
- 关系型数据库扩展性不强,难以改变结构
- 使用 C 语言实现,优化过的数据结构,
- 单线程(处理客户端数据是单线程的),无上下文切换的成本
- 基于非阻塞的 IO 多路复用机制
- 运行在内存中(因为索引机制不同,数据在内存中占用的空间比磁盘中少。)
Redis 6.0 后改用多线程:仍然还是单线程模型处理客户端请求,只是使用多线程来处理数据的读写和协议的解析。执行命令还是单线层。这样做的目的是因为 Redis 的瓶颈在于网络 IO 而非 CPU,使用多线程可以提高 IO 的效率,从而整体提高 Redis 的性能。
Redis 基础
redis 是一个高性能的 Key-Value 数据库
- 没有关联关系,数据结构简单,拓展表比较容易。
- Redis 支持 RDB 和 AOF 两种数据持久化方式将数据保存在磁盘中。
- Redis 不仅仅支持简单的 key-value 类型的数据,同时还提供 list\set\zset\hash 等数据结构。在 key 还存储了 value 类型和 encoding(使用 object encoding 查看)
- redis 默认有 16 个库(索引 0-15)。
- 使用 redis 做数据库和用作缓存是两码事。
- redis 是二进制安全的(按照字节流存储非字符流)。
Redis 的数据类型及使用场景
- String
常用命令:set\get\setrange\getrange\strlen
String 的类型是二进制安全的,可以包含任何类型的数据,最高 512MB。Redis 使用 SDS("简单动态字符串")来存储字符串,根据不同的字符串长度使用不同的结构体来存储,尽可能从底层优化。 String 类型有不同的编码(encoding,使用 object encoding 查看),编码不影响数据存储。不同的类型会封装一些基本的操作(例如数值类型的增减表,二进制位运算等,计算向数据靠近)。 string 类型的编码有:int、embstr、raw。
数值编码(int):
常用命令:incr\incrby\decr\decrby
使用场景:
- 抢购、秒杀、详情页异步查询数据(比如已购买的商品数、甚至点赞、评论数等),可以避免并发下对数据库的事务操作。由 redis 的内存操作代替。
- incrby 使用该命令生成全局ID,该命令有原子性。
- 限流,以访问者的IP(或其他信息)作为key,访问一次增加一次。
还有位图类型(Bitmaps,不是编码)
但他不是实际的数据类型,而是在字符串上定义的一组面向未的操作,位可以设置 2^32 个不同的位(即 42.9 亿个,512MB)
使用场景:(即对二进制的优点的各种运用)位统计
- 有用户系统,统计用户登录天数且窗口随机:用最多 50 个字节(400 个二进制)可以记录用户一年的登录天数,使用 bitcount 随机访问。
- 活跃用户统计,随机窗口 使用二进制下表(第几位)为用户 id 记录当天所有用户的登录状态。每天都创建一个新的二进制记录当天所有用户的登录记录,随机窗口活跃用户对窗口内的登录状态进行或运算(去重)即可得到活跃用户。
- hash
hash 是一个键值对的集合,使用链地址法来解决冲突。hash 的 value 类型也支持数值类型计算(可以实现一些点赞、收藏类的场景)。
命令(命令常为 H 开头): HMSET 、HGET。
- list
Redis 列表是简单的字符串列表,时双向无环链表结构,按照插入顺序,可以添加一个元素到列表的头部或尾部。list 的 key 包含头尾指针(head/tail),可用用来实现栈(同向命令)和队列(反向命令);也可实现简单的阻塞单播队列(b 开头的阻塞方法等待数据,如 blpop)。
常见命令:lush、lrange
使用场景
- 消息队列
List提供了两个阻塞的弹出操作:blpop/brpop 可以设置超时时间
blpop:blpop key1 timeout 移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
brpop:brpop key1 timeout 移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。- 商品标签
使用商品作为key,向里面添加多个标签
- set
set 是 string 类型的集合,是无序、去重的;集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
常见命令:sadd,smembers,
随机事件命令:SRANDMEMBER key count;当 count 为正数,取出一个去重的结果集(可能子集,可能不够 count 数),当 count 为负整数:取出一个带重复的结果,一定满足要求的数量。应用场景:抽奖:当有 10 个奖品,用户<10>10。中奖是否重复。在 set 中存放抽奖的人。当人小于礼物数时,count 参数为负。如果使用取出不放回的抽奖方式(例如公司年会),使用 spop,随机取出。
- sorted set(zset)
- String 类型元素的有序集合,且不允许出现重复的成员;有元素(member)、分值(score)、索引三种成员。
- 与 set 不同的是每个元素都会关联一个 double 类型的分数(score)。redis 正是通过分数来为集合中的成员进行从小到大的排序。
- zset 的成员是唯一的,但分数可以重复。
- 还支持集合操作,交集、并集。涉及到分值,集合操作很复杂。
- key 当中维护着第一个指针。
- 物理内存中左小右大排序,且不随命令发生变化。
- 使用 skip list(跳跃表)。
常用命令:
- ZCOUNT key min max 根据分值区间进行统计
- ZINCRBY key increment member 支持数值计算,增加某个元素的分值
- ZRANGEBYSCORE Key max
- ZRANK 查看排名
- ZSCORE 取出 Score
管道
多个命令一次性怼给一个链接,执行有序,批量返回执行结果。它不具备原子性,其中某一条失败不会影响其他命令执行。 管道,将多条命令打包,只需要一次网络开销(优化高并发下),减少客户端和服务端的调用,节约时间。但在redis2.6后,脚本的部分应用场景优于管道。
Lua 脚本
- Lua脚本具备了pipline的功能,并支持事务,在管道的基础上增加了原子操作。
- 可以替代redis事务功能,redis本身事务可用性不高,可以lua脚本保证事务。
- EVAL命令如: EVAL script numkeys key [key ...] arg [arg ...]
如:eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second。 - 在Lua脚本中可以使用redis.call()函数执行命令。 不要在Lua脚本里执行循环或者耗时的运算,会造成阻塞。
发布/订阅(Pub/Sub)
语法:
- PUBLISH channel message 发送,有监听之后发送方才能收到。
- SUBSCRIBE channel [channel] 监听,只能看到监听建立之后的信息。
使用场景:
快速地发布消息让所有人实时看到,例如直播时的弹幕。
站在客户端的角度数据分为历史性的和实时性的(一般全量数据是存在数据库里的,使用redis作为缓存,多数缓存是解决数据读的请求)
Redis事务
Redis的事务没有MySql那么完整
常用命令:
- MULTI 开启事务
- EXEC 停止事务
- DISCARD 取消命令
- WATCH 实现了乐观锁CAS(check-and-set)
Redis会在收到exec命令后一次性执行MTLTI和EXEC之间的命令,即未收到EXEC命令之前,不会执行MULTI后的命令。
Redis Key有效期
使用命令expire key seconds设置过期时间(重复执行刷新过期时间);使用persist命令清除超时,变为永久key。
key有效期的设置有业务逻辑来推动(即淘汰冷数据,内存有限)
基础:使用ttl命令查看key的有效期;过期时间不会随着访问等操作而延长;发生写入操作时回剔除过期时间;倒计时,且redis不能延长;定时。
过期策略(过期不会立马被删除):
- 被动方式,惰性删除,访问时判定,若过期则删除并返回null。
- 主动方式,定期删除,周期轮循判定:牺牲了内存但保证了redis性能为王的设计目的。
将Redis当做使用LRU算法的缓存来使用
Redis的持久化
一般数据库存储层持久化包括两种:1.快照/副本 2.日志
Redis有两种持久化方式:
- RDB(数据快照)
时点性:
数据写入磁盘需要时间,写入的数据并非是redis某一精确时间点的数据。并不是一个完整的时点数据。
有两种命令持久化:save阻塞Redis服务器进程备份,指导RDB文件创建完毕,在此期间,Redis服务无法助力Client的请求。bgsavebgsave会创建一个子线程(fork线程),异步创建RDB文件。
RDB的弊端:- 不支持拉链:永远只有一个dump.rdb文件 需要人为去干预复制出来
- 丢失数据相对多,时点与时点之间的的数据容易丢失。(例如备份文件持续了5分钟,这期间内的操作数据会丢失)
类似于Java中的序列化,恢复的速度相对快。如果只是使用Redis作为缓存,可以使用RDB,速度快,丢失一些数据也无所谓。
- AOF(Append only file,只会向文件中追加)
AOF是指将Redis的操作记录以日志的方式存储到文件中,在配置文件中使用appendonly yes打开-
丢失数据很少
-
弊端:体量无限巨大
-
源点:redis是内存数据库
写操作就会触发IO,会变慢。 有三个级别:NO\AWAYS\EverySec。配置文件使用
appendfsync参数选项,默认为everysec。redis中RDB和AOF可以同时打开。
-