一、什么是Redis?
redis是一款非关系型数据库(NoSql,not only sql - 不仅仅是sql),数据存放结构是按照key-value类型存放的,而且数据是保存在内存中的,相对于传统的关系型数据库(数据放在硬盘上),数据的读写速度非常的快(10W/s),另外相对于其他的 数据库,Redis也提供了持久化的功能,但是相对于传统的关系型数据库,数据安全性没有那么可靠,而且数据存放的大小也比不上关系型数据库,因此在实际开发过程中,项目既会用到关系型数据库也会用到非关系型数据。
二、Redis的运用场景
1)作为缓存服务器(常见)
2)处理高速读写时的数据一致性问题
3)进行数据共享
4)分布式锁
5)分布式序号递增
三、Redis的安装
基本的安装过程
解压:tar -zxf redis-3.2.4.tar.gz
进入解压包:cd redis-3.2.4
编译安装:
make
make install
启动redis
cd src
./redis-server
注意:当前会默认按照前台进程的方式启动redis
设置redis的后台进程的启动方式
拷贝配置文件:cp ../redis.conf ./
修改配置文件:vim redis.conf
启动redis:./redis-server redis.conf
设置redis的远程连接模式
开放6379的端口
修改配置文件:vim redis.conf
四、Java如何操作Redis
1)添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.0.1</version>
</dependency>
2)编写代码
五、Spring如何操作Redis
1)添加依赖
2)添加Spring的配置文件
3)使用Spring
注意:Spring提供了一个模板对象,该模板对象默认会对设置的值进行序列化和反序列化
六、SpringBoot如何操作Redis
1)添加依赖
2)配置application.yml
3)使用redis
七、Redis的基本数据结构
1)字符串
key - value
2)哈希
key - {field: value, field: value....}
3)链表
key - value1,value2,value3....
底层数据结构:双向链表
4)集合
key - [value3,value5,value1,value2....]
底层数据结构:哈希表
5)有序集合
key - [(score,value1), (score,value2), (score,value3).....]
底层数据结构:跳跃表
6)基数
redis中文网: https://www.redis.net.cn/
注意:redisTemplate根据不同的数据结构,将相关的操作方法封装到不同的对象当中。但是有些命令是通用的,就直接通过redisTemplate调用就可以了。比如删除一个key
八、redis的发布和订阅 - (RedisMQ)
RedisMQ和传统的MQ组件有什么区别?
RedisMQ:轻量级、消息实时性较好,但是消息可靠性较低,而且消息不能持久化,所以redis适合那种消息实时性要求较高但是可靠性要求较低的场景
传统的MQ:专业级、消息实时性相对较低,但是通常都有消息的可靠性保证机制,而且消息也能够持久化,并且消息的存储量级可以非常大。
九、超时命令
什么是超时命令?
超时命令就是用来设置一个key的过期时间
为什么需要超时时间?
redis的数据是存放在内存中的,通常数据库中的数据不可能完全的同步到redis中,提升系统性能。所以在实际开发过程中,通常需要redis中保留更多的热点数据,但是如何保证redis中尽可能的存储热点数据?就需要通过key的超时命令实现。对所有的key都设置一个超时命令,如果是热门数据,这个key过期后,自然会被立刻重建回缓存,但是冷门数据就会自动的删除掉,节省redis的内存空间。
注意:在很多公司都有一定的开发规范,通常都要求设置缓存key的时候,跟上超时时间
超时的相关命令:
ttl:查看当前key的超时时间
-1 - 表示永生(没有超时时间)
-2 - 表示当前key已经超时或者不存在
正整数 - 剩余的存活时间
expire:设置超时时间
persist:移除key的超时时间
SpringBoot如何设置过期时间:
面试题:redis中一个key过期之后,是否立刻从内存中移除?
no - 不是,因为如果要做到实时移除,会对redis的性能损耗巨大。
redis在3种情况下,会移除过期的key-value:
1)当用用户尝试获取一个过期的key时,会立刻从内存中移除这个key
2)redis自带一个定时器,定期扫描部分内存,移除其中过期的key
3)当redis的内存已经满了后,根据内存的淘汰策略可能淘汰掉某些过期的key(有可能永生的key也会被移除)
十、Lua脚本
1)redis的线程模型(单线程模型/多线程模型):
多线程模型(tomcat):
单线程模型(redis):
为什么redis会采用单线程模型?(单线程模型和多线程模型哪种更好?)
多线程模型通常用于处理请求较慢的服务,比如tomcat。如果tomcat设计成单线程模型的问题,在于多个用户同时发送请求时,有些用户的请求可能会涉及数据库的访问、网络的访问等等比较耗时的操作,那么tomcat的单线程就会阻塞到这些耗时请求之上,这些请求没有返回结果时,tomcat服务的cpu就会处于空闲的状态,一直到这些请求返回结果之后,tomcat才能继续处理其他的请求。因此单线程模型不适合tomcat服务,会极大的浪费cpu的资源。
单线程模型通常适合处理请求极快的服务,比如redis。因为redis不会有耗时的操作(比如访问数据库,访问网络的操作),而且redis是直接操作内存的,所以每个请求的操作速度都会非常的快。如果采用多线程模型,反而还需要进行线程间的切换,带来额外的服务器性能损耗。
既然redis是单线程模型,能不能认为redis的所有操作都是线程安全的?
NO,虽然redis是单线程模型,但是不能表示redis是线程安全的。因为用户的多个命令之间有可能会夹杂着其他用户的命令,从而导致最后的数据结果不一致。但是redis提供了一种命令原子性的特性 - Lua脚本。redis中执行lua脚本一定是原子操作,要么都不执行,要么都执行,在执行lua脚本的过程中是不会再去直接其他客户端命令的。
2)lua脚本的语法:
.....
3)redis如何操作lua脚本:
语法:eval "lua脚本" number [key1 key2....] [arg1 arg2.....]
number:表示lua脚本中,key的数量,如果没有key,则设置为0
[key1 key2....]:表示对应lua脚本中key的变量值
[arg1 arg2.....]:表示对应lua脚本中非key的变量值
案例:
eval "return 'Hello'" 0
eval "redis.call('set', 'name', 'xiaoming')" 0
eval "return redis.call('get', 'name')" 0
eval "redis.call('set', KEYS[1], ARGV[1])" 1 age 18 - 带变量
lua脚本的缓存:
缓存脚本:script load "return redis.call('get', KEYS[1])"
返回值:"4e6d8fc8bb01276962cce5371fa795a7763657ae"
执行缓存的脚本:evalsha "4e6d8fc8bb01276962cce5371fa795a7763657ae" 1 name
4)SpringBoot如何操作lua脚本
十一、Redis作为缓存服务器的使用+redis的分布式锁
十二、Spring提供的缓存注解
1)添加相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2)配置启动类
3)使用注解实现数据缓存
@Cacheable:当前执行一个标记了该注解的方法,会先去缓存服务器(redis)中获取缓存的数据,如果没有找到,则调用目标方法,如果找到了相关数据,则不会再调用目标方法。如果调用了目标方法,会自动的将方法的返回值重建到缓存服务器中。
@CacheEvict:调用当前方法后,直接删除指定缓存
@CachePut:该注解会将目标方法的返回值添加进缓存中,通常用户添加方法
@Caching:支持在一个方法上添加多个重复注解
4)配置application.yml设置超时时间
注意:spring提供的cache缓存,如果检测到redis的相关依赖,会自动的将缓存数据放入redis中
十三、redis的持久化
redis持久化的方式:
1)快照 - rdb(默认行为)
快照是通过记录当前内存一瞬间的数据结构的方式
相关命令:
save - redis就会执行一次快照操作,这个命令会在当前线程进行快照,在快照时,redis就不能写入数据
bgsave - redis会执行一次后台快照,前台仍然会接收写的命令
相关配置:
save 900 1
save 300 10
save 60 10000
- 配置redis的快照频率,900s之内有1个数据发生变化就快照一次,一次类推
stop-writes-on-bgsave-error yes
- 配置bgsave如果出错前台是否停止写入,默认yes
dbfilename dump.rdb
- 配置快照的文件名
dir ./
- 配置快照的存放路径
2)只追加文件 - aof
只追加文件会记录所有的写命令到一个文件中,当redis重启之后,重新直接记录的所有写命令,从而恢复数据
相关配置:
appendonly no
- 是否开启只追加模式,默认关闭
appendfilename "appendonly.aof"
- 配置只追加数据的文件名称
# appendfsync always
appendfsync everysec
# appendfsync no
- 配置只追加文件的频率
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
- 配置只追加文件什么时候重新创建一个新的文件记录命令
什么时候选择快照,什么时候选择只追加文件?
快照特点:恢复速度很快,但是数据相对来说安全性不高
aof特点:文件可能很大,并且恢复缓慢,但是数据相对比较安全,aof最多只会丢失1秒之内的数据
如果redis只作为缓存服务器使用,那么快照和aof都可以关闭。如果希望数据绝对安全,最好把两个持久化方式都开启,redis会按按照aof恢复数据,再按照快照恢复数据。通常来说,只要开启快照就足够了。
如何对aof文件进行优化?
优化aof的命令:bgrewriteaof
注意:redis2.4之后,服务会定期自动的执行该命令优化aof文件
十四、redis内存回收策略
什么是内存回收策略?
内存回收策略是指,如果redis内存已经满了,会按照哪种方式对待新的写入
相关配置:
maxmemory-policy noeviction
- 配置内存淘汰策略
maxmemory-samples 5
- 指定淘汰时抽取的样本数量
可选值:
volatile-lru(推荐) -> 从所有设置了超时时间的key中,找到最近最少被使用的key淘汰
allkeys-lru -> 从所有的key中,找到最近最少被使用的key淘汰
volatile-random -> 从所有设置了超时时间的key中,随机找到key淘汰
allkeys-random -> 从所有的key中,随机找到key淘汰
volatile-ttl(推荐) -> 从所有设置了超时时间的key中,找到存活时间最短的key淘汰
noeviction(默认值) -> 如果内存已经满了,redis变成只读模式
volatile - 会从所有设置了超时时间的key中淘汰(有可能这个key没有超时)
allkeys - 会从所有key中淘汰(永生的key也有可能被淘汰)
lru - 最近最少被使用的策略
random - 随机
ttl - 存活时间最短
注意:在redis中,ttl和lru都是近似算法,不是绝对算法
十五、redis的集群模式 - 读写分离(了解)
有状态服务:每个服务本身有自己独立的数据空间,比如redis、mysql、zookeeper.....
无状态服务:每个服务本身没有独立的数据空间,比如tomcat
无状态服务可以通过单纯的横向拓展就能实现集群的效果
有状态服务不能单纯的横向拓展实现集群,不同的服务有不同的实现方式
读写分离:
哨兵模式:
搭建主从复制:
1) 准备3个虚拟机:
192.168.227.142 - master
192.168.227.143 - slave1
192.168.227.144 - slave2
2)分别启动redis
3)配置slave
重启slave
搭建哨兵模式:
1)从redis的路径下,将哨兵的配置文件拷贝到src中
2)配置sentinal.conf配置文件
3)开放26379的端口(哨兵的端口,因为哨兵之间会两两监控,所以需要开放端口)
4)依次启动哨兵
./redis-sentinel sentinel.conf
springboot中如何配置哨兵的读写分离:
分片模式: