中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

跨境電商網(wǎng)站建設(shè)方案網(wǎng)站優(yōu)化排名易下拉穩(wěn)定

跨境電商網(wǎng)站建設(shè)方案,網(wǎng)站優(yōu)化排名易下拉穩(wěn)定,網(wǎng)站優(yōu)化排名的方法,酷炫網(wǎng)站設(shè)計(jì)風(fēng)格CaffeineRedis兩級緩存架構(gòu) 在高性能的服務(wù)項(xiàng)目中,我們一般會(huì)將一些熱點(diǎn)數(shù)據(jù)存儲(chǔ)到 Redis這類緩存中間件中,只有當(dāng)緩存的訪問沒有命中時(shí)再查詢數(shù)據(jù)庫。在提升訪問速度的同時(shí),也能降低數(shù)據(jù)庫的壓力。 但是在一些場景下單純使用 Redis 的分布…

Caffeine+Redis兩級緩存架構(gòu)

在高性能的服務(wù)項(xiàng)目中,我們一般會(huì)將一些熱點(diǎn)數(shù)據(jù)存儲(chǔ)到 Redis這類緩存中間件中,只有當(dāng)緩存的訪問沒有命中時(shí)再查詢數(shù)據(jù)庫。在提升訪問速度的同時(shí),也能降低數(shù)據(jù)庫的壓力。

但是在一些場景下單純使用 Redis 的分布式緩存不能滿足高性能的要求,所以還需要加入使用本地緩存Caffeine,從而再次提升程序的響應(yīng)速度與服務(wù)性能。于是,就產(chǎn)生了使用本地緩存(Caffeine)作為一級緩存,再加上分布式緩存(Redis)作為二級緩存的兩級緩存架構(gòu)。

image.png

兩級緩存架構(gòu)優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

  • 一級緩存基于應(yīng)用的內(nèi)存,訪問速度非???#xff0c;對于一些變更頻率低、實(shí)時(shí)性要求低的數(shù)據(jù),可以放在本地緩存中,提升訪問速度;
  • 使用一級緩存能夠減少和 Redis 的二級緩存的遠(yuǎn)程數(shù)據(jù)交互,減少網(wǎng)絡(luò) I/O 開銷,降低這一過程中在網(wǎng)絡(luò)通信上的耗時(shí)。

缺點(diǎn):

  • 數(shù)據(jù)一致性問題:兩級緩存與數(shù)據(jù)庫的數(shù)據(jù)要保持一致,一旦數(shù)據(jù)發(fā)生了修改,在修改數(shù)據(jù)庫的同時(shí),一級緩存、二級緩存應(yīng)該同步更新。
  • 分布式多應(yīng)用情況下:一級緩存之間也會(huì)存在一致性問題,當(dāng)一個(gè)節(jié)點(diǎn)下的本地緩存修改后,需要通知其他節(jié)點(diǎn)也刷新本地一級緩存中的數(shù)據(jù),否則會(huì)出現(xiàn)讀取到過期數(shù)據(jù)的情況。
  • 緩存的過期時(shí)間、過期策略以及多線程的問題

Caffeine+Redis兩級緩存架構(gòu)實(shí)戰(zhàn)

1、準(zhǔn)備表結(jié)構(gòu)和數(shù)據(jù)

準(zhǔn)備如下的表結(jié)構(gòu)和相關(guān)數(shù)據(jù)

DROP TABLE IF EXISTS user;CREATE TABLE user
(id BIGINT(20) NOT NULL COMMENT '主鍵ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年齡',email VARCHAR(50) NULL DEFAULT NULL COMMENT '郵箱',PRIMARY KEY (id)
);

插入對應(yīng)的相關(guān)數(shù)據(jù)

DELETE FROM user;INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

2、創(chuàng)建項(xiàng)目

創(chuàng)建一個(gè)SpringBoot項(xiàng)目,然后引入相關(guān)的依賴,首先是父依賴

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.6</version><relativePath/> <!-- lookup parent from repository --></parent>

具體的其他的依賴

