目录
概述
(Remote Dictionary Server)是一个开源的内存数据结构存储系统,使用ANSI C语言编写,支持多种数据结构,如字符串、哈希、列表、集合和有序集合。Redis以其高性能和丰富的数据类型而闻名,广泛应用于缓存、分布式锁和消息代理等场景。
特点
- 内存存储:Redis 将数据存储在内存中,因此它非常快速。
- 持久化选项:虽然 Redis 是内存存储,但它提供了两种持久化机制(RDB 和 AOF),可以将数据保存到磁盘。
- 高性能:Redis 在高并发场景下能提供非常快速的响应,特别适用于缓存系统、排行榜、计数器等场景。
- 支持丰富的数据结构:Redis 支持的基础数据类型包括字符串、哈希、列表、集合、有序集合、位图等,这些数据类型在实际应用中非常有用。
- 分布式支持:Redis 支持数据的分布式部署,使用 Redis Cluster 使数据能跨多个节点进行分片。
- 原子操作:Redis 提供了大量的原子操作,这让它非常适合做实时计数器、消息队列等。
常见应用场景
- 缓存:将常用数据存储在 Redis 中,减少数据库压力,提高响应速度。例如,缓存用户的登录状态或热门商品信息。
- 会话存储:在分布式应用中,Redis 用作会话存储,保持用户会话信息。
- 排行榜和计数器:Redis 的有序集合(Sorted Set)非常适合用于排行榜等需要排序的场景。
- 任务队列:通过 Redis 的列表和发布/订阅(pub/sub)功能,可以实现异步任务队列,处理任务调度等。
- 消息队列:Redis 提供了可靠的发布/订阅功能,适合用于实现消息传递系统。
安装
下载地址: https://github.com/MicrosoftArchive/redis/releases
下载完成后解压即可。
启动
将redis安装目录配置到环境变量path中
可以在redis目录中直接双击redis-server.exe启动服务,
或在cmd窗口中输入命令:redis-server
启动后的窗口如下,注意启动后窗口不要关闭,否则服务不能用
使用
在cmd中输入命令,连接redis服务
连接本地服务:
redis-cli
连接远程服务:
redis-cli -h 127.0.0.1 -p 6379
Redis-cli.exe -h 127.0.0.1 -p 6379
输入ping响应pong则说明redis服务已启动
Redis命令
Redis 命令用于在 redis 服务上执行操作
1、键key
Redis 键命令用于管理 redis 的键
序号 | 命令及描述 |
1 | DEL key |
2 | DUMP key |
3 | EXISTS key |
4 | EXPIRE key seconds |
5 | EXPIREAT key timestamp |
6 | PEXPIRE key milliseconds |
7 | PEXPIREAT key milliseconds-timestamp |
8 | KEYS pattern |
9 | MOVE key db |
10 | PERSIST key |
11 | PTTL key |
12 | TTL key -1为永不过期 |
13 | RANDOMKEY |
14 | RENAME key newkey |
15 | RENAMENX key newkey |
16 | TYPE key |
2、字符串string
字符串是Redis中最基本的数据类型,可以存储任何形式的字符串,包括文本、序列化后的对象、二进制数据等。单个键值对的最大存储容量为512MB。字符串类型在Redis中以二进制形式保存,字符串类型适用于缓存数据、计数器、分布式锁等场景
序号 | 命令及描述 |
1 | SET key value |
2 | GET key |
3 | GETRANGE key start end |
4 | GETSET key value |
5 | GETBIT key offset |
6 | MGET key1 [key2..] |
7 | SETBIT key offset value |
8 | SETEX key seconds value |
9 | SETNX key value |
10 | SETRANGE key offset value |
11 | STRLEN key |
12 | MSET key value [key value ...] |
13 | MSETNX key value [key value ...] |
14 | PSETEX key milliseconds value |
15 | INCR key |
16 | INCRBY key increment |
17 | INCRBYFLOAT key increment |
18 | DECR key |
19 | DECRBY key decrement |
20 | APPEND key value |
3、哈希hash
哈希类型可以看作是一个键值对的集合,类似于Python中的字典。每个哈希可以存储约40亿个键值对,适合存储对象的信息,如用户信息、配置数据等。哈希类型在Redis中通过字段和值的映射来实现,支持高效的操作如获取单字段(HGET)、设置单字段(HSET)等。哈希类型适用于存储用户信息、配置数据等场景
序号 | 命令及描述 |
1 | HDEL key field2 [field2] |
2 | HEXISTS key field |
3 | HGET key field |
4 | HGETALL key |
5 | HINCRBY key field increment |
6 | HINCRBYFLOAT key field increment |
7 | HKEYS key 获取所有哈希表中的字段 |
8 | HLEN key 获取哈希表中字段的数量 |
9 | HMGET key field1 [field2] 获取所有给定字段的值 |
10 | HMSET key field1 value1 [field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
11 | HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。 |
12 | HSETNX key field value |
13 | HVALS key |
14 | HSCAN key cursor [MATCH pattern] [COUNT count] |
4、列表list
列表类型在Redis中实现为一个双向链表,支持按照插入顺序存储多个字符串元素。列表类型适用于最新消息排行、消息队列等场景。常用命令包括LPUSH、RPUSH用于在列表头部或尾部插入元素,LPOP、RPOP用于从列表头部或尾部弹出元素。列表类型还支持范围查询操作,如LRANGE命令可以获取列表中指定范围的元
序号 | 命令及描述 |
1 | BLPOP key1 [key2 ] timeout 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
2 | BRPOP key1 [key2 ] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
3 | BRPOPLPUSH source destination timeout 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
4 | LINDEX key index 通过索引获取列表中的元素 |
5 | LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素 |
6 | LLEN key 获取列表长度 |
7 | LPOP key 移出并获取列表的第一个元素 |
8 | LPUSH key value1 [value2] 将一个或多个值插入到列表头部 |
9 | LPUSHX key value 将一个值插入到已存在的列表头部 |
10 | LRANGE key start stop 获取列表指定范围内的元素 0到-1表示获取所有 |
11 | LREM key count value 移除列表元素 |
12 | LSET key index value 通过索引设置列表元素的值 |
13 | LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 |
14 | RPOP key 移除并获取列表最后一个元素 |
15 | RPOPLPUSH source destination 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 |
16 | RPUSH key value1 [value2] 在列表中添加一个或多个值 |
17 | RPUSHX key value 为已存在的列表添加值 |
5、集合set
集合类型用于存储不重复的元素集合。集合类型适用于需要去重或进行交集、并集、差集运算的场景。常用命令包括SADD用于添加元素,SMEMBERS用于获取集合中所有成员。集合类型在社交网络中的应用广泛,如用户共同好友等
序号 | 命令及描述 |
1 | SADD key member1 [member2] 向集合添加一个或多个成员 |
2 | SCARD key 获取集合的成员数 |
3 | SDIFF key1 [key2] 返回给定所有集合的差集 |
4 | SDIFFSTORE destination key1 [key2] 返回给定所有集合的差集并存储在 destination 中 |
5 | SINTER key1 [key2] 返回给定所有集合的交集 |
6 | SINTERSTORE destination key1 [key2] 返回给定所有集合的交集并存储在 destination 中 |
7 | SISMEMBER key member 判断 member 元素是否是集合 key 的成员 |
8 | SMEMBERS key 返回集合中的所有成员 |
9 | SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合 |
10 | SPOP key 移除并返回集合中的一个随机元素 |
11 | SRANDMEMBER key [count] 返回集合中一个或多个随机数 |
12 | SREM key member1 [member2] 移除集合中一个或多个成员 |
13 | SUNION key1 [key2] 返回所有给定集合的并集 |
14 | SUNIONSTORE destination key1 [key2] 所有给定集合的并集存储在 destination 集合中 |
15 | SSCAN key cursor [MATCH pattern] [COUNT count] 迭代集合中的元素 |
6、有序集合Zset
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
序号 | 命令及描述 |
1 | ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
2 | ZCARD key 获取有序集合的成员数 |
3 | ZCOUNT key min max 计算在有序集合中指定区间分数的成员数 |
4 | ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment |
5 | ZINTERSTORE destination numkeys key [key ...] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
6 | ZLEXCOUNT key min max 在有序集合中计算指定字典区间内成员数量 |
7 | ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合成指定区间内的成员 |
8 | ZRANGEBYLEX key min max [LIMIT offset count] 通过字典区间返回有序集合的成员 |
9 | ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员 |
10 | ZRANK key member 返回有序集合中指定成员的索引 |
11 | ZREM key member [member ...] 移除有序集合中的一个或多个成员 |
12 | ZREMRANGEBYLEX key min max 移除有序集合中给定的字典区间的所有成员 |
13 | ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员 |
14 | ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员 |
15 | ZREVRANGE key start stop [WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到底 |
16 | ZREVRANGEBYSCORE key max min [WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序 |
17 | ZREVRANK key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
18 | ZSCORE key member 返回有序集中,成员的分数值 |
19 | ZUNIONSTORE destination numkeys key [key ...] 计算给定的一个或多个有序集的并集,并存储在新的 key 中 |
20 | ZSCAN key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值) |
7、发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道
序号 | 命令及描述 |
1 | PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道。 |
2 | PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。 |
3 | PUBLISH channel message 将信息发送到指定的频道。 |
4 | PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道。 |
5 | SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息。 |
6 | UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道。 |
8、事务
Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务:以 MULTI 开始一个事务
- 命令入队。
- 执行事务:EXEC 命令触发事务
序号 | 命令及描述 |
1 | DISCARD 取消事务,放弃执行事务块内的所有命令。 |
2 | EXEC 执行所有事务块内的命令。 |
3 | MULTI 标记一个事务块的开始。 |
4 | UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
5 | WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
9、连接
Redis 连接命令主要是用于连接 redis 服务
序号 | 命令及描述 |
1 | AUTH password 验证密码是否正确 |
2 | ECHO message 打印字符串 |
3 | PING 查看服务是否运行 |
4 | QUIT 关闭当前连接 |
5 | SELECT index 切换到指定的数据库 |
10、服务器
Redis 服务器命令主要是用于管理 redis 服务
序号 | 命令及描述 |
1 | BGREWRITEAOF 异步执行一个 AOF(AppendOnly File) 文件重写操作 |
2 | BGSAVE 在后台异步保存当前数据库的数据到磁盘 |
3 | CLIENT KILL [ip:port] [ID client-id] 关闭客户端连接 |
4 | CLIENT LIST 获取连接到服务器的客户端连接列表 |
5 | CLIENT GETNAME 获取连接的名称 |
6 | CLIENT PAUSE timeout 在指定时间内终止运行来自客户端的命令 |
7 | CLIENT SETNAME connection-name 设置当前连接的名称 |
8 | CLUSTER SLOTS 获取集群节点的映射数组 |
9 | COMMAND 获取 Redis 命令详情数组 |
10 | COMMAND COUNT 获取 Redis 命令总数 |
11 | COMMAND GETKEYS 获取给定命令的所有键 |
12 | TIME 返回当前服务器时间 |
13 | COMMAND INFO command-name [command-name ...] 获取指定 Redis 命令描述的数组 |
14 | CONFIG GET parameter 获取指定配置参数的值 |
15 | CONFIG REWRITE 对启动 Redis 服务器时所指定的 redis.conf 配置文件进行改写 |
16 | CONFIG SET parameter value 修改 redis 配置参数,无需重启 |
17 | CONFIG RESETSTAT 重置 INFO 命令中的某些统计数据 |
18 | DBSIZE 返回当前数据库的 key 的数量 |
19 | DEBUG OBJECT key 获取 key 的调试信息 |
20 | DEBUG SEGFAULT 让 Redis 服务崩溃 |
21 | FLUSHALL 删除所有数据库的所有key |
22 | FLUSHDB 删除当前数据库的所有key |
23 | INFO [section] 获取 Redis 服务器的各种信息和统计数值 |
24 | LASTSAVE 返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示 |
25 | MONITOR 实时打印出 Redis 服务器接收到的命令,调试用 |
26 | ROLE 返回主从实例所属的角色 |
27 | SAVE 异步保存数据到硬盘 |
28 | SHUTDOWN [NOSAVE] [SAVE] 异步保存数据到硬盘,并关闭服务器 |
29 | SLAVEOF host port 将当前服务器转变为指定服务器的从属服务器(slave server) |
30 | SLOWLOG subcommand [argument] 管理 redis 的慢日志 |
31 | SYNC 用于复制功能(replication)的内部命令 |
持久化
Redis是一个支持持久化的内存数据库。
由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据,也就是说redis需要经常将内存中的数据同步到磁盘来保证持久化。
Redis提供多种持久化方式:
- RDB(Redis DataBase)方式
- AOF(Append-only file)方式
- 虚拟内存方式
- diskstore
一、RDB
RDB 持久化是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置:
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000 #60秒内超过10000个key被修改,则发起快照保存
快照保存的过程:
- redis调用fork,现在有了子进程和父进程。
- 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
- 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出(fork一个进程内存占用会是原来的两倍)
client 也可以使用save或者bgsave命令通知redis做一次快照持久化:
save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用。
另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量同步。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。
另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改
二、AOF
aof 比快照方式有更好的持久化性,是由于在使用aof持久化方式时,redis会将每一个收到的写命令都通过write函数追加到文件中(默认是appendonly.aof)。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要通过fsync函数强制os写入到磁盘的时机。
有三种方式如下(默认是:每秒fsync一次)
appendonly yes #启用aof持久化方式
# appendfsync always #每次收到写命令就立即强制写入磁盘,最慢但是保证完全的持久化,不推荐
appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
# appendfsync no #完全依赖os,性能最好,持久化没保证
aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。
为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件
- redis调用fork ,现在有父子两个进程
- 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
- 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
- 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
- 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加
需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
三、优缺点
Redis支持两种主要的持久化方式:
-
RDB持久化:通过定时快照的方式将内存中的数据保存到磁盘上,具有快速恢复和高可靠性的特点。
-
AOF持久化:通过记录每次写操作的方式将日志追加到文件中,具有较高的可读性和恢复性。
RDB优点:
-
RDB 是一个非常紧凑(compact)的文件,它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份
-
非常适用于灾难恢复(disaster recovery)
-
RDB 可以最大化 Redis 的性能
父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作
4.RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快
RDB缺点:
-
如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失
-
由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟
AOF优点:
-
更高的数据安全性,即数据持久性
-
由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容
-
如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性
-
AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建
AOF缺点:
-
AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快
-
根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效
四、如何选
-
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
-
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
-
有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快, 除此之外, 使用 RDB 还可以避免之前提到的 AOF 程序的 bug
主从复制
一、概述
主从复制,就是主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
主从复制的好处:读写分离,性能扩展, 容灾快速恢复
一般来说,要将Redis运用于工程项目中,只使用一台redis是万万不能的,原因如下:
-
从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
-
从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内容容量为256G,也不能将所有内容用作Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。
这里暂只讨论第一点的解决方案:Redis主从复制,第二点可以使用Redis集群解决
Redis支持将数据同步到多台slave上,这种特性对提高读取性能非常有益
-
master可以有多台slave
-
除了多个slave连到相同master外,slave也可以连接到其它slave形成图状结构
-
主从复制不会阻塞master,也就是说当一个或多个slave与master连接进行复制时,master可以继续处理客户端发来的请求,相反slave在初次同步数据时则会阻塞,不能处理客户端请求
-
主从复制可以用来提高系统的伸缩性,我们可以用多个slave专门负责客户端的读请求,可以做数据冗余
-
可以在master禁用数据持久化,只需要注释掉master配置文件redis.conf的save配置,然后只在slave配置持久化
电子商务网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是“多读少写”。
对于这种场景,我们可以使下图中的这种架构。
如下图中所示,我们将一台Redis服务器作主库(Matser),其他三台作为从库(Slave),主库只负责写数据,每次有数据更新都将更新的数据同步到它所有的从库,而从库只负责读数据。这样一来,就有了两个好处:
-
读写分离,不仅可以提高服务器的负载能力,并且可以根据读请求的规模自由增加或者减少从库的数量
-
数据被复制成了了好几份,就算有一台机器出现故障,也可以使用其他机器的数据快速恢复。
需要注意的是:在Redis主从模式中,一台主库可以拥有多个从库,但是一个从库只能隶属于一个主库。
二、实现
1、配从不配主
-
准备三台服务器6379、6380、6381,因为是模拟,需要复制三份配置文件,并分别更改端口号
-
分别启动这三个服务,以下为在windows版演示
3.分别连接这三台服务
4.我们可以在三台服务上执行命令”info replication”,可以发现三台服务的角色都是master,
5.我们可以使用“slaveof <ip> <port> ”设置主从关系,这三台服务可以设置成两种关系:并联or串联。
6.我们先来设置并联(一主二仆),即6380和6381都是6379的slave
然后在master上写数据,再slave上取数据,结果是可以取到
另外,在slave上测试写数据,结果是不可以的,slave上不允许写数据
问题:
1)如果主机死掉了,默认情况下,主机重新启动,角色还是主机
2)如果从机死掉了,默认情况下,从机重新启动,角色变成没有独立主机了,需要重新配置,除非从机在redis.conf文件里配置了,否则从机不会重新连接主机
-
反客为主
刚刚提到了,当master死机的时候,重写启动还是master,现在是希望当master死机的时候,让某一台从机变成主机,这里是,让6380变成主机,那么需要操作
-
在6380上执行“slaveof no one”
-
在6381上重新配置“slaveof 127.0.0.1 6380”
2、哨兵模式
Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换
简单的说,哨兵模式,就是反客为主的自动版,通过监控主机,当主机挂掉的时候,从几台从机中通过投票选出新的主机。
sentinel默认监听26379端口
操作步骤:
基于之前的一主二仆结构
- 新建一个sentinel.conf文件,文件内容为:
sentinel monitor host6379 127.0.0.1 6379 1
说明:host6379为监控的主机名词,自定义的,后面的“1”表示,当主机挂掉的时候,从机自动进行投票,谁的票数首先超过“1”,谁就为主机
- 启动sentinel.conf 文件
redis-server sentinel.conf --sentinel
- 测试
先验证先前的一主二仆的主从复制是否正常,然后把原来master即6379服务停掉,观察哨兵日志,日志显示79停了,80切换成了master,81变成了80的从机,大功告成
Spring Boot 集成Redis
在 Spring Boot 中使用 Redis,一般会使用 Spring Data Redis 作为 Redis 客户端,它提供了对 Redis 的高层封装。你可以通过以下方式集成 Redis:
1. 添加依赖
在 pom.xml
中添加 spring-boot-starter-data-redis
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 配置 Redis 连接
在 application.properties
中配置 Redis 连接信息:
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=your_password
3. 使用 RedisTemplate
RedisTemplate 是 Spring Data Redis 提供的一个通用类,用于对 Redis 的操作。你可以通过它进行各种 Redis 操作。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;@Service
public class RedisService {@Autowired
private RedisTemplate<String, String> redisTemplate;public void setValue(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}public String getValue(String key) {
return redisTemplate.opsForValue().get(key);
}
}
redis常见问题及解决
1. 缓存穿透
原因:
缓存穿透是指查询一个不存在的数据,缓存和数据库中都没有该数据。每次请求都会直接访问数据库,绕过了缓存,导致缓存没有发挥作用,从而增加了数据库的压力。
例如,当用户查询某个不存在的商品信息时,缓存和数据库都没有这个商品的数据。每次请求都会访问数据库,导致大量无效查询。
解决办法:
-
缓存空值:
如果查询的结果为空(例如,数据库中没有该数据),可以在缓存中存储一个特殊的空值标记,避免每次查询都去数据库。这样可以防止缓存穿透。- 使用一个标志值(例如
null
或者一个特定的标记)存储空结果,设置一个较短的过期时间。
- 使用一个标志值(例如
@Cacheable(value = "product", key = "#productId")
public Product getProduct(Long productId) {
Product product = productRepository.findById(productId);
if (product == null) {
// 设置一个特殊值(例如:空对象),防止缓存穿透
redisTemplate.opsForValue().set("product:" + productId, "null", 5, TimeUnit.MINUTES);
return null;
}
return product;
}
参数校验:
对用户输入的参数进行校验,防止恶意的、无效的请求访问数据库。可以在服务层或控制器层进行输入参数的校验。
public Product getProduct(Long productId) {
if (productId == null || productId <= 0) {
throw new IllegalArgumentException("Invalid product ID");
}
return productRepository.findById(productId);
}
白名单/黑名单机制:
如果查询请求的参数是非法的(例如用户提交一个不存在的 ID),可以通过黑名单或白名单机制将不合法的请求提前拦截,避免缓存穿透。
2. 缓存击穿
原因:
缓存击穿是指在某个缓存项的有效期过期或失效时,多个并发请求同时访问这个缓存数据,而由于缓存不存在,所有的请求都访问数据库,从而导致数据库压力骤增。
例如,多个请求同时到达某个缓存失效的商品数据,都会尝试去数据库查询,这样就会对数据库造成严重的性能问题。
解决办法:
-
互斥锁(锁机制):
使用分布式锁(如 Redis 的SETNX
或Redisson
)来保证只有一个线程可以查询数据库并更新缓存,其他线程等待缓存更新完成后再读取缓存。- 使用
Redisson
(或者Redis
)来加锁,保证只有一个请求能够查询并更新缓存。
- 使用
@Cacheable(value = "product", key = "#productId")
public Product getProduct(Long productId) {
Product product = redisTemplate.opsForValue().get("product:" + productId);
if (product == null) {
synchronized (this) {
// 双重检查
product = redisTemplate.opsForValue().get("product:" + productId);
if (product == null) {
product = productRepository.findById(productId);
redisTemplate.opsForValue().set("product:" + productId, product);
}
}
}
return product;
}
-
后台异步更新缓存:
使用后台线程异步更新缓存,在缓存过期时,由后台任务定期去数据库查询并更新缓存,避免在请求时频繁查询数据库。 -
缓存预热:
在系统启动时或通过定时任务加载缓存,避免缓存失效后大量请求穿透到数据库。 -
加大缓存过期时间:
增加缓存过期时间,减少缓存失效的频率,从而降低缓存击穿的风险。
3. 缓存雪崩
原因:
缓存雪崩是指在某个时间点,大量缓存同时过期,导致大量请求涌向数据库,从而造成数据库压力激增,甚至系统崩溃。
例如,假设所有缓存的商品信息都设置了相同的过期时间,那么当这些缓存数据同时过期时,所有的请求都会同时查询数据库,导致数据库压力过大。
解决办法:
缓存过期时间分散:
可以对缓存的过期时间进行随机化处理,避免大量缓存同时过期。通过给每个缓存数据设置不同的过期时间,使得缓存的过期时间不集中,减少同一时刻大量缓存失效的风险。
@Cacheable(value = "product", key = "#productId")
public Product getProduct(Long productId) {
// 设置一个随机过期时间,例如在30分钟到1小时之间
long randomExpireTime = (long) (Math.random() * 30 + 30);
Product product = productRepository.findById(productId);
redisTemplate.opsForValue().set("product:" + productId, product, randomExpireTime, TimeUnit.MINUTES);
return product;
}
-
缓存预热:
系统启动时或在特定时机通过异步任务预加载热点数据到缓存中,避免在用户访问时产生大量缓存穿透和击穿。 -
使用本地缓存:
对于不频繁变动的数据,可以使用本地缓存(如 Caffeine)与 Redis 缓存结合,减少对 Redis 的依赖,降低缓存雪崩的风险。 -
采用多级缓存:
在缓存层设计上,使用多级缓存策略,例如在本地缓存与 Redis 缓存之间增加一层,减轻 Redis 的压力。
总结
- 缓存穿透:通过缓存空值或进行参数校验来避免无效的查询请求。
- 缓存击穿:使用分布式锁、异步更新缓存、缓存预热等方式解决缓存失效时的并发请求问题。
- 缓存雪崩:通过分散缓存的过期时间、缓存预热和多级缓存来避免大量缓存同时失效,导致数据库压力过大。