日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

使用数据库实现缓存功能

發布時間:2024/3/24 数据库 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用数据库实现缓存功能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

編寫目的

在某些特殊的項目中,想實現緩存,但是不能使用中間件,存內存又會導致內存大幅度上升,怎么辦呢?

降低預期,將需要緩存的數據存儲在數據庫,如何設計一套數據庫緩存呢。

設計思路

一個KV形式緩存中間件需要有哪些基礎功能?

  • 1、增加緩存(新增數據庫)
  • 2、緩存覆蓋(修改數據庫)
  • 3、緩存過期刪除(刪除數據庫數據)
  • 4、查詢緩存(查詢數據庫)

其實,就是對數據庫的增刪改查。但是緩存的數據一般情況是寫入和查詢比較頻繁的。

  • 查詢優化: 在字段KEY上建立唯一索引
  • 插入優化:使用隊列 + 定時任務異步入庫
  • 緩存覆蓋:使用隊列 + 定時任務異步更新
  • 過期刪除:使用隊列 + 定時任務異步刪除

表設計

主鍵,緩存KEY(唯一索引), 緩存值 , 過期時間

create table data_common_cache (cache_id bigint auto_increment comment '主鍵,生成序列號Id' primary key,cache_key varchar(100) not null comment '緩存的key 長度100 超過100的話通過編碼后縮短',cache_value text default null comment '緩存的值',cache_expire datetime default null comment '過期時間',constraint udx_cache_key unique (cache_key) ) comment '通用緩存表';

功能實現

使用SpringBoot + Mybatis Plus 實現通用緩存功能

依賴引入

<properties><java.version>1.8</java.version><mybatis-plus.version>3.4.0</mybatis-plus.version><mybatis-plus-generator.version>3.3.2</mybatis-plus-generator.version> </properties> <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>${mybatis-plus-generator.version}</version></dependency><!--mybatis-plus模板生成--><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.35</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version></dependency> </dependencies>

配置文件

server:port: 8081spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/test_db?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&autoReconnect=trueusername: rootpassword: root

生成entity, mapper

實體類

@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class DataCommonCache implements Serializable {private static final long serialVersionUID=1L;/*** 主鍵,生成序列號Id*/@TableId(value = "cache_id", type = IdType.AUTO)private Long cacheId;/*** 緩存的key 長度100 超過100的話通過編碼后縮短*/private String cacheKey;/*** 緩存的值*/private String cacheValue;/*** 過期時間*/private Date cacheExpire; }

mapper, 包含自定義SQL

@Mapper public interface DataCommonCacheMapper extends BaseMapper<DataCommonCache> {/*** 根據緩存key查詢緩存ID 如果ID為空 表示緩存不存在 不為空表示緩存存在* @param cacheKey 緩存key* @return 緩存的ID, 過期時間 為空表示緩存不存在*/DataCommonCache selectIdByKey(@Param("cacheKey") String cacheKey);/*** 根據緩存key查詢緩存ID 如果ID為空 表示緩存不存在 不為空表示緩存存在* @param cacheKey 緩存key* @return 緩存的ID, 過期時間,緩存值 為空表示緩存不存在*/DataCommonCache selectByKey(@Param("cacheKey") String cacheKey);/*** 根據緩存key查詢緩存ID 如果ID為空 表示緩存不存在 不為空表示緩存存在* @param cacheKey 緩存key* @return 緩存的ID 為空表示*/String selectValueByKey(@Param("cacheKey") String cacheKey);/*** 根據緩存key刪除數據* @param cacheKey 緩存key*/int deleteByCacheKey(@Param("cacheKey") String cacheKey); }

mapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itdl.mapper.DataCommonCacheMapper"><delete id="deleteByCacheKey" parameterType="java.lang.String">delete from data_common_cache where cache_key = #{cacheKey}</delete><!--根據緩存key獲取緩存信息,主要包含緩存ID和過期時間 結果為空表示沒有數據--><select id="selectIdByKey" parameterType="string" resultType="com.itdl.entity.DataCommonCache">select cache_id, cache_expire from data_common_cache where cache_key = #{cacheKey} limit 1</select><!--包含緩存值--><select id="selectByKey" parameterType="string" resultType="com.itdl.entity.DataCommonCache">select cache_id, cache_value, cache_expire from data_common_cache where cache_key = #{cacheKey} limit 1</select><!--根據緩存的key獲取緩存的值--><select id="selectValueByKey" parameterType="string" resultType="java.lang.String">select cache_value from data_common_cache where cache_key = #{cacheKey} limit 1</select> </mapper>

