Kould-Kache輕量級(jí)持久化緩存代理
Kache緩存框架專(zhuān)注于優(yōu)化高IO的Web應(yīng)用中數(shù)據(jù)持久化層次的讀寫(xiě)性能,并保證數(shù)據(jù)實(shí)時(shí)性,提高緩存數(shù)據(jù)庫(kù)的空間利用率。
Kache強(qiáng)調(diào)業(yè)務(wù)與緩存解耦,通過(guò)方法為粒度進(jìn)行動(dòng)態(tài)代理實(shí)現(xiàn)旁路緩存,內(nèi)部擁有特殊的參數(shù)編碼器,屏蔽鍵值編碼細(xì)節(jié),以四個(gè)基本的CRUD注解和Status枚舉或方法名匹配的形式對(duì)Kache表達(dá)方法的作用而面向抽象實(shí)現(xiàn)自動(dòng)化處理。
Kache的緩存實(shí)現(xiàn)為Guava Cache+Redis+Lua,減少網(wǎng)絡(luò)IO的消耗,且緩存分為索引緩存+元緩存,并在緩存存入時(shí)進(jìn)行"Echo"操作,僅將尚未空缺的緩存存入而減少重復(fù)的序列化。在集中于熱點(diǎn)的大數(shù)據(jù)量場(chǎng)景下,可以做到“接近無(wú)序列化程度”的緩存存入。
Kache適用廣泛,組件實(shí)現(xiàn)都面向抽象,默認(rèn)的實(shí)現(xiàn)都可以通過(guò)在Kache通過(guò)建造者模式時(shí)填入自定義的組件進(jìn)行替換,可以做到MongoDB、IndexDB甚至是MySQL的緩存實(shí)現(xiàn)。并且提供額外的策略接口,允許用戶(hù)對(duì)分布式、單機(jī)等環(huán)境進(jìn)行對(duì)應(yīng)的策略實(shí)現(xiàn)。
使用
該Kache為原生JDK進(jìn)行組件管理以支持Kotlin或scala等jdk語(yǔ)言使用,若是使用Spring框架請(qǐng)移步至:https://gitee.com/Kould/kache-spring
1、Kache依賴(lài)引入
2、Kache代理
3、Dao層寫(xiě)入注解
示例:
1.pom文件引入:
<dependency> <groupId>io.gitee.kould</groupId> <artifactId>Kache</artifactId> </dependency>
2.對(duì)Mapper進(jìn)行Kache的代理
Kache kache = Kache.builder().build(); // 需要對(duì)Kache進(jìn)行init與destroy以保證腳本的緩存載入與連接釋放 kache.init(); kache.destroy(); // 對(duì)Mapper進(jìn)行動(dòng)態(tài)代理,獲取到擁有緩存旁路功能的新Mapper // 示例: ArticleMapper proxy = kache.getProxy(articleMapper, Article.class);
3.其對(duì)應(yīng)的Dao層的Dao方法添加注釋?zhuān)?/p>
- 持久化方法注解:@DaoMethod
- Type:方法類(lèi)型:
- value = Type.SELECT : 搜索方法
- value = Type.INSERT : 插入方法
- value = Type.UPDATE : 更新方法
- value = Type.DELETE : 刪除方法
- Status:方法參數(shù)狀態(tài) 默認(rèn)為Status.BY_Field:
- status = Status.BY_FIELD : 非ID查詢(xún)方法
- status = Status.BY_ID : ID查詢(xún)方法
- Class<?>[] involve:僅在Type.SELECT Status.BY_Field時(shí)生效:用于使該條件搜索方法的索引能夠被其他緩存Class影響
- Type:方法類(lèi)型:
@Repository public interface TagMapper extends BaseMapper<Tag> { @Select("select t.* from klog_article_tag at " + "right join klog_tag t on t.id = at.tag_id " + "where t.deleted = 0 AND at.deleted = 0 " + "group by t.id order by count(at.tag_id) desc limit #{limit}") @DaoMethod(value = Type.SELECT,status = Status.BY_FIELD) // 通過(guò)條件查詢(xún)獲取數(shù)據(jù) List<Tag> listHotTagsByArticleUse(@Param("limit") int limit); @DaoMethod(Type.INSERT) // 批量新增方法(會(huì)導(dǎo)致數(shù)據(jù)變動(dòng)) Integer insertBatch(Collection<T> entityList); }
自定義配置或組件:
// 以接口類(lèi)型作為鍵值替換默認(rèn)配置或增加額外配置 // 用于無(wú)額外參數(shù)的配置或組件加載 load(Class<?> interfaceClass, Object bean); // 示例:注入MyBatis-Plus的包裝類(lèi)對(duì)象Page的PageDetails對(duì)象 private final Kache kache = Kache.builder() .load(PageDetails.class, new PageDetails<>(Page.class, "records", List.class)) .build();
原理 | Principle
Kache的原型的描述文章:
基于Redis的DTO應(yīng)用Service層緩存AOP
基于上述文章的主要變化為:
代理對(duì)象的轉(zhuǎn)移:
Service層緩存對(duì)于Dao層緩存來(lái)說(shuō)產(chǎn)生一個(gè)問(wèn)題:
- 緩存更新問(wèn)題:Service支持DTO概念時(shí),針對(duì)有一種PO卻產(chǎn)生有不同的形態(tài)的緩存,最容易導(dǎo)致的問(wèn)題是緩存刪除、更新、新增時(shí)帶來(lái)的一致性問(wèn)題,對(duì)于PO結(jié)果有Page類(lèi)對(duì)象封裝的緩存更甚,對(duì)于緩存的利用率較低
而Dao緩存則能夠去彌補(bǔ)上面所述的問(wèn)題,是高性能的緩存所必不可少的
但于此同時(shí)Dao又會(huì)導(dǎo)致一系列實(shí)際開(kāi)發(fā)上的問(wèn)題
- 標(biāo)準(zhǔn)Dao的開(kāi)發(fā)并不偏向業(yè)務(wù)化、不符合原先緩存Key邏輯
- Dao層有著一系列持久層框架帶來(lái)的默認(rèn)實(shí)現(xiàn),難以對(duì)其命名規(guī)范的同一化
- Service層對(duì)Dao層耦合大、Dao的修改對(duì)系統(tǒng)穩(wěn)定性是致命性的
可見(jiàn)與原先的緩存設(shè)計(jì)有著很大的沖突
于是使用了兩層的AOP:
- ServiceAop獲取Service方法信息摘要、通過(guò)ThreadLocal傳遞給DaoAop
- DaoAop獲取Service層方法進(jìn)行編碼為Key,使用Key獲取緩存的結(jié)果
以此避免同一Service方法下調(diào)用同一Dao方法但不同參數(shù)而引起的緩存沖突問(wèn)題
Aop轉(zhuǎn)變?yōu)榭蚣埽?/p>
- 主要通過(guò)注解的形式+切點(diǎn)的形式提供更好的兼容性
- 注解提高代碼的可讀性,且可以應(yīng)對(duì)多種不同包名的這種細(xì)節(jié)性問(wèn)題
- 一些框架的默認(rèn)實(shí)現(xiàn)無(wú)法修改代碼,則可以通過(guò)默認(rèn)提供的切點(diǎn)來(lái)修改包名而兼容
- 降低對(duì)原項(xiàng)目的耦合,使更多第三方項(xiàng)目也能使用上
- 修復(fù)原本設(shè)計(jì)帶來(lái)的一系列不足點(diǎn)的耦合問(wèn)題
- 通過(guò)注解降低其業(yè)務(wù)代碼侵入性,并簡(jiǎn)化使用
二級(jí)緩存設(shè)計(jì):
- 即使是NoSQL所帶來(lái)的提升也仍然會(huì)導(dǎo)致網(wǎng)絡(luò)IO的占用,為了更多的性能提升以及無(wú)用IO損耗則加入了進(jìn)程間緩存的概念。
- 使用了二級(jí)緩存調(diào)度器,允許用戶(hù)通過(guò)自定義二級(jí)緩存調(diào)度器去調(diào)度兩個(gè)緩存的使用。
緩存散列化:
- 通過(guò)解析結(jié)果的持久類(lèi)數(shù)據(jù)集,將持久類(lèi)數(shù)據(jù)拆分并分別儲(chǔ)存,將原包裝類(lèi)除去數(shù)據(jù)集后與數(shù)據(jù)集裝填入List類(lèi)型存入,去除實(shí)體數(shù)據(jù)與緩存索引的直接耦合
- 使實(shí)體數(shù)據(jù)原子化,加強(qiáng)數(shù)據(jù)一致性
- 減少緩存值重復(fù),提高緩存的空間利用率
- 更直觀觀察到熱點(diǎn)數(shù)據(jù)
- 散列開(kāi)的單一持久化類(lèi)有利于id查找
- Kache提供有KacheConfig.Status.BY_ID的狀態(tài)屬性用于標(biāo)記直接id搜索,而實(shí)際的查找流程往往為:分頁(yè)查詢(xún)-》單一數(shù)據(jù)查詢(xún)
- 如百度中搜索某一關(guān)鍵詞后,點(diǎn)擊其中提供內(nèi)容中的一條。
- 散列化可以使在如上種情景下,不存在該關(guān)鍵詞的緩存時(shí),獲取其分頁(yè)數(shù)據(jù)時(shí)保存其單一實(shí)體緩存,使第二步的單一數(shù)據(jù)時(shí)必定命中緩存(點(diǎn)擊其分頁(yè)內(nèi)容下),提高其緩存命中率且更加符合實(shí)際的應(yīng)用場(chǎng)景
緩存結(jié)構(gòu):
