1.Redis简介
Redis 是完全开源免费的,遵守BSD协议,是性能极高的nosql数据库,Key-Value 数据库,并提供多种语言的 API的非关系型数据库。
- Redis读的速度能达到110000次/s,写的速度能达到81000次/s 。
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- 丰富的数据类型:有五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
- Redis的所有操作是原子性的,意思就是要么成功执行要么失败完全不执行。
- 主从复制,在高并发的情景下可以通过多台服务器提供缓存服务
2.Redis应用场景
- 分布式锁、计数器、分布式全局ID
- 消息队列、抢购
- 抽奖、签到、点赞、关注模型(可能认识的人)、电商商品的筛选
- 排行榜、好友列表
- 分布式Session,接口限流
3.常用基本配置
命令 | 示例 | 说明 |
---|---|---|
daemonize | daemonize yes | 后台启动 |
port | port 6379 | 端口 |
logfile | logfile redislog.log | 日志文件名称 |
requirepass | requirepass 密码 | 设置密码 |
dir | dir ./ | 文件存放路径 |
4.Redis通用命令
命令 | 示例 | 说明 |
---|---|---|
select | select 0 | 选择数据库 |
keys | keys user* | 查询符合条件表达式的key |
dbsize | dbsize | 返回key的总数 |
exists | exists user | 检查key=user是否存在 |
del | del user | 删除key=user的数据 |
expire | expire user 60 | 设置key=user 60秒后过期 |
ttl | ttl user | 查看key=user的过期剩余时间 |
flushdb | flushdb | 清空数据 |
5.Redis数据类型
-
String 字符串类型
所有的value都是字符串,支持存储数字,最大存储512MB,建议单个kv不超过100KB
命令 | 示例 | 说明 |
---|---|---|
get | get user | 根据key取值 |
set | set user hello | 设置key value值 |
mset | mset user hello age 20 | 设置多个 |
mget | mget user age | 获取多个 |
del | del user | 删除指定key |
incr/decr | incr count /decr count | 递增(减)1 |
incrby/decrby | Incrby count 10 | 递增(减)指定值 |
- Hash 散列类型:Hash类型用于存储结构化数据,可以看做Map中的Map
命令 | 示例 | 说明 |
---|---|---|
hset | HSET key field value | 为哈希表中的字段赋值 |
hget | HGET key field | 获取存储在哈希表中指定字段的值 |
hmget | HMGET key field1 [field2] | 获取所有给定字段的值 |
hmset | HMSET key field1 value1 [field2 value2 ] | 同时将多个 field-value (域-值)对设置到哈希表 key 中 |
hgetall | HGETALL key | 获取在哈希表中指定 key 的所有字段和值 |
hdel | HDEL key field1 [field2] | 删除一个或多个哈希表字段 |
hexists | HEXISTS key field | 查看哈希表 key 中,指定的字段是否存在 |
hlen | HLEN key | 获取哈希表中字段的数量 |
-
List列表类型:
List 列表是简单的字符串列表,按照插入顺序排序。可以在列表的头部和尾部添加元素,一个列表可以存储2的32次方-1个元素。
指令 | 示例 | 说明 |
---|---|---|
rpush | RPUSH key value1 [value2] | 在列表中添加一个或多个值 |
lpush | LPUSH key value1 [value2] | 将一个或多个值插入到列表头部 |
rpop | RPOP key | 移除列表的最后一个元素,返回值为移除的元素 |
lpop | LPOP key | 移出并获取列表的第一个元素 |
llen | LLEN key | 获取列表长度 |
lrange | LRANGE key start stop | 获取列表指定范围内的元素 |
-
Set:集合类型:
Redis的Set是String 类型的无序集合,集合成员是唯一的,数据不能重复;集合中存储的最大数据数为2的32次方-1
命令 | 示例 | 说明 |
---|---|---|
sadd/srem | SADD key member1 [member2]SREM key member1[member2] | 添加移除集合中的元素 |
scard/smembers | SCARD key /SMEMBERS key | 获取集合的成员数/成员 |
srandmember | SRANDMEMBER key [count] | 返回集合中一个或多个随机元素 |
spop | SPOP key | 移除并返回集合中的一个随机元素 |
sdiff | SDIFF key1 [key2] | 返回给定所有集合的差集 |
sinter | SINTER key1 [key2] | 返回给定所有集合的交集 |
sunion | SUNION key1 [key2] | 返回所有给定集合的并集 |
-
Zse有序集合类型:
Redis 有序集合和集合一样也是String类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
命令 | 示例 | 说明 |
---|---|---|
zadd | ZADD key score1 member1 [score2 member2] | 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
zcard | ZCARD key | 获取有序集合的成员数 |
zscore | ZSCORE key member | 返回有序集中,成员的分数值 |
zrem | ZREM key member [member …] | 移除有序集合中的一个或多个成员 |
zrange | ZRANGE key start stop [WITHSCORES] | 通过索引区间返回有序集合成指定区间内的成员 |
zcount | ZCOUNT key min max | 计算在有序集合中指定区间分数的成员数 |
zrangebyscore | ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] | 通过分数返回有序集合指定区间内的成员 |
5.Redis客户端
- Window客户端RedisDesktopManager
- Another.Redis.Desktop.Manager
虚拟机中需要开放端口
firewall-cmd --zone=public --add-port=6379/tcp --permanent
firewall-cmd --reload
6.Java客户端快速开始
Jedis是java语言开发的Redis客户端工具包,用于Java语言与Redis数据进行交互,Jedis对Redis命令进行了封装,掌握Redis命令即可轻便上手。
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.3</version>
</dependency>
public class JedisUtils {
private static JedisPool jedisPool = null;
static {
JedisPoolConfig config = new JedisPoolConfig();
//最大连接数
config.setMaxTotal(30);
//最大空闲连接数
config.setMaxIdle(10);
jedisPool = new JedisPool(config,"192.168.171.175",6379);
}
public String get(String key) {
Jedis jedis = jedisPool.getResource();
jedis.auth("123456");
String res = jedis.get(key);
jedis.close();
return res;
}
}
7.持久化机制
Redis所有数据都保存在内存中,为防止数据丢失,会异步将数据保存在硬盘上
- 数据持久化方案
- 快照:Redis RDB
- 写日志:Redis AOF
- RDB方案的配置
save 60 1 #自动保存策略,60秒内有一个key发生变化就自动保存
dbfilename **.rdb #定义rdb文件名
dir ./ #定义rdb文件保存的路径
stop-writes-on-bgsave-error yes #发生错误中断写入
rdbcompression yes #数据文件压缩
rdbchecksum yes #开启错误校验
- AOF 日志追加
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Di0o2zL-1607875291351)(G:\笔记\images\image-20201125111805881.png)]
- AOF方案配置
appendonly yes #开启aof
appendfilename *.aof
appendfsync everysec #日志保存策略
#always 随时写入,不丢失数据,IO开销大
#everysec 每秒一次写入,丢失1秒数据
#no 依赖os规则写入,不可控,不推荐
no-appendfsync-on-rewrite yes
#yes表示重写的过程中不向aof文件中写入,重写结束后在写入
#no表示重写执行的同时也向aof追加信息
auto-aof-rewrite-percentage 100 #触发重写文件增长百分比,默认100%
auto-aof-rewrite-min-size 64mb #触发重写文件最小大小
8.主从复制
- Redis服务集群
- 为了避免单台Redis服务宕机
- 分担单台服务器压力及容量瓶颈。
- 主从复制
- 将多个Redis实例数据完全同步,将主服务(master)上的数据完全复制到从服务(slave)上。
- 主从复制是高可用的基础
- 数据流是单向的,从master到slave
- 主从复制底层采用RDB方式进行全量复制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KMr0dqrd-1607875291356)(G:\笔记\images\image-20201125113756660.png)]
- 主从复制在从服务节点中的配置文件中进行配置
slaveof host port
slave-read-only yes
9.哨兵机制
-
Redis故障转移
- Redis集群自身实现了高可用,当集群内少量节点出现故障时通过自动故障转移保证集群可以正常对外提供服务。
-
哨兵机制(sentinel)的高可用
- 当主节点出现故障时,由Redis Sentinel自动完成故障发现和转移,并通知应用方,实现高可用性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xy3jzLQv-1607875291359)(G:\笔记\images\image-20201125114920118.png)]
- Sentinel节点配置
sentinel monitor mymaster 127.0.0.1 6379 2 #监控主节点的名称,至少有2个sentinel节点发现master有故障,则进行故障转移
sentinel down-after-milliseconds mymaster 20000 #20秒ping不通,则主观认为该节点有故障,
sentinel parallel-syncs mymaster 1 #故障转移后,重新主从复制,是串行(1)还是并行(>1)复制
sentinel failover-timeout mymaster 60000 #60秒后没有完成故障转移,则认为故障转移失败
sentinel auth-pass mymaster 123456 # 配置连接master的密码,与master节点上配置的密码要一致
10.SpingBoot整合Redis开发
spring-data-redis提供了在Spring应用中通过简单的配置访问redis服务,对reids底层开发包进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化
通过RedisTemplate对象,简化Redis开发
- 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.8.1</version>
</dependency>
- 配置Sentinel连接节点
spring:
redis:
password: 123456
lettuce:
pool:
max-active: 10 #连接池最大连接数
max-wait: -1 #连接池最大阻塞时间
max-idle: 5 #连接池中最大空闲连接
min-idle: 1 #连接池中最小空闲连接
sentinel:
master: mymaster
nodes:
- 192.168.171.175:26379
- 192.168.171.175:26380
- 192.168.171.175:26381
-
注入RestTemplate对象
RedisTemplate中定义了对5种数据结构操作API
redisTemplate.opsForValue();//字符串
redisTemplate.opsForHash();//hash
redisTemplate.opsForList();//list
redisTemplate.opsForSet();//set
redisTemplate.opsForZSet();//zset
- 配置key-Value序列化器
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String,Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(RedisSerializer.string());
template.setValueSerializer(RedisSerializer.json());
return template;
}
11.声明式缓存
SpringCache是Spring生态圈的组件,默认支持声明式缓存,采用SpringAOP技术实现。
用于对主流缓存组件进行一致性集成,通过暴露统一接口,可以轻松进行组件之间的切换。比如Redis,EhCache,Google Guava Cache等。采用注解的方式进行缓存的开发,减少对代码的入侵。
- 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- 配置注解,开启声明式缓存
@EnableCaching
-
@Cacheable
可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法
参数 说明 example value 缓存的名称,在 spring 配置中定义,必须指定至少一个 例如: @Cacheable(value=”mycache”) @Cacheable(value={”cache1”,”cache2”} key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 @Cacheable(value=”testcache”,key=”#userName”) condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 @Cacheable(value=”testcache”,condition=”#userName.length()>2”) -
@CachePut
@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
-
@CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。
-
@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。
-
其中value、key和condition的语义与@Cacheable对应的属性类似。
-
allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false
-
-
Key-Value序列化
- 注册RedisCacheConfiguration
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeKeysWith(SerializationPair.fromSerializer(RedisSerializer.string()))
.serializeValuesWith(SerializationPair.fromSerializer(RedisSerializer.json()));
return configuration;
}
12.Redis的过期策略以及内存淘汰机制
-
Redis采用的定期删除+惰性删除策略。
- 定期删除,Redis默认每间隔100ms检查是否有过期的key,有过期key则删除。需要说明的是,Redis不是每间隔100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,Redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。另外虽然内存能够及时释放,但是十分消耗CPU资源。
- 惰性删除,也就是说在你获取某个key的时候,Redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
- 如果定期删除没有删除key。同时也没及时去请求key,也就是说惰性删除也没生效。这样,Redis的内存会越来越高。那么就应该采用内存淘汰机制。
-
在redis.conf中有一行配置
maxmemory-policy volatile-lru
该配置就是配内存淘汰策略的 -
Redis 的回收策略(淘汰策略)
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 .
- no-enviction(驱逐):禁止驱逐数据
- 注意这里的 6 种机制,volatile 和 allkeys 规定了是对已设置过期时间的数据集淘 汰数据还是从全部数据集淘汰数据,后面的 lru、ttl 以及 random 是三种不同的 淘汰策略,再加上一种 no-enviction 永不回收的策略。
-
使用策略规则:
1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用 allkeys-lru
2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用 allkeys-random
13.应用场景案例
- 计数器
如帖子的被浏览器次数
set key 0
incr key // incr readcount::{帖子id} 每阅读一次
get key // get readcount::{帖子id} 获取阅读量
- 分布式全局ID
实现类似于RDBMS的Sequence功能,生成一系列唯一的序列号
设置序列起始值:
SET sequence "10000"
获取一个序列值:
INCR sequence
当多个客户端同时向Redis申请自增序列时,Redis能够确保每个客户端得到的序列值或序列范围都是全局唯一的,绝对不会出现不同客户端得到了重复的序列值的情况
- 消息队列
# 实现方式一
# 一直往list左边放
lpush key value
# key这个list有元素时,直接弹出,没有元素被阻塞,直到等待超时或发现可弹出元素为止,上面例子超时时间为10s
brpop key 10
# 实现方式二
rpush key value
blpop key 10
- 抽奖
# 参加抽奖活动
sadd key {userId}
# 获取所有抽奖用户
smembers key
# 抽取count名中奖者,并从抽奖活动中移除
spop key count
# 抽取count名中奖者,不从抽奖活动中移除
srandmember key count
- 实现点赞,签到、like等功能
# 1001用户给8001帖子点赞
sadd like::8001 1001
# 取消点赞
srem like::8001 1001
# 检查用户是否点过赞
sismember like::8001 1001
# 获取点赞的用户列表
smembers like::8001
# 获取点赞用户数
scard like::8001
-
实现关注模型,可能认识的人(set)
seven关注的人:sevenSub -> {zhang, li, james}
张关注的人:zhangSub->{seven,jack,li,james}
# 返回sevenSub和zhangSub的交集,共同关注
sinter sevenSub zhangSub -> {li,james}
# 我可能认识的人,下面例子中我是seven
# 求zhangSub和sevenSub的差集,并存在sevenMayKnow集合中
sdiffstore sevenMayKnow zhangSub sevenSub -> {seven,jack}
- 电商商品筛选(set)
每个商品入库的时候即会建立静态标签列表如,品牌,尺寸,处理器,内存。
# 将拯救者y700P-001和ThinkPad-T480这两个元素放到集合brand::lenovo
sadd brand::lenovo 拯救者y700P-001 ThinkPad-T480
sadd screenSize::15.6 拯救者y700P-001 机械革命Z2AIR
sadd processor::i7 拯救者y700P-001 机械革命X8TIPlus
# 获取品牌为联想,屏幕尺寸为15.6,并且处理器为i7的电脑品牌(sinter为获取集合的交集)
sinter brand::lenovo screenSize::15.6 processor::i7 -> 拯救者y700P-001
- 排行版(zset)
zset做排行榜、好友列表, 去重, 历史记录等业务需求。
# user1的用户分数为 10
zadd ranking 10 user1
zadd ranking 20 user2
# 取分数最高的3个用户
zrevrange ranking 0 2 withscores