<!-- spring-boot-starter-web 的依賴 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 引入MyBatisPlus的依賴 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><!-- 數(shù)據(jù)庫使用MySQL數(shù)據(jù)庫 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- 數(shù)據(jù)庫連接池 Druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.14</version></dependency><!-- lombok依賴 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>

3、配置信息

然后我們需要在application.properties中配置數(shù)據(jù)源的相關(guān)信息

spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mp?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

然后我們需要在SpringBoot項(xiàng)目的啟動(dòng)類上配置Mapper接口的掃描路徑

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳

4、添加User實(shí)體

添加user的實(shí)體類

@ToString
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}

5、創(chuàng)建Mapper接口

在MyBatisPlus中的Mapper接口需要繼承BaseMapper.

/*** MyBatisPlus中的Mapper接口繼承自BaseMapper*/
public interface UserMapper extends BaseMapper<User> {
}

6、測試操作

然后來完成對User表中數(shù)據(jù)的查詢操作

@SpringBootTest
class MpDemo01ApplicationTests {@Autowiredprivate UserMapper userMapper;@Testvoid queryUser() {List<User> users = userMapper.selectList(null);for (User user : users) {System.out.println(user);}}}

7、日志輸出

為了便于學(xué)習(xí)我們可以指定日志的實(shí)現(xiàn)StdOutImpl來處理

# 指定日志輸出
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳

然后操作數(shù)據(jù)庫的時(shí)候就可以看到對應(yīng)的日志信息了:

手動(dòng)兩級緩存架構(gòu)實(shí)戰(zhàn)

@Configuration
public class CaffeineConfig {@Beanpublic Cache<String,Object> caffeineCache(){return Caffeine.newBuilder().initialCapacity(128)//初始大小.maximumSize(1024)//最大數(shù)量.expireAfterWrite(15, TimeUnit.SECONDS)//過期時(shí)間 15S.build();}
}
//Caffeine+Redis兩級緩存查詢public User query1_2(long userId){String key = "user-"+userId;User user = (User) cache.get(key,k -> {//先查詢 Redis  (2級緩存)Object obj = redisTemplate.opsForValue().get(key);if (Objects.nonNull(obj)) {log.info("get data from redis:"+key);return obj;}// Redis沒有則查詢 DB(MySQL)User user2 = userMapper.selectById(userId);log.info("get data from database:"+userId);redisTemplate.opsForValue().set(key, user2, 30, TimeUnit.SECONDS);return user2;});return user;}

在 Cache 的 get 方法中,會(huì)先從Caffeine緩存中進(jìn)行查找,如果找到緩存的值那么直接返回。沒有的話查找 Redis,Redis 再不命中則查詢數(shù)據(jù)庫,最后都同步到Caffeine的緩存中。

通過案例演示也可以達(dá)到對應(yīng)的效果。

另外修改、刪除的代碼可以看代碼案例!

注解方式兩級緩存架構(gòu)實(shí)戰(zhàn)

在 spring中,提供了 CacheManager 接口和對應(yīng)的注解

  • @Cacheable:根據(jù)鍵從緩存中取值,如果緩存存在,那么獲取緩存成功之后,直接返回這個(gè)緩存的結(jié)果。如果緩存不存在,那么執(zhí)行方法,并將結(jié)果放入緩存中。
  • @CachePut:不管之前的鍵對應(yīng)的緩存是否存在,都執(zhí)行方法,并將結(jié)果強(qiáng)制放入緩存。
  • @CacheEvict:執(zhí)行完方法后,會(huì)移除掉緩存中的數(shù)據(jù)。

使用注解,就需要配置 spring 中的 CacheManager ,在這個(gè)CaffeineConfig類中

 @Beanpublic CacheManager cacheManager(){CaffeineCacheManager cacheManager=new CaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder().initialCapacity(128).maximumSize(1024).expireAfterWrite(15, TimeUnit.SECONDS));return cacheManager;}

EnableCaching

在啟動(dòng)類上再添加上 @EnableCaching 注解

image.png

