目录
基础概念和问题
缓存相关概念
缓存相关问题
本地缓存
Guava Cache
EHCache
远程缓存
Redis集群
对于缓存大家应该都不陌生,缓存的核心是用空间换实践,通过分配一块高速存储区域(一般都是内存)来提高数据的读写效率,其实现的难点在于清空策略的实现,比较合理的思路就是定时回收与及时判断数据是否过期相结合。
下面文章是转发的,主要从本地缓存、远程缓存和分布式缓存集群几个方面来介绍缓存的相关概念。仅供大家参考学习。
Java的本地缓存很早就有了相关标准javax.cache
,要求的特性包括原子操作、缓存读写、缓存事件监听器、数据统计等内容。实际工作中本地缓存主要用于特别频繁的稳定数据,不然的话带来的数据不一致会得不偿失。实践中,常使用Guava Cache
,以及与Spring结合良好的EhCache
这是一个全内存的本地缓存实现,它提供了线程安全的实现机制,简单易用,性能好。其创建方式包括cacheLoader
和callable callback
两种,前者针对整个cache
,而后者比较灵活可以在get
时指定。 CacheBuilder.newBuilder()
方法创建cache
时重要的几个方法如下所示,之后是一个简单的使用示例。 maximumSize(long)
:设置容量大小,超过就开始回收。 expireAfterAccess(long, TimeUnit)
:在这个时间段内没有被读/写访问,就会被回收。 expireAfterWrite(long, TimeUnit)
:在这个时间段内没有被写访问,就会被回收 。 removalListener(RemovalListener)
:监听事件,在元素被删除时,进行监听。
@Service
public class ConfigCenterServiceImpl implements ConfigCenterService {
private final static long maximumSize = 20;
/**
* 最大20个,过期时间为1天
*/
private Cache> cache = CacheBuilder.newBuilder().maximumSize(maximumSize)
.expireAfterWrite(1, TimeUnit.DAYS).build();
@Autowired
private ConfigAppSettingDAO configAppSettingDAO;
@Override
public ConfigAppSettingDto getByTypeNameAndKey(String configType, String appID, String key) {
Map map = getByType(configType, appID);
return map.get(key);
}
/************************** 辅助方法 ******************************/
private Map getByType(String configType, String appID) {
try {
return cache.get(configType, new Callable
EHCache也是一个全内存的本地缓存实现,符合javax.cache JSR-107
规范,被应用在Hibernate中,过去存在过期失效的缓存元素无法被GC掉,造成内存泄露的问题,其主要类型及使用示例如下所示。 Element:缓存的元素,它维护着一个键值对。 Cache:它是Ehcache的核心类,它有多个Element,并被CacheManager管理,实现了对缓存的逻辑操作行为。 CacheManager:Cache的容器对象,并管理着Cache的生命周期。
Spring Boot整合Ehcache示例
//maven配置
org.springframework.boot
spring-boot-starter-cache
net.sf.ehcache
ehcache
//开启Cache
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
//方式1
@CacheConfig(cacheNames = "users")
public interface UserRepository extends JpaRepository {
@Cacheable
User findByName(String name);
}
//方式2
@Service
public class CacheUserServiceImpl implements CacheUserService {
@Autowired
private UserMapper userMapper;
@Override
public List getUsers() {
return userMapper.findAll();
}
// Cacheable表示获取缓存,内容会存储在people中,包含两个Key-Value
@Override
@Cacheable(value = "people", key = "#name")
public User getUser(String name) {
return userMapper.findUserByName(name);
}
//put是存储
@CachePut(value = "people", key = "#user.userid")
public User save(User user) {
User finalUser = userMapper.insert(user);
return finalUser;
}
//Evict是删除
@CacheEvict(value = "people")
public void remove(Long id) {
userMapper.delete(id);
}
}
//在application.properties指定spring.cache.type=ehcache即可
//在src/main/resources中创建ehcache.xml
常见的远程缓存组件包括memcached,redis等。前者性能高效,使用方便,但功能相对单一,只支持字符串类型的数据,需要结合序列化协议,只能用作缓存。后者是目前最流行的缓存服务器,具有高效的存取速度,高并发的吞吐量,并且有丰富的数据类型,支持持久化。因此,应用场景非常多,包括数据缓存、分布式队列、分布式锁、消息中间件等。
Redis支持更丰富的数据结构, 例如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 此外,Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。可以说Redis兼具了缓存系统和数据库的一些特性,因此有着丰富的应用场景。本文介绍Redis在Spring Boot中两个典型的应用场景。 场景1:数据缓存
//maven配置
org.springframework.boot
spring-boot-starter-redis
//application.properties配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# 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
//方法1
@Configuration
@EnableCaching
public class CacheConfig {
@Autowired
private JedisConnectionFactory jedisConnectionFactory;
@Bean
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
return redisCacheManager;
}
@Bean
public RedisTemplate
场景2:共享Session
//maven配置
org.springframework.session
spring-session-data-redis
//Session配置
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*)//
public class SessionConfig {
}
//示例
@RequestMapping("/session")
@RestController
public class SessionController {
@Autowired
private UserRepository userRepository;
@RequestMapping("/user/{id}")
public User getUser(@PathVariable Long id, HttpSession session) {
User user = (User) session.getAttribute("user" + id);
if (null == user) {
user = userRepository.findOne(id);
session.setAttribute("user" + id, user);
}
return user;
}
}
Spring Redis默认使用JDK进行序列化和反序列化,因此被缓存对象需要实现java.io.Serializable接口,否则缓存出错。
实现包括如下3种方式,相对于传统的大客户端分片和代理模式,路由查询的方式比较新颖,具体解决方案推荐redis-cluster。
redis集群具体的实现示例可以参考原文。还没有完全都理解透彻,就不在这里摘录了。
文章来源:Java缓存深入理解