简介
Redis是一个基于内存的key-value结构的数据库,它存储的value类型比较丰富,也被成为结构化的非关系型数据库。
特点:
- 基于内存存储,读写性能高。
- 适合存储热点数据(经常查询)
适用场景:
- 当作缓存
- 任务队列
- 消息队列
- 分布式锁
使用docker安装Redis
1
| docker run -itd --name redis-test -p 6379:6379 redis
|
Redis常用命令
字符串操作
set key value |
设置指定key的值 |
setex key seconds value |
seconds为该key过期时间 |
get key |
获取key的值 |
setex key value |
当key不存在时设置value |
1 2 3 4 5 6 7 8 9 10 11
| @Test public void testString(){ ValueOperations valueOperations = redisTemplate.opsForValue(); valueOperations.set("hello","world", 10, TimeUnit.SECONDS); System.out.println(valueOperations.get("hello")); valueOperations.setIfAbsent("hello","123", 10, TimeUnit.SECONDS); valueOperations.setIfPresent("hello","world",10,TimeUnit.SECONDS); }
|
哈希操作
hset key field value |
将哈希表key中的field字段设置为value |
hget key field |
获取哈希表key中field字段的value |
hdel key field |
删除哈希表key中的field字段 |
hkeys key |
获取哈希表key中所有key |
hvals key |
获取哈希表key中的所有value |
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test public void testHash(){ HashOperations hashOperations = redisTemplate.opsForHash(); hashOperations.put("user","name","张三"); hashOperations.put("user", "age", 10); hashOperations.put("user", "sex", "男"); System.out.println(hashOperations.get("user","name")); System.out.println(hashOperations.keys("user")); System.out.println(hashOperations.values("user")); }
|
列表操作
lpush key value1 [value2] |
从左边向列表key存值 |
rpush key value1 [value2] |
从右边向列表key存值 |
lrange key start stop |
从左边开始获取指定范围的值 |
rpop key |
获取最右边的值并删除(右侧同理) |
llen key |
获取列表key的长度 |
1 2 3 4 5 6 7 8 9
| @Test public void testList(){ ListOperations listOperations = redisTemplate.opsForList(); listOperations.leftPush("list","1"); listOperations.rightPush("list","2"); listOperations.rightPop("list"); System.out.println(listOperations.range("list",0,-1)); System.out.println(listOperations.size("list")); }
|
集合操作
sadd key value1 value2 |
向集合添加元素 |
smembers key |
返回集合所有元素 |
scard key |
获取集合元素数 |
sinter key1 key2 |
返回给定所有集合的交集 |
sunion key1 key2 |
返回给定所有集合的并集 |
srem key value1 value2 |
删除集合中的元素 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Test public void testSet(){ SetOperations setOperations = redisTemplate.opsForSet(); setOperations.add("set1","1"); setOperations.add("set1","2"); setOperations.add("set1","3"); setOperations.add("set2","2"); setOperations.add("set2","3"); setOperations.remove("set1", "3"); System.out.println(setOperations.members("set1")); System.out.println(setOperations.size("set1")); System.out.println(setOperations.intersect("set1", "set2")); System.out.println(setOperations.union("set1", "set2")); }
|
有序集合操作
zadd key score1 value1 score2 value2 |
向有序集合key添加元素(score为分数,有序列表根据该值有序) |
zrange key start stop |
获取指定范围的值 |
zincrby key increment value |
给有序集合key增加value的分数值(增加increment) |
zrem key value |
删除有序集合中的value |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Test public void testZSet(){ ZSetOperations zSetOperations = redisTemplate.opsForZSet(); zSetOperations.add("zset", "java", 10); zSetOperations.add("zset", "python", 20); System.out.println(zSetOperations.range("zset",0, -1)); System.out.println(zSetOperations.size("zset")); zSetOperations.incrementScore("zset", "java", 30); System.out.println(zSetOperations.range("zset", 0, -1)); zSetOperations.remove("zset", "java"); }
|
通用命令
keys pattern |
查找所有符合给定模式(pattern)的key |
exists key |
检查key是否存在 |
type key |
返回key的值类型 |
del key |
删除key |
rename oldkey newkey |
重命名 |
ping |
测试连接是否正常 |
expire key time |
设置key存活时间 |
ttl key |
返回key剩余存活时间 |
RedisTemplate配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.sky.config;
import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer;
@Slf4j @Configuration public class RedisConfiguration { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { log.info("初始化redisTemplate"); RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; } }
|
常见问题
缓存穿透
请求获取一个不存在的数据,导致该请求直接越过redis直接访问数据库。
解决方案:
- 缓存空对象,当有请求访问数据时,即使该数据不存在也会在redis中缓存一个空值,并设置一个较短的过期时间。
- 使用布隆过滤器,访问redis前先通过布隆过滤器判断该key是否存在。
缓存雪崩
大量缓存同一时间集体过期,导致大量请求直接访问到数据库造成数据库宕机。
解决方案:
- 随机过期时间,给每个缓存设置随机的过期时间,使其过期时间相互错开。
- 缓存预热,系统启动前将热点缓存主动加载到缓存中。
缓存击穿
当大量请求访问一个缓存时,而该缓存此时正好失效,那么大量请求就会越过redis直接对数据库进行冲击,导致数据库宕机。这就是缓存穿透。
解决方案:
- 设置线程锁,每次仅允许一个请求可以到达数据库,在数据库获取数据后重新存入redis,其他请求重新从redis获取数据。
- 将缓存设置为永不过期,并设置一个逻辑过期时间,当访问时检查是否逻辑过期,如果过期则会重新开辟一个线程进行更新。