首页 文章 精选 留言 我的

精选列表

搜索[分布式调度],共10000篇文章
优秀的个人博客,低调大师

深入浅出RedisTimeSeries-分布式数据库

Part 1 - 背景 Redis作为一个灵活的高性能 key-value数据结构存储,可以用来作为数据库、缓存和消息队列。Redis 对比其他 key-value缓存产品有以下特点: Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载到内存使用。 Redis支持字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等数据结构的存储。 时序数据是指一串按照时间维度索引的数据,其特点是没有严格的关系模型,记录的信息可以表示成键和值的关系,因此并不需要关系型数据库进行保存。在实际应用中,时序数据通常是持续高并发写入的。针对时序数据的这一特性,Redis基于自身数据结构和扩展模块,提供了用于保存时间序列数据的两种方案: 1、基于Hash和Sorted Set数据保存时间序列数据; 2、基于RedisTimeSeries模块实现。 1.基于Hash保存时间序列数据 基于Hash保存时间序列数据的特点是可以实现对单键的快速查询,能够满足对时间序列数据的单键查询需求。Redis的Hash实现方式是将内部存储的value作为一个HashMap,并提供了用于直接存取Map成员的接口,将时间戳作为Hash集合的key,设备状态值作为Hash集合的value,因此对数据的修改和存取都可直接通过其内部Map的Key来实现操作对应属性数据,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。 但是,基于Hash保存时间序列数据的短板在于无法支持对数据的范围查询,虽然时间序列是按照时间顺序插入Hash集合的,但是Hash类型的底层结构是Hash表,并没有实现对数据的有序索引,因此要对Hash类型进行范围查询,则需要扫描Hash集合中的所有数据,再将这些数据取回客户端进行排序,之后才能在客户端得到查询范围内的数据,查询效率很低。 2、基于Sorted Set保存时间序列数据 基于Sorted Set保存时间序列数据的特点是能够同时支持按时间戳范围的查询,能够根据元素的权重值来排序,在时序数据的情况下,将时间戳作为Sorted Set集合的权重值,后跟时间点上记录的测量数据,例如:<时间戳>:<测量值>。RedisSorte Set的内部使用Hash Map和SkipList来保证数据的存储有序,使用SkipList的结构可以保证具有较高的查询效率,并且在实现上比较简单。 但是,基于Sorted Set保存时间序列数据策略的短板在于其仅仅能支持范围查询,无法直接完成对时序数据的聚合计算。因此,只能先把时间范围内的数据取回到客户端,然后在客户端自行完成聚合计算。这个方法虽然能完成聚合计算,但是会带来一定的潜在风险,也就是大量数据在Redis实例和客户端间频繁传输,这会和其他操作命令竞争网络资源,导致其他操作变慢。因此SortedSets 不是一种节约内存的数据结构,其插入的时间复杂度是 O(log(N)),因此集群越大,写入耗时越长。 综合来讲,基于Hash和SortedSet保存时间序列的策略短板主要包含两个方面:其一是当执行聚合计算时,需要把数据读取到客户端内再进行聚合,当存在大量数据需要聚合时,数据传输开销大;其二是当使用该策略时,所有的数据会在两个数据类型中各保存一份,内存开销大。 2、基于RedisTimeSeries保存时间序列数据 RedisTimeSeries作为Redis的一个扩展模块,它弥补了Redis基于Hash和Sorted Set保存时间序列数据内存和数据传输开销大的缺陷,它专门面向时间序列数据提供了数据类型和访问接口,并且支持在Redis端上直接对数据进行时间范围的聚合计算。它使用固定大小的内存块作为时间序列样本,采用与Redis Streams 相同的Radix Tree来实现索引。RedisTimeSeries 的底层数据结构使用了链表,范围查询的复杂度是 O(N) 级别。这种基于RedisTimeSeries保存时间序列数据的策略具有以下特点: 保证大容量插入,低延迟读取; 按开始时间和结束时间查询; 支持任何时间桶的聚合查询(min、max、avg、sum、range、count、first、last);支持配置保留时间; 下采样/压缩-自动更新的聚合时间序列; 二级索引-每个时间序列都有标签,允许按标签查询。 Part 2 - RedisTimeSeries存储结构 RedisTimeSeries将所有的时序数据存储在chunks中。每个chunks均由双向链表中的两个相关数组组成(一个用于时间戳,一个用于样本值)。每个chunks都有预定义的样本大小,当chunks填满的时候,其他数据将自动存储到下一个chunks。chunks size可以通过参数 CHUNK_SIZE进行设置。(CHUNK_SIZE的设置必须为8的倍数,默认值:4096) RedisTimeSeries的Key由metric指标和tags组成,其中每个Sample是时间和值的组合。标签是我们附加到数据点的键值元数据,允许我们进行分组和过滤。它们可以是字符串或数值,并在创建时添加到时间序列。 Part 3 -RedisTimeSeries的使用 当用于时间序列数据存取时,RedisTimeSeries的操作主要包含以下几个方面: 1、TS.CREATE命令 TS.CREATE命令用于创建时间序列数据集合,使用时需要设置时间序列数据集合的key和数据过期时间(以毫秒为单位)。还可以为数据集合设置标签,来表示数据集合的属性。说明: RETENTION:选填,数据保留时间,默认:0; ENCODING:选填,指定系列样本编码格式,分为COMPRESSED、UNCOMPRESSED两种格式; CHUNK_SIZE:选填,块的大小; DUPLICATE_POLICY:选填,配置对重复样本执行的操作,默认BLOCK堵塞状态。状态类型:(BLOCK,FIRST,LAST,MIN,MAX,SUM) LABELS:必填,数据标签。 实例1: 2、TS.ADD命令 TS.ADD命令用于向时间序列集合中插入数据,其中包括时间戳和具体的数值。若先前尚未使用TS.CREATE创建时间序列,将自动创建时序数据集合。 注意:不能向最后一次使用的时间戳之前添加数据。使用 TS.ADD 命令添加值的时间戳必须要大于最后一个值的时间戳。 实例2: 也可以使用 * 让Redis将自动生成时间戳。 实例3: TS.MADD命令用于向已存在的时间序列集合中插入新样本数据。 实例4: 3、TS.GET命令 TS.GET命令用于读取时间序列集合的最新数据。 实例5: TS.MGET命令用于按标签查询集合中的最新数据。在使用TS.CREATE创建数据集合时,可以给集合设置标签属性。当进行查询时,就可以在查询条件中根据集合标签属性对数据样本进行匹配,其查询结果只返回满足匹配集合的最新数据。 实例6: 下列TS.MGET命令,以及FILTER设置(这个配置项用来于设置集合标签的过滤条件),查询area_id等于32的所有数据集合,并返回各自集合中最新一条数据。 4、TS.RANGE/TS.RERANGE命令 TS.RANGE/RERANGE命令用于支持时间序列集合聚合计算的范围查询; 说明: [FROM_TIMESTAMP][TO_TIMESTAMP]:必填,起始时间戳; [FILTER_BY_TS]:选填,按照时间戳过滤样本数据; [FILTER_BY_VALUES]:选填,按照value值过滤样本数据; [COUNT]:选填,返回样本的最大数量。 [AGGREGATION]:选填,指定要执行的聚合计算类型,RedisTimeSeries支持的聚合计算类型很丰富,包括AVG、MAX、MIN、SUM、COUNT、LAST、FIRST。 实例7: TS.MRANGE命令通过FILTERS过滤查询跨多个时间序列的范围。 说明: [FROM_TIMESTAMP][TO_TIMESTAMP]:起始时间戳,也看用"- +"表示从开始到最新时间戳的所有内容; [FILTER_BY_TS]:按时间戳过滤时间序列集合; [FILTER_BY_VALUES]:按value值过滤时间序列集合; [WITHLABELS]:包含时间序列元数据标签的键值对。若[WITHLABELS]或[SELECTED_LABELS]未设置,默认情况下,标签数组位置会回复一个空数组; [GROUPBY]:汇总不同时间序列的结果,按提供的label名称分组; [REDUCE]:用于聚合具有相同标签值得系列的reducer类型。 实例: 5、RedisTimeSeries其他命令 TS.DEL KEY_NAME FROM_TIMESTAMP TO_TIMESTAMP:删除给定KEY_NAME的时间戳范围内的值; DEL KEY_NAME:删除已创建的KEY; TS.ALTER KEY_NAME [RETENTION] LABELS:更改已创建键的元数据,包括label和保留值; TS.INCREBY/TS.DECREBY:在最新数据上增加/减少某个值; TS.INFO:返回时间序列的信息和统计数据; KEYS *:获取所有KEY; EXISTS KEY_NAME:检查给定KEY是否存在,若存在返回1,否返回0。 Part 4 - 总结 RedisTimeSeries作为Redis的一种扩展模块,它的出现为时序数据的存取提供了一种新方法,具有高效的查询性能,并且在存取过程中仅需很小的开销,便能够实现时序数据实时分析的愿望。

