分布式-Redis01-核心数据接口与原理

Redis简介

关系型数据库

​ 关系型数据天然就是二维的、表格式的,因此存储在数据表的行和列中,支持ACID4大事务特性,数据模型可扩展性差、全文检索功能较弱

NoSQL数据库

​ Not Only SQL简称NoSQL,有灵活的数据模型,易扩展。有非常高的读写性能。无须事先为要存储的数据建立字段,随时可以存储自定义的数据格式

NoSQL的应用场景

1、高并发数据读写

2、数据接口易扩展

3、存储大量数据

4、速度快

不适用场景:

1、对事务要求较高的场景

2、不适合结构化的sql查询方案

Redis的定义

​ redis是一个key-value的内存存储数据结构服务器,一般用作高速缓存、分布式锁、临时数据库等等

Memcache、Redis、MongoDB、HBase区别介绍

Memcache

1、支持简单的key-value模式存储,支持的数据结构很单一,仅支持string类型的操作

2、数据存储在内存中,不支持数据的持久化

Redis

1、除了key-value外,还支持丰富的存储接口如string、set、zset、hash、list、hyperLogLog等

2、数据存储在内存中,支持AOF、RDB等持久化方式(支持备份)

3、一般做缓存数据库

4、覆盖Memcache的功能

MongoDB

1、文档型数据库

2、数据都存在内存中、内存不足时把不常用的数据保存到硬盘

3、虽然是Key-value,但是对value(尤其是json)提供丰富的查询功能(比如sql)

4、支持二进制数据及大型对象

HBase

1、适用于大数据环境

2、基于Hadoop,数据最终存储在HDFS

3、支持存储非常庞大的表,单表支持10亿行数据,并且支持上百万个列

Redis VS MongDB

Redis主要把数据存储在内存中,其“缓存”的性质远大于其“数据存储“的性质,其中数据的增删改查也只是像变量操作一样简单;

MongoDB却是一个“存储数据”的系统,增删改查可以添加很多条件,就像SQL数据库一样灵活,这一点在面试的时候很受用。

Redis安装与使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
下载地址:http://redis.io/download 
安装步骤:
# 安装gcc
yum install gcc
# 把下载好的redis‐5.0.3.tar.gz放在/usr/local文件夹下,并解压
wget http://download.redis.io/releases/redis‐5.0.3.tar.gz
tar xzf redis‐5.0.3.tar.gz
cd redis‐5.0.3
# 进入到解压好的redis‐5.0.3目录下,进行编译与安装
make
# 启动并指定配置文件
src/redis‐server redis.conf(注意要使用后台启动,所以修改redis.conf里的daemonize改为y es)
# 验证启动是否成功
ps ‐ef | grep redis
# 进入redis客户端
src/redis‐cli
# 退出客户端
quit
# 退出redis服务:
(1)pkill redis‐server
(2)kill 进程号
(3)src/redis‐cli shutdown
1
2
3
4
5
6
7
pidfile /var/run/redis_6379.pid
logfile "6379.log"
protected-mode no
port 6379
logfile "6380.log"
dir /soft/redis‐5.0.3/6380
# bind 127.0.0.1 注释改行

StringRedisTemplate与RedisTemplate

spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。在 RedisTemplate中提供了几个常用的接口方法的使用,分别是:

image-20220410105536743

StringRedisTemplate继承自RedisTemplate,也一样拥有上面这些操作。 StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存 的。RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。

Redis客户端命令对应的RedisTemplate中的方法列表:

