{"id":16178953,"url":"https://github.com/zhongxunking/cache","last_synced_at":"2025-04-01T20:31:07.457Z","repository":{"id":46140164,"uuid":"314190774","full_name":"zhongxunking/cache","owner":"zhongxunking","description":"Cache是一款分布式场景下基于Redis的高性能强一致缓存组件，透明化的提供缓存高性能强一致能力、缓存防脏读能力、本地缓存能力、缓存防击穿能力、缓存防穿透能力、缓存防雪崩能力、缓存热key防御能力。 使用简单，兼容spring-cache，可与spring-boot无缝集成。","archived":false,"fork":false,"pushed_at":"2023-03-13T06:18:09.000Z","size":236,"stargazers_count":3,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-15T20:11:14.568Z","etag":null,"topics":["cache","redis","spring-boot","spring-cache"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zhongxunking.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-19T08:59:27.000Z","updated_at":"2025-02-26T08:02:04.000Z","dependencies_parsed_at":"2024-11-03T11:36:09.216Z","dependency_job_id":null,"html_url":"https://github.com/zhongxunking/cache","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongxunking%2Fcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongxunking%2Fcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongxunking%2Fcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zhongxunking%2Fcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zhongxunking","download_url":"https://codeload.github.com/zhongxunking/cache/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246660003,"owners_count":20813335,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cache","redis","spring-boot","spring-cache"],"created_at":"2024-10-10T05:24:58.580Z","updated_at":"2025-04-01T20:31:02.437Z","avatar_url":"https://github.com/zhongxunking.png","language":"Lua","readme":"# Cache\n\n1. 简介\n\u003e Cache是一款分布式场景下基于Redis的高性能强一致缓存组件，透明化的提供缓存高性能强一致能力、缓存防脏读能力、本地缓存能力、缓存防击穿能力、缓存防穿透能力、缓存防雪崩能力、缓存热key防御能力。 使用简单，兼容spring-cache，可与spring-boot无缝集成。\n\n\u003e 本组件已经上传到[Maven中央库](https://search.maven.org/search?q=g:org.antframework.cache%20AND%20a:cache)\n\n2. 环境要求\n\u003e * JDK1.8及以上\n\n3. [整体设计](https://mp.weixin.qq.com/s/RC_uxBE6TjEUjR6QGbN0IA)\n\n4. 技术支持\n\u003e 欢迎加我微信（zhong_xun_）入群交流。\u003cbr/\u003e\n\u003cimg src=\"https://note.youdao.com/yws/api/personal/file/WEB6b849e698db2a635b43eba5bc949ce1c?method=download\u0026shareKey=27623320b5ca82cbf768b61130c81de0\" width=150 /\u003e\n\n## 1. 将Cache引入进你的系统\n通过引入Maven依赖和进行少量配置即可将Cache引入进你的系统。\n\n### 1.1 引入Maven依赖\nCache支持SpringBoot v2.x，也支持SpringBoot v1.x\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.antframework.cache\u003c/groupId\u003e\n    \u003cartifactId\u003ecache\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.boot\u003c/groupId\u003e\n    \u003cartifactId\u003espring-boot-starter-data-redis\u003c/artifactId\u003e\n    \u003cversion\u003e${spring-boot版本}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### 1.2 配置\n在application.properties或application.yaml中配置Redis和Cache\n```properties\n# 必填：命名空间（也可以通过ant.cache.namespace配置）\nspring.application.name=customer    #这里使用customer（会员系统）作为举例\n\n# 必填：配置Redis（Cache采用的是spring-boot原生的Redis，所以原生的所有Redis配置都有效，以下以最简洁的配置举例）\n# Redis单例模式\nspring.redis.host=192.168.0.1\nspring.redis.port=6379\n# Redis集群模式\n#spring.redis.cluster.nodes=192.168.0.1:6379,192.168.0.2:6379,192.168.0.3:6379\n# Redis哨兵模式\n#spring.redis.sentinel.master=mymaster\n#spring.redis.sentinel.nodes=192.168.0.1:26379,192.168.0.2:26379,192.168.0.3:26379\n\n\n# 以下配置均是选填配置，使用方一般使用默认配置即可，无需自定义配置\n# Cache提供了灵活多样的配置，包括：开关相关配置、缓存有效期相关配置、本地缓存相关配置、统计相关配置等\n# 默认配置提供：缓存键值对在Redis的有效期为1小时，本地缓存键值对最大容量为10000，允许缓存null（更多细节配置可通过下面的配置查看和定制）\n\n# 开关相关配置\n# 选填：是否启用Cache（true为开启，false为关闭；默认启用）\nant.cache.enable=true\n# 选填：缓存开关（true为开启，false为关闭；默认开启）\nant.cache.cache-switch=true\n# 选填：默认是否允许缓存null（true为允许，false为不允许；默认允许）\n#      可以通过ant.cache.allow-null.caches.${cacheName}=false 来定制某个cache不允许缓存null\nant.cache.allow-null.def=true\n\n# 缓存有效期相关配置\n# 选填：默认的缓存键值对存活时长（单位：毫秒；-1表示永远有效不过期；默认为1小时）\n#      可以通过ant.cache.live-time.caches.${cacheName}=${具体时长} 来定制某个具体cache的键值对存活时长\nant.cache.live-time.def=3600000\n# 选填：默认的缓存键值对值为null的存活时长（单位：毫秒；-1表示永远有效不过期；默认为5分钟）\n#      可以通过ant.cache.null-value-live-time.caches.${cacheName}=${具体时长} 来定制某个具体cache的缓存键值对值为null的存活时长\nant.cache.null-value-live-time.def=300000\n# 选填：默认的缓存键值对存活时长的动态浮动比例（正数为向上浮动，负数为向下浮动；比如：-0.1表示向下浮动10%；默认为-0.1）\n#      可以通过ant.cache.live-time-float-rate.caches.${cacheName}=${具体浮动比例} 来定制某个具体cache的键值对存活时长的动态浮动比例\nant.cache.live-time-float-rate.def=-0.1\n\n# 缓存加锁相关配置\n# 选填：默认的缓存加写锁最长等待时长（单位：毫秒；-1表示永远等待直到加锁成功；默认为5秒）\n#      可以通过ant.cache.max-lock-wait-time.caches.${cacheName}=${具体时长} 来定制某个具体cache的最长等待时长\nant.cache.max-lock-wait-time.def=5000\n\n# 本地缓存相关配置\n# 选填：是否启用本地缓存（true为启用，false为不启用；默认启用）\nant.cache.local.enable=true\n# 选填：本地缓存内键值对最长存活时长（单位：毫秒；默认为5分钟）\nant.cache.local.live-time.max=300000\n# 选填：本地缓存键值对存活时长比率（比如：0.1表示本地缓存内的存活时长为标准存活时长的10%；默认为0.1）\nant.cache.local.live-time.scale-rate=0.1\n# 选填：默认的本地缓存的最大容量（-1表示无限制；默认为10000）\n#      可以通过ant.cache.local.max-size.caches.${cacheName}=${具体容量} 来定制某个具体本地缓存的最大容量\nant.cache.local.max-size.def=10000\n# 选填：是否启用本地缓存刷新（true为启用，false为不启用；默认启用）\nant.cache.local.refresher.enable=true\n# 选填：每隔多久将本地缓存与远程缓存不一致的键值对删掉（默认为5分钟）\nant.cache.local.refresher.period=300000\n# 选填：各缓存内键值对有修改时，是否通知各本地缓存删除被修改的键值对（true为通知，false为不通知；默认通知）\nant.cache.local.publisher.enable=true\n# 选填：各缓存键值对有变更时，发布消息的本地队列容量（默认为4096）\nant.cache.local.publisher.queue-size=4096\n# 选填：将通知消息放入本地队列时的超时时长（单位：毫秒；-1表示一直等待直到成功；默认为5秒）\nant.cache.local.publisher.in-queue-timeout=5000\n# 选填；最多将多少个通知消息打包成一个发布消息（默认为100）\nant.cache.local.publisher.max-batch-size=100\n# 选填：发布消息的线程数量（默认为4）\nant.cache.local.publisher.publish-threads=4\n# 选填：Redis发布器发布消息时使用的通道名称（默认为${命名空间}-cache-change）\nant.cache.local.publisher.redis.channel=customer-cache-change  #这里使用customer（会员系统）作为举例\n# 选填：监听缓存变更事件的优先级（默认为0）\nant.cache.local.listen-order=0\n\n# 缓存统计相关配置\n# 选填：是否启用缓存统计（true为启用，false为不启用；默认启用）\nant.cache.statistic.enable=true\n# 选填：统计的时间长度（单位：毫秒；默认为24小时）\nant.cache.statistic.time-length=86400000\n# 选填：统计的时间粒度（单位：毫秒；默认为1分钟）\nant.cache.statistic.time-granularity=60000\n\n# 缓存一致性方案5相关配置\n# 选填：是否启用缓存一致性方案5（true为启用，false为不启用；默认启用）\nant.cache.consistency-v5.enable=true\n# 选填：加锁器等待同步消息的最长时间（毫秒，默认为10秒）\nant.cache.consistency-v5.locker.max-wait-time=10000\n# 选填：发生异常时Redis中加锁器数据的存活时长（毫秒，默认为10分钟）\nant.cache.consistency-v5.locker.live-time=600000\n\n# 缓存BeanPostProcessor相关配置\n# 选填：装饰CacheInterceptor处理器的优先级（默认为Ordered.LOWEST_PRECEDENCE - 300）\nant.cache.bean-processor.decorate-cache-interceptor-order=2147483347\n# 选填：强制@Cacheable(sync=true)处理器的优先级（默认为Ordered.LOWEST_PRECEDENCE - 200）\nant.cache.bean-processor.force-sync-order=2147483447\n# 选填：装饰事务管理器BeanPostProcessor的优先级（默认为Ordered.LOWEST_PRECEDENCE - 100）\nant.cache.bean-processor.decorate-transaction-manager-order=2147483547\n```\n\n## 2. 使用Cache\nCache提供的各种能力对使用方来说几乎是透明的，使用方无需感知到Cache的存在，按照常规的使用spring-cache来操作缓存即可。\n具体如下：\n* 本地缓存能力、缓存防击穿能力、缓存防穿透能力、缓存防雪崩能力、缓存热key防御能力：对使用方来说是透明化的支持，使用方无需感知到Cache的存在。\n* 缓存高性能强一致能力、缓存防脏读能力：1、对于被缓存的对象是数据库中的数据，且数据库事务是通过spring-transaction来管理的场景（即95%以上的场景），对使用方来说是透明化的支持，使用方无需感知到Cache的存在。注意：在修改数据库中数据和缓存时，需先通过spring-transaction开启事务（通过@Transactional注解或PlatformTransactionManager开启事务），才能保证缓存强一致（即使没有本Cache，为了程序正确性，在修改数据库中数据时你也需要开启事务）。2、对于被缓存的对象不是数据库中的数据（文件或网络中的数据），或事务不是通过spring-transaction来管理的场景，则修改数据和缓存时需使用CacheTemplate才能保证缓存强一致和缓存防脏读。\n\nCache支持和兼容spring-cache的绝大部分能力，你可以直接使用spring-cache的注解和接口来透明的使用本Cache。 当然你也可以使用本Cache的接口和CacheTemplate来使用缓存。\n\n\u003e 注意：使用本Cache时无需担心spring-transaction管理的事务是否存在嵌套的情况（比如：一个service的@Transactional方法调用另一个service的@Transactional方法），本Cache都能很好的工作。并且本Cache具备对各个未提交事务的相互隔离能力，不会出现缓存脏读的情况。总而言之，使用本Cache让你可以透明化的具有缓存和数据库强一致的能力。\n\n### 2.1 通过spring-cache使用\n使用本Cache和自己使用spring-cache并无区别，按照常规的使用spring-cache来操作缓存即可，本Cache提供的各种能力对使用方来说是透明化的支持。\n\n#### 2.1.1 通过spring-cache的缓存注解使用（推荐）\n```java\n// 数据库操作Dao\n@org.springframework.stereotype.Repository\npublic class UserDao {\n    // 查询用户（有缓存则从缓存获取，无缓存则从数据库获取并设置缓存）\n    @org.springframework.cache.annotation.Cacheable(cacheNames = \"user\", key = \"#id\")\n    public User find(long id) {\n        // TODO 从数据库查询用户数据\n    }\n\n    // 新增用户（向数据库插入数据，并设置缓存）\n    @org.springframework.cache.annotation.CachePut(cacheNames = \"user\", key = \"#user.id\")\n    // @CacheEvict(cacheNames = \"user\", key = \"#user.id\")    // 也可以删除缓存\n    public User insert(User user) {\n        // TODO 向数据库插入用户数据\n    }\n\n    // 更新用户（向数据库修改数据，并设置缓存）\n    @org.springframework.cache.annotation.CachePut(cacheNames = \"user\", key = \"#user.id\")\n    // @CacheEvict(cacheNames = \"user\", key = \"#user.id\")    // 也可以删除缓存\n    public User update(User user) {\n        // TODO 向数据库更新用户数据\n    }\n\n    // 删除用户（向数据库删除数据，并删除缓存）\n    @org.springframework.cache.annotation.CacheEvict(cacheNames = \"user\", key = \"#id\")\n    public void delete(long id) {\n        // TODO 从数据库删除用户数据\n    }\n}\n\n// 业务操作Service\n@org.springframework.stereotype.Service\npublic class UserService {\n    @Autowired\n    private UserDao userDao;\n\n    // 查询用户\n    public User find(long id) {\n        return userDao.find(id);\n    }\n\n    // 新增用户\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void insert(User user) {\n        userDao.insert(user);\n    }\n\n    // 更新用户\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void update(User user) {\n        userDao.update(user);\n    }\n\n    // 删除用户\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void delete(long id) {\n        userDao.delete(id);\n    }\n}\n```\n\u003e 注意：\n\u003e 1. 本Cache不支持clear操作，所以@CacheEvict的allEntries属性不能设置为true\n\u003e 2. @Cacheable的sync属性已默认强制设置为true，所以cacheNames参数只能配置一个cacheName，配置多个会报类似这样错误：java.lang.IllegalStateException: @Cacheable(sync=true) only allows a single cache on 'Builder[public abstract demo.dal.App demo.dal.AppDao.findByAppId(java.lang.String)] caches=[app, app2] | key='#p0' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='true''\n\n#### 2.1.2 通过spring-cache的缓存接口使用\n```java\n// 业务操作Service\n@Service\npublic class UserService {\n    // 获取spring-cache的缓存接口\n    @Autowired\n    private org.springframework.cache.CacheManager cacheManager;\n\n    // 查询用户（有缓存则从缓存获取，无缓存则从数据库获取并设置缓存）\n    public User find(long id) {\n        Cache cache = cacheManager.getCache(\"user\");\n        return cache.get(id, () -\u003e {    // 对于读场景（有缓存则从缓存获取，无缓存则从数据库获取并设置缓存），需要使用本方法来获取数据，才能保证缓存的强一致（原因在注意事项中有说明）\n            // TODO 从数据库查询用户数据\n        });\n    }\n\n    // 新增用户（向数据库插入数据，并设置缓存）\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void insert(User user) {\n        // TODO 向数据库插入用户数据\n\n        // 设置缓存\n        Cache cache = cacheManager.getCache(\"user\");\n        cache.put(user.getId(), user);\n        // cache.evict(user.getId());   // 也可以删除缓存\n    }\n\n    // 更新用户（向数据库修改数据，并设置缓存）\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void update(User user) {\n        // TODO 向数据库更新用户数据\n\n        // 设置缓存\n        Cache cache = cacheManager.getCache(\"user\");\n        cache.put(user.getId(), user);\n        // cache.evict(user.getId());   // 也可以删除缓存\n    }\n\n    // 删除用户（向数据库删除数据，并删除缓存）\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void delete(long id) {\n        // TODO 从数据库删除用户数据\n\n        // 删除缓存\n        Cache cache = cacheManager.getCache(\"user\");\n        cache.evict(id);\n    }\n}\n```\n\u003e 注意：\n\u003e 1. 本Cache不支持clear操作，所以不能调用Cache.clear()方法\n\u003e 2. 为了保证缓存的强一致性，对于读场景（有缓存则从缓存获取，无缓存则从数据库获取并设置缓存），应该通过Cache.get(java.lang.Object, java.util.concurrent.Callable\u003cT\u003e)来获取数据，就像上面的查询用户方法一样。不能通过先调用Cache.get(java.lang.Object)获取缓存，自己判断缓存不存在再从数据库获取数据，最后调用Cache.put(java.lang.Object, java.lang.Object)方法设置缓存。否则的话可能会导致缓存不一致。因为Cache.get(java.lang.Object, java.util.concurrent.Callable)是原子性操作，而后面这种方式分散成了几个步骤后是非原子性操作。\n\n### 2.2 通过本Cache的接口和CacheTemplate使用\n\n#### 2.2.1 通过本Cache的接口使用\n本Cache的接口和spring-cache接口的名称、方法、作用都很相似\n```java\n// 业务操作Service\n@Service\npublic class UserService {\n    // 获取spring-cache的缓存接口\n    @Autowired\n    private org.antframework.cache.CacheManager cacheManager;\n\n    // 查询用户（有缓存则从缓存获取，无缓存则从数据库获取并设置缓存）\n    public User find(long id) {\n        Cache cache = cacheManager.getCache(\"user\");\n        return cache.get(id, User.class, () -\u003e {    // 对于读场景（有缓存则从缓存获取，无缓存则从数据库获取并设置缓存），需要使用本方法来获取数据，才能保证缓存的强一致（原因在注意事项中有说明）\n            // TODO 从数据库查询用户数据\n        });\n    }\n\n    // 新增用户（向数据库插入数据，并设置缓存）\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void insert(User user) {\n        // TODO 向数据库插入用户数据\n\n        // 设置缓存\n        Cache cache = cacheManager.getCache(\"user\");\n        cache.put(user.getId(), user);\n        // cache.evict(user.getId());   // 也可以删除缓存\n    }\n\n    // 更新用户（向数据库修改数据，并设置缓存）\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void update(User user) {\n        // TODO 向数据库更新用户数据\n\n        // 设置缓存\n        Cache cache = cacheManager.getCache(\"user\");\n        cache.put(user.getId(), user);\n        // cache.evict(user.getId());   // 也可以删除缓存\n    }\n\n    // 删除用户（向数据库删除数据，并删除缓存）\n    @org.springframework.transaction.annotation.Transactional  // 开启事务一是为了程序正确性（有数据修改），二是为了保证缓存强一致\n    public void delete(long id) {\n        // TODO 从数据库删除用户数据\n\n        // 删除缓存\n        Cache cache = cacheManager.getCache(\"user\");\n        cache.remove(id);\n    }\n}\n```\n\u003e 注意：\n\u003e 1. 本Cache不支持clear操作，所以Cache没有clear方法\n\u003e 2. 为了保证缓存的强一致性，对于读场景（有缓存则从缓存获取，无缓存则从数据库获取并设置缓存），应该通过Cache.get(java.lang.Object, java.lang.Class\u003cT\u003e, java.util.concurrent.Callable\u003cT\u003e)来获取数据，就像上面的查询用户方法一样。不能通过先调用Cache.get(java.lang.Object, java.lang.Class\u003cT\u003e)获取缓存，自己判断缓存不存在再从数据库获取数据，最后调用Cache.put(java.lang.Object, java.lang.Object)方法设置缓存。否则的话可能会导致缓存不一致。因为Cache.get(java.lang.Object, java.lang.Class\u003cT\u003e, java.util.concurrent.Callable\u003cT\u003e)是原子性操作，而后面这种方式分散成了几个步骤后是非原子性操作。\n\n#### 2.2.2 通过CacheTemplate使用\n对于被缓存的对象不是数据库中的数据（文件或网络中的数据），或事务不是通过spring-transaction来管理的场景，则修改数据和缓存时需使用CacheTemplate才能保证缓存强一致。\n```java\n// 业务操作Service\n@Service\npublic class UserService {\n    // 获取spring-cache的缓存接口\n    @Autowired\n    private org.antframework.cache.CacheManager cacheManager;\n    // 获取CacheTemplate\n    @Autowired\n    private CacheTemplate cacheTemplate;\n\n    // 查询数据（有缓存则从缓存获取，无缓存则从底层数据获取并设置缓存）\n    public MyData getData(long dataId) {\n        Cache cache = cacheManager.getCache(\"mydata\");\n        return cache.get(dataId, MyData.class, () -\u003e {    // 对于读场景（有缓存则从缓存获取，无缓存则从底层数据获取并设置缓存），需要使用本方法来获取数据，才能保证缓存的强一致（原因在注意事项中有说明）\n            // TODO 从数据库查询用户数据\n        });\n    }\n\n    // 修改底层数据和缓存\n    public void updateData(long dataId, MyData data) {\n        // 一致性执行（保证缓存与底层数据强一致）\n        cacheTemplate.consistentDo(cacheManager -\u003e {\n            // 缓存回调（修改缓存相关操作在这个回调里编写）\n\n            Cache cache = cacheManager.getCache(\"mydata\");\n            cache.put(dataId, data);\n            // 其他缓存修改操作。。。\n        }, () -\u003e {\n            // 数据回调（修改底层数据相关操作在这个回调里编写）\n\n            // TODO 修改底层数据（比如修改文件、修改网络数据等）\n            // 其他底层数据修改操作。。。\n        });\n    }\n}\n```\n\u003e 注意：\n\u003e 1. 为了保证缓存的强一致性，对于读场景（有缓存则从缓存获取，无缓存则从底层数据获取并设置缓存），应该通过Cache.get(java.lang.Object, java.lang.Class\u003cT\u003e, java.util.concurrent.Callable\u003cT\u003e)来获取数据，就像上面的查询用户方法一样。不能通过先调用Cache.get(java.lang.Object, java.lang.Class\u003cT\u003e)获取缓存，自己判断缓存不存在再从底层数据获取，最后调用Cache.put(java.lang.Object, java.lang.Object)方法设置缓存。否则的话可能会导致缓存不一致。因为Cache.get(java.lang.Object, java.lang.Class\u003cT\u003e, java.util.concurrent.Callable\u003cT\u003e)是原子性操作，而后面这种方式分散成了几个步骤后是非原子性操作。\n\n## 3. Cache统计\nCache默认提供统计最近24小时统计粒度为1分钟的缓存查询统计，包括：查询耗时、本地缓存命中和未命中次数、远程缓存命中和未命中次数、底层数据加载耗时、缓存功效等。使用方可通过org.antframework.cache.statistic.CounterManager获取Cache统计数据。\n\n通过如下方式获取统计结果：\n```java\n@Autowired\nprivate CounterManager counterManager;\n\n// 获取所有缓存过去2小时的统计数据（默认最多可以统计过去24小时）\npublic Map\u003cString, Counter.Statistic\u003e count() {\n    Map\u003cString, Counter.Statistic\u003e result = new HashMap\u003c\u003e();\n\n    long endTime = System.currentTimeMillis();\n    long startTime = endTime - 2 * 60 * 60 * 1000;\n\n    for (String name : counterManager.getNames()) {\n        Counter counter = counterManager.get(name);\n        Counter.Statistic statistic = counter.count(startTime, endTime);\n        result.put(name, statistic);\n    }\n\n    return result;\n}\n```\n统计结果示例如下：\n```json\n{\n  \"app\": {                              // app为被统计缓存的cacheName\n    \"load\": {                           // 加载底层数据的统计\n      \"hits\": 1,                        // 命中次数\n      \"averageHitTimeCost\": 62,         // 平均的命中耗时（单位：毫秒；-1表示无法计算）\n      \"misses\": 0,                      // 未命中次数\n      \"averageMissTimeCost\": -1         // 平均的未命中耗时（单位：毫秒；-1表示无法计算）\n    },\n    \"orderedNameStorages\": {            // 缓存统计\n      \"0-Local-Caffeine\": {             // 本地缓存统计\n        \"hits\": 39,                     // 命中次数\n        \"averageHitTimeCost\": 0,        // 平均的命中耗时（单位：毫秒；-1表示无法计算）\n        \"misses\": 2,                    // 未命中次数\n        \"averageMissTimeCost\": 0        // 平均的未命中耗时（单位：毫秒；-1表示无法计算）\n      },\n      \"1-Remote-Redis\": {               // 远程缓存统计\n        \"hits\": 1,                      // 命中次数\n        \"averageHitTimeCost\": 6,        // 平均的命中耗时（单位：毫秒；-1表示无法计算）\n        \"misses\": 1,                    // 未命中次数\n        \"averageMissTimeCost\": 21       // 平均的未命中耗时（单位：毫秒；-1表示无法计算）\n      }\n    },\n    \"efficacy\": 0.035011801730920535    // 缓存功效（实际读数据耗时/无缓存时读数据耗时；-1表示无法计算；值越小功效越强，比如：0.1表示因为缓存的存在读数据耗时缩短到原来的10%）\n  }\n}\n```\n\n## 4. 扩展性\nCache本身提供极其灵活的扩展：\n* 如果想自己实现缓存序列化，可实现org.antframework.cache.serialize.SerializerManager接口并放入Spring容器（默认使用Hessian作为序列化器）\n* 如果想自己实现缓存数据存储，可实现org.antframework.cache.storage.StorageManager接口并放入Spring容器（默认使用Caffeine作为本地缓存，Redis作为远程缓存）(采用缓存一致性方案4才有此扩展，缓存一致性方案5无此扩展)\n* 如果想自己实现缓存统计，可实现org.antframework.cache.statistic.CounterManager接口并放入Spring容器（默认使用本地内存暂存缓存统计信息）\n* 如果想自己实现缓存加锁，可实现org.antframework.cache.lock.LockerManager接口并放入Spring容器（默认采用基于Redis的分布式读写锁加锁器）(采用缓存一致性方案4才有此扩展，缓存一致性方案5无此扩展)\n\n更多扩展能力可自行查看org.antframework.cache.boot.CacheAutoConfiguration、org.antframework.cache.boot.configuration.CacheManagerConfiguration、org.antframework.cache.boot.configuration.ConsistencyV5CacheManagerConfiguration\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhongxunking%2Fcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzhongxunking%2Fcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzhongxunking%2Fcache/lists"}