目录
1.Linux环境下安装redis
2.redis的数据结构及命令
3.redis.conf配置文件常用配置
3.redis的事务操作
4.redis实现乐观锁
5.通过jedis操作redis
6.Springboot集成redis
7.自定义一个RedisTemplate
8.持久化策略 RDB和AOF
9.redis集群环境搭建
10.哨兵模式
11.缓存击穿,缓存穿透,缓存雪崩
1.Linux环境下安装redis
1. redis是由c语言写的,首先安装环境
yum install -y gcc-c++
2. 官网下载tar压缩包
官网下载
3. 解压
tar -zxvf redis-6.2.7.tar.gz
4. 进入解压目录,进行编译和安装
make 编译
make install 安装
5. 启动redis服务器
redis-server redis.conf
6. 启动redis客户端
redis-cli -h 127.0.0.1 -p 6379 //举个例子
7. 设置密码
config set requirepass "123456" 可以在配置文件中设置,也可以通过这个命令设置
config get requirepass 获取密码
8. 设置密码后登录
redis-cli -h 127.0.0.1 -p 6379 //举个例子
auth 123456
2.redis的数据结构及命令
(1) String类型
1. 设置一个键值对
set name zhangsan
2. 获取键值对
get name
3. 设置一个10s后过期的键值对
set name zhangsan ex 10 ex单位秒
setex name 10 zhangsan
set name zhangsan xx 10000 xx单位毫秒
4. 获取键值对的剩余存活时间
ttl name
5. 当key不存在时,才可以设置,可充当分布式锁
set name zhangsan nx
setnx name zhangsan
set lock thread ex 10 nx; // 设置分布式锁并设置过期时间
6.当存在key时,才可以设置,相当于更新操作,xx标识
set key value xx
7. 一次存储或获取多个key,value,减少网络通信次数
mset key1 value1 key2 value2 key3 value3
mget key1 key2 key3
8. 是否存在某个key
exists name
9. 在value指定位置上替换或拼接字符串
setrange name 3 lisi
10. 获取value指定范围的字符串
getrange name 0 -1
11. 若key值存在,则返回原value值并更新为lisi,若不存在,则返回nil
getset name lisi
加减运算命令
1. 针对value为Integer类型
incr key 加一操作
decr key 减一操作
incrby key 10 加10
incrby key -10 减10 或者使用decrby key 10
2. 针对小数
incrbyfloat key 1.1 加1.1
incrbyfloat key -1.1 减1.1
String适用场景
- 可以缓存用户会话信息,session等,将用户id作为键,将用户信息序列化为字符串作为值。
- 可以用来分布式锁setnx。将锁的标识称作为键,请求id作为值,并设置过期时间。
- 计数器,使用加减运算命令,如浏览量,点赞数等。
(2) Hash类型
相当于一种key key value.
1. 设置一个hash类型
hset user id 1
2. 获取hash值
hget user id
3. 是否存在KK
hexists user id
4. 删除一个或多个KK
hdel user id user name...
5. 查找K对应的所有K(filed)
hkeys user
6. 查找KK对应的所有value
hvals user
7. 查找K对应的所有key-value键值对
hgetall user
8. 一次设置或查找多个Hash类型的键值对
hmset user id 1 username lisi password 111
hmget user id username password
9. 获取指定哈希表中键值对的个数
hlen user
10. 当这个filed对应的id不存在才能插入
hsetnx user id
11. 获取value的长度
hstrlen user id
12. 对hash表中的value进行加减运算
hincrby 针对Integer类型
hincrby user id 1 id值加一
hincrbyfloat 针对小数类型
incrbyfloat user money 1.1
Hash哈希表适用场景
- 可以存储用户的个人信息属性等,将用户id作为键(key),用户的各个属性作为键(field),属性对应的值作为field的值。这种存储有利于快速获取,更新用户的属性信息。
(3) List类型
List底层数据结构是链表,redis中的l列表提供了头插(lpush),头删(lpop),尾插(rpush),尾删(rpop)。更像是一个双端队列。使用下标进行操作,元素可以重复,有顺序。
1. 头插尾插
lpush key value1 value2 ...
2. 头删尾删
lpop key
3. 按范围查找列表id的所有value ,-1 表示最后一个元素的位置
lrange id 0 -1
4. 查找下标为0的value
lindex key 0
5. 在元素2的前面或后面插入一个元素10 ,如果指定的元素在list中是有多个的。则linsert会从左到右找到第一个元素进行插入
linsert id before/after 2 10
lpushx,rpushx 当指定List数组存在时,才能插入
lpushx key value
6. 删除元素1,并且删除个数为5个。删除指定元素并且指定删除元素的个数 ,List是可以重复的。并且5的正负代表删除的方向,正数是从左到右找并删除,负数是从右到左找并删除
lrem id 5 1
7. 截取并更新原value,更新的值就是截取后的值,闭区间
ltrim id start end
8. 将下标为2的元素更新为10 下标越界会报错
lset id 2 10
9. 获取指定列表中元素的个数
llen id
10. blpop brpop 带有阻塞性质的出队操作
- 在列表有元素的情况下,和lpop,rpop表现是一样的。但如果列表中没有元素,非阻塞版本会立即返回nil,阻塞版本会等待阻塞一段时间timeout,若list中还是没有添加新的元素,才会返回nil。若在等待时间内有了新元素,则会立即返回,解除等待。并且阻塞期间也是可以执行其他命令。当 list 处于阻塞时,这时有多个客户端访问命令,后面list添加新元素了,会先来先到。并且这个超时等待时间是可以代码手动设置的,如下,获取过期时间为60s
blpop id 60
即pop之前没有元素,会阻塞等待60s,60s内有元素进来,则弹出去,没有就返回空。
List列表适用场景
- List提供了带有阻塞功能的出队(lpop,rpop)命令,可以用来做消息队列,以及最新消息展示等
(4) Set类型
Set集合中的元素具有唯一性,无序性
1. 向id集合中插入多个元素 元素若重复,只插入一个
sadd id 1 2 3 4 5...
2. 查看id集合中所有的元素
smembers id
3. 查看id集合中元素1是否存在
sismember id 1
4. 删除count个元素的个数
spop [count]
5. 随机从集合中返回一个或多个元素
srandmember key [count]
6. 将一个元素member从集合key1中移动到key2中,具体过程是key1中删除这个元素,key2中添加这个元素
smove key1 key2 member
7. 从集合中一次性删除一个或多个元素
srem key member1,member2...
8. 得到指定集合中元素的个数
scard key
Set集合适用场景
- 因为集合中提供了交并补,可以做一些个性化标签,共同好友,好友推荐等
(5) Zset类型
有序集合,引入了score属性,就是按照这个分数进行排序(升序),元素有序,不重复
zadd key [NX|XX] [CH] [INCR] score member [score member ...] 可以一次性插入多个
- XX:更新操作,该元素之前存在才操作
- NX:插入操作,即该元素之前不存在才操作
- INCR: 修改元素对应的分数做加法/减法运算
1. 设置元素
zadd score 10 zhangsan 20 lisi 50 wangwu 70 liujiang 80 wangchegn
2. 查找范围内的元素并升序输出,加上withscores还会返回对应的分
zrange key 0 -1 [withscores]
3. 同上,逆序输出,即降序
zrevrange key 0 -1 [withscores]
4. 获取集合中所有元素的个数
zcard key
5. 通过一个分数区间,返回在区间内的member个数,闭区间,若想要开区间,加左括号
zcount key (minscore (maxscore
zcount key minscore maxscore
6. 同上,也是指定一个分数区间,但返回值不同,返回的是集合的元素,而非个数,withscores返回分数
zrangebyscore key minscore maxscore [withscores]
zrangebyscore key -inf +inf [withscores]
7. 删除并返回分数最高/最低的count个元素
zpopmax/zpopmin key count
8. 带有阻塞性质的删除元素,超时等待,可以设置超时时间
bzpopmax/bzpopmin key timeout
9. 获取到指定元素的排名(下标)
zrank key member
10. 也是获取到指定元素的下标
zrevrank key member
11. 根据指定的member查找并返回score分数
zscore key member
12. 删除有序集合中的一个或多个元素
rem key member1 member2..
13. 指定一个区间(下标),对这个闭区间内的所有元素进行删除
zremrangebyrank start end
14. 同上 指定一个区间(分数score),对这个闭区间内的所有元素进行删除
zremrangebyscore key 0 -1
15. 给指定的元素member对应的score加一个数字,即修改分数
zincrby key 10 member 分数加10
有序集合zset适用场景
- 有序集合可以用来做排行榜,计分系统等
(6) 全局命令
1. 删除所有数据库的所有键值对
flushall
2. 删除当前数据库的所有数据
flushdb
3. 获取所有的key
keys *
4. 删除一个或多个key
del key1 key2...
5. 是否存在key
exists key
6. key的存活时间 单位s -1表示不会过期 -2 表示已经过期
ttl key
7. key的存活时间 单位ms -1表示不会过期 -2 表示已经过期
pttl key
8. 设置key的存活时间 单位s
expire key 60
9. 设置key的存活时间 单位ms
pexpire key 1000
10. 查看key对应的value的类型 如none(查找不存在的key),string,list,set,zset(压缩set),hash,stream(消息队列)等
type key
11. 查看key对应value的详细数据类型
object encoding key
(7) 集合中的交并补
1. 对多个集合求交集 返回交集元素
sinter key1 key2 key3 ...
2. 和上面一样也是求交集,不同的是此操作会将交集元素存放在destination这个指定的集合中,返回的是交集元素的个数
sinterstore destination key1 key2..
3. 对多个集合求并集 返回并集元素
sunion key1 key2...
4. 求并集,此操作会将并集元素存放在destination这个指定的集合中,返回的是交集元素的个数
sunionstore destination key1 key2..
5. 对多个集合求差集 返回并集元素
sdiff key1 key2...
6. 求差集,此操作会将差集元素存放在destination这个指定的集合中,返回的是交集元素的个数
sdiffstore destination key1 key2..
3.redis.conf配置文件常用配置
1. Redis默认不是以守护进程的方式运行,修改为yes启用守护进程
daemonize no
2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过以下配置项修改
pidfile /var/run/redis.pid
3. 指定Redis端口号,默认端口为6379
port 6379
4. 绑定的主机地址
bind 127.0.0.1
5.当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300
6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
loglevel verbose
7. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout 标准输出
logfile /var/log/redis/redis-server.log 自定义
8. 设置Redis数据库的数量,默认有16个,默认使用0号数据库,可以使用SELECT 命令在连接上指定数据库id
databases 16
9. Redis持久化策略-RDB,持久化规则设置
save 900 1 900秒(15分钟)内有1个更改操作就将数据持久化到RDB文件中
save 300 10 表示300秒(5分钟)内有10个更改操作就将数据持久化到RDB文件中
save 60 10000 表示60秒内有10000个更改操作就将数据持久化到RDB文件中
10. 指定存储至本地数据库时是否压缩数据,默认为yes
rdbcompression yes
11. 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
12. 指定本地数据库存放目录
dir ./
13. redis集群,若该机为从节点,就设置下列配置项。可以在配置文件中修改,也可以使用命令指定。可实现从主节点的数据同步
slaveof ip port
slaveof no one 该命令可将从节点设置回主节点
14. redis集群,当主节点设置了密码保护时,从节点连接主节点的密码
masterauth masterpassword
15. 设置Redis连接密码,可以在配置文件里修改,也可以通过命令修改。设置密码后,登录是需要auth+密码登录
config set requirepass "123456" 设置密码命令
config get requirepass 获取密码
登录(两步)
redis-cli -p 127.0.0.1 -h 6379
auth 123456
16. 设置同一时间客户端最大连接数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 1000
17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作
maxmemory bytes
18. 持久化机制-AOF,默认为no,不开启。设置为yes开启,开启后会将每次的写操作命令保存到AOF文件中
appendonly no
19. 指定AOF文件名,默认为appendonly.aof
appendfilename appendonly.aof
20. AOF将命令数据保存到文件中的时机(规则)。共有3个可选值
appendfsync no 交给操作系统进行同步,每次同步时间间隔不一样
appendfsync always 存入aof缓冲区并立即同步到aof文件中
appendfsync everysec 每隔一秒保存一次 默认值
21. aof文件重写的时机(规则) ,重写的目的也是为了避免文件太大
auto-aof-rewrite-min-size 64mb 当文件最小达到64mb时重写
auto-aof-rewrite-percentage 100 当文件与上一个重写文件比值达到100时重写
3.redis的事务操作
在redis中,单条命令是原子性的,但是事务是不保证原子性的,并且也没有隔离级别这个概念。
redis事务操作步骤
- 开启事务 multi
- 命令入队
- 执行事务 exec
- 放弃事务 discard
事务执行过程中可能会出现以下几个问题
- 多条命令入队时,有一条命令本身是错误的,就相当于是Java中的编译时异常。出现这种情况该事物中的所有命令都不会执行。
- 也是多条命令入队,命令本身没有问题,但在执行过程中会发生错误,比如对一个value为字符串的进行了加减运算。相当于运行时异常。出现这种情况时运行出现错误的命令不执行,其他运行正确的命令正常执行。
代码演示
4.redis实现乐观锁
乐观锁就是认为在任何情况下都保持乐观,都不会加锁。我们可以使用redis提供的 watch 命令对某个key进行监视,其原理和CAS一样,是通过比较所监视的值是否发生了变化,来判断是否应该执行操作,并且它也引入了version版本来避免ABA问题。unwatch命令放弃监视。
代码演示
开启两个客户端:一个在监视k1开启事务后,另一个客户端进行修改,最后当事务执行后,执行失败。原因就是在监视的过程中值被修改了。
5.通过jedis操作redis
- 1.从Maven仓库中导入依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
- 2.创建jedis对象
Jedis jedis = new Jedis("127.0.0.1",6379); // 创建对象
jedis.auth("123456"); //登录密码 没有密码不写
jedis.set("111","111");
System.out.println(jedis.keys("*"));
6.Springboot集成redis
springboot已经整合了不同的Redis客户端,如lettuce,jedis,即SpringDataRedis。它提供了RedisTemple工具类,里面封装 了各种对Redis的操作。
- 1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 2.配置文件中配置redis
// 在yml配置信息中设置
spring:
redis:
host: 127.0.0.1
port: 6379
password: 123456
database: 0 #默认是0号数据库
lettuce:
pool:
max-active: 8 #最大连接
max-idle: 8 #最大空闲连接
min-idle: 0 #最小空闲连接
max-wait: 1000ms #最大等待时间
- 3.注入RedisTemple
@Autowired
private RedisTemplate redisTemple;
7.自定义一个RedisTemplate
通过源码观察,RedisTemplate的序列化器是jdk序列化,会将键值对转为字节形式存储,可读性差,并且内存占用也大。当value值为对象时,该实体类还必须实现Serializable接口,否则报错。由于传入的值都为Object类型,所以还需要类型强转。
除了jdk序列化,观察源码发现还有其他类型的序列化,如json序列化等等。
那如果我们想使用其他的序列化方式,可以自己定义一个RedisTemplate。将所有的key的序列化方式变为String序列化,将所有的value的序列化方式变为json序列化。因为一般情况下,key值都是字符串型的,不需要Object型,还需要强转。并且jdk序列化会使可读性变差,还占用空间大。
@Configuration
public class RedisConf {
@Bean
public RedisTemplate<String, Object> redisTemplates(RedisConnectionFactory redisConnectionFactory) {
// key设置为String型
RedisTemplate<String, Object> template = new RedisTemplate();
// key值使用String序列化
template.setKeySerializer(new StringRedisSerializer());
// value值使用json序列化
template.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
// hashkey使用String序列化
template.setHashKeySerializer(new StringRedisSerializer());
// hashvalue使用json序列化
template.setHashValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
使用:
@Autowired
@Qualifier("redisTemplates") //要跟你自定义的方法名相同
private RedisTemplate redisTemplate;
8.持久化策略 RDB和AOF
redis是内存数据库,要是redis服务器宕机或者是断电,内存中的数据都会丢失。所以进行持久化是非常有必要的。redis中有两种持久化策略。一种是RDB,通过内存快照,将内存中的数据保存到rdb文件中,并且在下次redis服务器启动时,加载这些数据到内存中,并且在保存时是fork了一个子进程去执行写操作,不会影响性能。并且rdb文件可以进行压缩,占用空间相对来说小。另一种就是AOF,其则是将每一次的写操作命令存入AOF文件中,并且在下一次redis服务器启动时重新执行一遍这些命令。文件占荣空间相对RDB来说小。在默认情况下,在redis.conf配置文件中,AOF是关闭的。
RDB优点
- 手动bgsave或者是自动触发保存时会fork一个子进程去执行写操作,不会阻塞,不会影响性能。
- RDB文件可以压缩,占用空间相对来说较少。
- RDB是一个二进制压缩文件,所依数据恢复速度也是比较快的。
RDB缺点
- 持久化实际是是有时间间隔的,如果在间隔内服务器发生问题,就会导致最后一次的数据丢失。
RDB文件保存的触发机制
- 配置文件中的save规则满足时触发
- 手动save后者是bgsave是触发
- 退出redis(shutdown命令)时触发
- 执行flushall命令时触发
AOF优点
- 实时性,每次执行一个写操作命令,都会写入AOF缓存区中,然后会根据不同的策略同步到AOF文件。一种是存入AOF缓冲区并立即同步到AOF文件中,一种是每隔几秒再同步到AOF文件中,最后一种是同步交给操作系统,每次同步的时间间隔不一样。并且会进行文件重写,以达到压缩文件的目的。默认重写的时机是文件最小达到64mb,或者当前文件与上一个重写的aof文件比值达到100时重写文件。
AOF缺点
- 其恢复数据是在服务器启动时依次执行文件中的命令,比较耗时,耗性能,数据恢复速度慢。
- 文件占用空间相对较大。
9.redis集群环境搭建
Redis主从复制,读写分离。主节点负责写,从节点负责读。主节点写完后同步给从节点。同步操作是单向的,只能是由主节点向从节点同步。默认情况下,每个redis服务器都是主节点。可通过命令或者是在配置文件中修改为从节点。主节点不做任何操作。成为从节点后,只能进行读操作,不能进行写操作。redis集群一般至少有三个redis服务器,一主两从。
- 查看当前服务器的节点信息
info replication
- 配置从节点,在从节点服务器中执行该命令(以127.0.0.1为例)
slaveof 127.0.0.1 6379
- 关闭服务器
shutdown
- 将从节点设置回主节点
slaveof no one
10.哨兵模式
哨兵是一个独立的进程,独立运行。监视主节点的原理是向主节点发送请求信息,比如ping命令,通过主节点是否响应来判断主节点有没有宕机。一般情况下哨兵也要至少有三个,当多个哨兵都认为主节点宕机后,才会去更换主节点。更换主节点是通过投票(算法)实现的,剩下的从节点中谁的票数高,谁就成为主节点。主节点设置成功后,会通过发布订阅模式,来告诉所有哨兵该更换自己监控的主节点了。
- 1.在哨兵配置文件中配置
// 1表示主节点挂了,从节点投票选主节点
sentinel monitor mymaster 127.0.0.1 6379 1
- 2.启动哨兵模式
// 启动配置文件
redis-sentinel sentinel.conf
11.缓存击穿,缓存穿透,缓存雪崩
1.缓存穿透
即查询一个不存在的数据,在缓存和数据库中都不存在,有可能是恶意伪造。这样每次请求都会打到数据库,严重的话会使数据库宕机。
解决办法
- 缓存空数据
- 布隆过滤器
2.缓存击穿
指的是有一些热点key到达了过期时间,由于这些数据的请求量非常大,再加上如果这些数据量比较大的话,那缓存重建的时间就会较长,在这个缓存重建的时间段内,大量的请求都会到达数据库,会给数据库带来巨大压力。
解决办法
- 加互斥锁,setnx。当数据过期后,只有获得锁的线程进行缓存重建,其他未获得锁的只能阻塞等待,直到缓存重建完成。缺点是用户体验太差,要等很久。但保证了数据的实时性。
- 逻辑过期。并没有去真正设置过期时间,而是在数据中多加了一个过期时间属性,当某个线程查询到这个数据过期后,首先会获取互斥锁,获取成功后会开启一个新的线程来执行缓存重建操作,原来的线程也不会等待缓存重建完成,而是直接返回旧数据,其他未获取到锁的线程也是直接返回旧数据。缺点是数据不是最新的,优点是不用等待。
3.缓存雪崩
是指大量的热点数据同时失效或者是Redis服务器意外宕机,导致大量请求同时到达数据库,给数据库带来巨大压力。
解决办法
- 针对同时失效,可以设置不同的过期时间TTL,使用随机数。
- 针对Redis服务器宕机,可以进行Redis集群,部署多个Redis服务器,主从同步,读写分离,主节点负责写数据,从节点负责读数据。主从点写完后同步给从节点,并且为防止主节点发生宕机情况,可以使用哨兵模式进行监控,及时发现,及时更换主节点。