redis
key-value来存储数据,远程词典数据库
NoSql
特点:
- 方便扩展(数据之间没有关系,很好扩展)
- 大数据性能高
- 数据类型多样性(不需要事先设计数据库)
非关系型数据库
非结构化:键值型
非关联:字段之间没有关联
非SQL:不是sql语句查询
事务特性:base
存储方式:内存
特征
- 键值型,value支持多种不同的数据结构,功能丰富
- 单线程,每个命令具备原子性
- 6.0后对网络处理时支持多线程,核心部分是单线程
- 低延迟,速度快(基于内存,IO多路复用,良好的编码)
- 支持数据持久化
- 支持主从集群,分片集群(把数据拆分,分机器存储)
- 支持多语言客户端
常见命令:
数据结构:
redis是一个键值的数据库类型,key一般是String类型,不过value的类型是不顾行的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5cQo88tr-1690691460240)(C:\Users\lxy\AppData\Roaming\Typora\typora-user-images\image-20230715081200202.png)]
帮助命令 help @加类型(genneric表示全部)
通用命令:
对所有类型都适用
KEYS:查看符合模版的所有key
keys * 表示所有的key
keys a* 表示查看所有a开头的key
del key 删除一个指定的key
del k1 k2 k3 表示批量删除返回值代表删除的数量
exists key 查看某个key是否存在
expire key 秒 给key设置一个有效期,有效期到期时该key会被自动删除
ttl :查看key的有效期 -1 表示永久有效,-2表示不存在
String类型:
string:普通字符串类型
int:整数类型,可以做自增,自减操作
float:浮点类型,可以做自增,自减操作
底层都会由字节数组形式存储,只不过是编码方式不同,字符串类型最大空间不超过512m
set:添加或者修改已经存在的一个string类型的键值对
get:根据key获取string类型的value
mset:批量添加多个string类型的键值对
mget:根据多个key获取多累string类型的value
incr:让一个整形的key自增1
incrby:让一个整形的key自增并指定步长
increbyfloat:让一个浮点类型的数字自增并且指定步长
setnx:添加一个string类型的键值对,前提是这个key不存在,否则不执行
setex:添加一个string类型的键值对,并且指定有效期
区分不同类型的key:redis允许多个单词形成层级结构,多个单词之间用:隔开
eg: 项目名:业务名:类型:id
hash类型:
也叫散列,其value是一个无序字典,类似于java中的HashMap
hset key field value:添加或者修改hash类型key的field的值
hget key field:获取一个hash类型key的field的值
hmset:批量添加
hmget:批量删除
hgetall:获取一个hash类型的key中的所有field和value
hkeys:获取一个hash类型的key中的所有的field
hvals:获取一个hash类型的可以中所有的value
hincrby:自增
hsetnx:不存在就添加
list类型:
和linkedlist比较相似,可以看做一个双向链表结构,既可以支持正向检索,也可以支持反向检索
- 有序
- 元素可以重复
- 插入和删除快
- 查询速度一般
lpush key elemanet:向列表左侧插入一个或多个元素
lpop key:移除并返回列表左侧的第一个元素,没有则返回nil
rpush key element:向列表右侧插入一个或多个元素
rpop:移除并返回列表右侧的第一个元素,没有则返回nil
lrange key start end:返回一段角标范围内的所有元素
[BLPOP key1 key2 timeout:移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
set类型:
类似java中hashset可以看做是一个value为空的hashmap.
- 无序
- 元素不可以重复
- 查找快
- 支持交集,并集,差集
SADD key member1 :向集合添加一个或多个成员向集合添加一个或多个成员
SREM key member1:移除集合中一个或多个成员
SCARD key:获取集合的成员数
SISMEMBER key member:判断 member 元素是否是集合 key 的成员
SMEMBERS key:返回集合中的所有成员
SINTER key1 key2 返回给定所有集合的交集
SUNION key1 key2返回所有给定集合的并集
SDIFFSTORE key1 key2 返回给定所有集合的差集
SPOP key移除并返回集合中的一个随机元素
SMOVE source destination member 将 member 元素从 source 集合移动到 destination 集合
SortedSet类型:
可排序的集合和treeset集合类似,但是底层数据结构差别很大
- 可排序
- 元素不重复
- 查询速度快
用来实现排行榜
序号 | 命令及描述 |
---|---|
1 | [ZADD key score1 member1 score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
2 | ZCARD key 获取有序集合的成员数 |
3 | ZCOUNT key min max 计算在有序集合中指定区间分数的成员数 |
4 | ZINCRBY key increment member 有序集合中对指定成员的分数加上增量 increment |
5 | [ZINTERSTORE destination numkeys key key …] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 destination 中 |
6 | ZLEXCOUNT key min max 在有序集合中计算指定字典区间内成员数量 |
7 | [ZRANGE key start stop WITHSCORES] 通过索引区间返回有序集合指定区间内的成员 |
8 | [ZRANGEBYLEX key min max LIMIT offset count] 通过字典区间返回有序集合的成员 |
9 | [ZRANGEBYSCORE key min max WITHSCORES] [LIMIT] 通过分数返回有序集合指定区间内的成员 |
10 | ZRANK key member 返回有序集合中指定成员的索引 |
11 | [ZREM key member member …] 移除有序集合中的一个或多个成员 |
12 | ZREMRANGEBYLEX key min max 移除有序集合中给定的字典区间的所有成员 |
13 | ZREMRANGEBYRANK key start stop 移除有序集合中给定的排名区间的所有成员 |
14 | ZREMRANGEBYSCORE key min max 移除有序集合中给定的分数区间的所有成员 |
15 | [ZREVRANGE key start stop WITHSCORES] 返回有序集中指定区间内的成员,通过索引,分数从高到低 |
16 | [ZREVRANGEBYSCORE key max min WITHSCORES] 返回有序集中指定分数区间内的成员,分数从高到低排序 |
17 | ZREVRANK key member 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
18 | ZSCORE key member 返回有序集中,成员的分数值 |
19 | [ZUNIONSTORE destination numkeys key key …] 计算给定的一个或多个有序集的并集,并存储在新的 key 中 |
20 | [ZSCAN key cursor MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值) |
redis的java客户端:
jedis使用
1.引入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.0.0-alpha2</version>
</dependency>
2.使用
//建立连接
Jedis jedis = new Jedis(“”, 6379);
//设置密码
jedis.auth("");
//选择库
jedis.select(0);
//执行命令
String name = jedis.get("");
System.out.println(name);
//释放资源
jedis.close();
Jedis连接池
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大连接
jedisPoolConfig.setMaxIdle(8);
//最大空闲连接
jedisPoolConfig.setMaxIdle(8);
//最小空闲连接
jedisPoolConfig.setMaxIdle(0);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "8.130.131.103", 6379);
//获取jedis对象
Jedis resource = jedisPool.getResource();
SpringDataRedis
依赖导入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
使用
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
ValueOperations ops= redisTemplate.opsForValue();
ops.set("aaa","aaa");
Object o = ops.get("aaa");
System.out.println(o);
}
默认使用jdk的序列化来进行操作一般不使用
自定义redistemplate序列化方式
1.导入依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
2.编写自定义配置类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
//创建redistemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
//设置连接工厂
template.setConnectionFactory(connectionFactory);
//创建json序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//设置key的序列化方式
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
//设置value
template.setValueSerializer(jsonRedisSerializer);
template.setHashKeySerializer(jsonRedisSerializer);
return template;
}
}
3.测试
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class User implements Serializable {
private String name;
private int age;
}
@Test
public void saveUser(){
ValueOperations ops = redisTemplate.opsForValue();
ops.set("user",new User("张三",20));
User o = (User)redisTemplate.opsForValue().get("user");
System.out.println(o);
}
为了节省内存空间,一般不会使用json序列化器来处理value,而是统一使用String序列化器,要求只使用string存储value,而spring里面的StringRedisTemplates的序列化方式默认就是String
@Test
void testStringRedisTemplate() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String string = objectMapper.writeValueAsString(new User("sf", 12));
stringRedisTemplate.opsForValue().set("testuser",string);
}
缓存
就是数据交换的缓冲区,是储存临时数据的地方,一般读写性能较高,缓存未命中的时候会落到数据库上
作用:降低后端的负载,提高读写效率,降低相应时间。
缓存称成本:数据一致性成本
缓存更新策略
1.内存淘汰
不用自己维护,利用redis的内存淘汰机制,当内存不足时自动淘汰部分数据,下次查询时更新缓存,一致性较差
2.超时剔除
给缓存数据添加ttl时间,到期后自动删除缓存,下次查询时更新缓存。一致性一般,
3.主动更新、
自己编写业务逻辑,在修改数据库的同时,更新缓存,一致性较好。
低一致性:使用内存淘汰机制,
高一致性:主动更新,并使用超时剔除作为兜底方案。
先更新数据库再删除缓存。
缓存穿透
客户端请求的数据在缓存和数据库中都不存在这样缓存永远不会生效,这些请求都会打到数据库。
解决方案:缓存空对象
缺点:额外的 内存消耗。造成短期的不一致。
布隆过滤:
增加id的复杂度,避免id被猜到规律
做好数据的基础格式校验
加强用户权限检测
做好热点参数的限流
缓存雪崩
在同一时段内大量的缓存key同时失效或者redis服务宕机,导致大量请求达到数据库。
解决方案:
- 给不同key的ttl添加随机值
- 利用redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
缓存击穿
缓存击穿也叫热点key问题,就是一个被高并发访问而且缓存重建业务较复杂的key突然失效了,无数的请求访问会给数据库带来巨大的压力
解决方案:
使用互斥锁,没有额外的内存消耗,保证一致性,实现简单。缺点:线程需要等待,性能会受影响,可能有死锁风险
逻辑过期:不设置过期时间,在添加的时候在当前value里面添加过期时间,线程无需等待,性能较好。缺点:不保证一致性,有额外内存消耗,实现复杂。
增加id的复杂度,避免id被猜到规律
做好数据的基础格式校验
加强用户权限检测
做好热点参数的限流
缓存雪崩
在同一时段内大量的缓存key同时失效或者redis服务宕机,导致大量请求达到数据库。
解决方案:
- 给不同key的ttl添加随机值
- 利用redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
缓存击穿
缓存击穿也叫热点key问题,就是一个被高并发访问而且缓存重建业务较复杂的key突然失效了,无数的请求访问会给数据库带来巨大的压力
解决方案:
使用互斥锁,没有额外的内存消耗,保证一致性,实现简单。缺点:线程需要等待,性能会受影响,可能有死锁风险
逻辑过期:不设置过期时间,在添加的时候在当前value里面添加过期时间,线程无需等待,性能较好。缺点:不保证一致性,有额外内存消耗,实现复杂。