启动Redis:
redis-cli
实用配置文件启动
./redis-cli redis.conf
远程连接Redis
redis-cli -h host -p port -a password
校验Redis服务器是否可用ping
ping
Redis的key的格式:
[项目名]:[业务名]:[类型]:[id]
String类型,也就是字符串类型,是Redis中最简单的存储类型。其value是字符串,不过根据字符串的格式不同,又可以分为3类:
不管是哪种格式,底层都是字节数组形式存储,只不过是编码方式不同。字符串类型的最大空间不能超过512m。
常用命令:
127.0.0.1:6379> SET runoob "菜鸟教程"
OK
127.0.0.1:6379> GET runoob
"\xe8\x8f\x9c\xe9\xb8\x9f\xe6\x95\x99\xe7\xa8\x8b"
Redis hash 是一个键值(key=>value)对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
Hash类型,也叫散列,其value是一个无序字典,类似于Java中的HashMap结构。
String结构是将对象序列化为JSON字符串后存储,当需要修改对象某个字段时很不方便:
Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD:
Hash的常见命令有:
127.0.0.1:6379> hmset test test01 "hello" test02 "world"
OK
127.0.0.1:6379> hget test test01
"hello"
127.0.0.1:6379> hget test test02
"world"
Redis中的List类型与Java中的LinkedList类似,可以看做是一个双向链表结构。既可以支持正向检索和也可以支持反向检索。
特征也与LinkedList类似:
常用来存储一个有序数据,例如:朋友圈点赞列表,评论列表等。
List的常见命令有:
127.0.0.1:6379> lpush my_list redis
(integer) 1
127.0.0.1:6379> lpush my_list hello
(integer) 2
127.0.0.1:6379> lpush my_list word
(integer) 3
127.0.0.1:6379> lrange my_list 0 10
1) "word"
2) "hello"
3) "redis"
127.0.0.1:6379>
String的常见命令有:
127.0.0.1:6379> sadd my_set redis
(integer) 1
127.0.0.1:6379> sadd my_set hello
(integer) 1
127.0.0.1:6379> sadd my_set world
(integer) 1
127.0.0.1:6379> sadd my_set world
(integer) 0
127.0.0.1:6379> smembers my_set
1) "hello"
2) "redis"
3) "world"
127.0.0.1:6379>
Redis的SortedSet是一个可排序的set集合,与Java中的TreeSet有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。
SortedSet具备下列特性:
因为SortedSet的可排序特性,经常被用来实现排行榜这样的功能。
SortedSet的常见命令有:
127.0.0.1:6379> zadd my_zset 0 set01
(integer) 1
127.0.0.1:6379> zadd my_zset 10 set02
(integer) 1
127.0.0.1:6379> zadd my_zset 20 set03
(integer) 1
127.0.0.1:6379> zadd my_zset 30 set04
(integer) 1
127.0.0.1:6379> zrangebyscore my_zset 0 1000
1) "set01"
2) "set02"
3) "set03"
4) "set04"
127.0.0.1:6379>
tring(字符串) | 二进制安全 | 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M | — |
---|---|---|---|
Hash(字典) | 键值对集合,即编程语言中的Map类型 | 适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去) | 存储、读取、修改用户属性 |
List(列表) | 链表(双向链表) | 增删快,提供了操作某一段元素的API | 1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列 |
Set(集合) | 哈希表实现,元素不重复 | 1、添加、删除,查找的复杂度都是O(1)。2、为集合提供了求交集、并集、差集等操作 | 1、共同好友。2、利用唯一性,统计访问网站的所有独立ip。3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐 |
Sorted Set(有序集合) | 将Set中的元素增加一个权重参数score,元素按score有序排列 | 数据插入集合时,已经进行天然排序 | 1、排行榜2、带权重的消息队列 |
SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis 提供了对不同Redis客户端的整合(Lettuce和Jedis) 提供了RedisTemplate统一API来操作Redis
RedisTemplate
简单介绍SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。
并且将不同数据类型的操作API封装到了不同的类型中:
API | 返回值类型 | 说明 |
---|---|---|
redisTemplate.opsForValue() | ValueOperations | 操作String类型数据 |
redisTemplate.opsForHash() | HashOperations | 操作Hash类型数据 |
redisTemplate.opsForList() | ListOperations | 操作List类型数据 |
redisTemplate.opsForSet() | SetOperations | 操作Set类型数据 |
redisTemplate.opsForZSet() | ZSetOperations | 操作SortedSet类型数据 |
redisTemplate | 通用的命令 |
1.pom.xml配置
<!--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>
</dependency>
2.配置文件(application.Properties)
spring.redis.host=192.168.150.101
spring.redis.port=6379
spring.redis.password=123321
spring.redis.lettuce.pool.max-active=8 # 最大连接数
spring.redis.lettuce.pool.max-idle=8 # 最大空闲连接
spring.redis.lettuce.pool.min-idle=0 # 最小空闲连接
spring.redis.lettuce.pool.max-wait=100 # 连接等待时间
3.具体代码
@Autowired
private RedisTemplate redisTemplate;
@SpringBootTest
public class RedisTest {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
// 插入一条string类型数据
redisTemplate.opsForValue().set("name", "李四");
// 读取一条string类型数据
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
}
1.StringRedisSerializer
@Bean(name = "redisTemplate")
@SuppressWarnings("unchecked")
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//使用fastjson序列化
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(new StringRedisSerializer());
template.setHashValueSerializer(new StringRedisSerializer());
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
2.GenericJackson2JsonRedisSerializer
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); // 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory); // 设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// key和 hashKey采用 string序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// value和 hashValue采用 JSON序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
尽管JSON的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。
为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate的过程:
@Autowired
private StringRedisTemplate stringRedisTemplate;
// JSON工具
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testStringTemplate() throws JsonProcessingException {
// 准备对象
User user = new User("虎哥", 18);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入一条数据到redis
stringRedisTemplate.opsForValue().set("user:200", json);
// 读取数据
String val = stringRedisTemplate.opsForValue().get("user:200");
// 反序列化
User user1 = mapper.readValue(val, User.class);
System.out.println("user1 = " + user1);
}
基于Redis的分布式锁实现思路:
特性:
基于setnx实现的分布式锁存在下面的问题:
核心配置:
slaveof 121.5.193.35 6379
配置解析:
13 | slaveof <masterip> <masterport> | 设置当本机为 slave 服务时,设置 master 服务的 IP 地址及端口,在 Redis 启动时,它会自动从 master 进行数据同步 |
---|
实际配置文件(master):
bind 0.0.0.0
# 端口号
port 6379
# 日志输出文件
logfile "6379.log"
# 指定存储数据文件名
dbfilename "dump-6379.rdb"
# 是否开启守护线程形式
daemonize yes
# 存储至本地数据库时是否压缩数据
rdbcompression yes
实际配置文件(cluster):
bind 0.0.0.0
port 6380
logfile "6380.log"
dbfilename "dump-6380.rdb"
daemonize yes
rdbcompression yes
slaveof 121.5.193.35 6379
启动Redis服务
./redis-server redis.conf
缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
常见的解决方案有两种:
缓存空对象
优点:
缺点:
布隆过滤
优点:
缺点:
总结:
缓存穿透产生的原因是什么?
缓存穿透的解决方案有哪些?
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。
解决方案:
缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
常见的解决方案有两种:
解决方式一:互斥锁
解决方式二:逻辑过期
优缺点: