redis介绍
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
数据模型
- string(字符串)
- hash(哈希)
- list(列表)
- set(集合)
- zset(sorted set:有序集合)
优势
Redis 的优势包括它的速度、它对富数据类型的支持、它的操作的原子性,以及它的通用性:
- 性能极高,它每秒可执行约 100,000 个 set 以及约 100,000 个 get 操作。
- 丰富的数据类型,Redis 对大多数开发人员已知道的大多数数据类型提供了原生支持,这使得各种问题得以轻松解决。
- 原子性,因为所有 Redis 操作都是原子性的,所以多个客户端会并发地访问一个 Redis 服务器,获取相同的更新值。
- 丰富的特性,Redis 是一个多效用工具,有非常多的应用场景,包括缓存、消息队列(Redis 原生支持发布/订阅)、短期应用程序数据(如 Web 会话、Web 页面命中计数)等。
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
application配置
# Redis 数据库索引(默认为0)
spring.redis.database=0
# Redis 服务器地址
spring.redis.host=192.168.0.71
# Redis 服务器连接端口
spring.redis.port=6379
# Redis 服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=10000
缓存配置(可以不设置)
在这里我们可以为 Redis 设置一些全局配置,比如配置主键的生产策略 KeyGenerator,如不配置会默认使用参数名作为主键。
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
//设置缓存过期时间
//rcm.setDefaultExpiration(60);//秒
return rcm;
}
}
注意:我们使用了注解:@EnableCaching来开启缓存。在配置 CacheManager 的方法中,也可以配置缓存默认的过期时间。
使用
在spring boot中可以直接使用自动配置的StringRedisTemplate或者RedisTemplate直接进行redis操作。
StringRedisTemplate和RedisTemplate的区别在于使用的序列化类不同。参考
- RedisTemplate使用的是 JdkSerializationRedisSerializer
- StringRedisTemplate使用的是 StringRedisSerializer
一般情况下:
- 当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可,
- 但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是 更好的选择。
通常情况下我们可以实现一个RedisService完成对redis的读写操作:
样板一:
@Service
public class RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 一周有多少秒
*/
private static final long WEEK_SECONDS = 7 * 24 * 60 * 60;
/**
* 将 key,value 存放到redis数据库中,默认设置过期时间为一周
* @param key
* @param value
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), WEEK_SECONDS, TimeUnit.SECONDS);
}
/**
* 将 key,value 存放到redis数据库中,设置过期时间单位是秒
*
* @param key
* @param value
* @param expireTime
*/
public void set(String key, Object value, long expireTime) {
redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), expireTime, TimeUnit.SECONDS);
}
/**
* 判断 key 是否在 redis 数据库中
*
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 获取与 key 对应的对象
* @param key
* @param clazz 目标对象类型
* @param <T>
* @return
*/
public <T> T get(String key, Class<T> clazz) {
String s = get(key);
if (s == null) {
return null;
}
return JsonUtil.convertString2Obj(s, clazz);
}
/**
* 获取 key 对应的字符串
* @param key
* @return
*/
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}
/**
* 删除 key 对应的 value
* @param key
*/
public void delete(String key) {
redisTemplate.delete(key);
}
}
样板二:
@Service
public class RedisService {
private Logger logger = LoggerFactory.getLogger(RedisService.class);
@Autowired
private RedisTemplate redisTemplate;
/**
* set value
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
logger.error("set error: key {}, value {}",key,value,e);
}
return result;
}
/**
* set value with expireTime
* @param key
* @param value
* @param expireTime
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
logger.error("set error: key {}, value {},expireTime {}",key,value,expireTime,e);
}
return result;
}
/**
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* @param key
* @return
*/
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
return result;
}
/**
* remove single key
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* batch delete
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* batch delete with pattern
* @param pattern
*/
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0)
redisTemplate.delete(keys);
}
/**
* hash set
* @param key
* @param hashKey
* @param value
*/
public void hashSet(String key, Object hashKey, Object value){
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put(key,hashKey,value);
}
/**
* hash get
* @param key
* @param hashKey
* @return
*/
public Object hashGet(String key, Object hashKey){
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
return hash.get(key,hashKey);
}
/**
* list push
* @param k
* @param v
*/
public void push(String k,Object v){
ListOperations<String, Object> list = redisTemplate.opsForList();
list.rightPush(k,v);
}
/**
* list range
* @param k
* @param l
* @param l1
* @return
*/
public List<Object> range(String k, long l, long l1){
ListOperations<String, Object> list = redisTemplate.opsForList();
return list.range(k,l,l1);
}
/**
* set add
* @param key
* @param value
*/
public void setAdd(String key,Object value){
SetOperations<String, Object> set = redisTemplate.opsForSet();
set.add(key,value);
}
/**
* set get
* @param key
* @return
*/
public Set<Object> setMembers(String key){
SetOperations<String, Object> set = redisTemplate.opsForSet();
return set.members(key);
}
/**
* ordered set add
* @param key
* @param value
* @param scoure
*/
public void zAdd(String key,Object value,double scoure){
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
zset.add(key,value,scoure);
}
/**
* rangeByScore
* @param key
* @param scoure
* @param scoure1
* @return
*/
public Set<Object> rangeByScore(String key,double scoure,double scoure1){
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
return zset.rangeByScore(key, scoure, scoure1);
}
}
其他给数据类型的操作
实体类
@Test
public void testObj(){
User user=new User(***);
ValueOperations<String, User> operations=redisTemplate.opsForValue();
operations.set("user", user);
User u=operations.get("user");
System.out.println("user: "+u.toString());
}
hash(哈希)
@Test
public void testHash() {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put("hash","you","you");
String value=(String) hash.get("hash","you");
System.out.println("hash value :"+value);
}
hash set 的时候需要传入三个参数,第一个为 key,第二个为 field,第三个为存储的值。一般情况下 Key 代表一组数据,field 为 key 相关的属性,而 value 就是属性对应的值。
List
List 可以轻松的实现一个队列
@Test
public void testList() {
ListOperations<String, String> list = redisTemplate.opsForList();
list.leftPush("list","i");
list.leftPush("list","miss");
list.leftPush("list","you");
String value=(String)list.leftPop("list");
System.out.println(value.toString());
}
输出:you
list有许多可操作的api不多介绍
set
Redis set 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重的,当需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。
@Test
public void testSet() {
String key="set";
SetOperations<String, String> set = redisTemplate.opsForSet();
set.add(key,"i");
set.add(key,"miss");
set.add(key,"miss");
set.add(key,"you");
Set<String> values=set.members(key);
for (String v:values){
System.out.println("set value :"+v);
}
}
输出:
set value :i
set value :miss
set value :you
Zset
Redis sorted set 的使用场景与 set 类似,区别是 set 不是自动有序的,而 sorted set 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序,即自动排序。
在使用 Zset 的时候需要额外的输入一个参数 score,Zset 会自动根据 Score 的值对集合进行排序,我们可以利用这个特性来做具有权重的队列,比如普通消息的 score 为1,重要消息的 score 为2,然后工作线程可以选择按 score 的倒序来获取工作任务。
@Test
public void testZset(){
String key="zset";
redisTemplate.delete(key);
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
zset.add(key,"it",1);
zset.add(key,"you",6);
zset.add(key,"know",4);
zset.add(key,"neo",3);
Set<String> zsets=zset.range(key,0,3);
for (String v:zsets){
System.out.println("zset value :"+v);
}
Set<String> zsetB=zset.rangeByScore(key,0,3);
for (String v:zsetB){
System.out.println("zsetB value :"+v);
}
}
输出结果:
zset value :it
zset value :neo
zset value :know
zset value :you
zsetB value :it
zsetB value :neo
Zset 的数据会自动根据 score 进行排序