一、介绍
Infinispan 其实就是一个分布式缓存和数据网格平台,提供了高度可扩展和高性能数据缓存解决方案。Infinispan可以作为本地缓存或分布式缓存使用,支持事务、查询、处理大数据等功能。简单地说,Infinispan 可以理解为是 MySQL 的内存版本。
与 Redis 对比
Infinispan 与 Redis 都是一种缓存解决方案或工具,但两者之间存在着许多不同的地方。
- 设计目标:Infinispan 是一种数据网格和分布式计算平台,提供丰富的数据管理功能;而 Redis 主要设计为高性能的键值存储。
- 数据模型:Infinispan 主要提供键值存储模型,支持复杂的查询和事务,支持 JAVA 数据类型存储;Redis支持丰富的数据结构,如列表、集合等。
- 分布式特性:Infinispan 天生设计为分布式数据网格,提供数据分片、负载均衡等分布式特性;Redis 虽然也支持分布式部署,但需要通过 Redis 集群、哨兵模式等方式实现。
- 事务和查询:Infinispan 支持事务和基于 Lucene 的全文搜索、基于 SQL 的查询;Redis 的事务支持较为基础,查询能力主要限于基本的数据结构操作。
- 使用场景:Infinispan 适合需要复杂数据处理、分布式计算的场景,如大数据处理、实时分析等;Redis 适用于需要快速读写、高效缓存、简单消息队列的场景。
二、缓存模式
在 Infinispan 中,有一个缓存管理器叫做 CacheManager,能够用于创建和控制使用不同的缓存模式,比如本地缓存、分布式缓存、失效缓存等。
缓存模式分类
- Local(本地)
- Infinispan 作为单一节点运行
- 适用场景:不需要跨多个节点共享数据
- Replicated(复制)
- Infinispan 在集群中的所有节点上复制所有的缓存数据,并且只执行本地读取操作,即集群中每个节点的数据都是一致的,从任何一个节点都能读取到所需要的数据
- 适用场景:读密集型
- Distributed(分布式)
- Infinispan 在集群中的部分节点上复制缓存数据,并将数据分配给固定的大多数节点,即集群中的任意节点都不会有全部的数据,但读取的时候会请求拥有该数据的节点进行读取
- 适用场景:读/写操作均衡,并且需要数据在部分节点之间共享的场景
- Invalidation(失效)
- 每当有操作修改缓存数据时,Infinispan 会从所有节点中清理过期的数据,只进行本地读取操作(不会跨节点操作数据)
- 适用场景:一致性需求较高,但不需要分布式读取
- Scattered(分散)
- Infinispan 将缓存数据存储在集群中的部分节点上。默认情况下,Infinispan 为分散缓存分配一个主要拥有节点和一个备份拥有节点,主要拥有节点的分配方式与分布式缓存相同,而备份拥有节点始终是发起写入操作的节点。Infinispan 从至少一个拥有节点请求读取操作以确保返回正确的值。
- 适用场景:高并发读写,并且数据能够在多个节点之间进行备份和恢复。
缓存模式比较
三、Spring Boot 集成
项目demo地址:https://gitee.com/regexpei/infinispan-demo
1. 创建项目,添加依赖
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-spring-boot-starter-embedded</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-query</artifactId>
</dependency>
2.缓存配置
@Configuration
public class InfinispanCacheConfig {
@Bean
public InfinispanGlobalConfigurationCustomizer globalCustomizer() {
return builder -> builder.clusteredDefault().transport()
.clusterName("MyCluster")
.machineId("myMachine")
.rackId("Rack").siteId("China").build();
}
@Bean
public InfinispanCacheConfigurer cacheConfigurer() {
return manager -> {
final org.infinispan.configuration.cache.Configuration ispnConfig = new ConfigurationBuilder()
.clustering()
.cacheMode(CacheMode.LOCAL) // 设置缓存模式
.persistence() // 开启持久化
.passivation(false) // 缓存数据时持久化数据
.addSoftIndexFileStore() // 使用软索引文件存储方式
.dataLocation("cache-data") // 持久化数据存储位置
.indexLocation("cache-index") // 持久化索引存储位置
.shared(false) // 不共享持久化存储
.memory().maxCount(1000000L) // 设置缓存最大数量, 1000000L 表示存储一百万条数据
.build();
manager.defineConfiguration("local-sync-config", ispnConfig);
};
}
@Bean(name = "large-cache")
public org.infinispan.configuration.cache.Configuration largeCache() {
return new ConfigurationBuilder()
.clustering().cacheMode(CacheMode.LOCAL)
.indexing().enable().storage(IndexStorage.FILESYSTEM).path("my-fs-cache-inf")
.addIndexedEntities(UserInfoEntity.class)
.memory().maxCount(1000000L)
.build();
}
}
3.实体类
@Data
@Table(value = "user_info")
@Indexed
public class UserInfoEntity implements Serializable {
@Id(keyType = KeyType.Auto)
private Long id;
@KeywordField
private String name;
private String idCard;
private String phoneNumber;
@FullTextField
private String desc;
private String email;
private String bankCard;
@GenericField
private LocalDateTime updateTime;
private LocalDateTime createTime;
}
- 实体类需要实现序列化
- 实体类需要添加
@Indexed
注解 - 字段注解
@KeywordField
:关键词索引,建议用于内容只有数字和字母的字段上,比如id、taskNo,数据类型只能是String@FullTextField
:全文索引,建议用在需要模糊匹配、文本检索的字段上,比如taskName,对于纯数字字母内容的字段,只能全匹配,模糊匹配会失效,数据类型只能是String@GenericField
:普通索引,建议除了添加@KeywordField和@FullTextField注解之外的字段都加上这个索引,因为SQL中出现使用@FullTextField注解的字段不能和没有添加注解的字段起使用
4. Controller
@Slf4j
@RestController
@RequestMapping("users")
public class UserController {
@Autowired
UserInfoMapper userInfoMapper;
@Autowired
UserInfoService userInfoService;
@Autowired
EmbeddedCacheManager cacheManager;
@GetMapping("{id}")
public UserInfoEntity getOne(@PathVariable Long id){
return userInfoMapper.selectOneByCondition(USER_INFO_ENTITY.ID.eq(id));
}
@GetMapping("add-cache")
public String addCache(@RequestParam long page, @RequestParam(defaultValue = "10000") long pageSize){
long count = userInfoMapper.selectCountByCondition(QueryCondition.createEmpty());
long total = count / pageSize + 1;
log.info("分页总数:{}", total);
int coreSize = Runtime.getRuntime().availableProcessors() * 2 + 1;
ThreadPoolExecutor executor = ThreadUtil.newExecutor(coreSize, coreSize + 5);
for (long i = 1; i <= total; i++) {
long finalI = i;
executor.execute(() -> userInfoService.addToCache(finalI, pageSize));
}
log.info("缓存完成!");
return "ok";
}
@PostMapping("search")
public List<UserInfoEntity> query(@RequestBody Map<String, Object> parameters){
String sql = (String) parameters.get("sql");
Integer page = (Integer) parameters.get("page");
Integer pageSize = (Integer) parameters.get("pageSize");
return userInfoService.search(sql, page, pageSize);
}
@DeleteMapping("{id}")
public int del(@PathVariable Long id){
return userInfoMapper.deleteByCondition(USER_INFO_ENTITY.ID.eq(id));
}
@GetMapping("count")
public long count() {
Cache<String, UserInfoEntity> cache = cacheManager.getCache("large-cache");
OptionalLong optionalLong = Search.getQueryFactory(cache)
.create("from cn.regexp.infinispan.entity.UserInfoEntity")
.execute().hitCount();
return optionalLong.orElse(0L);
}
@GetMapping("clear")
public String clear() {
Cache<String, UserInfoEntity> cache = cacheManager.getCache("large-cache");
cache.clear();
return "ok";
}
}
四、问题记录
-
ISPN000343: Must have a transport set in the global configuration in order to define a clustered cache
infinispan.xml配置文件添加以下内容:
<infinispan xmlns="urn:infinispan:config:13.0"> <cache-container> <transport cluster="MyCluster" machine="LinuxServer01" rack="Rack01" site="US-WestCoast"/> </cache-container> </infinispan>
-
ISPN000403: No indexable classes were defined for this indexed cache. The configuration must contain classes or protobuf message types annotated with ‘@Indexed’
infinispan.xml配置文件添加以下内容:
<infinispan xmlns="urn:infinispan:config:13.0"> <cache-container> <replicated-cache segments="256" mode="SYNC" statistics="true" name="replicatedCache"> <indexing enabled="true" storage="local-heap"> <index-reader refresh-interval="1000"/> <indexed-entities> <indexed-entity>cn.regexp.infinispan.entity.UserInfoEntity</indexed-entity> </indexed-entities> </indexing> </replicated-cache> </cache-container> </infinispan>