String类型结构
Redis RedisTemplate rt
set key value rt.opsForValue().set(“key”,”value”)
get key rt.opsForValue().get(“key”)
del key rt.delete(“key”)
strlen key rt.opsForValue().size(“key”)
getset key value rt.opsForValue().getAndSet(“key”,”value”)
getrange key start end rt.opsForValue().get(“key”,start,end)
append key value rt.opsForValue().append(“key”,”value”)
Hash结构
hmset key field1 value1 field2 value2… rt.opsForHash().putAll(“key”,map) //map是一个集合对象
hset key field value rt.opsForHash().put(“key”,”field”,”value”)
hexists key field rt.opsForHash().hasKey(“key”,”field”)
hgetall key rt.opsForHash().entries(“key”) //返回Map对象
hvals key rt.opsForHash().values(“key”) //返回List对象
hkeys key rt.opsForHash().keys(“key”) //返回List对象
hmget key field1 field2… rt.opsForHash().multiGet(“key”,keyList)
hsetnx key field value rt.opsForHash().putIfAbsent(“key”,”field”,”value”
hdel key field1 field2 rt.opsForHash().delete(“key”,”field1”,”field2”)
hget key field rt.opsForHash().get(“key”,”field”)
List结构
lpush list node1 node2 node3… rt.opsForList().leftPush(“list”,”node”)
rt.opsForList().leftPushAll(“list”,list) //list是集合对象
rpush list node1 node2 node3… rt.opsForList().rightPush(“list”,”node”)
rt.opsForList().rightPushAll(“list”,list) //list是集合对象
lindex key index rt.opsForList().index(“list”, index)
llen key rt.opsForList().size(“key”)
lpop key rt.opsForList().leftPop(“key”)
rpop key rt.opsForList().rightPop(“key”)
lpushx list node rt.opsForList().leftPushIfPresent(“list”,”node”)
rpushx list node rt.opsForList().rightPushIfPresent(“list”,”node”)
lrange list start end rt.opsForList().range(“list”,start,end)
lrem list count value rt.opsForList().remove(“list”,count,”value”)
lset key index value rt.opsForList().set(“list”,index,”value”)
Set结构
sadd key member1 member2… rt.boundSetOps(“key”).add(“member1”,”member2”,…)
rt.opsForSet().add(“key”, set) //set是一个集合对象
scard key rt.opsForSet().size(“key”)
sidff key1 key2 rt.opsForSet().difference(“key1”,”key2”) //返回一个集合对象
sinter key1 key2 rt.opsForSet().intersect(“key1”,”key2”)//同上
sunion key1 key2 rt.opsForSet().union(“key1”,”key2”)//同上
sdiffstore des key1 key2 rt.opsForSet().differenceAndStore(“key1”,”key2”,”des”)
sinter des key1 key2 rt.opsForSet().intersectAndStore(“key1”,”key2”,”des”)
sunionstore des key1 key2 rt.opsForSet().unionAndStore(“key1”,”key2”,”des”)
sismember key member rt.opsForSet().isMember(“key”,”member”)
smembers key rt.opsForSet().members(“key”)
spop key rt.opsForSet().pop(“key”)
srandmember key count rt.opsForSet().randomMember(“key”,count)
srem key member1 member2… rt.opsForSet().remove(“key”,”member1”,”member2”,…)

Lua脚本

代码示例上面已经给出:

管道(Pipeline)

客户端可以一次性发送多个请求而不用等待服务器的响应,待所有命令都发送完后再一次性读取服务的响应,这样可以极大的降低多条命令执行的网络传输开销,管道执行多条命令的网络开销实际上只相当于一 次命令执行的网络开销。需要注意到是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。 pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信 息;也就是pipeline并不是表达“所有command都一起成功”的语义,管道中前面命令失败,后面命令 不会有影响,继续执行。

特定

1、减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器 上完成。使用脚本,减少了网络往返时延。这点跟管道类似

2、原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。管道不是原子的,不过 redis的批量操作命令(类似mset)是原子的

3、替代redis的事务功能:redis自带的事务功能很鸡肋,报错不支持回滚,而redis的lua脚本几乎实现了 常规的事务功能,支持报错回滚操作,官方推荐如果要使用redis的事务功能可以用redis lua替代

注意,不要在Lua脚本中出现死循环和耗时的运算,否则redis会阻塞,将不接受其他的命令, 所以使用 时要注意不能出现死循环、耗时的运算。redis是单进程、单线程执行脚本。管道不会阻塞redis。

Redis核心数据结构与原理

Redis核心数据结构与应用

Redis核心数据结构

5种核心数据结构

image-20220328223125611

String

字符串常用操作

1
2
3
4
5
6
7
SET  key  value 			//存入字符串键值对
MSET key value [key value ...] //批量存储字符串键值对
SETNX key value //存入一个不存在的字符串键值对
GET key //获取一个字符串键值
MGET key [key ...] //批量获取字符串键值
DEL key [key ...] //删除一个键
EXPIRE key seconds //设置一个键的过期时间(秒)

原子加减

1
2
3
4
INCR  key 			//将key中储存的数字值加1
DECR key //将key中储存的数字值减1
INCRBY key increment //将key所储存的值加上increment
DECRBY key decrement //将key所储存的值减去decrement

String应用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
单值缓存
SET key value
GET key

对象缓存
1) SET user:1 value(json格式数据)
2) MSET user:1:name zhuge user:1:balance 1888
MGET user:1:name user:1:balance

