Redis
概念:redis是一款高性能的NoSQL非关系型数据库。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题
优点:
1)成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购买使用,相比关系型数据库价格便宜。
2)查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及nosql数据库。
3)存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
4)扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难。
redis的数据结构:
redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构。
- value的数据结构:
1) 字符串类型 string
2) 哈希类型 hash : map格式
3) 列表类型 list : linkedlist格式。支持重复元素
4) 集合类型 set : 不允许重复元素
5) 有序集合类型 sortedset:不允许重复元素,且元素有顺序
命令操作
-
redis的数据结构:
* redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构
* value的数据结构:
1) 字符串类型 string
2) 哈希类型 hash : map格式
3) 列表类型 list : linkedlist格式。支持重复元素
4) 集合类型 set : 不允许重复元素
5) 有序集合类型 sortedset:不允许重复元素,且元素有顺序 -
字符串类型 string
1. 存储: set key value
127.0.0.1:6379> set username zhangsan
OK
2. 获取: get key
127.0.0.1:6379> get username
“zhangsan”
3. 删除: del key
127.0.0.1:6379> del age
(integer) 1-
哈希类型 hash
-
存储: hset key field value
127.0.0.1:6379> hset myhash username lisi
(integer) 1
127.0.0.1:6379> hset myhash password 123
(integer) 1 -
获取:
- hget key field: 获取指定的field对应的值
127.0.0.1:6379> hget myhash username
“lisi” - hgetall key:获取所有的field和value
127.0.0.1:6379> hgetall myhash- “username”
- “lisi”
- “password”
- “123”
- hget key field: 获取指定的field对应的值
-
删除: hdel key field
127.0.0.1:6379> hdel myhash username
(integer) 1
-
-
列表类型 list:可以添加一个元素到列表的头部(左边)或者尾部(右边)
- 添加:
-
lpush key value: 将元素加入列表左表
-
rpush key value:将元素加入列表右边
127.0.0.1:6379> lpush myList a
(integer) 1
127.0.0.1:6379> lpush myList b
(integer) 2
127.0.0.1:6379> rpush myList c
(integer) 3
-
- 获取:
- lrange key start end :范围获取
127.0.0.1:6379> lrange myList 0 -1- “b”
- “a”
- “c”
- lrange key start end :范围获取
- 删除:
- lpop key: 删除列表最左边的元素,并将元素返回
- rpop key: 删除列表最右边的元素,并将元素返回
- 添加:
-
-
集合类型 set : 不允许重复元素
1. 存储:sadd key value
127.0.0.1:6379> sadd myset a
(integer) 1
127.0.0.1:6379> sadd myset a
(integer) 0
2. 获取:smembers key:获取set集合中所有元素
127.0.0.1:6379> smembers myset
1) “a”
3. 删除:srem key value:删除set集合中的某个元素
127.0.0.1:6379> srem myset a
(integer) 1-
有序集合类型 sortedset:不允许重复元素,且元素有顺序.每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
-
存储:zadd key score value
127.0.0.1:6379> zadd mysort 60 zhangsan
(integer) 1
127.0.0.1:6379> zadd mysort 50 lisi
(integer) 1
127.0.0.1:6379> zadd mysort 80 wangwu
(integer) 1 -
获取:zrange key start end [withscores]
127.0.0.1:6379> zrange mysort 0 -1- “lisi”
- “zhangsan”
- “wangwu”
127.0.0.1:6379> zrange mysort 0 -1 withscores
- “zhangsan”
- “60”
- “wangwu”
- “80”
- “lisi”
- “500”
-
删除:zrem key value
127.0.0.1:6379> zrem mysort lisi
(integer) 1
-
-
-
通用命令
1. keys * : 查询所有的键
2. type key : 获取键对应的value的类型
3. del key:删除指定的key value
持久化
redis持久化机制:
1.RDB:
RDB 是 Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。
触发RDB快照:
1 在指定的时间间隔内,执行指定次数的写操作
2 执行save(阻塞, 只管保存快照,其他的等待) 或者是bgsave (异步)命令
3 执行flushall 命令,清空数据库所有数据,意义不大。
4 执行shutdown 命令,保证服务器正常关闭且不丢失任何数据,意义…也不大。
RDB的优缺点:
优点:
1 适合大规模的数据恢复。
2 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
缺点:
1 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
2 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来两倍)
最后再将临时文件替换之前的备份文件。
2.AOF
Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
触发AOF快照: 根据配置文件触发,可以是每次执行触发,可以是每秒触发,可以不同步。
AOF 的优缺点
优点:数据的完整性和一致性更高
缺点:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢
个人小项目使用redis总结:
SSM项目整合redis
通过百度,发现ssm项目大多数使用了Jedis和JedisPool,发现还不错。
导入依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
在spring中配置jedis
<!-- Redis连接池的配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.pool.maxActive}"/>
<property name="maxIdle" value="${redis.pool.maxIdle}"/>
<property name="minIdle" value="${redis.pool.minIdle}"/>
<property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
<property name="testOnReturn" value="${redis.pool.testOnReturn}"/>
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg index="0" ref="jedisPoolConfig"/>
<constructor-arg index="1" value="${redis.hostName}"/>
<constructor-arg index="2" value="${redis.port}"/>
<constructor-arg index="3" value="${redis.timeout}"/>
<!--Redis密码-->
<constructor-arg index="4" value="${redis.password}"/>
</bean>
在service层使用redis读取数据
存储数据:
//设置点赞数
Jedis jedis = jedisPool.getResource();
long likeCount = jedis.scard("BBS:"+pid+":like");
post.setLikeCount((int)likeCount);
if(jedis!=null){
jedis.close();
}
读取数据
//从redis返回集合结果
String result = String.valueOf(jedis.scard("BBS:"+pid+":like"));
springBoot2.x使用redis
无论使用spring-boot的哪个版本,我们都需要先配置redis连接,两个版本的redis客户端连接池使用有所不同。
spring-boot版本 默认客户端类型
1.5.x jedis
2.x lettuce
但好像官方推荐redisTemplate。
spring 封装了两种不同的对象来进行对redis的各种操作,分别是StringTemplate与redisTemplate。
两者的关系是StringRedisTemplate继承RedisTemplate。
两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。
SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。
StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。
RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
其实通俗的来讲:
当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可。
但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是
更好的选择。(对象需要实现序列化)
springboot引入redis的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Redis数据库索引(默认为0)
spring.redis.database=0
Redis服务器地址
spring.redis.host=localhost
Redis服务器连接端口
spring.redis.port=6379
Redis服务器连接密码(默认为空)
spring.redis.password=root
连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
连接超时时间(毫秒)
spring.redis.timeout=10000
当我们的数据是复杂的对象类型,那么可以采用RedisTemple,首先我们需要手动创建Redis的配置类,来自定义序列化。
@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport {
private static final Logger logger = LoggerFactory.getLogger(RedisConfiguration.class);
/**
* redis模板,存储关键字是字符串,值jackson2JsonRedisSerializer是序列化后的值
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//使用StringRedisSerializer来序列化和反序列化redis的key值
RedisSerializer redisSerializer = new StringRedisSerializer();
//key
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
//value
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
业务层使用
@RestController
public class RedisController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("/get/{key}")
public String getRedis(@PathVariable(name="key") String key){
return stringRedisTemplate.opsForValue().get(key);
}
@PostMapping("/set/{key}/{value}")
public String getRedis(@PathVariable(name="key")String key,@PathVariable(name="value")String value){
stringRedisTemplate.opsForValue().set(key,value);
return "SUCCESS";
}
@GetMapping("/postEntity")
public String postEntity(){
User user=new User();
user.setId("1");
user.setName("pwl");
user.setAge("25");
redisTemplate.opsForValue().set(user.getId(),user);
return "SUCCESS";
}
@GetMapping("/getEntity/{key}")
public Object getEntity(@PathVariable(name="key")String key){
return redisTemplate.opsForValue().get(key);
}
}
后面查询发现SpringCache和redis整合,可以使用注解方式来缓存
需要配置缓存管理器,来替换spring自带的缓存管理器
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题)
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
也可以这样配置
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置CacheManager的值序列化方式为json序列化
RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair .fromSerializer(jsonSerializer);
RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(pair);
//设置默认超过期时间是30秒
defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
// 初始化RedisCacheManager
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
至于缓存注解
@Cacheable
@CacheEvict
@CachePut
我会单独整理文章