Redis 简介 Redis是基于内存的key-value结构数据库
基于内存存储,读写性能高
适合存储热点数据(热点商品、咨询、新闻),短时间内大量用户访问
安装与启动
1 2 3 4 5 brew install redis //方式一 brew services start redis //方式二 redis-server /opt/homebrew/etc/redis.conf
redis.conf位置
1 /opt/homebrew/etc/redis.conf
Redis 常见操作命令 字符串操作命令 Redis 字符串类型常用命令:
SET key value 设置指定key的值GET key 获取指定key的值MSET,MGET 批量设置、获取SETEX key seconds value 设置指定key的值,并将key 的过期时间设为seconds秒SETNX key value 只有在key 不存在时设置key 的值
哈希操作命令 Redis hash 是一个string类型的 field 和value 的映射表,hash特别适合用于存储对象,常用命令:
HSET key field value 将哈希表 key 中的字段field的值设为valueHGET key field 获取存储在哈希表中指定字段的值HDEL key field 删除存储在哈希表中的指定字段HKEYS key 获取哈希表中所有字段HVALS key 获取哈希表中所有值
列表操作命令 Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:
LPUSH key value1 [value2] 将一个或多个值插入到列表头部
LRANGE key start stop 获取列表指定范围内的元素
RPOP key移除并获列表最后一个元素
LLEN key获取列表长度
集合操作命令 Redis set 是string类型的无序集合。集合成员是唯一的,集合中不能出现重复的数据,常用命令:
SADD key member1 [member2] 向集合添加一个或多个成员
SMEMBERS key 返回集合中的所有成员
SCARD key 获取集合的成员数
SINTER key1 [key2] 返回给定所有集合的交集
SUNION key1 [key2] 返回所有给定集合的并集
SREM key member1 [member2] 删除集合中一个或多个成员
有序集合操作命令 Redis有序集合是string类型元素的集合,且不允许有重复成员。每个元素都会关联一个double类型的分数。常用命令:
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员
ZRANGE key start stop [WITHSCORES]通过索引区间返回有序集合中指定区间内的成员写上
ZINCRBY key increment member有序集合中对指定成员的分数加上增量 increment
ZREM key member [member ...] 移除有序集合中的一个或多个成员
通用命令 Redis的通用命令是不分数据类型的,都可以使用的命令:
在 Java中操作Redis 导入Maven坐标
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-redis</artifactId > </dependency >
在application.yml里配置Redis数据源
1 2 3 4 5 redis: host: ${sky.redis.host} port: ${sky.redis.port} password: ${sky.redis.password} database: ${sky.redis.database}
编写配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Configuration @Slf4j public class RedisConfiguration { @Bean public RedisTemplate redisTemplate (RedisConnectionFactory redisConnectionFactory) { log.info("开始创建redis模版对象..." ); RedisTemplate redisTemplate=new RedisTemplate (); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(new StringRedisSerializer ()); return redisTemplate; } }
通过RedisTemplate对象操作Redis
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 @SpringBootTest public class SpringDataRedisTest { @Autowired private RedisTemplate redisTemplate; @Test public void testRedisTemplate () { System.out.println(redisTemplate); ValueOperations valueOperations=redisTemplate.opsForValue(); HashOperations hashOperations = redisTemplate.opsForHash(); ListOperations listOperations = redisTemplate.opsForList(); SetOperations setOperations = redisTemplate.opsForSet(); ZSetOperations zSetOperations = redisTemplate.opsForZSet(); } @Test public void testString () { redisTemplate.opsForValue().set("city" ,"北京" ); String city = (String) redisTemplate.opsForValue().get("city" ); System.out.println(city); redisTemplate.opsForValue().set("code" ,"1234" ,3 , TimeUnit.MINUTES); redisTemplate.opsForValue().setIfAbsent("lock" ,"1" ); redisTemplate.opsForValue().setIfAbsent("lock" ,"2" ); } @Test public void testHash () { HashOperations hashOperations = redisTemplate.opsForHash(); hashOperations.put("100" ,"name" ,"tom" ); hashOperations.put("100" ,"age" ,"20" ); String name = (String) hashOperations.get("100" , "name" ); System.out.println(name); Set keys = hashOperations.keys("100" ); System.out.println(keys); List values = hashOperations.values("100" ); System.out.println(values); hashOperations.delete("100" ,"age" ); } @Test public void testList () { ListOperations listOperations = redisTemplate.opsForList(); listOperations.leftPushAll("mylist" , "a" , "b" , "c" ); listOperations.leftPush("mylist" , "d" ); List mylist = listOperations.range("mylist" , 0 , -1 ); System.out.println(mylist); listOperations.rightPop("mylist" ); Long size = listOperations.size("mylist" ); System.out.println(size); } @Test public void testSet () { SetOperations<String, String> setOperations = redisTemplate.opsForSet(); setOperations.add("set1" , "a" , "b" , "c" , "d" ); setOperations.add("set2" , "a" , "b" , "x" , "y" ); Set<String> members = setOperations.members("set1" ); System.out.println(members); Long size = setOperations.size("set1" ); System.out.println(size); Set<String> intersect = setOperations.intersect("set1" , "set2" ); System.out.println(intersect); Set<String> union = setOperations.union("set1" , "set2" ); System.out.println(union); setOperations.remove("set1" , "a" , "b" ); } @Test public void testZSet () { ZSetOperations zSetOperations = redisTemplate.opsForZSet(); zSetOperations.add("zset1" , "a" , 10 ); zSetOperations.add("zset1" , "b" , 12 ); zSetOperations.add("zset1" , "c" , 9 ); Set zset1 = zSetOperations.range("zset1" , 0 , -1 ); System.out.println(zset1); zSetOperations.incrementScore("zset1" , "c" , 10 ); zSetOperations.remove("zset1" , "a" , "b" ); } @Test public void testCommon () { Set keys = redisTemplate.keys("*" ); System.out.println(keys); Boolean name = redisTemplate.hasKey("name" ); Boolean set1 = redisTemplate.hasKey("set1" ); for (Object key : keys) { DataType type = redisTemplate.type(key); System.out.println(type.name()); } redisTemplate.delete("mylist" ); } }
缓存穿透 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
解决办法:
缓存雪崩 缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案:
给不同的Key的TTL添加随机值
利用Redis集群提高服务的可用性
给缓存业务添加降级限流策略
给业务添加多级缓存
缓存击穿 缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
常见的解决方案有两种:
基于redis实现短信登陆(可以解决集群session共享问题) 利用拦截器检验登陆状态 利用互斥锁和逻辑过期解决缓存击穿问题 利用缓存空对象解决缓存穿透问题 用乐观锁解决优惠券秒杀超卖问题 基于Redis的分布式锁解决一人一单问题
乐观锁 乐观锁的关键是判断之前查询得到的数据是否有被修改过,常见的方式有两种:
版本号法 CAS法 实际上就是不用单创建一个版本号字段来判断是否有变化,因为本身的字段就可以用来判断