分布式锁
SETNX product:10001 true //返回1代表获取锁成功
SETNX product:10001 true //返回0代表获取锁失败
。。。执行业务操作
DEL product:10001 //执行完业务释放锁
SET product:10001 true ex 10 nx //防止程序意外终止导致死锁

计数器
INCR article:readcount:{文章id} 
GET article:readcount:{文章id}

Web集群session共享
spring session + redis实现session共享

分布式系统全局序列号(一个机器一次拿1000个缓存在本地,用完再继续拿)
INCRBY orderId 1000 //redis批量生成序列号提升性能

image-20220329211712401

Hash

Hash常用操作

1
2
3
4
5
6
7
8
9
HSET  key  field  value 			//存储一个哈希表key的键值
HSETNX key field value //存储一个不存在的哈希表key的键值
HMSET key field value [field value ...] //在一个哈希表key中存储多个键值对
HGET key field //获取哈希表key对应的field键值
HMGET key field [field ...] //批量获取哈希表key中多个field键值
HDEL key field [field ...] //删除哈希表key中的field键值
HLEN key //返回哈希表key中field的数量
HGETALL  key //返回哈希表key中所有的键值
HINCRBY key field increment //为哈希表key中field键的值加上增量increment

Hash应用场景

1
2
3
4
对象缓存
HMSET user {userId}:name zhuge {userId}:balance 1888
HMSET user 1:name zhuge 1:balance 1888
HMGET user 1:name 1:balance
1
2
3
4
5
6
7
8
9
10
11
电商购物车
1)以用户id为key
2)商品id为field
3)商品数量为value

购物车操作
1)添加商品hset cart:1001 10088 1
2)增加数量hincrby cart:1001 10088 1
3)商品总数hlen cart:1001
4)删除商品hdel cart:1001 10088
5)获取购物车所有商品hgetall cart:1001

优缺点

优点

1)同类数据归类整合储存,方便数据管理
2)相比string操作消耗内存与cpu更小
3)相比string储存更节省空间

缺点

1)过期功能不能使用在field上,只能用在key上

2)Redis集群架构下不适合大规模使用(由于集群模式根据key来确定存储在那个节点,如果某个hset存储内容过多,会造成某个节点的存储量偏大,造成数据分布不均匀)

image-20220329212006826

List结构

List常用操作

1
2
3
4
5
6
7
8
9
LPUSH  key  value [value ...] 		//将一个或多个值value插入到key列表的表头(最左边)
RPUSH key value [value ...] //将一个或多个值value插入到key列表的表尾(最右边)
LPOP key //移除并返回key列表的头元素
RPOP key //移除并返回key列表的尾元素
LLen key //返回key的长度
LRANGE key start stop //返回列表key中指定区间内的元素,区间以偏移量start和stop指定

BLPOP key [key ...] timeout //从key列表表头弹出一个元素,若列表中没有元素,阻塞等待 timeout秒,如果timeout=0,一直阻塞等待
BRPOP key [key ...] timeout //从key列表表尾弹出一个元素,若列表中没有元素,阻塞等待 timeout秒,如果timeout=0,一直阻塞等待

List应用场景

1
2
3
Stack(栈) = LPUSH + LPOP  FILO
Queue(队列)= LPUSH + RPOP
Blocking MQ(阻塞队列)= LPUSH + BRPOP

