{"id":13635456,"url":"https://github.com/houbb/cache","last_synced_at":"2025-04-04T18:07:45.704Z","repository":{"id":39975266,"uuid":"259248854","full_name":"houbb/cache","owner":"houbb","description":"The basic cache tool for java.(java 手写实现渐进式 redis 缓存工具)","archived":false,"fork":false,"pushed_at":"2024-06-03T10:32:56.000Z","size":158,"stargazers_count":405,"open_issues_count":3,"forks_count":89,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-28T17:07:30.796Z","etag":null,"topics":["cache","ehcache","guava-cache","hight-performance","memcached","redis"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/houbb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","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-04-27T08:12:52.000Z","updated_at":"2025-03-25T03:54:35.000Z","dependencies_parsed_at":"2024-06-03T12:44:04.689Z","dependency_job_id":null,"html_url":"https://github.com/houbb/cache","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/houbb%2Fcache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/houbb%2Fcache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/houbb%2Fcache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/houbb%2Fcache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/houbb","download_url":"https://codeload.github.com/houbb/cache/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247226215,"owners_count":20904465,"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","ehcache","guava-cache","hight-performance","memcached","redis"],"created_at":"2024-08-02T00:00:45.805Z","updated_at":"2025-04-04T18:07:45.669Z","avatar_url":"https://github.com/houbb.png","language":"Shell","readme":"# 项目简介\n\n[Cache](https://github.com/houbb/cache) 用于实现一个可拓展的本地缓存。\n\n有人的地方，就有江湖。\n\n有高性能的地方，就有 cache。\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.houbb/cache/badge.svg)](http://mvnrepository.com/artifact/com.github.houbb/cache)\n[![Build Status](https://www.travis-ci.org/houbb/cache.svg?branch=master)](https://www.travis-ci.org/houbb/cache?branch=master)\n[![](https://img.shields.io/badge/license-Apache2-FF0080.svg)](https://github.com/houbb/cache/blob/master/LICENSE.txt)\n[![Open Source Love](https://badges.frapsoft.com/os/v2/open-source.svg?v=103)](https://github.com/houbb/cache)\n\n## 创作目的\n\n- 为日常开发提供一套简单易用的缓存框架\n\n- 便于后期多级缓存开发\n\n- 学以致用，开发一个类似于 redis 的渐进式缓存框架\n\n## 特性\n\n- MVP 开发策略\n\n- fluent 流式编程体验，纵享丝滑\n\n- 支持 cache 固定大小\n\n- 支持自定义 map 实现策略\n\n- 支持 expire 过期特性\n\n- 支持自定义 evict 驱除策略\n\n内置 FIFO 和 LRU 驱除策略\n\n- 支持自定义删除监听器\n\n- 日志整合框架，自适应常见日志\n\n- 支持 load 初始化和 persist 持久化\n\nRDB 和 AOF 两种模式\n\n# 变更日志\n\n\u003e [变更日志](https://github.com/houbb/cache/blob/master/doc/CHANGELOG.md)\n\n# 快速开始\n\n## 准备\n\nJDK1.7 及其以上版本\n\nMaven 3.X 及其以上版本\n\n## maven 项目依赖\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.houbb\u003c/groupId\u003e\n    \u003cartifactId\u003ecache-core\u003c/artifactId\u003e\n    \u003cversion\u003e0.0.15\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## 入门测试\n\n```java\nICache\u003cString, String\u003e cache = CacheBs.\u003cString,String\u003enewInstance()\n                .size(2)\n                .build();\n\ncache.put(\"1\", \"1\");\ncache.put(\"2\", \"2\");\ncache.put(\"3\", \"3\");\ncache.put(\"4\", \"4\");\n\nAssert.assertEquals(2, cache.size());\n```\n\n默认为先进先出的策略，此时输出 keys，内容如下：\n\n```\n[3, 4]\n```\n\n## 引导类配置属性\n\n`CacheBs` 作为缓存的引导类，支持 fluent 写法，编程更加优雅便捷。\n\n上述配置等价于：\n\n```java\nICache\u003cString, String\u003e cache = CacheBs.\u003cString,String\u003enewInstance()\n                .map(Maps.\u003cString,String\u003ehashMap())\n                .evict(CacheEvicts.\u003cString, String\u003efifo())\n                .size(2)\n                .build();\n```\n\n## 淘汰策略\n\n目前内置了几种淘汰策略，可以直接通过 `CacheEvicts` 工具类创建。\n\n| 策略 | 说明 |\n|:---|:---|\n| none | 没有任何淘汰策略 |\n| fifo | 先进先出（默认策略） |\n| lru | 最基本的朴素 LRU 策略，性能一般 |\n| lruDoubleListMap | 基于双向链表+MAP 实现的朴素 LRU，性能优于 lru |\n| lruLinkedHashMap | 基于 LinkedHashMap 实现的朴素 LRU，与 lruDoubleListMap 差不多 |\n| lru2Q | 基于 LRU 2Q 的改进版 LRU 实现，命中率优于朴素LRU |\n| lru2 | 基于 LRU-2 的改进版 LRU 实现，命中率优于 lru2Q |\n\n## 过期支持\n\n```java\nICache\u003cString, String\u003e cache = CacheBs.\u003cString,String\u003enewInstance()\n        .size(3)\n        .build();\n\ncache.put(\"1\", \"1\");\ncache.put(\"2\", \"2\");\n\ncache.expire(\"1\", 10);\nAssert.assertEquals(2, cache.size());\n\nTimeUnit.MILLISECONDS.sleep(50);\nAssert.assertEquals(1, cache.size());\nSystem.out.println(cache.keySet());\n```\n\n`cache.expire(\"1\", 10);` 指定对应的 key 在 10ms 后过期。\n\n# 删除监听器\n\n## 说明\n\n淘汰和过期，这些都是缓存的内部行为。\n\n如果用户也关心的话，可以自定义删除监听器。\n\n## 自定义监听器\n\n直接实现 `ICacheRemoveListener` 接口即可。\n\n```java\npublic class MyRemoveListener\u003cK,V\u003e implements ICacheRemoveListener\u003cK,V\u003e {\n\n    @Override\n    public void listen(ICacheRemoveListenerContext\u003cK, V\u003e context) {\n        System.out.println(\"【删除提示】可恶，我竟然被删除了！\" + context.key());\n    }\n\n}\n```\n\n## 使用\n\n```java\nICache\u003cString, String\u003e cache = CacheBs.\u003cString,String\u003enewInstance()\n        .size(1)\n        .addRemoveListener(new MyRemoveListener\u003cString, String\u003e())\n        .build();\n\ncache.put(\"1\", \"1\");\ncache.put(\"2\", \"2\");\n```\n\n- 测试日志\n\n```\n【删除提示】可恶，我竟然被删除了！2\n```\n\n# 添加慢操作监听器\n\n## 说明\n\nredis 中会存储慢操作的相关日志信息，主要是由两个参数构成：\n\n（1）slowlog-log-slower-than 预设阈值,它的单位是毫秒(1秒=1000000微秒)默认值是10000\n\n（2）slowlog-max-len 最多存储多少条的慢日志记录\n\n不过 redis 是直接存储到内存中，而且有长度限制。\n\n根据实际工作体验，如果我们可以添加慢日志的监听，然后有对应的存储或者报警，这样更加方便问题的分析和快速反馈。\n\n所以我们引入类似于删除的监听器。\n\n## 自定义监听器\n\n实现接口 `ICacheSlowListener`\n\n这里每一个监听器都可以指定自己的慢日志阈值，便于分级处理。\n\n```java\npublic class MySlowListener implements ICacheSlowListener {\n\n    @Override\n    public void listen(ICacheSlowListenerContext context) {\n        System.out.println(\"【慢日志】name: \" + context.methodName());\n    }\n\n    @Override\n    public long slowerThanMills() {\n        return 0;\n    }\n\n}\n```\n\n## 使用\n\n```java\nICache\u003cString, String\u003e cache = CacheBs.\u003cString,String\u003enewInstance()\n        .addSlowListener(new MySlowListener())\n        .build();\n\ncache.put(\"1\", \"2\");\ncache.get(\"1\");\n```\n\n- 测试效果\n\n```\n[DEBUG] [2020-09-30 17:40:11.547] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.before] - Cost start, method: put\n[DEBUG] [2020-09-30 17:40:11.551] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.after] - Cost end, method: put, cost: 10ms\n【慢日志】name: put\n[DEBUG] [2020-09-30 17:40:11.554] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.before] - Cost start, method: get\n[DEBUG] [2020-09-30 17:40:11.554] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.after] - Cost end, method: get, cost: 1ms\n【慢日志】name: get\n```\n\n实际工作中，我们可以针对慢日志数据存储，便于后期分析。\n\n也可以直接接入报警系统，及时反馈问题。\n\n# 添加 load 加载器\n\n## 说明\n\n有时候我们需要在 cache 初始化的时候，添加对应的数据初始化。\n\n后期可以从文件等地方加载数据。\n\n## 实现\n\n实现 `ICacheLoad` 接口即可。\n\n```java\npublic class MyCacheLoad implements ICacheLoad\u003cString,String\u003e {\n\n    @Override\n    public void load(ICache\u003cString, String\u003e cache) {\n        cache.put(\"1\", \"1\");\n        cache.put(\"2\", \"2\");\n    }\n\n}\n```\n\n我们在缓存初始化的时候，放入 2 个元素。\n\n## 测试效果\n\n```java\nICache\u003cString, String\u003e cache = CacheBs.\u003cString,String\u003enewInstance()\n        .load(new MyCacheLoad())\n        .build();\n\nAssert.assertEquals(2, cache.size());\n```\n\n# 添加 persist 持久化类\n\n## 说明\n\n如果我们只是把文件放在内存中，应用重启信息就丢失了。\n\n有时候我们希望这些 key/value 信息可以持久化，存储到文件或者 database 中。\n\n## 持久化\n\n`CachePersists.\u003cString, String\u003edbJson(\"1.rdb\")` 指定将数据文件持久化到文件中。\n\n定期执行，暂时全量持久化的间隔为 10min，后期考虑支持更多配置。\n\n```java\npublic void persistTest() throws InterruptedException {\n    ICache\u003cString, String\u003e cache = CacheBs.\u003cString,String\u003enewInstance()\n            .load(new MyCacheLoad())\n            .persist(CachePersists.\u003cString, String\u003edbJson(\"1.rdb\"))\n            .build();\n\n    Assert.assertEquals(2, cache.size());\n    TimeUnit.SECONDS.sleep(5);\n}\n```\n\n- 1.rdb\n\n文件内容如下：\n\n```\n{\"key\":\"2\",\"value\":\"2\"}\n{\"key\":\"1\",\"value\":\"1\"}\n```\n\n## 加载器\n\n存储之后，可以使用对应的加载器读取文件内容：\n\n```java\nICache\u003cString, String\u003e cache = CacheBs.\u003cString,String\u003enewInstance()\n        .load(CacheLoads.\u003cString, String\u003edbJson(\"1.rdb\"))\n        .build();\n\nAssert.assertEquals(2, cache.size());\n```\n\n# 后期 Road-MAP\n\n## 淘汰策略\n\n- [ ] CLOCK 算法\n\n- [ ] SC 二次机会\n\n- [ ] 老化算法\n\n- [ ] 弱引用\n\n## 过期特性\n\n- [ ] 过期策略添加随机返回\n\n- [ ] expireAfterWrite()\n\n- [ ] expireAfterAccess()\n\n## 持久化\n\n- [ ] AOF 混合 RDB\n\n## 统计\n\n- [ ] 命中率\n\n- [ ] keys 数量\n\n- [ ] evict 数量\n\n- [ ] expire 数量\n\n- [ ] 耗时统计\n\n## 并发\n\n- [ ] 并发安全保障\n\n## 其他\n\n- [ ] 异步 callable 操作\n\n- [ ] spring 整合\n\n提供 `@Cacheable` 系列注解\n\n- [ ] 文件压缩\n\n- [ ] 独立服务端\n\n提供类似于 redis-server + redis-client 的拆分，便于独立于应用作为服务存在。\n\n# 拓展阅读\n\n[java从零手写实现redis（一）如何实现固定大小的缓存？](https://mp.weixin.qq.com/s/6J2K2k4Db_20eGU6xGYVTw)\n\n[java从零手写实现redis（三）redis expire 过期原理](https://mp.weixin.qq.com/s/BWfBc98oLqhAPLN2Hgkwow)\n\n[java从零手写实现redis（三）内存数据如何重启不丢失？](https://mp.weixin.qq.com/s/G41SRZQm1_0uQXBAGHAYbw)\n\n[java从零手写实现redis（四）添加监听器](https://mp.weixin.qq.com/s/6pIG3l_wkXBwSuJvj_KwMA)\n\n[java从零手写实现redis（五）过期策略的另一种实现思路](https://mp.weixin.qq.com/s/Atrd36UGds9_w_NFQDoEQg)\n\n[java从零手写实现redis（六）AOF 持久化原理详解及实现](https://mp.weixin.qq.com/s/rFuSjNF43Ybxy-qBCtgasQ)\n\n[java从零开始手写redis（七）LRU 缓存淘汰策略详解](https://mp.weixin.qq.com/s/X-OIqu_rgLskvbF2rZMP6Q)\n\n[java从零开始手写redis（八）朴素 LRU 淘汰算法性能优化](https://mp.weixin.qq.com/s/H8gOujnlTinctjVQqW0ITA)\n\n[java 从零开始手写 redis（九）LRU 缓存淘汰算法如何避免缓存污染](https://mp.weixin.qq.com/s/jzM_wDw37QXTeYMFYtRJaw)\n\n[java 从零开始手写 redis（十）缓存淘汰算法 LFU 最少使用频次](https://mp.weixin.qq.com/s/mUyCTCVObwY8XdLcO1pOWg)\n\n[java 从零开始手写 redis（11）clock时钟淘汰算法详解及实现](https://mp.weixin.qq.com/s/h9oub0TT94ObaiKZ7s5VsA)\n\n[java 从零开始手写 redis（12）redis expire 过期如何实现随机获取keys？](https://mp.weixin.qq.com/s/YEBPtrOWIyBl9dsHa59JYg)\n\n[java从零开始手写 redis（13）HashMap 源码原理详解](https://mp.weixin.qq.com/s/SURVmTf6K_ou85fShFzrNA)\n\n[java 从零开始手写 redis（14）redis渐进式rehash详解](https://mp.weixin.qq.com/s/gPD-4wCwirdyO5QyHrXOIA)\n\n[java 从零开始手写 redis（15）实现自己的 HashMap](https://mp.weixin.qq.com/s/e5fskVfeDMTuJhjEAd1gQw)\n\n[java 从零开始手写 redis（16）实现渐进式 rehash map](https://mp.weixin.qq.com/s/Lwp2js4lrHAbuQ5Fexer6w)\n\n【实战汇总】\n\n[缓存实战（1）缓存雪崩、缓存击穿和缓存穿透入门简介及解决方案](https://mp.weixin.qq.com/s/yYE-zqJOyiLlEYXRj5by9g)\n\n[缓存实战（2）布隆过滤器是啥？guava 的 BloomFilter 使用](https://mp.weixin.qq.com/s/dY-0jE23jggU3wqjdHGyZQ)\n\n[缓存实战（3）让你彻底搞懂布隆过滤器！实现一个自己的BloomFilter](https://mp.weixin.qq.com/s/UsIjHfiy96aZgpzybuBYgg)\n\n[缓存实战（4）bloom filter 使用最佳实践，让你少踩坑](https://mp.weixin.qq.com/s/obqh0FMzahRFa5sNe5eq3g)\n\n[java 从零实现属于你的 redis 分布式锁](https://mp.weixin.qq.com/s/MzybPDRGwaWXX8viE8adAA)\n\n[3天时间，我是如何解决redis bigkey删除问题的？](https://mp.weixin.qq.com/s/06tjn76uebvgfzYaahdY0g)\n\n[redis 多路复用](http://houbb.github.io/2018/09/08/redis-learn-45-multi-io)\n\n# 中间件等工具开源矩阵\n\n[heaven: 收集开发中常用的工具类](https://github.com/houbb/heaven)\n\n[rpc: 基于 netty4 实现的远程调用工具](https://github.com/houbb/rpc)\n\n[mq: 简易版 mq 实现](https://github.com/houbb/mq)\n\n[ioc: 模拟简易版 spring ioc](https://github.com/houbb/ioc)\n\n[mybatis: 简易版 mybatis](https://github.com/houbb/mybatis)\n\n[cache: 渐进式 redis 缓存](https://github.com/houbb/cache)\n\n[jdbc-pool: 数据库连接池实现](https://github.com/houbb/jdbc-pool)\n\n[sandglass: 任务调度时间工具框架](https://github.com/houbb/sandglass)\n\n[sisyphus: 支持注解的重试框架](https://github.com/houbb/sisyphus)\n\n[resubmit: 防止重复提交框架，支持注解](https://github.com/houbb/resubmit)\n\n[auto-log: 日志自动输出](https://github.com/houbb/auto-log)\n\n[async: 多线程异步并行框架](https://github.com/houbb/async)\n\n# 缓存相关工具\n\n[cache: 手写渐进式 redis](https://github.com/houbb/cache)\n\n[common-cache: 通用缓存标准定义](https://github.com/houbb/common-cache)\n\n[redis-config: 兼容各种常见的 redis 配置模式](https://github.com/houbb/redis-config)\n\n[lock: 开箱即用的分布式锁](https://github.com/houbb/lock)\n\n[resubmit: 防重复提交](https://github.com/houbb/resubmit)\n\n[rate-limit: 限流](https://github.com/houbb/rate-limit/)\n\n","funding_links":[],"categories":["测试","缓存库"],"sub_categories":["客户端"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoubb%2Fcache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhoubb%2Fcache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoubb%2Fcache/lists"}