springboot集成redis之字典緩存詳解
什么是redis的字典緩存?
Redis的緩存是Redis內(nèi)部用于存儲(chǔ)鍵值對(duì)數(shù)據(jù)結(jié)構(gòu)的一種基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)。在Redis中,所有的鍵值對(duì)都是通過(guò)字典這種數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)的。字典在Redis中扮演著核心角色,因?yàn)樗粌H用于數(shù)據(jù)庫(kù)中的鍵值對(duì)存儲(chǔ),還用于實(shí)現(xiàn)其他如哈希、集合等復(fù)雜數(shù)據(jù)結(jié)構(gòu)。
以下是關(guān)于Redis字典緩存的一些關(guān)鍵點(diǎn):
- 數(shù)據(jù)結(jié)構(gòu):Redis的字典使用哈希表作為底層實(shí)現(xiàn),這樣可以提供快速的查找、添加和刪除操作。哈希表通常是一個(gè)數(shù)組,數(shù)組的每個(gè)元素是一個(gè)指向鍵值對(duì)結(jié)構(gòu)的指針。
- 哈希沖突解決:當(dāng)不同的鍵通過(guò)哈希函數(shù)映射到同一個(gè)位置時(shí),Redis使用鏈表法來(lái)解決沖突。如果一個(gè)位置有多個(gè)鍵值對(duì),它們會(huì)形成一個(gè)鏈表。
- rehash:隨著鍵值對(duì)數(shù)量的增加或減少,為了維持哈希表的性能,Redis會(huì)進(jìn)行rehash操作,即重新計(jì)算所有鍵的哈希值,并將它們重新分布到新的哈希表中。
- 漸進(jìn)式rehash:為了避免rehash操作帶來(lái)的性能問(wèn)題,Redis使用漸進(jìn)式rehash。它將rehash操作分散到對(duì)字典的每個(gè)添加、刪除、查找和更新操作中,從而避免了一次性rehash可能導(dǎo)致的長(zhǎng)時(shí)間延遲。
- 緩存作用:由于字典的高效訪問(wèn)特性,Redis可以快速讀寫(xiě)數(shù)據(jù),這使得Redis非常適合作為緩存系統(tǒng)使用。在字典中存儲(chǔ)的數(shù)據(jù)可以直接從內(nèi)存中訪問(wèn),大大減少了數(shù)據(jù)讀取的時(shí)間。
- 持久化:雖然字典是內(nèi)存中的數(shù)據(jù)結(jié)構(gòu),但Redis支持將字典中的數(shù)據(jù)持久化到硬盤上,以保證在系統(tǒng)故障時(shí)數(shù)據(jù)不會(huì)丟失。
- 類型特定字典:Redis支持多種數(shù)據(jù)類型,如字符串、列表、集合、哈希、有序集合等,每種數(shù)據(jù)類型在內(nèi)部都可能使用到字典結(jié)構(gòu)來(lái)存儲(chǔ)元數(shù)據(jù)或數(shù)據(jù)本身。
Redis的字典緩存是支撐其高性能的一個(gè)關(guān)鍵因素,它使得Redis能夠以極快的速度處理大量的數(shù)據(jù)。
項(xiàng)目目錄

代碼實(shí)踐
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;
/**
* 級(jí)別,1省,2市,3區(qū),4街道
*/
@TableField("level")
private Integer level;
/**
* 創(chuàng)建時(shí)間
*/
@TableField("create_time")
private Date createTime;
/**
* 中心點(diǎn)
*/
@TableField("center")
private String center;
/**
* 是否被撤銷,0否,1是
*/
@TableField("is_revoke")
private Integer isRevoke;
/**
* 父級(jí)編碼
*/
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 = "字典樹(shù)")
@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));
}
}測(cè)試
根據(jù)code獲取名稱

字典列表

字典樹(shù)

字典在redis客戶端的存儲(chǔ)

項(xiàng)目說(shuō)明
只需要配置好本地的數(shù)據(jù)庫(kù),連接上自己本地的redis,啟動(dòng)項(xiàng)目,就會(huì)自動(dòng)初始化數(shù)據(jù)庫(kù)腳本到本地?cái)?shù)據(jù)庫(kù)。
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ù)庫(kù)腳本開(kāi)始*************");
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ù)庫(kù)腳本結(jié)束*************");
}
}在配置文件那里配置,設(shè)置init.enabled=true
init: enabled: false
項(xiàng)目地址
到此這篇關(guān)于springboot集成redis之字典緩存的文章就介紹到這了,更多相關(guān)springboot集成redis內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
maven依賴關(guān)系中的<scope>provided</scope>使用詳解
這篇文章主要介紹了maven依賴關(guān)系中的<scope>provided</scope>使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java本地高性能緩存的幾種常見(jiàn)實(shí)現(xiàn)方式
在Java中緩存是一種常用的性能優(yōu)化技術(shù),用于在應(yīng)用程序中加速訪問(wèn)和查詢數(shù)據(jù)的速度,下面這篇文章主要給大家介紹了關(guān)于Java本地高性能緩存的幾種常見(jiàn)實(shí)現(xiàn)方式,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07
SpringBoot JS-SDK自定義微信分享的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot JS-SDK自定義微信分享的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Jmeter正則表達(dá)式提取器實(shí)現(xiàn)過(guò)程圖解
這篇文章主要介紹了Jmeter正則表達(dá)式提取器實(shí)現(xiàn)過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
Springboot如何獲取配置文件application.yml中自定義的變量并使用
這篇文章主要介紹了Springboot中獲取配置文件(application.yml)中自定義的變量并使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
springboot集成本地緩存Caffeine的三種使用方式(小結(jié))
本文主要介紹了springboot集成本地緩存Caffeine的三種使用方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
掌握J(rèn)ava拼音轉(zhuǎn)換:pinyin4j庫(kù)使用方法及應(yīng)用價(jià)值
pinyin4j是一個(gè)開(kāi)源的Java庫(kù),用于將漢字轉(zhuǎn)換為拼音,它支持將中文字符轉(zhuǎn)換為標(biāo)準(zhǔn)的全拼形式,并能夠處理多音字和聲調(diào),本文詳細(xì)解析pinyin4j庫(kù)的核心特性、使用方法及其應(yīng)用價(jià)值,感興趣的朋友一起看看吧2025-08-08