整合MybatisPlus

整合MybatisPlus用于增刪改差, 并實現了MybtaisPlus真正的批量新增和批量修改

數據源配置類DatasourceConfig

包含了數據源的配置和SqlSessionFactory配置,且注入了MybatisPlus的配置

/*** @Description 數據庫相關配置* @Author itdl* @Date 2022/08/10 09:20*/ @Configuration @MapperScan(basePackages = "com.itdl.mapper", sqlSessionFactoryRef = "sqlSessionFactory") public class DatasourceConfig {@Bean(name = "dataSource")@ConfigurationProperties(prefix = "spring.datasource")public DataSource dataSource() throws SQLException {return DataSourceBuilder.create().build();}@Bean("easySqlInjector")public EasySqlInjector easySqlInjector() {return new EasySqlInjector();}@Beanpublic GlobalConfig globalConfig(EasySqlInjector easySqlInjector){GlobalConfig globalConfig = new GlobalConfig();globalConfig.setSqlInjector(easySqlInjector);return globalConfig;}@Bean(name = "sqlSessionFactory")public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource, GlobalConfig globalConfig) throws Exception {MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();sessionFactoryBean.setDataSource(dataSource);sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**/*.xml"));sessionFactoryBean.setPlugins(new PaginationInterceptor());//添加自定義sql注入接口sessionFactoryBean.setGlobalConfig(globalConfig);//添加自定義sql注入接口return sessionFactoryBean.getObject();}}

Mybatis 批量插入/更新配置

public class EasySqlInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {List<AbstractMethod> methodList = super.getMethodList(mapperClass);methodList.add(new InsertBatchSomeColumn());methodList.add(new UpdateBatchMethod());return methodList;}}/*** 批量更新方法實現,條件為主鍵,選擇性更新*/ @Slf4j public class UpdateBatchMethod extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {String sql = "<script>\n<foreach collection=\"list\" item=\"item\" separator=\";\">\nupdate %s %s where %s=#{%s} %s\n</foreach>\n</script>";String additional = tableInfo.isWithVersion() ? tableInfo.getVersionFieldInfo().getVersionOli("item", "item.") : "" + tableInfo.getLogicDeleteSql(true, true);String setSql = sqlSet(false, false, tableInfo, false, "item", "item.");String sqlResult = String.format(sql, tableInfo.getTableName(), setSql, tableInfo.getKeyColumn(), "item." + tableInfo.getKeyProperty(), additional);//log.debug("sqlResult----->{}", sqlResult);SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);// 第三個參數必須和RootMapper的自定義方法名一致return this.addUpdateMappedStatement(mapperClass, modelClass, "updateBatch", sqlSource);} }

緩存實現

實現思路

  • 1、配置一個可調度的線程池,用于異步隊列的調度
  • 2、編寫一個基礎調度父類,實現調度的基本邏輯
  • 3、編寫緩存插入,覆蓋,刪除的調度邏輯
  • 4、將調度邏輯整合為一個緩存工具類
  • 5、使用Controller接口測試緩存增刪改查

配置可調度的線程池

/*** @Description 通用配置及* @Author itdl* @Date 2022/08/09 17:57*/ @Configuration public class CommonConfig {@Bean("scheduledThreadPoolExecutor")public ScheduledThreadPoolExecutor scheduledThreadPoolExecutor() {//線程名String threadNameStr = "統一可調度線程-%d";//線程工廠類就是將一個線程的執行單元包裝成為一個線程對象,比如線程的名稱,線程的優先級,線程是否是守護線程等線程;// guava為了我們方便的創建出一個ThreadFactory對象,我們可以使用ThreadFactoryBuilder對象自行創建一個線程.ThreadFactory threadNameVal = new ThreadFactoryBuilder().setNameFormat(threadNameStr).build();// 單線程池return new ScheduledThreadPoolExecutor(// 核心線程池4,// 最大線程池threadNameVal,// 使用策略為拋出異常new ThreadPoolExecutor.AbortPolicy());} }

編寫可調度的公共父類,實現調度的基本邏輯

@Slf4j public abstract class BaseCacheHelper<T> {@Resourceprivate ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;// 隊列private final BlockingQueue<T> QUEUE = new ArrayBlockingQueue<>(1024);// listener執行次數 計數器private final AtomicInteger EXECUTE_COUNT = new AtomicInteger();// 事件集合private final List<T> eventStorageList = Collections.synchronizedList(new ArrayList<>());/*** 判斷隊列是否為空*/public boolean checkQueueIsEmpty() {return QUEUE.isEmpty();}/*** 入隊方法* @param datas 批量入隊*/public void producer(List<T> datas) {for (T data : datas) {producer(data);}}/*** 入隊方法* @param data 單個入隊*/public void producer(T data) {try {if (QUEUE.contains(data)){return;}// 入隊 滿了則等待QUEUE.put(data);} catch (InterruptedException e) {e.printStackTrace();}log.info("================>>>通用隊列:{}:當前隊列存在數據:{}", this.getClass().getName(), QUEUE.size());}@PostConstructpublic void consumer() {scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {try {// 隊列數量達到指定消費批次數量if (EXECUTE_COUNT.get() >= getBatchSize()) {doConsumer();} else {while (EXECUTE_COUNT.get() < getBatchSize() && QUEUE.size() != 0) {// 加入事件final T take = QUEUE.take();eventStorageList.add(take);EXECUTE_COUNT.incrementAndGet();}// 隊列為空了 同樣需要處理,及時沒有滿if (EXECUTE_COUNT.get() < getBatchSize() && QUEUE.size() == 0) {doConsumer();}}} catch (InterruptedException e) {e.printStackTrace();}}, 3000, getPeriodTime(), TimeUnit.MILLISECONDS);}/*** 消費數據*/protected void doConsumer() {// 這里面開始真正的寫磁盤if (ObjectUtils.isEmpty(eventStorageList)) {return;}// 批處理final StopWatch stopWatch = new StopWatch();stopWatch.start();log.info("=========>>>>消費數據{}條", eventStorageList.size());for (T t : eventStorageList) {StopWatch subStopWatch = new StopWatch();subStopWatch.start();// 處理每一個消費者的邏輯 用于子類實現doHandleOne(t);subStopWatch.stop();}// 重置數據EXECUTE_COUNT.set(0);eventStorageList.clear();stopWatch.stop();log.info("=========>>>>通用隊列:{}:消費完成,總耗時:{}s<<<<=========", this.getClass().getName(), String.format("%.4f", stopWatch.getTotalTimeSeconds()));}/*** 消費一條數據** @param data 處理數據*/protected abstract void doHandleOne(T data);/*** 批次大小 默認每次消費100條** @return 此次大小*/protected Integer getBatchSize() {return 100;}/*** 批次大小 執行完任務后 間隔多久再執行 單位 毫秒 默認5秒** @return 此次大小*/protected Integer getPeriodTime() {return 1000;} }

增刪改調度任務實現

@Slf4j @Component public class DbCacheHelper{@Slf4j@Componentpublic static class InsertCache extends BaseCacheHelper<DataCommonCache>{@Autowiredprivate DataCommonCacheMapper dataCommonCacheMapper;@Overrideprotected void doHandleOne(DataCommonCache data) {final String cacheKey = data.getCacheKey();log.info("=====================開始插入緩存數據cacheKey:{}===========================", cacheKey);try {dataCommonCacheMapper.insert(data);} catch (Exception e) {log.error("=======>>>>插入緩存數據失敗:{}", e.getMessage());e.printStackTrace();}log.info("=====================完成插入緩存數據cacheKey:{}===========================", cacheKey);}}@Slf4j@Componentpublic static class UpdateCache extends BaseCacheHelper<DataCommonCache>{@Autowiredprivate DataCommonCacheMapper dataCommonCacheMapper;@Overrideprotected void doHandleOne(DataCommonCache data) {final String cacheKey = data.getCacheKey();log.info("=====================開始覆蓋寫入緩存數據cacheKey:{}===========================", cacheKey);try {dataCommonCacheMapper.updateById(data);} catch (Exception e) {log.error("=======>>>>覆蓋寫入緩存數據失敗:{}", e.getMessage());e.printStackTrace();}log.info("=====================完成覆蓋寫入緩存數據cacheKey:{}===========================", cacheKey);}}@Slf4j@Componentpublic static class DeleteCache extends BaseCacheHelper<String>{@Autowiredprivate DataCommonCacheMapper dataCommonCacheMapper;@Overrideprotected void doHandleOne(String cacheKey) {log.info("=====================開始刪除緩存數據cacheKey:{}===========================", cacheKey);try {dataCommonCacheMapper.deleteByCacheKey(cacheKey);} catch (Exception e) {log.error("=======>>>>刪除緩存數據失敗:{}", e.getMessage());e.printStackTrace();}log.info("=====================完成刪除寫入緩存數據cacheKey:{}===========================", cacheKey);}} }

緩存工具類編寫

在工具類里面實現緩存的邏輯

新增緩存思路

  • 1、根據緩存key查詢緩存ID和過期時間
  • 2、結果為空,表示沒有緩存,發送數據到緩存隊列,等待新增緩存隊列任務調度
  • 3、結果不為空,繼續判斷過期時間,過期時間不為空,并且已經過期了,則發送到過期刪除隊列,等待調度
  • 4、沒有過期,真正查詢緩存的值, 比較值是是否更新,更新了發送更新數據到更新隊列,沒更新則不管

查詢緩存思路

  • 1、根據緩存key查詢數據(包含緩存值)
  • 2、結果為空,表示緩存不存在,直接返回null
  • 3、結果不為空,判斷是否過期,過期則發送過期刪除到刪除隊列
  • 4、返回查詢結果

刪除緩存思路

  • 1、根據緩存key查詢數據(不包含緩存值)
  • 2、為空則不需要刪除,不為空發送到刪除隊列,等待調度

代碼實現

@Component @Slf4j public class DbCacheUtil {@Autowiredprivate DataCommonCacheMapper dataCommonCacheMapper;@Autowiredprivate DbCacheHelper.InsertCache insertCache;@Autowiredprivate DbCacheHelper.UpdateCache updateCache;@Autowiredprivate DbCacheHelper.DeleteCache deleteCache;/*** 插入緩存數據* @param cacheKey 緩存key* @param cacheValue 緩存值* @param ttl 單位毫秒 緩存失效時間 小于0表示永不過期*/public synchronized void putCache(String cacheKey, String cacheValue, long ttl){// 根據緩存key查詢緩存 ID/過期時間等final DataCommonCache cache = dataCommonCacheMapper.selectIdByKey(cacheKey);if (cache == null){// 新增數據DataCommonCache commonCache = buildInsertData(cacheKey, cacheValue, ttl);// 發送給入庫隊列insertCache.producer(commonCache);return;}// 緩存設置了過期時間 并且緩存國企時間比當前時間小(過期了)if (cache.getCacheExpire() != null && cache.getCacheExpire().getTime() < System.currentTimeMillis()){// 發送刪除過期Key隊列deleteCache.producer(cacheKey);return;}// 都不是 表示需要覆蓋緩存 也就是更新緩存// 先判斷緩存的值是否和數據庫的值一致 一致則無需覆蓋final String cacheValueResult = dataCommonCacheMapper.selectValueByKey(cacheKey);if (StringUtils.equals(cacheValueResult, cacheValue)){log.info("=============>>>>緩存key:{}的value與數據庫一致,無需覆蓋", cacheValue);return;}// 發送一個覆蓋的請求final DataCommonCache dataCommonCache = buildInsertData(cacheKey, cacheValue, ttl);dataCommonCache.setCacheId(cache.getCacheId());updateCache.producer(dataCommonCache);}/*** 根據緩存從數據庫查詢* @param cacheKey 緩存key* @return 緩存值cacheValue 這里返回的值可能是已過期的 知道過期key刪除之后才會返回新的數據*/public synchronized String getCache(String cacheKey){// 根據緩存key查詢緩存 ID/過期時間等final DataCommonCache cache = dataCommonCacheMapper.selectByKey(cacheKey);if (cache == null){log.info("===========緩存不存在, 請請先調用putCache緩存===========");return null;}// 緩存設置了過期時間 并且緩存國企時間比當前時間小(過期了)if (cache.getCacheExpire() != null && cache.getCacheExpire().getTime() < System.currentTimeMillis()){// 發送刪除過期Key隊列deleteCache.producer(cacheKey);// 等待異步線程處理刪除過期,但是這里還是返回緩存數據,從而減少數據庫的壓力,直到緩存刪除后再次查詢到結果在返回}log.info("================命中緩存cacheKey為:{}=================", cacheKey);// 不為空,返回數據庫return cache.getCacheValue();}/*** 根據key刪除緩存* @param cacheKey 緩存key*/public synchronized void deleteCache(String cacheKey){// 根據緩存key查詢緩存 ID/過期時間等final DataCommonCache cache = dataCommonCacheMapper.selectIdByKey(cacheKey);if (cache == null){log.info("===========緩存不存在 無需刪除===========");return;}// 發送刪除消息到隊列deleteCache.producer(cacheKey);}private DataCommonCache buildInsertData(String cacheKey, String cacheValue, long ttl) {final DataCommonCache commonCache = new DataCommonCache();commonCache.setCacheKey(cacheKey);commonCache.setCacheValue(cacheValue);// 失效時間為當前是時間 + ttl時間Date expireTime = null;if (ttl > 0){expireTime = new Date(System.currentTimeMillis() + ttl);}commonCache.setCacheExpire(expireTime);return commonCache;} }

緩存測試接口

@RestController @RequestMapping("/dbCache") public class DbCacheController {@Autowiredprivate DbCacheUtil dbCacheUtil;/**緩存時間設置為5分鐘, 可以自行設置*/private static final Long ttl = 300 * 1000L;@GetMapping("/test/putCache")public String putCache(@RequestParam("cacheKey") String cacheKey, @RequestParam("cacheValue") String cacheValue){dbCacheUtil.putCache(cacheKey, cacheValue, ttl);return "success";}@GetMapping("/test/getCache")public String getCache(@RequestParam("cacheKey") String cacheKey){return dbCacheUtil.getCache(cacheKey);}@GetMapping("/test/deleteCache")public String deleteCache(@RequestParam("cacheKey") String cacheKey){dbCacheUtil.deleteCache(cacheKey);return "success";} }

接口測試

新增接口:http://localhost:8081/dbCache/test/putCache?cacheKey=test_name&cacheValue=張三

查詢接口:http://localhost:8081/dbCache/test/getCache?cacheKey=test_name

刪除接口:http://localhost:8081/dbCache/test/deleteCache?cacheKey=test_name

測試結果

2022-08-10 09:31:36.699 INFO 19572 --- [nio-8081-exec-2] com.itdl.cache.util.DbCacheUtil : ===========緩存不存在, 請請先調用putCache緩存=========== 2022-08-10 09:31:39.815 INFO 19572 --- [nio-8081-exec-3] com.itdl.cache.BaseCacheHelper : ================>>>通用隊列:com.itdl.cache.DbCacheHelper$InsertCache:當前隊列存在數據:1 2022-08-10 09:31:40.812 INFO 19572 --- [ 統一可調度線程-1] com.itdl.cache.BaseCacheHelper : =========>>>>消費數據1條 2022-08-10 09:31:40.813 INFO 19572 --- [ 統一可調度線程-1] c.itdl.cache.DbCacheHelper$InsertCache : =====================開始插入緩存數據cacheKey:test_name=========================== 2022-08-10 09:31:40.851 INFO 19572 --- [ 統一可調度線程-1] c.itdl.cache.DbCacheHelper$InsertCache : =====================完成插入緩存數據cacheKey:test_name=========================== 2022-08-10 09:31:40.852 INFO 19572 --- [ 統一可調度線程-1] com.itdl.cache.BaseCacheHelper : =========>>>>通用隊列:com.itdl.cache.DbCacheHelper$InsertCache:消費完成,總耗時:0.0383s<<<<========= 2022-08-10 09:31:42.296 INFO 19572 --- [nio-8081-exec-4] com.itdl.cache.util.DbCacheUtil : ================命中緩存cacheKey為:test_name================= 2022-08-10 10:18:51.256 INFO 19572 --- [nio-8081-exec-8] com.itdl.cache.BaseCacheHelper : ================>>>通用隊列:com.itdl.cache.DbCacheHelper$DeleteCache:當前隊列存在數據:1 2022-08-10 10:18:51.882 INFO 19572 --- [ 統一可調度線程-1] com.itdl.cache.BaseCacheHelper : =========>>>>消費數據1條 2022-08-10 10:18:51.882 INFO 19572 --- [ 統一可調度線程-1] c.itdl.cache.DbCacheHelper$DeleteCache : =====================開始刪除緩存數據cacheKey:test_name=========================== 2022-08-10 10:18:51.890 INFO 19572 --- [ 統一可調度線程-1] c.itdl.cache.DbCacheHelper$DeleteCache : =====================完成刪除寫入緩存數據cacheKey:test_name=========================== 2022-08-10 10:18:51.890 INFO 19572 --- [ 統一可調度線程-1] com.itdl.cache.BaseCacheHelper : =========>>>>通用隊列:com.itdl.cache.DbCacheHelper$DeleteCache:消費完成,總耗時:0.0079s<<<<========= 2022-08-10 10:19:01.817 INFO 19572 --- [nio-8081-exec-9] com.itdl.cache.util.DbCacheUtil : ===========緩存不存在, 請請先調用putCache緩存===========

項目地址

https://github.com/HedongLin123/db-cache-demo.git

總結

以上是生活随笔為你收集整理的使用数据库实现缓存功能的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。