1 Redis基础概述
Redis 是C语言开发的一个开源高性能键值对的内存数据库,可以用来做数据库、缓存、消息中间件等场景,是一种NoSQL(not-only sql,非关系型数据库)的数据库
1.1 参考网站:
官网
中文文档
1.2 安装
linux安装Redis7
1.3 Redis优势
- 性能极高,Redis能读的速度是11万/秒,写的速度是8万/秒
- Redis数据类型丰富,不仅支持简单的key-value类型数据,同时还提供list,set, zset, hash等数据结构的存储。
- Redis支持数据的持久化,可以将内存中的数据持久化到硬盘中,重启的时候可以再次加载进行使用。
- Redis支持数据备份,即master-slave模式的数据备份。
1.4 redis版本说明
版本号第二位如果是奇数,则为非稳定版本 如2.7、2.9、3.1
版本号第二位如果是偶数,则为稳定版本 如2.6、2.8、3.0、3.2
当前奇数版本就是下一个稳定版本的开发版本,如2.9版本是3.0版本的开发版本
2 Redis十大数据类型
2.1 Redis十大数据类型介绍
2.1.1 string
- string是redis最基本的类型,一个key对应一个value。
- string类型是二进制安全的,意思是redis的string可以包含任何数据,比如jpg图片或者序列化的对象
- string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
2.1.2 list
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。它的底层实际是个双端链表,最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)
2.1.3 hash
-
Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。
-
Redis 中每个 hash 可以存储 2^32 - 1 键值对(40多亿)
2.1.4 set
-
Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,集合对象的编码可以是 intset 或者 hashtable。
-
Redis 中Set集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
-
集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储40多亿个成员)
2.1.5 zset
zset(sorted set:有序集合)
-
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
-
不同的是每个元素都会关联一个double类型的分数,redis正是通过分数来为集合中的成员进行从小到大的排序。
-
zset集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 2^32 - 1
2.1.6 geo
Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,包括
-
添加地理位置的坐标。
-
获取地理位置的坐标。
-
计算两个位置之间的距离。
-
根据用户给定的经纬度坐标来获取指定范围内的地理位置集合
2.1.7 hyperloglog
-
HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定且是很小的。
-
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
-
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
2.1.8 bitmap
由0和1状态表现的二进制位的bit数组
2.1.9 bitfield
-
通过bitfield命令可以一次性操作多个比特位域(指的是连续的多个比特位),它会执行一系列操作并返回一个响应数组,这个数组中的元素对应参数列表中的相应操作的执行结果。
-
说白了就是通过bitfield命令我们可以一次性对多个比特位域进行操作。
2.1.10 stream
-
Redis Stream 主要用于消息队列(MQ,Message Queue),Redis 本身是有一个 Redis 发布订阅 (pub/sub) 来实现消息队列的功能,但它有个缺点就是消息无法持久化,如果出现网络断开、Redis 宕机等,消息就会被丢弃。
-
简单来说发布订阅 (pub/sub) 可以分发消息,但无法记录历史消息。
-
而 Redis Stream 提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
2.2 Redis命令使用
Redis命令手册
2.2.1 Redis键key
- keys *
- exists key
- type key
- del key
- unlink key:非阻塞删除,仅仅将keys从keyspace元数据中删除,真正删除操作在后续异步中操作。
- ttl key:查看还有多少秒过期, -1表示永不过期,-2表示已过期。
- expire key 秒数
- move key dbindex(0 - 15) :将当前数据库key移动到给定db中
- select dbindex:切换dbindex(0-15)默认为0
- dbsize
- flushdb:清空当前库
- flushall:清空所有库
- help @类型:例如 help @set
2.2.2 string
-
set:从2.6.12版本开始,redis为SET命令增加了一系列选项:
(1)EX seconds – 设置键key的过期时间,单位时秒
(2)PX milliseconds – 设置键key的过期时间,单位时毫秒
(3)NX – 只有键key不存在的时候才会设置key的值
(4)XX – 只有键key存在的时候才会设置key的值
(5)KEEPTTL – 获取 key 的过期时间
-
get
-
mset:批量设置,例如mset name zhangsan age 15
-
mget
-
getrange key_name start end:截取得到的子字符串。从零到负一表示全部
-
setrange key_name offset value:从偏移量 offset 开始, 用 value 参数覆盖键 key 储存的字符串值。
一定要是数字才可以加减
- incr key:递增
- incrby key value:增加指定的整数
- decr key:递减
- decrby key value:减少指定的整数
- strlen key:字符串长度
- append key value
- getset key:将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
应用场景:点赞。
2.2.3 list
-
lpush:头插
-
rpush:尾插
-
lrange:遍历
-
lpop
-
rpop
-
lindex: 按照索引下标获取某值
-
llen
-
lrem:删除前 count 个值等于 element 的元素
-
ltrim :截取指定范围内的值再赋值给key
-
lset key index value
-
linset :在list某个已有值的前后再添加具体值
应用场景:微信公众号订阅的消息。
2.2.4 hash
- hset
- hget
- hmset
- hmget
- hgetall
- hdel
- hlen
- exists
- hkeys
- hvals
- hincrby
应用场景:早期购物车设计
新增商品 → hset shopcar:uid1024 334488 1
新增商品 → hset shopcar:uid1024 334477 1
增加商品数量 → hincrby shopcar:uid1024 334477 1
商品总数 → hlen shopcar:uid1024
全部选择 → hgetall shopcar:uid1024
2.2.5 set
- sadd key member
- smembers key:遍历
- sismember key member:判断member是否在集合中
- srem key member :删除元素
- scard:获取集合中元素个数
- srandmember key [count]:从集合中随机展现count个元素,元素不删除
- spop key [count]:从集合中随机展现count个元素,元素删除
- smove k1 k2 k1中存在的值:将 k1中存在的某个值赋给k2
- sdiff key [key…]:差集
- sunion:并集
- sinter:交集
应用场景:
微信小程序抽奖
微信朋友圈查看共同赞的朋友
QQ可能认识的人
2.2.6 zset
- zadd key score member [score member…]
- zrange key start stop [withscores]: 按照分数从小到大返回索引从start到stop的值。withscores同时返回分数
- zrevrange
- zrangebyscore key min max [withscores] [limit offset count]
- zscore key member:获取元素分数
- zcard key:获取集合中元素个数
- zrem key 某个score下对应的value值:删除
- zincrby key score member:增加某个元素的分数
- zcount key min max:获取指定元素范围内的元素个数
- zrank key member:获取index
- zrevrank key member:获取逆序index
应用场景:根据商品销售对商品进行排序显示。
思路:定义商品销售排行榜(sorted set集合),key为goods:sellsort,分数为商品销售数量。
2.2.7 geo
redis-geo
- geoadd [longitude latitude member …]
例如:geoadd city 116.403963 39.915119 “天安门” 116.403414 39.924091 “故宫” - geopos key member:获取经纬度
- geodist key member1 member2 [M|KM|FT|MI]:返回两个地方的距离
- georadius key longitude latitude radius M|KM|FT|MI [WITHCOORD]:以给定经纬度为中心,返回不超过给定的最大距离的元素
- georadiusbymember
- geohash
2.2.8 hyperloglog
基数统计。统计某个网站独立访客(一般理解为客户端IP),用户搜索网站关键词的数量,统计每天搜索不同词条的个数。
去重复统计功能的基数估计算法就是hyperloglog
- pfadd
- pfcount
- pfmerge
2.2.9 bitmap
由0和1表示的二进制位的bit数组。可用于打卡,签到统计等。用String类型作为底层数据结构实现的一种统计二值状态的数据类型。位图本质是数组。
-
setbit key offset value
-
getbit key offset
-
strlen key :统计字节数
-
bitcount key:统计1的个数
-
bitop 查找第一个指定的二进制位值
2.2.10 bitfield
了解即可
2.2.11 stream
redis版的消息中间件+阻塞队列。
redis stream
3 Redis持久化和事务
3.1 持久化
Redis 一共有 2 种持久化方式,分别是 RDB 和 AOF
3.1.1 持久化之全量写入RDB
RDB(Redis 数据库):RDB 持久性以指定的时间间隔执行数据集的时间点快照。就是把某一时刻的数据和状态以文件的形式写到磁盘上。
修改配置文件 redis.conf
save 3600 1 # 每3600秒钟里redis数据库有1条数据被修改则触发RDB
save 300 100 # 每300秒钟里redis数据库有100条数据被修改则触发RDB
save 60 10000 # 每60秒钟里redis数据库有10000条数据被修改则触发RDB
dbfilename "dump6380.rdb" # 持久化文件名称
dir "/myredis" # 持久化数据文件存放的路径
除了上述配置文件中 save < secends > < changes > 自动触发外,还支持手动触发。Redis提供了save和bgsave两个命令生成rdb文件。
-
save:会阻塞当前Redis服务器。线上禁用。
-
bgsave:会创建一个子进程专门去把内存中的数据库状态写入RDB文件里。但子进程基本是复制的父进程,这等于两个相同大小的redis进程在系统上运行,会造成内存使用率的大幅增加。
3.1.2 AOF
与RDB的保存整个redis数据库状态不同,AOF是通过保存对redis服务端的写命令(如set、sadd、rpush)来记录数据库状态的,即保存你对redis数据库的写操作,以下就是AOF文件的配置内容
dir "/myredis" # 持久化数据文件存放的路径
appendonly yes #开启AOF持久化,默认关闭
appendfilename "appendonly6380.aof" #AOF文件名称(默认)
appendfsync no #AOF持久化策略
auto-aof-rewrite-percentage 100 #触发AOF文件重写的条件(默认)
auto-aof-rewrite-min-size 64mb #触发AOF文件重写的条件(默认)
reids开启AOF后,服务端每执行一次写操作(如set、sadd、rpush)就会把该条命令追加到一个单独的AOF缓冲区的末尾,这就是命令追加;然后把AOF缓冲区的内容写入AOF文件里。
而何时进行文件同步则是根据配置的appendfsync来进行:
appendfsync有三个选项:always、everysec和no。默认的AOF 持久化策略是每秒钟fsync 一次。
- always:同步写回。每个写命令执行完立刻同步地将日志写回磁盘。
- everysec:每秒写回。每个写命令执行完,只是把日志写到AOF缓冲区的末尾,每隔一秒把缓存中的内容写回磁盘。
- no:操作系统控制写回。每个写命令执行完,只是把日志写到AOF缓冲区的末尾,由操作系统决定何时将缓存中的内容写回磁盘。
2、配置成everysec的话服务端每执行一次写操作(如set、sadd、rpush)也会把该条命令追加到一个单独的AOF缓冲区的末尾,并将AOF缓冲区写入AOF文件,然后每隔一秒才会进行一次文件同步把内存缓冲区里的AOF缓存数据真正写入AOF文件里,这个模式兼顾了效率的同时也保证了数据的完整性,即使在服务器宕机也只会丢失一秒内对redis数据库做的修改;
3、将appendfsync配置成no则意味redis数据库里的数据就算丢失你也可以接受,它也会把每条写命令追加到AOF缓冲区的末尾,然后写入文件,但什么时候进行文件同步真正把数据写入AOF文件里则由系统自身决定,即当内存缓冲区的空间被填满或者是超过了设定的时限后系统自动同步。这种模式下效率是最快的,但对数据来说也是最不安全的,如果redis里的数据都是从后台数据库如mysql中取出来的,属于随时可以找回或者不重要的数据,那么可以考虑设置成这种模式。
3.1.3 总结
-
RDB优点:如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB 方式要比AOF 方式更加的高效。
-
RDB缺点:如果你对数据的完整性非常敏感,那么RDB 方式就不太适合你,因为即使你每5 分钟都持久化一次,当Redis 故障时,仍然会有近5 分钟的数据丢失。
-
AOF 优点:我们通过一个场景再现来说明。某同学在操作Redis 时,不小心执行了FLUSHALL,导致Redis 内存中的数据全部被清空了,这是很悲剧的事情。只要Redis 配置了AOF 持久化方式,且AOF文件还没有被重写(rewrite),我们就可以用最快的速度暂停Redis 并编辑AOF文件,将最后一行的FLUSHALL 命令删除,然后重启Redis,就可以恢复Redis的所有数据到FLUSHALL 之前的状态了。这就是AOF 持久化方式的好处之一。但是如果AOF 文件已经被重写了,那就无法通过这种方法来恢复数据了。
-
AOF缺点:在同样数据规模的情况下,AOF文件要比RDB文件的体积大。而且,AOF 方式的恢复速度也要慢于RDB 方式
-
如果redis开启了AOF持久化功能,那么当redis服务重启时会优先使用AOF文件来还原数据库。
RDB是每隔一段时间触发持久化,因此数据安全性低,AOF可以做到实时持久化,数据安全性较高
RDB文件默认采用压缩的方式持久化,AOF存储的是执行指令,所以RDB在数据恢复的时候性能比AOF要好
推荐使用方式:RDB和AOF混合使用。
3.2 Redis事务
3.2.1 Redis事务是什么
Redis事务是将一系列命令按顺序串行化执行,期间不会被其他客户端的指令插队。。
Redis事务的三大命令
- multi:开启事务
- exec:执行事务
- discard:取消事务
如下图,通过multi,当前客户端就会开启事务,后续的指令都会按序存到队列中。当用户键入exec后,这些指令都会按顺序执行。若开启multi后输入若干指令,在键入discard,则之前的指令通通取消执行。
3.2.2 事务的错误和回滚
分别有组队时错误和执行命令时错误两种情况
组队时错误
在组队时输入错误的指令,redis所有指令都会失效,因为这是一个问题队列。
执行命令时错误
按序处理所有指令,遇到错误就按正常流程处理继续执行下去。
为什么组队时出错和运行时出错会出现两种不同的情况呢?
- 组队时出错,错误对于redis来说是已知的,从设计者的角度出发,对于已知的错误我们需要提醒用户进行处理,所以就让事务中的所有指令都失效。组队时错误支持事务回滚
- 运行时出错:因为错误是未知的,所以redis必须执行时才能知道错误,而redis也无错误回滚机制,所以就出现了将错就错,继续执行后续指令并有效的情况。运行时错误不支持事务回滚
4 Redis管道
redis 是 C/S 模式,即客户端 + 服务端。
- 客户端向服务端发送命令分四步(发送命令→命令排队→命令执行→返回结果),并监听Socket返回,通常以阻塞模式等待服务端响应。
- 服务端处理命令,并将结果返回给客户端。
如果同时需要执行大量的命令,那么就要等待上一条命令应答后再执行,这中间不仅仅多了RTT(Round Time Trip),而且还频繁调用系统IO,发送网络请求,同时需要redis调用多次read()和write()系统方法,系统方法会将数据从用户态转移到内核态,这样就会对进程上下文有比较大的影响了,性能不太好。
redis 提供了 pipeline 解决方案,可以按批次进行查询和响应。
演示:
pipeline 与原生命令的区别
- 原生命令批量命令(mset, mget)是原子性的,pipeline 是非原子性的
- 原生命令批量命令一次只能执行一种命令,pipeline 支持批量处理多种命令
5 Redis发布订阅
Redis 的发布与订阅
6 Redis 复制哨兵集群
6.1 Redis 复制
就是主从复制,master以写为主,slave以读为主。当master数据发生改变时,自动将新的数据异步同步到其他slave数据库。
6.1.1 搭建一主两从Redis
master如果配置了requirepass参数,需要密码登录,那slave 就要配置masterauth来设置master校验密码,否则master会拒绝slave连接。
slave配置文件加上
replicaof 192.168.208.101 6380 # 主节点
masterauth 123456 #主节点密码
常用命令:
- info replication:可以查看复制节点的主从关系和配置信息
- slaveof 新主库节点ip 新主节点端口:可以断开之前的主从关系和新主节点建立主从关系。slaveof no one表示自己变为主节点。
6.1.2 Reds特点
- 主机能读写,从机只能读,不能写
- slave 启动后首次将 master所有数据同步,后续跟随(master写,slave跟)
- 主机shutdown后,从机不会替代主机,原地待命,从机数据可以正常使用
- 主机shutdown后,重启后主从关系还在
- 某台从机shutdown后,重启后主从关系是否存在要看主从关系是配置的方式产生的还是命令,命令只对当此生效,配置持久稳定
6.1.3 复制缺点
(1)复制延时, 信号衰弱。由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
(2)由于主从模式是读写分离的,如果主节点(master)挂了,那么将没有主节点来服务客户端的写操作请求,也没有主节点给从节点(slave)进行数据同步了。
6.2 Redis 哨兵
它会监测主节点是否存活,如果发现主节点挂了,它就会选举一个从节点切换为主节点,并且把新主节点的相关信息通知给从节点和客户端。俗称无人值守运维。
6.2.1 搭建Redis Sentinel
Redis Sentinel架构:3个哨兵(自动监控和维护集群,不存放数据), 一主二从(同6.1.1)
步骤:
- 拷贝或新建sentinel.conf, 本地搭建三个sentenel,配置文件分别为sentinel26380.conf,sentinel26381.conf, sentinel26382.conf。
修改配置文件
- sentinel monitor mymaster masterip master端口 法定票数
- sentinel auth-pass mymaster master密码
启动三个哨兵
- redis-sentinel sentinel26380.conf --sentinel
- redis-sentinel sentinel26381.conf --sentinel
- redis-sentinel sentinel26382.conf --sentinel
测试
- 手动关掉master服务
- 查看sentinel26380.log,sentinel26381.log, sentinel26382.log 可以发现其中一个slave节点被选为master,以前的master节点变为slave, 另外一个slave节点还是从节点,只是主从关系变为新的master。
- master_redis.conf, slave1_redis.conf,slave2_redis.conf内容都发生了变化即master_redis.conf多了一行slaveof,sentinel的监控主节点也发生改变。
6.2.2 哨兵运行流程和选举原理
(1)两个概念
- 主观下线:单个sentinel检测到master状态,从sentinel角度看,如果发送ping心跳包后一定时间没有收到回复就达到了主观下线的条件。
- 客观下线:多个哨兵达成一致意见觉得master宕机
当主节点被判断客观下线后,各个哨兵节点会选举产生新的master节点。选举算法为Raft算法。
(2)sentinel选举步骤
6.2.3 哨兵使用建议
- 哨兵本身应该是集群保证高可用
- 哨兵节点的数量应该是基数
- 各个哨兵的配置应该一样
- 哨兵加复制并不能保证数据零丢失(主从切换的过程,可能会导致数据丢失)
6.3 Redis 集群
参考:
高手过招, 为什么 Redis Cluster 是16384个槽位?
Redis集群搭建及原理,肝了
哨兵模式的缺点:
(1)当master挂掉的时候,sentinel 会选举出来一个 master,选举的时候是没有办法去访问Redis的,会存在访问瞬断的情况
(2)哨兵模式对外只有master节点可以写,slave节点只能用于读。尽管Redis单节点最多支持10W的QPS,但是在电商大促的时候,写数据的压力全部在master上。
(3)Redis的单节点内存不能设置过大,若数据过大在主从同步将会很慢;在节点启动的时候,时间特别长;(从节点上有主节点的所有数据)
Redis Cluster 是 在 3.0 版本正式推出的高可用集群方案,相比Redis Sentinel,Redis Cluster方案不需要额外部署Sentinel集群,而是通过集群内部通信实现集群监控,故障时主从切换;同时,支持内部基于哈希实现数据分片,支持动态水平扩容
根据官方推荐,集群部署至少要3台以上的master节点,最好使用3主3从六个节点的模式。
6.3.1 数据分片
将整个数据集按照一定规则分配到多个节点上,称为数据分片,Redis Cluster采用的分片方案是哈希分片
基本原理如下:
Redis Cluster首先定义了编号0 ~ 16383的区间,称为槽,所有的键根据哈希函数映射到0 ~ 16383整数槽内,计算公式:slot = CRC16(key) % 16384。每一个节点负责维护一部分槽以及槽所映射的键值数据
7 SpringBoot集成Redis
SpringBoot集成Redis