在UserService類對應(yīng)的方法上添加 @Cacheable 注解

 //Caffeine+Redis兩級緩存查詢-- 使用注解@Cacheable(value = "user", key = "#userId")public User query2_2(long userId){String key = "user-"+userId;//先查詢 Redis  (2級緩存)Object obj = redisTemplate.opsForValue().get(key);if (Objects.nonNull(obj)) {log.info("get data from redis:"+key);return (User)obj;}// Redis沒有則查詢 DB(MySQL)User user = userMapper.selectById(userId);log.info("get data from database:"+userId);redisTemplate.opsForValue().set(key, user, 30, TimeUnit.SECONDS);return user;}

然后就可以達(dá)到類似的效果。

@Cacheable 注解的屬性:

參數(shù)解釋col3
key緩存的key,可以為空,如果指定要按照SpEL表達(dá)式編寫,如不指定,則按照方法所有參數(shù)組合@Cacheable(value=”testcache”, key=”#userName”)
value緩存的名稱,在 spring 配置文件中定義,必須指定至少一個(gè)例如:@Cacheable(value=”mycache”)
condition緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進(jìn)行緩存@Cacheable(value=”testcache”,
condition=”#userName.length()>2”)
methodName當(dāng)前方法名#root.methodName
method當(dāng)前方法#root.method.name
target當(dāng)前被調(diào)用的對象#root.target
targetClass當(dāng)前被調(diào)用的對象的class#root.targetClass
args當(dāng)前方法參數(shù)組成的數(shù)組#root.args[0]
caches當(dāng)前被調(diào)用的方法使用的Cache#root.caches[0].name

這里有一個(gè)condition屬性指定發(fā)生的條件

示例表示只有當(dāng)userId為偶數(shù)時(shí)才會(huì)進(jìn)行緩存

 //只有當(dāng)userId為偶數(shù)時(shí)才會(huì)進(jìn)行緩存@Cacheable(value = "user", key = "#userId", condition="#userId%2==0")public User query2_3(long userId){String key = "user-"+userId;//先查詢 Redis  (2級緩存)Object obj = redisTemplate.opsForValue().get(key);if (Objects.nonNull(obj)) {log.info("get data from redis:"+key);return (User)obj;}// Redis沒有則查詢 DB(MySQL)User user = userMapper.selectById(userId);log.info("get data from database:"+userId);redisTemplate.opsForValue().set(key, user, 30, TimeUnit.SECONDS);return user;}

CacheEvict

@CacheEvict是用來標(biāo)注在需要清除緩存元素的方法或類上的。

當(dāng)標(biāo)記在一個(gè)類上時(shí)表示其中所有的方法的執(zhí)行都會(huì)觸發(fā)緩存的清除操作。

@CacheEvict可以指定的屬性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的語義與@Cacheable對應(yīng)的屬性類似。即value表示清除操作是發(fā)生在哪些Cache上的(對應(yīng)Cache的名稱);key表示需要清除的是哪個(gè)key,如未指定則會(huì)使用默認(rèn)策略生成的key;condition表示清除操作發(fā)生的條件。下面我們來介紹一下新出現(xiàn)的兩個(gè)屬性allEntries和beforeInvocation。

 //清除緩存(所有的元素)@CacheEvict(value="user", key = "#userId",allEntries=true)public void deleteAll(long userId) {System.out.println(userId);}//beforeInvocation=true:在調(diào)用該方法之前清除緩存中的指定元素@CacheEvict(value="user", key = "#userId",beforeInvocation=true)public void delete(long userId) {System.out.println(userId);}

自定義注解實(shí)現(xiàn)兩級緩存架構(gòu)實(shí)戰(zhàn)

首先定義一個(gè)注解,用于添加在需要操作緩存的方法上:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DoubleCache {String cacheName();String key(); //支持springEl表達(dá)式long l2TimeOut() default 120;CacheType type() default CacheType.FULL;
}

l2TimeOut 為可以設(shè)置的二級緩存 Redis 的過期時(shí)間

CacheType 是一個(gè)枚舉類型的變量,表示操作緩存的類型

public enum CacheType {FULL,   //存取PUT,    //只存DELETE  //刪除
}

從前面我們知道,key要支持 springEl 表達(dá)式,寫一個(gè)ElParser的方法,使用表達(dá)式解析器解析參數(shù):

public class ElParser {public static String parse(String elString, TreeMap<String,Object> map){elString=String.format("#{%s}",elString);//創(chuàng)建表達(dá)式解析器ExpressionParser parser = new SpelExpressionParser();//通過evaluationContext.setVariable可以在上下文中設(shè)定變量。EvaluationContext context = new StandardEvaluationContext();map.entrySet().forEach(entry->context.setVariable(entry.getKey(),entry.getValue()));//解析表達(dá)式Expression expression = parser.parseExpression(elString, new TemplateParserContext());//使用Expression.getValue()獲取表達(dá)式的值,這里傳入了Evaluation上下文String value = expression.getValue(context, String.class);return value;}
}
package com.msb.caffeine.cache;import com.github.benmanes.caffeine.cache.Cache;
import lombok.AllArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;@Slf4j
@Component
@Aspect
@AllArgsConstructor
public class CacheAspect {private final Cache cache;private final RedisTemplate redisTemplate;@Pointcut("@annotation(com.msb.caffeine.cache.DoubleCache)")public void cacheAspect() {}@Around("cacheAspect()")public Object doAround(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();//拼接解析springEl表達(dá)式的mapString[] paramNames = signature.getParameterNames();Object[] args = point.getArgs();TreeMap<String, Object> treeMap = new TreeMap<>();for (int i = 0; i < paramNames.length; i++) {treeMap.put(paramNames[i],args[i]);}DoubleCache annotation = method.getAnnotation(DoubleCache.class);String elResult = ElParser.parse(annotation.key(), treeMap);String realKey = annotation.cacheName() + ":" + elResult;//強(qiáng)制更新if (annotation.type()== CacheType.PUT){Object object = point.proceed();redisTemplate.opsForValue().set(realKey, object,annotation.l2TimeOut(), TimeUnit.SECONDS);cache.put(realKey, object);return object;}//刪除else if (annotation.type()== CacheType.DELETE){redisTemplate.delete(realKey);cache.invalidate(realKey);return point.proceed();}//讀寫,查詢CaffeineObject caffeineCache = cache.getIfPresent(realKey);if (Objects.nonNull(caffeineCache)) {log.info("get data from caffeine");return caffeineCache;}//查詢RedisObject redisCache = redisTemplate.opsForValue().get(realKey);if (Objects.nonNull(redisCache)) {log.info("get data from redis");cache.put(realKey, redisCache);return redisCache;}log.info("get data from database");Object object = point.proceed();if (Objects.nonNull(object)){//寫入RedisredisTemplate.opsForValue().set(realKey, object,annotation.l2TimeOut(), TimeUnit.SECONDS);//寫入Caffeinecache.put(realKey, object);}return object;}
}

切面中主要做了下面幾件工作:

  • 通過方法的參數(shù),解析注解中 key 的 springEl 表達(dá)式,組裝真正緩存的 key。
  • 根據(jù)操作緩存的類型,分別處理存取、只存、刪除緩存操作。
  • 刪除和強(qiáng)制更新緩存的操作,都需要執(zhí)行原方法,并進(jìn)行相應(yīng)的緩存刪除或更新操作。
  • 存取操作前,先檢查緩存中是否有數(shù)據(jù),如果有則直接返回,沒有則執(zhí)行原方法,并將結(jié)果存入緩存。

然后使用的話就非常方便了,代碼中只保留原有業(yè)務(wù)代碼,再添加上我們自定義的注解就可以了:

    @DoubleCache(cacheName = "user", key = "#userId",type = CacheType.FULL)public User query3(Long userId) {User user = userMapper.selectById(userId);return user;}@DoubleCache(cacheName = "user",key = "#user.userId",type = CacheType.PUT)public int update3(User user) {return userMapper.updateById(user);}@DoubleCache(cacheName = "user",key = "#user.userId",type = CacheType.DELETE)public void deleteOrder(User user) {userMapper.deleteById(user);}

兩級緩存架構(gòu)的緩存一致性問題

就是如果一個(gè)應(yīng)用修改了緩存,另外一個(gè)應(yīng)用的caffeine緩存是沒有辦法感知的,所以這里就會(huì)有緩存的一致性問題

外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳

解決方案也很簡單,就是在Redis中做一個(gè)發(fā)布和訂閱。

遇到修改緩存的處理,需要向?qū)?yīng)的頻道發(fā)布一條消息,然后應(yīng)用同步監(jiān)聽這條消息,有消息則需要?jiǎng)h除本地的Caffeine緩存。

核心代碼如下:

http://www.risenshineclean.com/news/48467.html

相關(guān)文章:

  • 附近網(wǎng)站建設(shè)公司哪家好注冊網(wǎng)站免費(fèi)注冊
  • 要找企業(yè)做網(wǎng)站應(yīng)該注意什么網(wǎng)站優(yōu)化外包推薦
  • 河源建設(shè)用地競拍網(wǎng)站2024年新冠疫情最新消息今天
  • 信息網(wǎng)站建設(shè)預(yù)算windows優(yōu)化大師和360哪個(gè)好
  • 怎么在自己電腦上建網(wǎng)站今日競彩足球最新比賽結(jié)果查詢
  • 營銷系統(tǒng)有哪些南昌seo排名外包
  • 網(wǎng)站主頁模板友情鏈接檢測平臺(tái)
  • 建一家網(wǎng)站多少錢濰坊網(wǎng)站建設(shè)方案咨詢
  • 游戲類網(wǎng)頁設(shè)計(jì)優(yōu)化推廣
  • 網(wǎng)站建設(shè)設(shè)計(jì)時(shí)代創(chuàng)信好找廣告商的平臺(tái)
  • 企業(yè)網(wǎng)站平臺(tái)如何做網(wǎng)絡(luò)推廣視頻剪輯培訓(xùn)
  • 網(wǎng)站備案主體查詢河南seo優(yōu)化
  • 網(wǎng)絡(luò)規(guī)劃設(shè)計(jì)師2022云優(yōu)化
  • 怎做賣東西的網(wǎng)站如何快速網(wǎng)絡(luò)推廣
  • ecshop 獲取網(wǎng)站域名日照網(wǎng)絡(luò)推廣公司
  • 網(wǎng)站pc開發(fā)上海如何寫好軟文
  • 網(wǎng)站流量分析系統(tǒng)百度企業(yè)認(rèn)證怎么認(rèn)證
  • 高性能網(wǎng)站建設(shè)進(jìn)階指南 pdf知乎seo排名帝搜軟件
  • 英語課件做的好的網(wǎng)站百度云資源
  • 企業(yè)網(wǎng)站的建立視頻廣州各區(qū)風(fēng)險(xiǎn)區(qū)域最新動(dòng)態(tài)
  • 做網(wǎng)站的咋掙錢搜索引擎大全全搜網(wǎng)
  • 做網(wǎng)站建設(shè)公司賺錢seo關(guān)鍵詞優(yōu)化排名哪家好
  • 電商網(wǎng)站如何做c2b如何宣傳推廣自己的產(chǎn)品
  • 做神馬網(wǎng)站優(yōu)化快速網(wǎng)絡(luò)營銷推廣策劃方案
  • 廈門區(qū)塊鏈網(wǎng)站開發(fā)網(wǎng)站排名快速提升工具
  • 專做機(jī)械零配件的網(wǎng)站營銷型企業(yè)網(wǎng)站推廣的方法有哪些
  • web網(wǎng)站開發(fā)學(xué)習(xí)seo排名優(yōu)化北京
  • 網(wǎng)站換域名怎么做百度seo多少錢一個(gè)月
  • 無錫市網(wǎng)站搭建學(xué)網(wǎng)絡(luò)運(yùn)營需要多少錢
  • dns是不是做網(wǎng)站用的快手seo軟件下載