优秀的个人博客,低调大师

高性能分布式文件系统 FastCFS V3.0 发布

经过整整5个月的潜心研发,FastCFS v3.0终于发布了。FastCFS 3.0 主要改进:核心组件FastDIR通过插件方式实现数据存储引擎,采用binlog + 存储引擎插件,按需加载inode数据,单机以有限内存(如64GB)支持100亿级的海量文件。 通过binlog实现数据持久化比较简单,程序重启时通过binlog重放将inode数据全部加载到内存中,这种方式存储海量文件存在如下两个问题: 1. 程序启动就绪时间长; 2. 对内存空间要求非常高。 V3.0引入存储引擎插件,很好地解决了单纯通过binlog实现数据持久化的两大问题。后续会有技术文章详细介绍FastDIR存储引擎的原理和特点,敬请期待。 另外,FastCFS 3.0 修复了如下3个bug: [fdir] increase/decrease parent's nlink on rename operation [fdir] set dentry->kv_array->count to 0 correctly [fstore] should init barray->count to 0 欢迎大家安装使用最新版本的FastCFS,有任何疑问和建议随时交流。

优秀的个人博客,低调大师

OOX - 0.2.0 发布, 使用 Node.JS 快速构建分布式服务

更新内容 使用AsyncLocalStorage实现链路调用跟踪, 替换旧的属性名记录上下文@5eb5fe8 修复微服务模式下, 入口文件为index.js且未创建服务目录时, 栈溢出问题@ffa212d Socket.IO 更新至4.4.0 OOX 是什么 基于 Node.JS 实现的,更简单更快速的微服务解决方案,告别路由定义和各种配置文件,和调用本地函数一样调用远程服务,同时当不需要微服务运行时,也可以像传统应用一样单例运行。 服务端口不变同时支持 HTTP / Socket.IO,既能满足一般类 Web 的接口调用需求,又能满足长连接流式数据传输。 示例 // demo/index.js const calculator = require ( './calculator' ) exports.calc = async exp => { const [ arg1, arg2 ] = exp.match ( /\d+/g ) const [ op ] = exp.match(/[\+\-\*\/]/g) const returns = await calculator [ op ] ( +arg1, +arg2 ) return `${arg1}${op}${arg2}=${returns}` } // demo/calculator.js module.exports = { '+': ( a, b ) => a + b, '-': ( a, b ) => a - b, '*': ( a, b ) => a * b, '/': ( a, b ) => a / b, } 单例运行 oox index.js port=8001 微服务运行 # 8001端口运行 index 服务 oox group=demo demo/index.js port=8001 # 随机端口运行 culator 服务, 并在主服务注册 oox group=demo demo/calculator.js registry=:8001 访问 # httpie 为例 http :8001 action=calc params='3*5'