诸葛微博消息和微信公号消息

微博关注了MacTalk,备胎说车等大V

1
2
3
4
5
6
1)MacTalk发微博,消息ID为10018
LPUSH msg:{诸葛老师-ID} 10018
2)备胎说车发微博,消息ID为10086
LPUSH msg:{诸葛老师-ID} 10086
3)查看最新的5条关注微博消息
LRANGE msg:{诸葛老师-ID} 0 5

image-20220330220809036

Set结构

Set常用操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Set常用操作
SADD key member [member ...] //往集合key中存入元素,元素存在则忽略, 若key不存在则新建
SREM key member [member ...] //从集合key中删除元素
SMEMBERS key //获取集合key中所有元素
SCARD key //获取集合key的元素个数
SISMEMBER key member //判断member元素是否存在于集合key中
SRANDMEMBER key [count] //从集合key中选出count个元素,元素不从key中删除
SPOP key [count] //从集合key中选出count个元素,元素从key中删除

Set运算操作
SINTER key [key ...] //交集运算
SINTERSTORE destination key [key ..] //将交集结果存入新集合destination中
SUNION key [key ..] //并集运算
SUNIONSTORE destination key [key ...] //将并集结果存入新集合destination中
SDIFF key [key ...] //差集运算
SDIFFSTORE destination key [key ...] //将差集结果存入新集合destination中

Set应用场景

微信抽奖小程序

1
2
3
4
5
6
1)点击参与抽奖加入集合
SADD key {userlD}
2)查看参与抽奖所有用户
SMEMBERS key
3)抽取count名中奖者
SRANDMEMBER key [count] / SPOP key [count]

image-20220330221547322

微信微博点赞,收藏,标签

1
2
3
4
5
6
7
8
9
10
11
12
微信微博点赞,收藏,标签
1) 点赞
SADD like:{消息ID} {用户ID}
2) 取消点赞
SREM like:{消息ID} {用户ID}
3) 检查用户是否点过赞
SISMEMBER like:{消息ID} {用户ID}
4) 获取点赞的用户列表
SMEMBERS like:{消息ID}
5) 获取点赞用户数
SCARD like:{消息ID}

集合操作

1
2
3
SINTER set1 set2 set3   { c }
SUNION set1 set2 set3 { a,b,c,d,e }
SDIFF set1 set2 set3 { a }

image-20220330222741101

集合操作实现微博微信关注模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
集合操作实现微博微信关注模型
1) 诸葛老师关注的人:
zhugeSet-> {yangguo, sima, luban}
2) 杨过老师关注的人:
yangguoSet--> {zhuge, sima, luban,guojia}
3) 司马老师关注的人:
simaSet-> {zhuge, yangguo, guojia, luban, xunyu)
4) 我和杨过老师共同关注:
SINTER zhugeSet yangguoSet--> {sima, luban}
5) 我关注的人也关注他(杨过老师):
SISMEMBER simaSet yangguo
SISMEMBER lubanSet yangguo
6) 我可能认识的人:
SDIFF yangguoSet zhugeSet->(zhuge, guojia}

image-20220330222922268

集合操作实现电商商品筛选

1
2
3
4
5
6
7
8
9
10
集合操作实现电商商品筛选
SADD brand:huawei P30
SADD brand:xiaomi mi-6X
SADD brand:iPhone iphone8
SADD os:android P30 mi-6X
SADD cpu:brand:intel P30 mi-6X
SADD ram:8G P30 mi-6X iphone8

SINTER os:android cpu:brand:intel ram:8G  {P30,mi-6X}

image-20220330223600732

Zset结构

ZSet常用操作

1
2
3
4
5
6
7
8
9
10
11
ZSet常用操作
ZADD key score member [[score member]…] //往有序集合key中加入带分值元素
ZREM key member [member …] //从有序集合key中删除元素
ZSCORE key member //返回有序集合key中元素member的分值
ZINCRBY key increment member //为有序集合key中元素member的分值加上increment
ZCARD key //返回有序集合key中元素个数
ZRANGE key start stop [WITHSCORES] //正序获取有序集合key从start下标到stop下标的元素
ZREVRANGE key start stop [WITHSCORES] //倒序获取有序集合key从start下标到stop下标的元素
Zset集合操作
ZUNIONSTORE destkey numkeys key [key ...] //并集计算
ZINTERSTORE destkey numkeys key [key …] //交集计算

