Redis
redis在线入门 : http://try.redis.io/
redis 中文资料站: http://www.redis.cn/
https://www.runoob.com/redis/redis-tutorial.html
# Redis 基本介绍
# NoSQL
常用的关系型数据库: Oracle,MySQL,PostgreSQL 常用的非关系数据库:
- Redis,MongoDB,ElasticSearch, Hbase
非关系性数据库的特点:
键值(Key-Value)存储数据库 这一类数据库主要会使用到一个哈希表 (opens new window),这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署。但是如果数据库管理员 (opens new window)(DBA (opens new window))只对部分值进行查询或更新的时候,Key/value就显得效率低下了。
文档型数据库 文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可以看作是键值数据库的升级版,允许之间嵌套键值,在处理网页等复杂数据时,文档型数据库比传统键值数据库的查询效率更高。如:CouchDB, MongoDb. 国内也有文档型数据库SequoiaDB,已经开源。
redis下载:这是官方推荐的win下载方式
# radis 有哪些特点?
IO是毫秒级别、内存是纳秒级别。不是一个数量级的速度比较
特点:
- 满足数据高并发读写的性能要求,因为直接是内存中进行读写的,类似于CPU的分级缓存机制
- 单线程操作,每个操作都是原子操作,没有并发相关问题(redis6)
- 对海量数据的高效率存储和访问
- 对数据的可拓展性和高可用性
- 6379端口
# 为什么是这样设计的?
为什么是单线程:因为CPU内部时间片之间相互切换的成本太高了。当未来CPU发展之后,极大降低时间片的切换成本后,有变回单线程的可能性
Redis定位是缓存,满足数据高并发读写的性能要求;减轻对数据库存储与访问压力
- 当我们要用到缓存,先思考是否有需要使用
- 在实际使用中,一些请求直接打到缓存上,能够从缓存中获取到数据,就不用到数据库或者磁盘中重新获取,减轻了数据库存储与访问压力
- 作为缓存,例如,相同的筛选条件,我们可以存储好图片的Id,下次相同的筛选条件进来之后,返回一样的图片id,而不是重新到MySQL数据库中找
常用的数据类型
# 常用的数据类型
常用的数据类型有5个:使用字符串存储的String类型,代表对象的哈希类型,集合的 set 类型,有序序列的 sorted set 类型,列表(它的底层实现是双向队列)。
常用:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)
# string
命令格式 | 功能 | 案例 |
---|---|---|
set key value | 将key-value缓存redis中 | set name dafei |
get key | 从redis中获取key对应value值 | get name |
incr key | 将key对应value值 + 1 | incr age |
decr key | 将key对应value值-1 | decr age |
setex key seconds value | 将key-value缓存到redis中,seconds 秒后失效 | setex sex 10 man |
ttl key | 查看key存活时间 | ttl sex |
del key | 从redis中删除key | del name |
setnx key value | 如果key已经存,不做任何操作,如果key不存,直接添加 | setnx name xiaofei |
- SET stores a string value.
- SETNX stores a string value only if the key doesn't already exist. Useful for implementing locks.
- GET retrieves a string value.
- MGET retrieves multiple string values in a single operation.
使用场景
- 缓存查询结果,降低数据库的访问压力。例如搜索某个东西的时候,我们可以把查询条件加上固定 id 作为 key ,查询的结果,作为 value。
- 缓存视频播放数量,使用 redis 作为视频播放数的计数器
- 共享 session
- 当我们的服务器做了负载均衡,我们的请求就会发送到不同的服务器上进行处理
- 当我们的 a 服务器承受的访问压力较大时,此时负载均衡可能将新进来的访问给到 b 服务器。这个时候,用户刷新一次再进行访问,就可能需要重新登录
- 为了避免这种不正常的情况,我们可以用 redis 将用户的 session 集中管理。在这种模式下,我们只需要保证 redis 的高可用和拓展性就可以保证每次自己的登录信息都是一致的。每次获取用户更新或者查询的信息时,统一从 redis 中获取即可。
# Hash类型
命令格式 | 功能 | 案例 |
---|---|---|
hset key field value | 将field value对缓存到redis中hash中,键值为key | hset user name dafei |
hget key field | 从key对应hash列表中获取field字段 | hget user name |
hexists key field | 判断key对应的hash列表是否存在 field字段 | hexists user age |
hdel key field | 删除key对应hash列表中field字段 | hdel user age |
hincrby key field increment | 给key对应hash列表中field字段 + increment | hincrby user age 10 |
hlen key | 查看key对应的hash列表field的数量 | hlen user |
hkeys key | 获取key对应的hash列表所有的field值 | hkeys user |
hvals key | 获取key对应的hash列表所有的field对应的value值 | kvals user |
hgetall key | 获取key对应的hash列表中所有的field及其对应的value值 | hgetall user |
使用场景
跟上面的字符串存储的类型是类似的,但是这种方式更利于内部信息的更改操作。
对于登录信息的缓存有两种方案,
- 第一种方案是将对象的信息转为 json 格式的字符串存在 redis,这就是 string 的字符串的做法。
- 第二种方式是将 user 对象转换为哈希对象存储。在 redis 里面,这种方式更侧重于修改、更改信息,但是查询操作相对来说更加麻烦
# List类型
我们从操作 list 命令中就可以看到,它里面有 push 和 pop 命令,猜也能猜到,它就是一个队列。
但实际使用跟猜测的还是有一点出入的。当整个列表的元素数量较小,或者是元素的体积较小时,它会采用一种紧凑的结构,叫做压缩列表。当整个列表的元素较大以及数量较多时,它会采用另外一种数据结构,叫做双向链表。
- 这样做的好处有很多,当整个列表的元素较小,数量较少时,我们采用压缩列表就可以很大的程度上节省空间。
- 压缩列表的底层实现是:数字加元素内容(非常紧凑的实现方式)
常用的命令
命令格式 | 功能 | 案例 |
---|---|---|
rpush key value | 从右边往key集合中添加value值 | rpush hobby java |
lrange key start stop | 从左边开始列表key集合,从start位置开始,stop位置结束 | lrange hobby 0 -1 |
lpush key value | 从左边往key集合中添加value值 | lpush hobby c++ |
lpop key | 弹出key集合中最左边的数据 | lpop hobby |
rpop key | 弹出key集合中最右边的数据 | rpop hobby |
llen key | 获取列表长度 | llen hooby |
- 可以用来存储用户收藏文章的列表,以及关注人列表等等。一些比较多增删改的地方
# Set类型
Set集合是String类型的无序集合,set是通过HashTable实现的,对集合我们可以取交集、并集、差集。
常用的命令
命令格式 | 功能 | 案例 |
---|---|---|
sadd key members [....] | 往key 集合中添加member元素 | sadd myset a b c |
smembers key | 遍历key集合中所有的元素 | smembers myset |
srem key members [....] | 删除key集合中members元素 | srem myset a |
spop key count | 从key集合中随机弹出count个元素 | spop myset 1 |
- 去重(set 集合自带的特性)
- 抽奖(pop随机抽取)
# Sorted set 类型
Sorted set 也称Zset类型,是一种具有排序效果的set集合。它跟set集合一样也是 string 类型元素的集合,且不允许重复的成员。并且要求每个元素都会关联一个double 类型的分数。后续可以通过分数来为集合中的成员进行从小到大的排序。
Sorted set集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
命令格式 | 功能 | 案例 |
---|---|---|
zadd key score member | 往key集合中添加member元素,分数为score | zadd players 100 a |
zincrby key increment member | 将key集合中的member元素 分数 + increment | zadd players 100 a |
zrange key start stop [withscores] | 将key集合中的元素按分数升序排列 [显式分数] | zrange players 0 -1 withscores |
zrevrange key start stop [withscores] | 将key集合中的元素按分数降序排列 [显式分数] | zrevrange players 0 -1 withscores |
zrank key member | 返回member元素在key结合中的正序排名 | zrank players a |
zrevrank key member | 返回member元素在key结合中的倒序排名 | zrevrank players a |
zcard key | 返回key集合元素个数 | zcard players |
- 排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
- 按照新增时间进行排序、按照操作的新增时间对关注人列表进行排序
127.0.0.1:6379> help set
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
summary: Set the string value of a key
since: 1.0.0
group: string
2
3
4
5
6
# 全局命令
全局命令针对的是所有的key,大部分用来做运维,做管理的
常用的全局key
命令格式 | 功能 | 案例 |
---|---|---|
keys pattern | 按照pattern 匹配规则,列表redis中所有的key | keys xxx:* |
exists key | 判断key是否存在 | exists name |
expire key seconds | 给key设置过期时间,超时:seconds | expire name 10 |
persist key | 取消key过期时间 | persist name |
select index | 切换数据库,默认是第0个,共有【0,15】个 | select 0 |
move key db | 从当前数据库将key移动到指定db库 | move name 1 |
randomkey | 随机返回一个key | randomkey |
rename key newkey | 将key改名为newkey | rename name newname |
echo message | 打印message信息 | echo message |
dbsize | 查看key个数 | dbsize |
info | 查看redis数据库信息 | info |
config get * | 查看所有redis配置信息 | config get * |
flushdb | 清空当前数据库 | flushdb |
flushall | 清空所有数据库 | flushall |
# 理论知识
# 事务
Redis事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- redis 的事务类似于 bash 的指令集,仅代表批量操作
- 批量操作一旦开始,其他现成的操作不能在中间插入
- 批量操作一旦开始,中间有错误,不会回滚,会继续向下执行
# 持久化
Redis持久化机制目前以后3种,分别为:
1>快照方式(RDB, Redis DataBase)
2>文件追加方式(AOF, Append Only File)
3>混合持久化方式(Redis4版本之后)
# RDB
第一种快照方式就是 Redis DataBase来存储数据,
Snapshotting(快照)默认方式,将内存数据中以快照的方式写入到二进制文件中,默认为dump.rdb。触发RDB持久化过程分手动触发与自动触发。
- Redis 主线程:主线程负责处理客户端发送的命令请求,执行数据操作,处理持久化,以及其他与 Redis 服务器相关的任务。主线程不会因为执行持久化操作而自行阻塞,因为 Redis 使用了异步的持久化机制来确保主线程的高性能和可用性。
- BGSAVE 执行时机:BGSAVE 操作的执行时机通常是在满足一定条件下触发的,例如当服务器的内存使用量达到一定阈值,或者根据配置文件中设置的自动持久化策略。当满足了执行条件后,Redis 会自动触发 BGSAVE 操作。
- BGSAVE 子线程:当执行 BGSAVE 操作时,Redis 会创建一个子进程来执行持久化操作。这个子进程会复制主进程的内存数据,并在内存中进行持久化操作,然后将持久化后的数据写入到磁盘上的持久化文件中。完成持久化操作后,子进程会自行销毁,不会影响主线程的正常运行。
- 内存数据的持久化:在 BGSAVE 操作中,子进程会将主线程的内存数据复制一份,并在内存中进行持久化操作,然后将持久化后的数据写入到磁盘上的持久化文件中。这样可以确保在持久化操作期间,主线程仍然可以继续处理客户端请求,保持高性能和可用性。
# AOF
第二种方式AOF, Append Only File
可以将 AOF 持久化方式理解为备份数据和还原数据的过程:
- 写操作(备份数据): 当客户端对 Redis 执行写操作时,例如设置键值对、删除键等,Redis 将这些写操作以追加的方式记录到 AOF 文件中。这个过程可以看作是备份数据的过程,将内存中的数据持久化到磁盘上的 AOF 文件中。
- 读操作(还原数据): 当 Redis 服务器启动时,或者执行
BGREWRITEAOF
命令时,Redis 会读取 AOF 文件,并逐行执行其中的命令来还原数据。这个过程可以看作是从 AOF 文件中读取数据,并将其加载到内存中,从而恢复数据到 Redis 数据库中。
对 AOF 的理解
- 写操作:AOF(Append Only File)是 Redis 的一种持久化方式。它将客户端的写操作以追加的方式记录到一个文本文件中。
- 读操作:redis 服务器启动的时候,会读取 AOF文件到内存中,并逐行执行其中的 redis 命令来还原数据
- 优势
- 对 CPU 和内存友好,相较于 RDB,不需要那么多的额外性能来执行持久化操作
- 定期记录客户端的写操作到文本文件中(不是每次都写到磁盘上,而是有一个缓冲区),确保数据的完整性和一致性
- 劣势
- AOF 文件大小通常比较大。
- 可能存在一些冗余操作,导致在数据恢复时可能较慢。
- 【优化冗余】AOF 文件重写(AOF rewrite):定期对 AOF 文件进行重写,以减少文件的大小和冗余操作,提高数据恢复的运行效率。
# RDB+AOF
Redis 4.0 之后推出的 RDB-AOF 混合持久化模式,其作为默认配置来使用。
- 全量备份(RDB): 当执行备份操作时,Redis 首先会将当前所有的数据以全量备份的方式保存到 RDB 文件中。这个全量备份包含了当前数据的完整快照,可以用于在 Redis 重启时快速恢复数据。
- 增量备份(AOF): 紧接着,Redis 会将后续的客户端写操作以增量备份的方式记录在 AOF 文件中。这个增量备份记录了对数据的修改操作,可以保证即使在 Redis 重启时发生故障,也能够恢复到最后一次持久化的状态。
- 恢复过程: 在 Redis 重启时,先使用 RDB 文件进行快速恢复数据,然后再重新执行 AOF 文件中记录的操作命令,来完成数据的完整恢复。这样一来,既能够保证 Redis 重启的速度,又能够降低数据丢失的风险。
混合持久化模式结合了 RDB 和 AOF 的优点,为 Redis 提供了更为可靠的数据持久化和恢复机制。这种模式在实际应用中被广泛采用,以确保数据的安全和可靠性。
讲了这么多,但最终在生产条件下,推荐的使用方式是混合持久化模式,也就是 Redis 4.0 版本之后提供的默认持久化方式
# 内存淘汰机制
- LRU (Least Recently Used):最近最少使用算法。根据数据最近被访问的时间来决定哪些数据将被淘汰。当内存空间不足时,将优先淘汰最近最少被访问的数据。
- LFU (Least Frequently Used):最不经常使用算法。根据数据被访问的频率来决定哪些数据将被淘汰。当内存空间不足时,将优先淘汰访问频率最低的数据。
- Random:随机删除算法。随机选择一些数据项进行删除。这种方法简单粗暴,但无法保证删除的数据是最不重要的。
- TTL (Time To Live):生存时间算法。通过设置数据的过期时间来自动淘汰过期的数据。当数据的过期时间到达时,Redis 会自动将其删除。
- Maxmemory Policy:根据策略选择淘汰数据。除了以上几种具体的算法外,Redis 还提供了一种灵活的淘汰策略,允许用户根据具体的需求选择淘汰算法。常见的策略包括:
volatile-lru
:只对设置了过期时间的键使用 LRU 算法进行淘汰。volatile-ttl
:只对设置了过期时间的键进行淘汰,删除剩余时间较短的键。volatile-random
:只对设置了过期时间的键进行随机淘汰。allkeys-lru
:对所有键使用 LRU 算法进行淘汰。allkeys-random
:对所有键进行随机淘汰。noeviction
:不淘汰任何数据,当内存不足时,新增操作会报错,Redis
默认内存淘汰策略;
volatile 意味着变化
在 Redis 中,"volatile" 键表示具有过期时间的键,其 TTL(Time To Live)属性会随着时间的推移而变化。
allkeys-xxx
表示从所有的键值中淘汰数据,而volatile-xxx
表示从设置了过期键的键值中淘汰数据。
127.0.0.1:6379> config get maxmemory
1) "maxmemory"
2) "0"
127.0.0.1:6379> config get maxmemory-policy
1) "maxmemory-policy"
2) "noeviction"
127.0.0.1:6379> config set maxmemory-policy volatile-ttl
OK
2
3
4
5
6
7
8
# 删除策略
Redis采用了惰性删除和定期删除的策略来处理过期键。在这种策略下,Redis定期扫描只会检查设置了过期时间的键,而不会扫描所有键。过期键的信息会被存储在一个单独的过期字典(expires)中,以避免全盘扫描的情况发生。
Redis每隔一段时间(默认为100毫秒)会随机抽取过期字典中的键,检查它们是否已经过期,并将过期的键删除。通过这种随机扫描的方式,避免了对所有键进行扫描,从而降低了对服务的影响,避免了可能的不可用情况。
对于已经过期但未被扫描的键,当客户端尝试访问这些键时,Redis会检查其过期状态。如果键已经过期,Redis会在获取键的时候将其删除,并返回null值给客户端。
过期字典中的键是指向实际数据库键空间中键对象的指针,而值则是键的过期时间,以毫秒精度的Unix时间戳表示。通过这种方式,过期字典中的键和实际数据库中的键相互对应,避免了重复对象的出现,同时也最大程度地节省了空间。
typedef struct redisDb {
dict *dict; //键空间,存放所有的key-value键值对
dict *expires; //设置了过期时间的key到它的过期时间的键值对
} redisDb;
2
3
4
# 怎么用?
操作技巧:spring-data-redis api方法名是redis命令的全称
# 思考的点
- 该功能点是否需要用缓存?
- 直接使用一个 map 能否解决问题?
- 设计 redis 的key value
- 需要排序吗?
- 不需要排序,推荐使用 string,利用 json object 完成数据存储
# 整体总结
redis概念,定位,优点,缺点,运用
- redis 定位是一个高性能的缓存,可以帮助减轻数据库的访问压力
- 优点是速度快!能够满足高性能高并发下的性能要求,显著地降低数据库的存储与访问压力
- 缺点是不支持 acid 的特性,缺少一些关系性的维护
常用的数据类型,各种命令
我们平常比较常用的数据类型5个,第一个是 string,string 是最简单并且最通用的数据类型,当然,这也是我们平常工作中最常用的数据类型。它可以以文本方式存储字符串、整数以及浮点型的数据,甚至 object 对象也可以存储。第2个是哈希,哈希是用来存储某个特定对象的,类似于字典或者 map,可以将多个键子对存储在一个键里面,适合用来存储对象的属性。第3个是 set,set 就是无序集,它可以用来帮助我们完成去重或者随机抽取的操作。第4个是 sorted set,也叫做 zset,这个集合是根据 score 属性去进行排序的,当我们需要借助 redis 去帮我们完成自动排序,那么我们就需要用到 sorted set。第5个是 list list,它的实现方式有两种,第一个是双向链表,第2个是压缩列表。我们使用的时候只需要理解它是一个队列,并且支持从两端进行元素的插入和删除(仅提供 pop 以及 push api,类似于队列或栈,只能对两端的元素进行操作)
- 这里需要注意,在List类型中。redis 它这样的设计是为了保证整体的操作性能,并且我们常用的操作也是仅在数据的头和尾进行增删操作,而对中间元素的增删操作比较少见。所以 redis 更倾向于优化对数据的头尾元素的操作,是为了提高整体的性能效率
- 很多命令我们不知道的可以直接从 redis 里面拿到,其实跟我们 api 里面的操作是非常类似的
redis 全局命令
redis-cli -h 127.0.0.1
redis-cli -h 127.0.0.1 -a test123
2
3
dbsize
keys *
keys aa?
del key
expire key 5
ttl key
说明: >=0:表示剩余的过期时间 -1:不存在过期时间(持久存在) -2:找不到这个键,过期了或者不存在
2
3
4
5
6
7
8
9
10
# redis 安全与事务
Linux系统
编辑 redis.conf文件,找到下面进行保存修改
- 在配置文件中,将
requirepass
后面的值设置为您希望设置的密码。例如:requirepass 自定义密码
重启Redis服务,访问时,使用带密码的命令:
redis-cli -a 自定义密码
redis-cli -h your_redis_host -p your_redis_port -a your_password
1
否则会提示: (error)NOAUTH Authentication required.
Window系统
跟Linux系统一样,区别是,window系统的文件是
redis.window-service.config
# 缓存管理机制
定时删除+惰性删除+内存淘汰
缓存管理机制的主要目的是有效管理过期的缓存,以避免占用过多内存空间。Redis采用随机删除算法,每隔一定时间进行随机删除,但这种算法有一定概率永远无法删除某些键,因此需要惰性删除。惰性删除在键被调用时检查是否过期,并删除过期的键,但未满足过期条件的键仍无法删除。
过长的过期时间可能导致无用数据一直存在,因此引入了内存淘汰策略,包括LRU(最近最少使用)、LFU(最不经常使用)和过期键删除等算法,以保证内存中存储的键是高效有用的。然而,这些策略也会出现问题,如缓存击穿和缓存雪崩。
缓存击穿指客户端请求未命中缓存,反复请求数据库,可以通过布隆过滤器有效防止。而缓存雪崩则是指大量热点数据同时过期或删除,为了解决这个问题,可以采用设置随机过期时间、增加热点数据永不过期等策略,以确保热点数据不会在同一时间被删除,有效解决了缓存雪崩问题。
# 哨兵分布式集群
以业务优先,为了解决高可用问题。这个在其他篇幅继续说