springboot集成redis之字典緩存詳解
什么是redis的字典緩存?
Redis的緩存是Redis內(nèi)部用于存儲鍵值對數(shù)據(jù)結(jié)構(gòu)的一種基礎數(shù)據(jù)結(jié)構(gòu)。在Redis中,所有的鍵值對都是通過字典這種數(shù)據(jù)結(jié)構(gòu)來存儲的。字典在Redis中扮演著核心角色,因為它不僅用于數(shù)據(jù)庫中的鍵值對存儲,還用于實現(xiàn)其他如哈希、集合等復雜數(shù)據(jù)結(jié)構(gòu)。
以下是關(guān)于Redis字典緩存的一些關(guān)鍵點:
- 數(shù)據(jù)結(jié)構(gòu):Redis的字典使用哈希表作為底層實現(xiàn),這樣可以提供快速的查找、添加和刪除操作。哈希表通常是一個數(shù)組,數(shù)組的每個元素是一個指向鍵值對結(jié)構(gòu)的指針。
- 哈希沖突解決:當不同的鍵通過哈希函數(shù)映射到同一個位置時,Redis使用鏈表法來解決沖突。如果一個位置有多個鍵值對,它們會形成一個鏈表。
- rehash:隨著鍵值對數(shù)量的增加或減少,為了維持哈希表的性能,Redis會進行rehash操作,即重新計算所有鍵的哈希值,并將它們重新分布到新的哈希表中。
- 漸進式rehash:為了避免rehash操作帶來的性能問題,Redis使用漸進式rehash。它將rehash操作分散到對字典的每個添加、刪除、查找和更新操作中,從而避免了一次性rehash可能導致的長時間延遲。
- 緩存作用:由于字典的高效訪問特性,Redis可以快速讀寫數(shù)據(jù),這使得Redis非常適合作為緩存系統(tǒng)使用。在字典中存儲的數(shù)據(jù)可以直接從內(nèi)存中訪問,大大減少了數(shù)據(jù)讀取的時間。
- 持久化:雖然字典是內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),但Redis支持將字典中的數(shù)據(jù)持久化到硬盤上,以保證在系統(tǒng)故障時數(shù)據(jù)不會丟失。
- 類型特定字典:Redis支持多種數(shù)據(jù)類型,如字符串、列表、集合、哈希、有序集合等,每種數(shù)據(jù)類型在內(nèi)部都可能使用到字典結(jié)構(gòu)來存儲元數(shù)據(jù)或數(shù)據(jù)本身。
Redis的字典緩存是支撐其高性能的一個關(guān)鍵因素,它使得Redis能夠以極快的速度處理大量的數(shù)據(jù)。
項目目錄
代碼實踐
entity層
package com.wyl.redis.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; import javax.persistence.*; import java.io.Serializable; import java.time.LocalDateTime; import java.util.Date; /** * @Description * @Author wuyilong * @Date 2024-07-03 */ @Data @TableName("full_city") @Entity @Table(name="full_city") public class FullCity extends Model<FullCity> { private static final long serialVersionUID = 1L; /** * 主鍵id */ @TableId(value = "id", type = IdType.AUTO) @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** * 名稱 */ @TableField("name") private String name; /** * 行政編碼 */ @TableField("code") private String code; /** * 全名稱 */ @TableField("full_name") private String fullName; /** * 級別,1省,2市,3區(qū),4街道 */ @TableField("level") private Integer level; /** * 創(chuàng)建時間 */ @TableField("create_time") private Date createTime; /** * 中心點 */ @TableField("center") private String center; /** * 是否被撤銷,0否,1是 */ @TableField("is_revoke") private Integer isRevoke; /** * 父級編碼 */ private String parentCode; @Override public Serializable pkVal() { return this.id; } }
service層
package com.wyl.redis.service.impl; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.TreeUtil; import cn.hutool.core.map.MapUtil; import com.wyl.redis.bean.DictionaryBean; import com.wyl.redis.constant.DictionaryConst; import com.wyl.redis.entity.FullCity; import com.wyl.redis.service.DictionaryOperate; import com.wyl.redis.service.FullCityService; import com.wyl.redis.vo.FullCityVo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * @Description * @Author WuYiLong * @Date 2024/7/3 17:36 */ @Slf4j @Service public class FullCityOperate implements DictionaryOperate { @Autowired private FullCityService fullCityService; @Autowired private RedisTemplate redisTemplate; @Override public List list(String key) { if(!redisTemplate.hasKey(key)) { List<FullCity> list = fullCityService.list(); List<DictionaryBean> dictionaryBeans = list.stream().map(m -> { DictionaryBean dictionaryBean = new DictionaryBean(); dictionaryBean.setCode(m.getCode()); dictionaryBean.setName(m.getName()); dictionaryBean.setLevel(m.getLevel()); dictionaryBean.setParentCode(m.getParentCode()); return dictionaryBean; }).collect(Collectors.toList()); redisTemplate.opsForValue().set(key,dictionaryBeans); return dictionaryBeans; } List<DictionaryBean> list = (List<DictionaryBean>)redisTemplate.opsForValue().get(key); return list; } @Override public List<Tree<String>> tree(String key) { if(!redisTemplate.hasKey(key)) { List<FullCity> list = fullCityService.list(); List<Tree<String>> build = TreeUtil.build(list, "0", (t1, t2) -> { t2.setId(t1.getCode()); t2.setName(t1.getName()); t2.setParentId(t1.getParentCode()); }); redisTemplate.opsForValue().set(key,build); return build; } List<Tree<String>> trees = (List<Tree<String>>)redisTemplate.opsForValue().get(key); return trees; } @Override public String codeNameMap(String key, String code) { if(!redisTemplate.opsForHash().hasKey(key,code)) { FullCityVo fullCityVo = fullCityService.getByCode(code); if(fullCityVo != null) { redisTemplate.opsForHash().putIfAbsent(key,fullCityVo.getCode(),fullCityVo.getName()); return fullCityVo.getName(); } return null; } String name = (String)redisTemplate.opsForHash().get(key, code); return name; } @Override public String nameCodeMap(String key, String name) { if(!redisTemplate.opsForHash().hasKey(key,name)) { FullCityVo fullCityVo = fullCityService.getByFullName(name); if(fullCityVo != null) { redisTemplate.opsForHash().putIfAbsent(key,fullCityVo.getFullName(),fullCityVo.getCode()); return fullCityVo.getCode(); } return null; } String code = (String)redisTemplate.opsForHash().get(key, name); return code; } @Override public String supportType() { return DictionaryConst.FULL_CITY; } }
package com.wyl.redis.service.impl; import cn.hutool.core.lang.tree.Tree; import com.wyl.redis.constant.DictionaryConst; import com.wyl.redis.exception.BusinessException; import com.wyl.redis.service.DictionaryOperate; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.util.*; /** * @Description * @Author WuYiLong * @Date 2024/7/3 17:23 */ @Slf4j @Component public class DictionaryService implements ApplicationContextAware { private Map<String,DictionaryOperate> dictionaryMaps = new HashMap<>(); @Autowired private RedisTemplate redisTemplate; public DictionaryOperate buildDictionaryOperate(String key) { DictionaryOperate dictionaryOperate = dictionaryMaps.get(key); if(dictionaryOperate == null) { throw new BusinessException("字典的key不存在"); } return dictionaryOperate; } public List list(String key) { String listKey = DictionaryConst.DIC+key+DictionaryConst.LIST; if(key.contains(":")) { String[] split = key.split(":"); key = split[0]; listKey = DictionaryConst.DIC+key+DictionaryConst.LIST+":"+split[1]; } List list = buildDictionaryOperate(key).list(listKey); return list; } public List<Tree<String>> tree(String key) { String listKey = DictionaryConst.DIC+key+DictionaryConst.TREE; if(key.contains(":")) { String[] split = key.split(":"); key = split[0]; listKey = DictionaryConst.DIC+key+DictionaryConst.TREE+":"+split[1]; } List<Tree<String>> tree =buildDictionaryOperate(key).tree(listKey); return tree; } public String codeNameMap(String key, String code) { String name = buildDictionaryOperate(key).codeNameMap(DictionaryConst.DIC+key+":codeNameMap", code); return name; } public String nameCodeMap(String key, String name) { String code = buildDictionaryOperate(key).nameCodeMap(DictionaryConst.DIC+key+":nameCodeMap", name); return code; } public void refresh() { Set keys = redisTemplate.keys("dic*"); keys.forEach(v->{ redisTemplate.delete(v); }); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map<String, DictionaryOperate> dictionaryOperateMap = applicationContext.getBeansOfType(DictionaryOperate.class); dictionaryOperateMap.forEach((k,v)->{ dictionaryMaps.put(v.supportType(),v); }); } }
controller層
package com.wyl.redis.controller; import com.wyl.common.bean.ResponseData; import com.wyl.redis.service.impl.DictionaryService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * * @Description * @Author WuYiLong * @Date 2024/7/8 10:21 */ @Api(tags = "字典api") @RestController @RequestMapping(value = "dictionary") public class DictionaryController { @Autowired private DictionaryService dictionaryService; @ApiOperation(value = "字典刷新") @GetMapping(value = "refresh") public ResponseData refresh() { dictionaryService.refresh(); return ResponseData.success(); } @ApiOperation(value = "字典列表") @GetMapping(value = "list") public ResponseData list(String key) { return ResponseData.successInstance(dictionaryService.list(key)); } @ApiOperation(value = "字典樹") @GetMapping(value = "tree") public ResponseData tree(String key) { return ResponseData.successInstance(dictionaryService.tree(key)); } @ApiOperation(value = "根據(jù)code獲取名稱") @GetMapping(value = "codeNameMap") public ResponseData codeNameMap(String key, String code) { return ResponseData.successInstance(dictionaryService.codeNameMap(key,code)); } @ApiOperation(value = "根據(jù)名稱獲取code") @GetMapping(value = "nameCodeMap") public ResponseData nameCodeMap(String key, String name) { return ResponseData.successInstance(dictionaryService.nameCodeMap(key, name)); } }
測試
根據(jù)code獲取名稱
字典列表
字典樹
字典在redis客戶端的存儲
項目說明
只需要配置好本地的數(shù)據(jù)庫,連接上自己本地的redis,啟動項目,就會自動初始化數(shù)據(jù)庫腳本到本地數(shù)據(jù)庫。
package com.wyl.redis.config; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty; import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties; import com.baomidou.dynamic.datasource.support.ScriptRunner; import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.stereotype.Component; import javax.sql.DataSource; import java.util.Map; /** * @Description 公共初始化配置 * @Author WuYiLong * @Date 2024/7/8 9:38 */ @Slf4j @ConditionalOnProperty(prefix = "init",value = "enabled",havingValue = "true") @Component public class InitConfig implements ApplicationRunner { @Autowired private DynamicDataSourceProperties dynamicDataSourceProperties; @Override public void run(ApplicationArguments args) throws Exception { log.info("****************初始化數(shù)據(jù)庫腳本開始*************"); Map<String, DataSourceProperty> datasource = dynamicDataSourceProperties.getDatasource(); DataSourceProperty master = datasource.get("master"); DataSource build = DataSourceBuilder .create() .url(master.getUrl()) .driverClassName(master.getDriverClassName()) .password(master.getPassword()) .type(master.getType()) .username(master.getUsername()) .build(); ScriptRunner scriptRunner = new ScriptRunner(true, ";"); scriptRunner.runScript(build,"classpath:/db/**"); log.info("****************初始化數(shù)據(jù)庫腳本結(jié)束*************"); } }
在配置文件那里配置,設置init.enabled=true
init: enabled: false
項目地址
到此這篇關(guān)于springboot集成redis之字典緩存的文章就介紹到這了,更多相關(guān)springboot集成redis內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 數(shù)組轉(zhuǎn)List的四種方式小結(jié)
本文主要介紹了四種將Java數(shù)組轉(zhuǎn)換為List的方法,包括使用Arrays.asList、ArrayList構(gòu)造器、Collections.addAll以及JDK8的Stream,具有一定的參考價值,感興趣的可以了解一下2024-10-10Spring?MVC啟動之HandlerMapping作用及實現(xiàn)詳解
這篇文章主要為大家介紹了Spring?MVC啟動之HandlerMapping作用及實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03ReentrantReadWriteLock?讀寫鎖分析總結(jié)
這篇文章主要介紹了ReentrantReadWriteLock 讀寫鎖分析總結(jié),ReentranReadWriteLock中有兩把鎖,一把讀鎖,一把寫鎖,關(guān)于這兩把鎖的介紹,需要的小伙伴可以參考一下2022-05-05基于springboot的RestTemplate、okhttp和HttpClient對比分析
這篇文章主要介紹了基于springboot的RestTemplate、okhttp和HttpClient對比分析,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09java 中Comparable與Comparator詳解與比較
這篇文章主要介紹了java 中Comparable與Comparator詳解與比較的相關(guān)資料,需要的朋友可以參考下2017-04-04Java數(shù)據(jù)結(jié)構(gòu)之圖的兩種搜索算法詳解
在很多情況下,我們需要遍歷圖,得到圖的一些性質(zhì)。有關(guān)圖的搜索,最經(jīng)典的算法有深度優(yōu)先搜索和廣度優(yōu)先搜索,接下來我們分別講解這兩種搜索算法,需要的可以參考一下2022-11-11使用自定義注解和@Aspect實現(xiàn)責任鏈模式的組件增強的詳細代碼
責任鏈模式是一種行為設計模式,其作用是將請求的發(fā)送者和接收者解耦,從而可以靈活地組織和處理請求,本文講給大家介紹如何使用自定義注解和@Aspect實現(xiàn)責任鏈模式的組件增強,文中有詳細的代碼示例供大家參考,感興趣的同學可以借鑒一下2023-05-05