分布式緩存
緩存使用場(chǎng)景
redis作緩存中間件
引入redis依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置redis
# ip地址
spring.redis.host=124.222.43.217
# 端口
spring.redis.port=6379
堆外內(nèi)存溢出
<!--引入redis,排除lettuce,使用jedis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
緩存失效問(wèn)題
緩存穿透
緩存雪崩
緩存擊穿
Redisson分布式鎖
導(dǎo)入依賴
<!-- 以后使用redisson作為所有分布式鎖,分布式對(duì)象等功能框架 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
</dependency>
redisson配置類
@Configuration
public class MyRedissonConfig {
@Bean(destroyMethod="shutdown")
RedissonClient redissonClient() throws IOException {
Config config = new Config();
// 這里必須以redis://開(kāi)頭,否則會(huì)報(bào)錯(cuò)
config.useSingleServer().setAddress("redis://124.222.43.217:6379");
return Redisson.create(config);
}
}
可重入鎖
可重入鎖解釋:無(wú)論是公平方式還是非公平方式,進(jìn)門(mén)坐下來(lái)之后,你可以問(wèn)醫(yī)生問(wèn)題一次,兩次,無(wú)數(shù)次( 重入),只要你還坐著,你都可以問(wèn),但是一旦起身離開(kāi)座位,你的位置就會(huì)被搶,除非沒(méi)人排隊(duì),不然你失去了提問(wèn)的資格。
@ResponseBody
@GetMapping("/hello")
public String hello() {
//1 獲取一把鎖,只要鎖的名字一樣,就是同一把鎖
RLock lock = redisson.getLock("my-lock");
// 2 加鎖
lock.lock(); // 阻塞式等待,默認(rèn)加的鎖都是30s
// 鎖的自動(dòng)續(xù)期,如果業(yè)務(wù)超長(zhǎng),運(yùn)行期間自動(dòng)給鎖續(xù)上新的30s。不用擔(dān)心業(yè)務(wù)時(shí)間長(zhǎng),鎖自動(dòng)過(guò)期被刪除
// 加鎖的業(yè)務(wù)只要運(yùn)行完成,就不會(huì)給當(dāng)前鎖續(xù)期,即使不手動(dòng)解鎖,鎖默認(rèn)在30s以后自動(dòng)刪除
lock.lock(10, TimeUnit.SECONDS); //10s自動(dòng)解鎖,自動(dòng)解鎖時(shí)間一定要大于業(yè)務(wù)的執(zhí)行時(shí)間
// lock.lock(10, TimeUnit.SECONDS); // 鎖時(shí)間到了之后,不會(huì)自動(dòng)續(xù)期
try {
System.out.println("加鎖成功,執(zhí)行業(yè)務(wù)" + Thread.currentThread().getId());
// 模擬長(zhǎng)業(yè)務(wù)5s
Thread.sleep(5000);
} catch (Exception e) {
}finally {
// 3 解鎖
System.out.println("釋放鎖" + Thread.currentThread().getId());
lock.unlock();
}
return "hello";
}
讀寫(xiě)鎖
讀鎖(共享鎖)會(huì)等待寫(xiě)鎖(互斥鎖,排他鎖)釋放,保證一定能讀到最新數(shù)據(jù)
寫(xiě)鎖也會(huì)等待讀鎖釋放,保證寫(xiě)數(shù)據(jù)時(shí)不能讀取數(shù)據(jù)
總結(jié):讀寫(xiě)互斥,不管是先讀后寫(xiě),還是先寫(xiě)后讀,都會(huì)進(jìn)行阻塞等待
@GetMapping("/write")
@ResponseBody
public String writeValue() {
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
String s = "";
// 改數(shù)據(jù),加寫(xiě)鎖
RLock rLock = lock.writeLock();
try {
rLock.lock();
System.out.println("nihao");
s = UUID.randomUUID().toString();
Thread.sleep(30000);
redisTemplate.opsForValue().set("writeValue", s);
} catch (Exception e) {
e.printStackTrace();
}finally {
rLock.unlock();
}
return s;
}
@GetMapping("/read")
@ResponseBody
public String readValue() {
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
String s = "";
// 讀數(shù)據(jù),加讀鎖
RLock rLock = lock.readLock();
try {
rLock.lock();
s = redisTemplate.opsForValue().get("writeValue");
} catch (Exception e) {
e.printStackTrace();
}finally {
rLock.unlock();
}
return s;
}
緩存一致性解決
雙寫(xiě)模式:寫(xiě)操作后,同時(shí)修改緩存
失效模式:寫(xiě)操作后,刪除緩存
緩存-SpringCache
簡(jiǎn)介
@Cacheable
引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
properties配置
# 使用redis作為緩存
spring.cache.type=redis
# 設(shè)置有效時(shí)間,毫秒為單位,3600*1000
spring.cache.redis.time-to-live=3600000
# 是否使用緩存前綴
spring.cache.redis.use-key-prefix=true
# 指定緩存前綴,如果不指定,則默認(rèn)使用注解中value指向的值(分組名),如果value沒(méi)有指向任何值,則無(wú)緩存前綴
# spring.cache.redis.key-prefix=WSKH_CACHE_
# 是否緩存空值,防止緩存穿透
spring.cache.redis.cache-null-values=true
spring.session.store-type=redis
啟動(dòng)類上加注解,開(kāi)啟緩存
@EnableCaching
在要開(kāi)啟緩存的方法上加上@Cacheable注解,示例如下所示:
// 每一個(gè)需要緩存的數(shù)據(jù)我們都要指定放到哪個(gè)名字的緩存?!揪彺娴姆謪^(qū)】
// 當(dāng)前方法的結(jié)果需要緩存,如果緩存中有,方法不調(diào)用,如果緩存中沒(méi)有,會(huì)調(diào)用方法,最后將方法的結(jié)果放入緩存
// value = {"category"} 表示:屬于哪個(gè)緩存分區(qū)(分組),當(dāng)沒(méi)有指定緩存前綴的時(shí)候,就會(huì)使用這個(gè)分組名作為前綴
// key = "#root.method.name" 表示:將方法名作為存入redis中的鍵
// sync = true 表示:是否為同步代碼塊
@Cacheable(value = {"category"},key = "#root.method.name",sync = true)
@Override
public List<CategoryEntity> getLevel1Categorys() {
long l = System.currentTimeMillis();
List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
System.out.println("消耗時(shí)間," + (System.currentTimeMillis() - 1));
return categoryEntities;
}
自定義緩存配置
默認(rèn)緩存數(shù)據(jù)是保存JDK序列化后的數(shù)據(jù),一般業(yè)務(wù)上需要使用JSON格式進(jìn)行緩存的存儲(chǔ)(JSON具有跨語(yǔ)言,跨平臺(tái)的高兼容性)
@EnableConfigurationProperties(CacheProperties.class)
@Configuration
@EnableCaching
public class MyCacheConfig {
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
@CacheEvict
- value = “category”:指定分組名,即緩存默認(rèn)前綴
- allEntries = true:指定刪除value指向的分組下的所有緩存數(shù)據(jù)
- key = " ‘myKey’ ":指定緩存的key值,如果是普通字符串,則需要在雙引號(hào)中加單引號(hào),將其包裹
@CacheEvict(value = "category",allEntries = true) // 失效模式
@CachePut // 雙寫(xiě)模式
@Transactional
@Override
public void updateCasecade(CategoryEntity category) {
this.updateById(category);
categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
}
同時(shí)清除多個(gè)緩存
@CachePut
雙寫(xiě)模式,修改完數(shù)據(jù)庫(kù)后,將返回的結(jié)果更新到緩存中,如果方法返回修飾符為void,那么就不能使用@CachePut注解
原理與不足
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-493397.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-493397.html
到了這里,關(guān)于【系統(tǒng)開(kāi)發(fā)】尚硅谷 - 谷粒商城項(xiàng)目筆記(五):分布式緩存的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!