Redis6.0学习笔记
一、Redis概述入门
1、NoSQL概述
NoSQL
是指not only Sql
,是一种非关系型数据库。其中NoSQL共有四种分类
- KV键值
- 文档型数据库(bson、MongoDB)
- 列存储数据库(HBase、分布式文件系统)
- 图关系数据库(存放关系、例如Neo4j)
2、Redis介绍
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis官网:https://redis.io/
Redis中文官网:http://www.redis.cn/
3、Redis安装
windows安装:https://github.com/dmajkic/redis/downloads(不推荐win开发)
Linux安装:
1 | # 从官网下载redis最新版 |
4、Redis压力测试
Redis-benchmark官方默认压测工具
序号 | 选项 | 描述 | 默认值 |
---|---|---|---|
1 | -h | 指定服务器主机名 | 127.0.0.1 |
2 | -p | 指定服务器端口号 | 6379 |
3 | -s | 指定服务器socket | |
4 | -c | 指定并发连接数 | 50 |
5 | -n | 指定请求数 | 10000 |
6 | -d | 以字节的形式指定SET/GET值的数据大小 | 3 |
7 | -k | 1=keep alive 0=reconnect | 1 |
8 | -r | SET/GET/INCR使用随机key,SADD使用随机值 | |
9 | -P | 通过管道传输 | 1 |
10 | -q | 强制退出redis。仅显示query/sec值 | |
11 | –csv | 以CSV格式输出 | |
12 | -l | 生成循环,永久执行 | |
13 | -t | 仅运行以逗号分隔的测试命令列表 | |
14 | -I | Idle模式。仅打开N个idle连接并等待 |
1 | # 开启服务后在当前目录进行测试 |
5、基础知识
redis默认16个数据库,默认使用第一个,使用select
进行切换数据库,Redis6之前是单线程的,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽,而单线程复杂度低,又不需要CPU上下文切换,也无需加锁。而在Redis6开始支持多线程,默认仍然是不开启,开启需要在redis.conf
进行设置,其中Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。
1 | 127.0.0.1:6379> PING |
二、Redis五大基本数据类型
Redis 是一种开源(BSD 许可)、内存中数据结构存储,用作数据库、缓存和消息代理。 Redis 提供了诸如字符串、散列、列表、集合、带范围查询的排序集合、位图、超级日志、地理空间索引和流等数据结构。 Redis 内置复制、Lua 脚本、LRU 驱逐、事务和不同级别的磁盘持久化,并通过 Redis Sentinel 和 Redis Cluster 自动分区提供高可用性。
Redis有五大基本数据类型:
- String(字符串类型)
- Hash(哈希,类似java的Map)
- List(列表)
- Set(集合)
- ZSet(有序集合)
1、Redis-key
1 | 127.0.0.1:6379> set name shawn |
2、String类型
1 | # ====================================================== |
3、列表List
list相当于双向链表,可以用作队列,也可以作栈,可以做消息队列,在两端操作效率高,最中间操作效率会低
1 | # ====================================================== |
4、集合Set
set中的值不能重复,是无序不重复的
1 | # ====================================================== |
5、哈希Hash
Map集合,相当于key-Map,通常来存储经常变动的对象
1 | # ====================================================== |
6、有序集合Zset
Zset增加了权重参数score,可以用来设置任务的重要程度,例如排行榜应用,Top N
1 | # ====================================================== |
三、Redis三种特殊数据类型
1、GEO地理位置
GEO 的数据结构总共有六个常用命令:geoadd、geopos、geodist、georadius、 georadiusbymember、gethash
官方文档:https://www.redis.net.cn/order/3685.html
因为存在中文,redis客户端启动时命令redis-cli -p 6379 --raw
geoadd
1 | # 语法 |
geopos
1 | # 语法 |
geodist
1 | # 指定单位的参数 unit 必须是以下单位的其中一个: |
georadious
以给定的经纬度为中心, 找出某一半径内的元素
1 | # 附近范围内查询,比如附近的人功能的实现,count限制查询出来的数量 |
georadiusbymember
1 | #找出指定元素旁边的位置 |
geohash
该命令将返回11个字符的Geohash字符串
1 | # Redis使用geohash将二维经纬度转换为一维字符串,字符串越长表示位置更精确,两个字符串越相似表示距离越近。很少使用 |
zrem
1 | # geo底层使用了zset,故可以用此方法进行删除 |
2、Hyperloglog
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的,固定12KB。可以用来计数网站用户量(允许小量容错)
1 | 127.0.0.1:6379> pfadd mykey a b c d e f g #创建第一组元素 |
3、Bitmaps
位存储。统计用户信息,活跃,不活跃,未登录等两个状态,都可以使用Bitmaps(只有0和1)
1 | # 使用 bitmap 来记录上述事例中一周的打卡记录如下所示: |
四、事务
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。若为编译型错误,则事务无法执行。事务的执行是按顺序执行的,且事务没有隔离级别概念。
Redis事务:
- 开启事务()
- 命令入队()
- 执行事务()
1 | 127.0.0.1:6379> multi #开启事务 |
悲观锁
悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿到这个数据就会block直到它拿到锁。传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在操作之前先上锁。
乐观锁
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁。但是在更新的时候会判断一下再此期间别人有没有去更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量,乐观锁策略:提交版本必须大于记录当前版本才能执行更新。
1 | # 用watch监视,成功就修改,可以用来做乐观锁 |
五、Java连接Redis操作
1、Jedis
Jedis是Redis官方推荐的Java连接开发工具。
首先新建一个空的maven项目
1 | <!--进入maven仓库查找最新版--> |
1 | // 成功连接,输出pong,jedis中已经集成了常用的API,使用.即可查询 |
2、SpringBoot整合Redis
2.1 简单使用
首先在pom.xml
中导入依赖
1 | <!--spring2.0后底层使用lettuce,性能更高,2.0之前采用jedis--> |
配置application.yml
1 | #配置redis |
测试
1 |
|
2.2 源码分析
在External Libraries
中找到Redis的自动配置类,在RedisProperties.class
也可以看到配置信息
1 | false) (proxyBeanMethods = |
2.3 序列化工具封装
自定义序列化RedisTemplate
,这里提供两种,一种是jackson,另一种是fastjson
jackson序列化
1 |
|
fastjson序列化
1 | import com.alibaba.fastjson.JSON; |
1 | import org.springframework.context.annotation.Bean; |
fastjson第二个例子
1 |
|
创建工具类(可自定义取舍,这里仅供参考)
1 |
|
3、RedisSerializer(Redis 序列化器)总结
- 默认情况下,
RedisTemplate
使用JdkSerializationRedisSerializer
,也就是 JDK 序列化,容易产生 Redis 中保存了乱码的错觉 - 通常考虑到易读性,可以设置 Key 的序列化器为
StringRedisSerializer
。但直接使用RedisSerializer.string()
,相当于使用了UTF_8
编码的StringRedisSerializer
,需要注意字符集问题 - 如果希望 Value 也是使用 JSON 序列化的话,可以把 Value 序列化器设置为
Jackson2JsonRedisSerializer
。默认情况下,不会把类型信息保存在 Value 中,即使我们定义 RedisTemplate 的 Value 泛型为实际类型,查询出的 Value 也只能是LinkedHashMap
类型。如果希望直接获取真实的数据类型,你可以启用JacksonObjectMapper
的activateDefaultTyping
方法,把类型信息一起序列化保存在 Value中 - 如果希望 Value 以
JSON
保存并带上类型信息,更简单的方式是,直接使用RedisSerializer.json()
快捷方法来获取序列化器
六、Redis.conf配置信息
config get *
获取全部配置信息
配置文件信息在/opt/redis-6.2.4/redis.conf
,常用配置信息如下
1 | # 绑定IP,这里指所有ipv4和ipv6都可以访问 |
七、Redis持久化
Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能!
1、RDB(Redis DataBase)
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失,且备份时需要消耗内存。
RDB快照
1 | # 对于RDB来说,提供了三种机制:save、bgsave、自动触发。 |
2、AOF(Append Only File)
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
1 | #若需要使用aof,需要在配置信息里开启 |
3、总结
1、RDB 持久化方式能够在指定的时间间隔内对数据进行快照存储
2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis 协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
3、只做缓存,可以不使用任何持久化
4、同时开启两种持久化方式时
- 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
- RDB 的数据不实时,同时使用两者时服务器重启也只会找AOF文件,建议不要只使用AOF,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的Bug,留着作为一个万一的手段。
5、性能建议
- 因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。
- 如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
- 如果不Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。
八、Redis发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。 Redis 客户端可以订阅任意数量的频道。
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 …]] | 退订给定的频道 |
测试
1 | #开启一个客户端,订阅一个频道 |
原理
-
Redis是使用C实现的,通过分析 Redis 源码里的 pubsub.c 文件,了解发布和订阅机制的底层实现,籍此加深对 Redis 的理解
-
Redis 通过 PUBLISH 、SUBSCRIBE 和 PSUBSCRIBE 等命令实现发布和订阅功能
-
通过 SUBSCRIBE 命令订阅某频道后,redis-server 里维护了一个字典,字典的键就是一个个 channel ,而字典的值则是一个链表,链表中保存了所有订阅这个 channel 的客户端。SUBSCRIBE 命令的关键,就是将客户端添加到给定 channel 的订阅链表中
-
通过 PUBLISH 命令向订阅者发送消息,redis-server 会使用给定的频道作为键,在它所维护的 channel 字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者
-
Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息
使用场景
- Pub/Sub构建实时消息系统
- Pub/Sub构建的实时聊天系统
九、Redis主从、哨兵和集群
这里实验都在一台机器上,故只修改端口,正式操作时应该分布在不同的机器中
1、主从复制
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。 Master以写为主,Slave 以读为主。默认每台Redis服务器都是主节点,单台Redis内存不应超过20G。
对于读多写少的电商
主从复制作用
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
- 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
环境配置
1 | #查看信息 |
配置主从复制,至少一主二从
1 | #生成三份配置文件,myredis.conf这里我已经存在了 |
命令行配置(效果暂时,一般是配置文件配置)
1 | #仅在从机进行配置即可,我的两个从机端口为6370和6371 |
配置文件配置
1 | # 进入REPLICATION部分,修改从机配置文件 |
测试细节
- 主机能读写,从机只能读,且从机会自动复制主机内容
- 主机宕机,从机只能进行读操作
- 若命令行操作,从机宕机,重新启动后变为主机,重新设置变为从机后可获取主机最新信息
复制原理
Slave 启动成功连接到 master 后会发送一个sync命令,Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
- 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
- 增量复制:Master 继续将新的所有收集到的修改命令依次传给slave,完成同步
2、哨兵模式
哨兵模式能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
一般哨兵模式要开启6个进程,假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为==主观下线==。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为==客观下线==。
测试配置
1 | # 一主二从配置不变,加入哨兵进程 |
依次修改3份哨兵配置文件,保证端口、pid文件和日志文件不重名,日志文件在/tmp目录下
1 | port 26381 |
1 | #在当前目录下依次启动,即完成哨兵模式 |
配置文件详解
1 | # 哨兵sentinel实例运行的端口 默认26379 |
3、Redis集群
Redis集群由多个节点(Node)组成,Redis 的数据分布在这些节点中。集群中的节点分为主节点和从节点,只有主节点负责读写请求和集群信息的维护,从节点只进行主节点数据和状态信息的复制。Redis集群采用哈希分区的方式对数据进行分区,哈希分区就是对数据的特征值进行哈希,然后根据哈希值决定数据放在哪个节点。其中redis cluster集群是去中心化的,每个节点都是平等的,连接哪个节点都可以获取和设置数据。
Redis集群的作用有下面几点:
- 数据分区:突破单机的存储限制,将数据分散到多个不同的节点存储;
- 负载均衡:每个主节点都可以处理读写请求,提高了并发能力;
- 高可用:集群有着和哨兵模式类似的故障转移能力,提升集群的稳定性;
普通端口:即客户端访问端口,如默认的6379;
集群端口:普通端口号加10000,如6379的集群端口为16379,用于集群节点之间的通讯
配置
分配6个配置文件
ID | IP | Host | 类型 | 从节点 |
---|---|---|---|---|
A | 127.0.0.1 | 6381 | 主 | AA |
B | 127.0.0.1 | 6382 | 主 | BB |
C | 127.0.0.1 | 6383 | 主 | CC |
AA | 127.0.0.1 | 6391 | 从 | / |
BB | 127.0.0.1 | 6392 | 从 | / |
CC | 127.0.0.1 | 6393 | 从 | / |
1 | #分别修改6个目录中的redis.conf文件,主要开启集群以及修改端口和文件路径 |
十、Redis缓存
1、缓存穿透
缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。在日常工作中出于容错的考虑,如果从持久层查不到数据则不写入缓存层,缓存穿透将导致不存在的数据每次请求都要到持久层去查询,失去了缓存保护后端持久的意义
2、缓存击穿
系统中存在以下两个问题时需要引起注意:当前key是一个热点key(例如一个秒杀活动),并发量非常大;重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的SQL、多次IO、多个依赖等。在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。
3、缓存雪崩
由于缓存层承载着大量请求,有效地保护了存储层,但是如果缓存层由于某些原因不可用(宕机)或者大量缓存由于超时时间相同在同一时间段失效(大批key失效/热点数据失效),大量请求直接到达存储层,存储层压力过大导致系统雪崩。
参考文章:
https://blog.csdn.net/wsdc0521/article/details/106316972
https://blog.csdn.net/weixin_43445935/article/details/115393205
https://www.bilibili.com/video/BV1S54y1R7SB?p=12&spm_id_from=pageDriver