Zset应用场景

1
2
3
4
5
6
7
8
9
10
Zset集合操作实现排行榜
1)点击新闻
ZINCRBY hotNews:20190819 1 守护香港
2)展示当日排行前十
ZREVRANGE hotNews:20190819 0 10 WITHSCORES
3)七日搜索榜单计算
ZUNIONSTORE hotNews:20190813-20190819 7
hotNews:20190813 hotNews:20190814... hotNews:20190819
4)展示七日排行前十
ZREVRANGE hotNews:20190813-20190819 0 10 WITHSCORES

image-20220330224305930

HyperLogLog

​ Redis HyperLogLog 是用来做基数统计的算法

​ 基数计算(cardinality counting)指的是统计一批数据中的不重复元素的个数,常见于计算独立用户数(UV)、维度的独立取值数等等

Redis更多应用场景

微博、微信、陌陌<附近的人>
微信<摇一摇><抢红包>
滴滴打车、摩拜单车<附近的车>
美团和饿了么<附近的餐馆>
搜索自动补全
布隆过滤器
。。。

Redis核心原理

Redis 为什么一开始选择单线程模型

​ 因为Redis是一个基于内存的数据库,还要处理大量的外部的网络请求,这就不可避免的要进行多次IO。好在Redis使用了很多优秀的机制来保证了它的高效率。那么为什么Redis要设计成单线程模式的呢?

1、IO多路复用

Redis在网络I/O模型上采用了多路复用技术,来减少网络瓶颈带来的影响。很多场景中使用单线程模型并不意味着程序不能并发的处理任务。Redis 虽然使用单线程模型处理用户的请求,但是它却使用 I/O 多路复用技术“并行”处理来自客户端的多个连接,同时等待多个连接发送的请求。使用 I/O 多路复用技术能极大地减少系统的开销,系统不再需要为每个连接创建专门的监听线程,避免了由于大量的线程创建带来的巨大性能开销。

2、可维护性

多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题。单线程模式下,可以方便地进行调试和测试。

3、基于内存操作

多线程能够充分利用 CPU 的资源,但对于Redis来说,由于基于内存速度那是相当的高,能达到在一秒内处理10万个用户请求,如果一秒十万还不能满足,那我们就可以使用 Redis 分片的技术来交给不同的 Redis 服务器。这样的做饭避免了在同一个 Redis 服务中引入大量的多线程操作。 而且基于内存,除非是要进行 AOF 备份,否则基本上不会涉及任何的 I/O 操作。这些数据的读写由于只发生在内存中,所以处理速度是非常快的;用多线程模型处理全部的外部请求可能不是一个好的方案

4、线程上下文切换问题

多线程场景下会发生线程上下文切换。线程是由 CPU 调度的,CPU 的一个核在一个时间片内只能同时执行一个线程,在 CPU 由线程A切换到线程B的过程中会发生一系列的操作,主要过程包括保存线程A的执行现场,然后载入线程B的执行现场,这个过程就是“线程上下文切换”。其中涉及线程相关指令的保存和恢复。 频繁的线程上下文切换可能会导致性能急剧下降,这会导致我们不仅没有提升处理请求的速度,反而降低了性能。

Redis6.0之前的版本真的是单线程?

​ Redis在处理客户端的请求时,包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。但如果严格来讲从Redis4.0之后并不是单线程,除了主线程外,它也有后台线程在处理一些较为缓慢的操作,例如清理脏数据、无用连接的释放、大key的删除等等

image-20221218205038561

Redis6.0为什么引入多线程?

参考文档

​ 简单来说,可以充分利用服务器CPU资源,目前主线程只能利用一个核,多线程任务可以分摊Redis同步IO读写负荷。

Redis 将所有数据放在内存中,内存的响应时长大约为100纳秒,对于小数据包,Redis 服务器可以处理80,000到100,000 QPS,这也是Redis处理的极限了,对于80%的公司来说,单线程的Redis已经足够使用了。

