Redis
Redis
Redis是现在最受欢迎的NoSQL数据库之一。
- 基于内存运行,性能高效
- 支持分布式,理论上可以无限扩展
- 支持持久化和集群以及事务
- key-value存储系统
Redis的用途
1、内存存储、持久化,内存中是断电即失、所以说持久化很重要(rdb、aof)
2、效率高,可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量!)
Redis的基本知识以及操作
Redis有16个数据库,默认是第0个。
Redis的端口是6379
切换数据库:select 2
显示数据库大小: DBSIZE
清空数据库: flush/flushall
查看所有的key: keys *
设置过期时间: expire name 10
查看当前的key的剩余时间:ttl name
判断一个key是否存在: exist name
设置过期时间: persist(坚持)
移除当前的key: move
查看当前key的类型: type name
自动增加1:incr no
打印: echo
返回值的类型:type addr
如果存在则返回0,不存在则可以存入:setnx
测试连接是否连通:ping
退出连接:quit
返回当前数据库中key的数目:dbsize
为什么单线程的Redis能那么快
- CPU不是redis的性能瓶颈,内存大小和网络带宽才有可能是redis的瓶颈
- Redis 采用多路复用策略,省去了上下文切换的时间
- redis是基于内存的,内存的读写速度非常快
- 高效的数据结构string,list,set,zset,hash
Redis的五大数据类型
当最后一个元素移除时,数据结构都会被删除
Redis所有的数据结构都可以设置过期时间,时间到了,Redis会自动删除相应的对象
String
最简单的类型一个Key对应一个Value
Redis的字符串是动态字符串,内部实现类似于Java中的ArrayList采用预分配冗余空间的方式来减少内存的频繁分配。当字符串长度小于1M时,扩容都是加倍现有空间,当超过1M时扩容只会多扩1M。
hash
添加用户:zadd salary 2500 xiaohong
显示全部的用户 从小到大:ZRANGEBYSCORE salary -inf +inf
从大到进行排序:ZREVRANGE salary 0 -1
是一个String类型的field和value的映射表,他的添加和删除都是O(1)。相当于Java的HashMap,同样是数组和链表的二维结构。
为了不阻塞服务采用了渐进式的rehash策略
list
左侧插入:lpush list 1
移除list的第一个元素:Lpop list
右侧插入:Rpush list 2
移除list的最后一个元素:Rpop list
获取list的值: LRANGE list 0 -1
通过区间获取具体的值:LRANGE list 0 1
通过下标获得 list 中的某一个值:lindex list 1
返回列表的长度:Llen list
主要用于消息排队。
是一个链表结构,相当于Java语言的LinkedList。就是一个每个子元素都是String的双向链表,可以同时作为队列和栈两种数据结构。
并不是一个简单的LinkedList而是一种quicklist的快速列表。qulicklist是ziplist和linked的混合体,他将linkedlist分解成多段,每一段用ziplist来紧凑存储,多个ziplist之间使用双向指针串联起来。
为了进一步节约空间Redis还会对ziplist进行压缩存储使用LZF算法压缩,可以选择压缩深度。
qulicklist默认压缩深度是0,为了支持快速的push/pop操作quicklist的首尾两个ziplist不进行压缩,此时深度为1。
如果首尾的两个不进行压缩,则深度为2。
set
set集合中添加:sadd myset "hello"
查看指定set的所有值:Smembers myset
判断某一个值是不是在set集合中:SISMEMBER myset hello
获取set集合中的内容元素个数:scard myset
交集:SINTER key1 key2
并集:SUNION key1 key2
set是hashSet实现的,具有自动去重的功能,还可以进行取交集(SINTER)和并集(SUNION)(实现共同好友和可能认识的人)
zset
是一个有顺序的Set,每个元素都会关联一个double类型的score,sorted set的实
现是skip list和hash table的混合体。
内部是一种跳跃表
Redis提供了三个高级数据结构
geospatital(地理空间)
推算地理位置、计算两地距离、找附近的人
# getadd 添加地理位置
# 规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
# 有效的经度从-180度到180度。
# 有效的纬度从-85.05112878度到85.05112878度。
# 当坐标位置超出上述指定范围时,该命令将会返回一个错误。
# 参数 key 值()
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqi 114.05 22.52 shengzhen (integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
获得当前定位:一定是一个坐标值
127.0.0.1:6379> GEOPOS china:city beijing # 获取指定的城市的经度和纬度!
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> GEOPOS china:city beijing chongqi
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
两人之间的距离
127.0.0.1:6379> GEODIST china:city beijing shanghai km # 查看上海到北京的直线距离 "1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqi km # 查看重庆到北京的直线距离 "1464.0708"
HyperLogLog
HyperLogLog是Redis的高级数据结构,是统计基数的利器。
UV:统计有多少人访问,一个人多次访问算一次
PV:统计有多少点击量
HyperLogLog提供了三个指令,PFADD(增加计数)和PFCOUNT(获取计数)PFMERGE(将多个PFCOUNT数值累加在一起形成一个新的PFCOUNT)
127.0.0.1:6379> PFadd mykey a b c d e f g h i j # 创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> PFCOUNT mykey # 统计 mykey 元素的基数数量
(integer) 10
127.0.0.1:6379> PFadd mykey2 i j z x c v b n m # 创建第二组元素 mykey2
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并两组 mykey mykey2 => mykey3 并集 OK
127.0.0.1:6379> PFCOUNT mykey3 # 看并集的数量!
(integer) 15
位图
应用场景:
- 用户签到
- 用户在线状态
- 统计活跃用户
- 各种状态值
- 自定义布隆过滤器
- 点赞功能
记录打卡:
查看某一天是否有打卡
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
统计操作,统计 打卡的天数
127.0.0.1:6379> bitcount sign # 统计这周的打卡记录,就可以看到是否有全勤!
(integer) 3
位图不是Redis的一种数据结构,它的内容就是普通的字符串。
Redis事务
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。redis 事务不保证原子性,且没有回滚,中间某条命令执行失败,前面已执行的命令不回滚,后续的指令继续执行。
redis的事务本质是一组命令的集合,一个事务中所有的命令都会被序列化,在事务执行过程中会按照顺序来执行。
Redis事务 没有隔离级别的概念,所有的命令在事务中,并没有被直接执行,只有发起执行后命令才会执行
Redis的事务:
- 开启事务:multi
- 命令入队:set key value
- 执行事务:exec
Redis事务命令
- watch:(相当于乐观锁)进行监控,一旦一个键被修改或者被删除,后面的命令都会停。(在当多线程即多个用户对数据修改的时候包保证了安全)
- MULTI:开启一个事务,存放于队列
- EXEC:执行所有事务块内的命令(事务中任意命令执行失败,前面已执行的命令不回滚,后续的命令继续执行。)
批量操作在发送 EXEC(执行) 命令前被放入队列缓存收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,前面已执行的命令不回滚,后续的命令继续执行。
Jedis
是 Redis 官方推荐的 java连接开发工具! 使用Java 操作Redis 中间件
在 SpringBoot2.x 之后,原来使用的jedis 被替换为了 lettuce。
需要导入对应的依赖
所有的api命令,就是我们对应的上面学习的指令,一个都没有变化!
SpringBoot整合
1、导入依赖
2、配置连接
3、测试
RedisTemplete
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 我们为了自己开发方便,一般直接使用 <String, Object>
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
// Json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet(); return template; } }
}
Redis.conf详解
网络
bind 127.0.0.1 # 绑定的ip
protected-mode yes # 保护模式
port 6379 # 端口设置
快照
# 如果900s内,如果至少有一个1 key进行了修改,我们及进行持久化操作
save 900 1
# 如果300s内,如果至少10 key进行了修改,我们及进行持久化操作
save 300 10
# 如果60s内,如果至少10000 key进行了修改,我们及进行持久化操作
save 60 10000
# 我们之后学习持久化,会自己定义这个测试!
stop-writes-on-bgsave-error yes # 持久化如果出错,是否还需要继续工作!
rdbcompression yes # 是否压缩 rdb 文件,需要消耗一些cpu资源!
rdbchecksum yes # 保存rdb文件的时候,进行错误的检查校验!
dir ./ # rdb 文件保存的目录!
Redis 缓存处理请求的两种情况
- 缓存命中:Redis 中有相应数据,就直接读取 Redis,性能非常快。
- 缓存缺失:Redis 中没有保存相应数据,就从后端数据库中读取数据,性能就会变慢。而且,一旦发生缓存缺失,为了让后续请求能从缓存中读取到数据,我们需要把缺失的数据写入 Redis,这个过程叫作缓存更新。缓存更新操作会涉及到保证缓存和数据库之间的数据一致性问题。
缓存的类型
按照 Redis 缓存是否接受写请求,我们可以把它分成只读缓存和读写缓存。
只读缓存
只读缓存直接在数据库中更新数据的好处是,所有最新的数据都在数据库中,而数据库是提供数据可靠性保障的,这些数据不会有丢失的风险。
读写缓存
和只读缓存不一样的是,在使用读写缓存时,最新的数据是在 Redis 中,而 Redis 是内存数据库,一旦出现掉电或宕机,内存中的数据就会丢失。
同步直写是指,写请求发给缓存的同时,也会发给后端数据库进行处理,等到缓存和数据库都写完数据,才给客户端返回。
而异步写回策略,则是优先考虑了响应延迟。此时,所有写请求都先在缓存中处理。等到这些增改的数据要被从缓存中淘汰出来时,缓存将它们写回后端数据库。
Redis核心-管道
Redis默认每次执行请求都会创建和断开一次连接池的操作,如果想执行多条命令的时候会在这件事情上消耗过多的时间,因此我们可以使用Redis的管道来一次性发送多条命令并返回多个结果,节约发送命令和创建连接的时间提升效率。
Redis管道的本质:
Redi的过期策略
- 定时删除
- 懒汉删除
- 定期删除
定时删除和定期删除为主动删除:Redis会定期主动淘汰一批已过去的key
惰性删除为被动删除:用到的时候才会去检验key是不是已过期,过期就删除惰性删除为redis服务器内置策略
Redis的持久化
内存快照(RDB)和日志(AOF)
AOF
优点:
1、每一次修改都同步,文件的完整会更加好!
2、每秒同步一次,可能会丢失一秒的数据
3、从不同步,效率最高的!
缺点:
1、相对于数据文件来说,aof远远大于 rdb,修复的速度也比 rdb慢!
2、Aof 运行效率也要比 rdb 慢,所以我们redis默认的配置就是rdb持久化
AOF执行过程
AOF会先让系统执行命令,只有命令能执行成功,才会被记录到日志中,否则,系统就会直接向客户端报错。所以,Redis 使用写后日志这一方式的一大好处是,可以避免出现记录错误命令的情况。
AOF的三种写回策略
- Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
- Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
- No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
想要获得高性能,就选择 No 策略;如果想要得到高可靠性保证,就选择 Always 策略;如果允许数据有一点丢失,又希望性能别受太大影响的话,那么就选择 Everysec 策略。
重写日志
实际上,重写机制具有“多变一”功能。所谓的“多变一”,也就是说,旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。
RDB
优点:
1、适合大规模的数据恢复!
2、对数据的完整性要不高!
缺点:
1、需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有的了!
2、fork进程的时候,会占用一定的内容空间
和 AOF 相比,RDB 记录的是某一时刻的数据,并不是操作,所以,在做数据恢复时,我们可以直接把 RDB 文件读入内存,很快地完成恢复。
Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave。
- save:在主线程中执行,会导致阻塞;
- bgsave:创建一个子进程,专门用于写入 RDB 文件,避免了主线程的阻塞,这也是 Redis RDB 文件生成的默认配置。
为了快照而暂停写操作,肯定是不能接受的。所以这个时候,Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。
写时复制机制保证快照期间数据可修改:
做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。
AOF和RDB的混用
Redis集群的数据同步
Redis的高可靠性:一是数据尽量少丢失,二是服务尽量少中断。AOF 和 RDB 保证了前者,而对于后者,Redis 的做法就是增加副本冗余量,将一份数据同时保存在多个实例上。即使有一个实例出现了故障,需要过一段时间才能恢复,其他实例也可以对外提供服务,不会影响业务使用。
Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式。
- 读操作:主库、从库都可以接收;写操作:首先到主库执行,然后,主库将写操作同步给从库。
- 写操作:首先到主库执行,然后,主库将写操作同步给从库。
主从库间如何进行第一次同步
Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式
- 第一阶段建立连接,进行协商同步,
- 第二阶段主库向同步发送RDB文件将同步数据给从库
- 第三阶段主库发送新的写命令给从库。也可以选择一些从库进行为其他从库进行复制
主从级联模式分担全量复制时的主库压力
我们可以再选择一些从库(例如三分之一的从库),在这些从库上执行如下命令,让它们和刚才所选的从库,建立起主从关系。
主从库间网络断了怎么办?
网络断了之后,主从库会采用增量复制的方式继续同步。
全量复制是同步所有数据,而增量复制只会把主从库网络断连期间主库收到的命令,同步给从库。
当主从库断连后,主库会把断连期间收到的写操作命令,写入 replication buffer,同时也会把这些操作命令也写入 repl_backlog_buffer 这个缓冲区。repl_backlog_buffer 是一个环形缓冲区,主库会记录自己写到的位置,从库则会记录自己已经读到的位置。
Redis repl_backlog_buffer 的使用
Redis 增量复制流程
不过,有一个地方我要强调一下,因为 repl_backlog_buffer 是一个环形缓冲区,所以在缓冲区写满后,主库会继续写入,此时,就会覆盖掉之前写入的操作。如果从库的读取速度比较慢,就有可能导致从库还未读取的操作被主库新写的操作覆盖了,这会导致主从库间的数据不一致。
主库挂了,怎么办?
哨兵其实就是一个运行在特殊模式下的 Redis 进程,主从库实例运行的同时,它也在运行。哨兵主要负责的就是三个任务:监控、选主(选择主库)和通知。
-
监控是指哨兵进程在运行时,周期性地给所有的主从库发送 PING 命令,检测它们是否仍然在线运行。如果从库没有在规定时间内响应哨兵的 PING 命令,哨兵就会把它标记为“下线状态”;同样,如果主库也没有在规定时间内响应哨兵的 PING 命令,哨兵就会判定主库下线,然后开始自动切换主库的流程。
-
这个流程首先是执行哨兵的第二个任务,选主。主库挂了以后,哨兵就需要从很多个从库里,按照一定的规则选择一个从库实例,把它作为新的主库。这一步完成后,现在的集群里就有了新主库。
-
然后,哨兵会执行最后一个任务:通知。在执行通知任务时,哨兵会把新主库的连接信息发给其他从库,让它们执行 replicaof 命令,和新主库建立连接,并进行数据复制。同时,哨兵会把新主库的连接信息通知给客户端,让它们把请求操作发到新主库上。
哨兵机制,它通常会采用多实例组成的集群模式进行部署,这也被称为哨兵集群。引入多个哨兵实例一起来判断,就可以避免单个哨兵因为自身网络状况不好,而误判主库下线的情况。同时,多个哨兵的网络同时不稳定的概率较小,由它们一起做决策,误判率也能降低。
新主库的选择过程
首先,哨兵会按照在线状态、网络状态,筛选过滤掉一部分不符合要求的从库,然后,依次按照优先级、复制进度、ID 号大小再对剩余的从库进行打分,只要有得分最高的从库出现,就把它选为新主库。。
哨兵模式
主库挂掉了通过哨兵选出新主库
哨兵主要负责的就是三个任务:监控、选主(选择主库)和通知。
- 监控:负责监控 redis master 和 slave 进程是否正常工作
- 选主:如果 master node 挂掉了,会自动转移到 slave node 上(哨兵会先排除一些不适合做主库的从库,然后通过比较进行打分得分最高的推举为新主库)
- 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制,哨兵只要和主库建立起了连接,就可以在主库上发布消息了,
同时,它也可以从主库上订阅消息,获得其他哨兵发布的连接信息
网络断了之后,主从库会采用增量复制的方式继续同步
哨兵间的通信
哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制。
哨兵只要和主库建立起了连接,就可以在主库上发布消息了,比如说发布它自己的连接信息(IP 和端口)。同时,它也可以从主库上订阅消息,获得其他哨兵发布的连接信息。当多个哨兵实例都在主库上做了发布和订阅操作后,它们之间就能知道彼此的 IP 地址和端口。
为了区分不同应用的消息,Redis 会以频道的形式,对这些消息进行分门别类的管理。所谓的频道,实际上就是消息的类别。当消息类别相同时,它们就属于同一个频道。反之,就属于不同的频道。只有订阅了同一个频道的应用,才能通过发布的消息进行信息交换。
Redis发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
subscribe kuangshenshuo(channel) # 订阅一个频道
publish kuangshenshuo "hello" # 发布者发布消息到频道
保证缓存与数据库双写时的数据一致性
- 一种情况是串行化,但是吞吐率会大幅度降低
- 另一种是先更新数据库后删除缓存。通过维护一个删除失败队列(失败后再删除)或者设置一个过期时间
Redis的过期删除策略
- 定时过期:到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源,从而影响缓存的响应时间和吞吐量。
- 惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好
- 定期过期:每隔一段时间,对一些key进行检查,删除里面过期的key。
Redis中同时使用了惰性过期和定期过期两种过期策略。通过配合使用这两种过期键的删除策略,
服务器可以很好地在合理使用CPU时间和避免浪费内存空间之间取得平衡。
Redis的内存淘汰策略:
全局的键空间选择性移除:移除最近最少使用的key
设置过期时间的键空间选择性移除:在设置了过期时间的键空间中,移除最近最少使用的key。
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
redis为什么不支持回滚
- Redis 命令只会因为错误的语法而失败
- 错误应该在开发的过程中被发现,而不应该出现在生产环境中。
- 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
缓存异常:
-
缓存雪崩是指缓存同一时间大面积的失效:
缓存数据过期时间随机
热点数据不设置过期时间 -
缓存穿透是指缓存和数据库中都没有的数据
从缓存取不到的数据,在数据库中也没有取到,
这时也可以将key-value对写为key-null,缓存有效时间可以设置短点
布隆过滤器 -
缓存击穿
缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是缓存同一时间大面积失效。
热点数据不设置过期时间
SpringBoot使用Redis做缓存
一个系统在于数据库交互的过程中,内存的速度远远快于硬盘速度,当我们重复的获取相同数据时一次又一次的请求数据库,在性能上进行了极大的浪费于是用Redis做缓存是一个不错的选择。
Cache的缓存注解
- @Cacheable:标记在一个方法或者类上,缓存该类所有的方法的返回值。
- @CacheEvict:从缓存中移除数据
- @CachePut:方法支持缓存功能。与@Cacheable区别是不会去检查缓存中是否有这个结果,而是每次都会执行该方法,并将执行的结果以键值对的形式存入指定的缓存中。
io多路复用的三种方法(Linux)
IO复用是Linux中的IO模型之一,IO复用就是进程告诉内核需要监视的IO条件,使得内核一旦发现进程指定的一个或多个IO条件就绪,就通过进程处理,从而不会在单个IO上阻塞了,Linux中,提供了select、poll、epoll三种接口来实现IO复用
select与poll
epoll
Redis的分布式锁
Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问。Redis中可以使用SETNX命令实现分布式锁
- 可重入锁
- 不可重入锁
分布式锁的缺陷
在集群模式时候并不是绝对安全的
Redis在哨兵模式下主节点挂掉之前某个客户端申请了一把锁。新的主节点并不知道,就把锁分给了另一个客户端。导致同样一把锁被两个客户端同时持有。
为了解决这个问题–红锁
Redis的启动命令
使用配置文件启动Redis的服务器
方法一:独占式启动Redis
方法二:非独占式启动Redis