一、pom添加依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.13.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
二、注入Bean RedisTemplate, 配置RedisConfig
想获得RedisTemplate分为两种情况:
1.redis缓存的对象只有String,可以直接注入StringRedisTemplate。
遵循springboot配置最简化的思想,springcloud在引入spring-boot-starter-data-redis就初始化注入了StringRedisTemplate,我们只需要直接注入使用就行了。StringRedisTemplate满足所有对字符串的操作。
如下为redis一般操作,和一个用redis缓存做的分布式锁(略去Service):
@Service public class RedisServiceImpl implements IRedisService { private static final Logger LOG = LoggerFactory.getLogger(RedisServiceImpl.class); @Autowired private StringRedisTemplate redisTemplate; private Integer defaultExpire = 20 * 1000; private static final String LOCK_TITLE = "redisLock_"; private static final Integer LOCK_TRY_INTERVAL = 500; /** * 释放锁 * @param lockName 锁Key */ public void unlock(String lockName) { remove(LOCK_TITLE + lockName); } /** * 删除对应的value * @param key */ @Override public void remove(final String key) { if (exists(key)) { redisTemplate.delete(key); } } /** * 批量删除对应的value * @param keys */ @Override public void remove(final String... keys) { for (String key : keys) { remove(key); } } /** * 判断缓存中是否有对应的value * @param key * @return */ @Override public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * 写入缓存 * @param key * @param value * @return */ @Override public boolean set(final String key, String value) { boolean result = true; try { redisTemplate.opsForValue().set(key, value); } catch (Exception e) { result = false; e.printStackTrace(); } return result; } /** * 写入缓存并设置过期时间 * @param key * @param value * @param expireTime:过期时间 * @return */ @Override public boolean set(final String key, String value, Long expireTime) { boolean result = true; try { redisTemplate.opsForValue().set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); } catch (Exception e) { result = false; e.printStackTrace(); } return result; } /** * 读取缓存 * @param key * @return */ @Override public Object get(final String key) { Object result = null; try { result = redisTemplate.opsForValue().get(key); } catch (Exception e) { e.printStackTrace(); } return result; } /** * 默认失效时间30s * @param lockName * @return */ @Override public Boolean lock(String lockName) { return this.tryLock(lockName, 0, defaultExpire, TimeUnit.MILLISECONDS); } @Override public Boolean tryLock(String key, long timeout, long expireTime, TimeUnit unit) { try { key = LOCK_TITLE + key; long startTimeMillis = System.currentTimeMillis(); do { long newValue = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(expireTime, unit); Boolean isOk = redisTemplate.opsForValue().setIfAbsent(key, newValue + ""); if (isOk) { redisTemplate.expire(key, expireTime, unit); return true; } // 获取过期时间 String oldExpireTime = redisTemplate.opsForValue().get(key); if (null == oldExpireTime) { oldExpireTime = "0"; } long oldExpire = Long.parseLong(oldExpireTime); if (oldExpire >= System.currentTimeMillis()) { //过了超时时间,直接返回false if ((System.currentTimeMillis() - startTimeMillis) > timeout) { return false; } Thread.sleep(LOCK_TRY_INTERVAL); } // 过了设置的失效时间,重新分配锁 if (oldExpire < System.currentTimeMillis()) { long newExpireTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(expireTime, unit); String currentExpireTime = redisTemplate.opsForValue().getAndSet(key, newExpireTime + ""); if (null == currentExpireTime) { currentExpireTime = "0"; } if (currentExpireTime.equals(oldExpireTime)) {//通过redis操作的原子性保证只有一个线程拿到锁 // 获取到锁 redisTemplate.expire(key, expireTime, unit); return true; } } } while (true); } catch (Exception e) { e.printStackTrace(); return false; } } }
2.redis缓存需要操作其他数据类型,需配置RedisTemplate<N, T>。
这里需要注意的是redis在开发环境配置单机版,到正式环境却是集群,前面说的StringRedisTemplate已经做好了兼容。自己配置时候也要做一个兼容配置,这里我们通过集群节点“nodes”是否有值来区分redis是单机还是集群。
@Configuration public class RedisConfig { private static final Logger LOG = LoggerFactory.getLogger(RedisConfig.class); @Value("${spring.redis.cluster.nodes}") private String nodes; /** * 公共连接池 */ @ConfigurationProperties(prefix = "spring.redis.pool") public JedisPoolConfig getRedisConfig() { return new JedisPoolConfig(); } public JedisConnectionFactory getConnectionFactoryCluster() { JedisPoolConfig jedisPoolConfig = this.getRedisConfig(); RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(); if (StringUtils.isNotBlank(nodes)) { List<RedisNode> nodeList = new ArrayList<>(); String[] cNodes = nodes.split(","); for (String node : cNodes) { String[] hp = node.split(":"); nodeList.add(new RedisNode(hp[0], Integer.parseInt(hp[1]))); } redisClusterConfiguration.setClusterNodes(nodeList); } return new JedisConnectionFactory(redisClusterConfiguration, jedisPoolConfig); } @ConfigurationProperties(prefix = "spring.redis") public JedisConnectionFactory getSingleConnectionFactory() { JedisConnectionFactory factory = new JedisConnectionFactory(); JedisPoolConfig config = getRedisConfig(); factory.setPoolConfig(config); return factory; } @Bean @ConfigurationProperties(prefix = "spring.redis") public JedisConnectionFactory getConnectionFactory() { JedisConnectionFactory factory; if (StringUtils.isNotBlank(nodes)) { factory = getConnectionFactoryCluster(); } else { factory = getSingleConnectionFactory(); } return factory; } @Bean @Primary public RedisTemplate<String, Object> redisTemplate() { JedisConnectionFactory jedisConnectionFactory = getConnectionFactory(); 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); RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(jedisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(jackson2JsonRedisSerializer); return template; } }
这样配置后我们使用RedisTemplate,就可以操作Redis的其他数据类型了。
三、附各个数据类型应用场景:
类型 | 简介 | 特性 | 场景 |
---|---|---|---|
String(字符串) | 二进制安全 | 可以包含任何数据,比如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、带权重的消息队列 |