但随着越来越复杂的业务场景,有些公司动不动就上亿的交易量,因此需要更大的QPS。

常见的解决方案是在分布式架构中对数据进行分区并采用多个服务器,但该方案有非常大的缺点,例如要管理的Redis服务器太多,维护代价大;某些适用于单个 Redis 服务器的命令不适用于数据分区;数据分区无法解决热点读/写问题;数据偏斜,重新分配和放大/缩小变得更加复杂等等。

虽然现在很多服务器都是多个 CPU 核的,但是对于Redis来说,因为使用了单线程,在一次数据操作的过程中,有大量的 CPU 时间片是耗费在了网络 IO 的同步处理上的,并没有充分的发挥出多核的优势。

如果能采用多线程,使得网络处理的请求并发进行,就可以大大的提升性能。多线程除了可以减少由于网络 I/O 等待造成的影响,还可以充分利用 CPU 的多核优势。

Redis6.0引入的多线程部分,实际上只是用来处理网络数据的读写和协议解析,执行命令仍然是单一工作线程,而且多线程开关默认关闭

Zset的底层原理

底层结构

底层实现 = hash(映射分数和成员) + 压缩列表(zipList) 或者 跳表(skipList)

什么是跳表

跳表在 Redis 的 zset 中主要用于支持范围查询操作,因为它能够在对数时间复杂度内完成查找、插入和删除操作。跳表实际上是一种多层有序链表,每一层链表都是有序的,且层数越高的链表包含的节点越少,这样通过多层链表的辅助,可以快速定位到目标节点。

什么时候用跳表

当 ZSET 中的元素数量较小时,Redis 会选择使用有序数组作为底层实现。这是因为有序数组在内存使用效率上更高,而且对于较小的集合来说,插入、删除和查找操作的时间复杂度都很低。

然而,当 ZSET 中的元素数量较大时,Redis 会自动将底层实现从有序数组切换为跳跃表。跳跃表在处理大型有序集合时更加高效,因为它可以在对数时间内完成插入、删除和查找操作,并且在内存占用上也比较合理。

跳表和红黑树的对比

总的来说,跳表和红黑树的查询复杂度都能达到O(LogN),但是红黑树在增删过程中需要平衡,实现难度比较复杂,而跳表需要维护多个链表,就空间而言需要更多内存,但更为直观。

使用zipList的条件

  1. 成员数量小于128个: 当成员数量较少时,使用有序数组作为底层实现更为合适。有序数组可以通过紧凑的方式存储成员和分数,节省内存空间,并且在插入、删除和查找操作上有较低的时间复杂度。因此,当成员数量较少时,使用有序数组可以更有效地利用内存和提高性能。
  2. 每个成员的字符串长度小于64个字节: 在有序数组中,每个成员都需要存储其对应的字符串以及相关的分数。为了保持内存占用的合理性,限制每个成员的字符串长度可以有效控制存储空间的使用。较短的字符串长度意味着每个成员占用的空间更小,从而可以存储更多的成员在有限的内存空间内。

其他高级命令

keys

keys: 全量遍历键,用来列出所有满足特定正则字符串规则的key,当redis数据量比较大时, 性能比较差,要避免使用

1、keys *

2、keys a*

scan

scan:渐进式遍历键:SCAN cursor [MATCH pattern] [COUNT count] scan 参数提供了三个参数,第一个是 cursor 整数值,第二个是 key 的正则模式,第三个是一次遍 历的key的数量,并不是符合条件的结果数量。第一次遍历时,cursor 值为 0,然后将返回结果中第 一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。

image-20220404200315343

Info

Info:查看redis服务运行信息,分为 9 大块,每个块都有非常多的参数,这 9 个块分别是:

Server 服务器运行的环境参数

Clients 客户端相关信息

Memory 服务器运行内存统计数据

Persistence 持久化信息

Stats 通用统计数据

Replication 主从复制相关信息

CPU CPU 使用情况

Cluster 集群信息

KeySpace 键值对统计数量信息