优秀的个人博客,低调大师

搞懂分布式技术14:Spring Boot使用注解集成Redis缓存

2 --> 为了提高性能,减少数据库的压力,使用缓存是非常好的手段之一。本文,讲解 Spring Boot 如何集成缓存管理。 Spring注解缓存 Spring 3.1之后,引入了注解缓存技术,其本质上不是一个具体的缓存实现方案,而是一个对缓存使用的抽象,通过在既有代码中添加少量自定义的各种annotation,即能够达到使用缓存对象和缓存方法的返回对象的效果。Spring的缓存技术具备相当的灵活性,不仅能够使用SpEL(Spring Expression Language)来定义缓存的key和各种condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存集成。其特点总结如下: 少量的配置annotation注释即可使得既有代码支持缓存; 支持开箱即用,不用安装和部署额外的第三方组件即可使用缓存; 支持Spring Express Language(SpEL),能使用对象的任何属性或者方法来定义缓存的key和使用规则条件; 支持自定义key和自定义缓存管理者,具有相当的灵活性和可扩展性。 和Spring的事务管理类似,Spring Cache的关键原理就是Spring AOP,通过Spring AOP实现了在方法调用前、调用后获取方法的入参和返回值,进而实现了缓存的逻辑。而Spring Cache利用了Spring AOP的动态代理技术,即当客户端尝试调用pojo的foo()方法的时候,给它的不是pojo自身的引用,而是一个动态生成的代理类。 如图12所示,实际客户端获取的是一个代理的引用,在调用foo()方法的时候,会首先调用proxy的foo()方法,这个时候proxy可以整体控制实际的pojo.foo()方法的入参和返回值,比如缓存结果,比如直接略过执行实际的foo()方法等,都是可以轻松做到的。Spring Cache主要使用三个注释标签,即@Cacheable、@CachePut和@CacheEvict,主要针对方法上注解使用,部分场景也可以直接类上注解使用,当在类上使用时,该类所有方法都将受影响。我们总结一下其作用和配置方法,如表1所示。 表1 标签类型 作用 主要配置参数说明 @Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 value:缓存的名称,在 Spring 配置文件中定义,必须指定至少一个;key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则默认按照方法的所有参数进行组合;condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 @CachePut 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用 value:缓存的名称,在 spring 配置文件中定义,必须指定至少一个;key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则默认按照方法的所有参数进行组合;condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 @CacheEvict 主要针对方法配置,能够根据一定的条件对缓存进行清空 value:缓存的名称,在 Spring 配置文件中定义,必须指定至少一个;key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则默认按照方法的所有参数进行组合;condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存;allEntries:是否清空所有缓存内容,默认为 false,如果指定为 true,则方法调用后将立即清空所有缓存;beforeInvocation:是否在方法执行前就清空,默认为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,默认情况下,如果方法执行抛出异常,则不会清空缓存 可扩展支持:Spring注解cache能够满足一般应用对缓存的需求,但随着应用服务的复杂化,大并发高可用性能要求下,需要进行一定的扩展,这时对其自身集成的缓存方案可能不太适用,该怎么办?Spring预先有考虑到这点,那么怎样利用Spring提供的扩展点实现我们自己的缓存,且在不改变原来已有代码的情况下进行扩展?是否在方法执行前就清空,默认为false,如果指定为true,则在方法还没有执行的时候就清空缓存,默认情况下,如果方法执行抛出异常,则不会清空缓存。 这基本能够满足一般应用对缓存的需求,但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓存不满意了,因为其不支持高可用性,也不具备持久化数据能力,这个时候,你就需要自定义你的缓存方案了,还好,Spring也想到了这一点。 我们先不考虑如何持久化缓存,毕竟这种第三方的实现方案很多,我们要考虑的是,怎么利用Spring提供的扩展点实现我们自己的缓存,且在不改原来已有代码的情况下进行扩展。这需要简单的三步骤,首先需要提供一个CacheManager接口的实现(继承至AbstractCacheManager),管理自身的cache实例;其次,实现自己的cache实例MyCache(继承至Cache),在这里面引入我们需要的第三方cache或自定义cache;最后就是对配置项进行声明,将MyCache实例注入CacheManager进行统一管理。 声明式缓存 Spring 定义 CacheManager 和 Cache 接口用来统一不同的缓存技术。例如 JCache、 EhCache、 Hazelcast、 Guava、 Redis 等。在使用 Spring 集成 Cache 的时候,我们需要注册实现的 CacheManager 的 Bean。 Spring Boot默认集成CacheManager Spring Boot 为我们自动配置了多个 CacheManager 的实现。Spring Boot 为我们自动配置了 JcacheCacheConfiguration、 EhCacheCacheConfiguration、HazelcastCacheConfiguration、GuavaCacheConfiguration、RedisCacheConfiguration、SimpleCacheConfiguration 等。 默认的 ConcurrenMapCacheManager Spring 从 Spring3.1 开始基于 java.util.concurrent.ConcurrentHashMap 实现的缓存管理器。所以, Spring Boot 默认使用 ConcurrentMapCacheManager 作为缓存技术。 以下是我们不引入其他缓存依赖情况下,控制台打印的日志信息。 Bean'cacheManager' of type [class org.springframework.cache.concurrent.ConcurrentMapCacheManager] 实战演练 Maven 依赖 首先,我们先创建一个 POM 文件。 <projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE</version> </parent> <groupId>com.lianggzone.demo</groupId> <artifactId>springboot-action-cache</artifactId> <version>0.1</version> <packaging>jar</packaging> <name>springboot-action-cache</name> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <defaultLibBundleDir>lib</defaultLibBundleDir> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>UTF-8</encoding> <useDefaultDelimiters>false</useDefaultDelimiters> <escapeString>\</escapeString> <delimiters> <delimiter>${*}</delimiter> </delimiters> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 其中,最核心的是添加 spring-boot-starter-cache 依赖。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> 开启缓存支持 在 Spring Boot 中使用 @EnableCaching 开启缓存支持。 @Configuration @EnableCaching publicclassCacheConfiguration{} 服务层 创建一个服务类 @Service("concurrenmapcache.cacheService") publicclassCacheService{ } 首先,我们先来讲解下 @Cacheable 注解。@Cacheable 在方法执行前 Spring 先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放进缓存。有两个重要的值, value,返回的内容将存储在 value 定义的缓存的名字对象中。key,如果不指定将使用默认的 KeyGenerator 生成。 我们在查询方法上,添加 @Cacheable 注解,其中缓存名称为 concurrenmapcache。 @Cacheable(value ="concurrenmapcache") publiclong getByCache(){ try{ Thread.sleep(3*1000); }catch(InterruptedException e){ e.printStackTrace(); } returnnewTimestamp(System.currentTimeMillis()).getTime(); } @CachePut 与 @Cacheable 类似,但是它无论什么情况,都会将方法的返回值放到缓存中, 主要用于数据新增和修改方法。 @CachePut(value ="concurrenmapcache") publiclong save(){ long timestamp =newTimestamp(System.currentTimeMillis()).getTime(); System.out.println("进行缓存:"+ timestamp); return timestamp; } @CacheEvict 将一条或多条数据从缓存中删除, 主要用于删除方法,用来从缓存中移除相应数据。 @CacheEvict(value ="concurrenmapcache") publicvoiddelete(){ System.out.println("删除缓存"); } 控制层 为了展现效果,我们先定义一组简单的 RESTful API 接口进行测试。 @RestController("concurrenmapcache.cacheController") @RequestMapping(value ="/concurrenmapcache/cache") publicclassCacheController{ @Autowired privateCacheService cacheService; /** * 查询方法 */ @RequestMapping(value ="", method =RequestMethod.GET) publicString getByCache(){ Long startTime =System.currentTimeMillis(); long timestamp =this.cacheService.getByCache(); Long endTime =System.currentTimeMillis(); System.out.println("耗时: "+(endTime - startTime)); return timestamp+""; } /** * 保存方法 */ @RequestMapping(value ="", method =RequestMethod.POST) publicvoid save(){ this.cacheService.save(); } /** * 删除方法 */ @RequestMapping(value ="", method =RequestMethod.DELETE) publicvoiddelete(){ this.cacheService.delete(); } } 运行 @RestController @EnableAutoConfiguration @ComponentScan(basePackages ={"com.lianggzone.springboot"}) publicclassWebMain{ publicstaticvoid main(String[] args)throwsException{ SpringApplication.run(WebMain.class, args); } } 课后作业 我们分为几个场景进行测试。 多次调用查询接口,查看缓存信息是否变化,控制台日志是否如下?你得到的结论是什么? 调用保存接口,再调用查询接口,查看缓存信息是否变化?你得到的结论是什么? 调用删除接口,再调用查询接口,接口响应是否变慢了?你再看看控制台日志,你得到的结论是什么? 扩展阅读 如果想更深入理解 Spring 的 Cache 机制,这边推荐两篇不错的文章。 Spring Cache 抽象详解 Spring 4.1 新特性 - Spring 缓存框架增强 源代码 Spring Boot In Practice:Redis缓存实战 阅读本文需要对Spring和Redis比较熟悉。 Spring Framework 提供了Cache Abstraction对缓存层进行了抽象封装,通过几个annotation可以透明给您的应用增加缓存支持,而不用去关心底层缓存具体由谁实现。目前支持的缓存有java.util.concurrent.ConcurrentMap,Ehcache 2.x,Redis等。 一般我们使用最常用的Redis做为缓存实现(Spring Data Redis), 需要引入的starter:spring-boot-starter-data-redis,spring-boot-starter-cache; 自动配置生成的Beans:RedisConnectionFactory,StringRedisTemplate,RedisTemplate,RedisCacheManager,自动配置的Bean可以直接注入我们的代码中使用; I. 配置 application.properties # REDIS (RedisProperties) spring.redis.host=localhost # Redis server host. spring.redis.port=6379 # Redis server port. spring.redis.password= # Login password of the redis server. 具体对Redis cluster或者Sentinel的配置可以参考这里 开启缓存支持 @SpringBootApplication @EnableCaching//开启caching public class NewsWebServer { //省略内容 } 定制RedisTemplate 自动配置的RedisTemplate并不能满足大部分项目的需求,比如我们基本都需要设置特定的Serializer(RedisTemplate默认会使用JdkSerializationRedisSerializer)。 Redis底层中存储的数据只是字节。虽然Redis本身支持各种类型(List, Hash等),但在大多数情况下,这些指的是数据的存储方式,而不是它所代表的内容(内容都是byte)。用户自己来决定数据如何被转换成String或任何其他对象。用户(自定义)类型和原始数据类型之间的互相转换通过RedisSerializer接口(包org.springframework.data.redis.serializer)来处理,顾名思义,它负责处理序列化/反序列化过程。多个实现可以开箱即用,如:StringRedisSerializer和JdkSerializationRedisSerialize。Jackson2JsonRedisSerializer或GenericJackson2JsonRedisSerializer来处理JSON格式的数据。请注意,存储格式不仅限于value 它可以用于key,Hash的key和value。 声明自己的RedisTemplate覆盖掉自动配置的Bean: //通用的RedisTemplate @Bean public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(jedisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //template.setHashKeySerializer(template.getKeySerializer()); //template.setHashValueSerializer(template.getValueSerializer()); return template; } 这里我们使用GenericJackson2JsonRedisSerializer而不是Jackson2JsonRedisSerializer,后者的问题是你需要为每一个需要序列化进Redis的类指定一个Jackson2JsonRedisSerializer因为其构造函数中需要指定一个类型来做反序列化: redis.setValueSerializer(new Jackson2JsonRedisSerializer<Product>(Product.class)); 如果我们应用中有大量对象需要缓存,这显然是不合适的,而前者直接把类型信息序列化到了JSON格式中,让一个实例可以操作多个对象的反序列化。 定制RedisCacheManager 有时候Spring Boot自动给我们配置的RedisCacheManager也不能满足我们应用的需求,我看到很多用法都直接声明了一个自己的RedisCacheManager,其实使用CacheManagerCustomizer可以对自动配置的RedisCacheManager进行定制化: @Bean public CacheManagerCustomizer<RedisCacheManager> cacheManagerCustomizer() { return new CacheManagerCustomizer<RedisCacheManager>() { @Override public void customize(RedisCacheManager cacheManager) { cacheManager.setUsePrefix(true); //事实上这是Spring Boot的默认设置,为了避免key冲突 Map<String, Long> expires = new HashMap<>(); expires.put("myLittleCache", 12L*60*60); // 设置过期时间 key is cache-name expires.put("myBiggerCache", 24L*60*60); cacheManager.setExpires(expires); // expire per cache cacheManager.setDefaultExpiration(24*60*60);// 默认过期时间:24 hours } }; } II. 使用 缓存Key的生成 我们都知道Redis是一个key-value的存储系统,无论我们想要缓存什么值,都需要制定一个key。 @Cacheable(cacheNames = "user") public User findById(long id) { return userMapper.findById(id); } 上面的代码中,findById方法返回的对象会被缓存起来,key由默认的org.springframework.cache.interceptor.SimpleKeyGenerator生成,生成策略是根据被标注方法的参数生成一个SimpleKey对象,然后由RedisTemplate中定义的KeySerializer序列化后作为key(注意StringRedisSerializer只能序列化String类型,对SimpleKey对象无能为力,你只能定义其他Serializer)。 不过大多数情况下我们都会采用自己的key生成方案,方式有两种: 1.实现自己的KeyGenerator; @Configuration @EnableCaching public class CacheConfig extends CachingConfigurerSupport { @Bean public KeyGenerator customKeyGenerator() { return new KeyGenerator() { @Override public Object generate(Object o, Method method, Object... objects) { StringBuilder sb = new StringBuilder(); sb.append(o.getClass().getName()); sb.append(method.getName()); for (Object obj : objects) { sb.append(obj.toString()); } return sb.toString(); } }; } } 2.在@Cacheable标注中直接声明key: @Cacheable(cacheNames = "user", key="#id.toString()") ❶ public User findById(long id) { return userMapper.findById(id); } @Cacheable(cacheNames = "user", key="'admin'") ❷ public User findAdmin() { return userMapper.findAdminUser(); } @Cacheable(cacheNames = "user", key="#userId + ':address'") ❸ public List<Address> findUserAddress(long userId) { return userMapper.findUserAddress(userId); } key的声明形式支持SpEL。❶ 最终生成的Redis key为:user:100234,user部分是因为cacheManager.setUsePrefix(true),cacheName会被添加到key作为前缀避免引起key的冲突。之所以#id.toString()要long型转为String是因为我们设置的KeySerializer为StringRedisSerializer只能用来序列化String。❷ 如果被标注方法没有参数,我们可以用一个静态的key值,最终生成的key为user:admin。❸ 最终生成的key为user:100234:address。 这种方式更符合我们以前使用Redis的习惯,所以推荐。 直接使用RedisTemplate 有时候标注不能满足我们的使用场景,我们想要直接使用更底层的RedisTemplate。 @Service public class FeedService { @Resource(name="redisTemplate") ❶ private ZSetOperations<String, Feed> feedOp; public List<Feed> getFeed(int count, long maxId) { return new ArrayList<>(feedOp.reverseRangeByScore(FEED_CACHE_KEY, 0, maxId, offset, count)); } //省略 } ❶ 我们可以直接把RedisTemplate的实例注入为ZSetOperations、ListOperations、ValueOperations等类型(Spring IoC Container帮我们做了转化工作,可以参考org.springframework.data.redis.core.ZSetOperationsEditor)。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册