欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報邀請碼778899分享:數(shù)據(jù)庫 緩存 Redis實戰(zhàn)

柚子快報邀請碼778899分享:數(shù)據(jù)庫 緩存 Redis實戰(zhàn)

http://yzkb.51969.com/

短信登錄功能

發(fā)送短信驗證碼實現(xiàn)流程

提交手機(jī)號校驗手機(jī)號生成驗證碼,并保存保存驗證碼到redis發(fā)送驗證碼

@Override

public Result sendCode(String phone) {

//校驗手機(jī)號

if(RegexUtils.isPhoneInvalid(phone)){

return Result.fail(UserErrorConstant.PHONE_FORMAT_ERROR);

}

//生成驗證碼

String code = RandomUtil.randomNumbers(6);

//保存驗證碼(有效期:5min)

stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY +phone,code,RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);

//發(fā)送驗證碼

log.debug("發(fā)送短信驗證碼成功,驗證碼:{}",code);

return Result.ok();

}

短信驗證碼登錄注冊實現(xiàn)流程

提交手機(jī)號和驗證碼驗證驗證碼根據(jù)手機(jī)號查詢用戶用戶是否存在(如果不存在創(chuàng)建新用戶并保存用戶到數(shù)據(jù)庫)保存用戶到redis

@Override

public Result loginByCode(LoginFormDTO loginFormDTO) {

//校驗手機(jī)號

String phone = loginFormDTO.getPhone();

if(RegexUtils.isPhoneInvalid(phone)){

return Result.fail(UserErrorConstant.PHONE_FORMAT_ERROR);

}

//從redis獲取驗證碼并驗證

String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY+phone);

String code = loginFormDTO.getCode();

if(cacheCode==null||!cacheCode.equals(code)){

return Result.fail(UserErrorConstant.VERIFICATION_CODE_ERROR);

}

//根據(jù)手機(jī)號查詢用戶

User user = query().eq("mobile",phone).one();

//判斷用戶是否存在

if(user==null){

user = createUserWithPhone(phone);

}

//使用jwt令牌

String token = UUID.randomUUID().toString();

//將user對象轉(zhuǎn)成HashMap存儲(由于使用的是stringRedisTemplate,所以要把id轉(zhuǎn)為string)

UserDTO userDTO = BeanUtil.copyProperties(user,UserDTO.class);

userDTO.setToken(token);

Map userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),

CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName, fieldValue) -> fieldValue == null ? "" : fieldValue.toString()));

stringRedisTemplate.opsForHash().putAll(RedisConstants.LOGIN_USER_KEY+token,userMap);

stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY+token,RedisConstants.LOGIN_USER_TTL,TimeUnit.HOURS);

//返回token

return Result.ok(userDTO);

}

校驗登錄狀態(tài)實現(xiàn)流程

請求并攜帶cookie從redis獲取用戶判斷用戶是否存在(如果不存在則攔截)如果存在則保存用戶到threadlocal

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

//從請求頭中獲取token

String token = request.getHeader("authorization");

if (StringUtils.isEmpty(token)) {

//不存在token

return true;

}

//從redis中獲取用戶

Map userMap =

stringRedisTemplate.opsForHash()

.entries(RedisConstants.LOGIN_USER_KEY + token);

//用戶不存在

if (userMap.isEmpty()) {

return true;

}

//hash轉(zhuǎn)UserDTO存入ThreadLocal

UserHolder.saveUser(BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false));

//token續(xù)命

stringRedisTemplate.expire(RedisConstants.LOGIN_USER_KEY + token, RedisConstants.LOGIN_USER_TTL, TimeUnit.MINUTES);

return true;

}

緩存功能

緩存就是數(shù)據(jù)交換的緩沖區(qū)(Cache),是存儲數(shù)據(jù)的臨時地方,一般讀寫性能較高。

作用:

降低后端負(fù)載。提高讀寫效率,降低響應(yīng)時間。(解決高并發(fā)問題)

成本:

數(shù)據(jù)一致性成本代碼維護(hù)成本(緩存擊穿)運(yùn)維成本

緩存更新策略

內(nèi)存淘汰:利用redis的內(nèi)存淘汰機(jī)制,內(nèi)存不足時自動淘汰部分?jǐn)?shù)據(jù)。

超時剔除:添加ttl時間,到期自動刪除緩存(兜底)。

主動更新:在修改數(shù)據(jù)庫的同時,更新緩存。

方案1:由緩存的調(diào)用者,在更新數(shù)據(jù)庫的同時更新緩存。方案2:緩存與數(shù)據(jù)庫整合為一個服務(wù),由服務(wù)來維護(hù)一致性。方案3:調(diào)用者只操作緩存,由其他線程異步的將緩存數(shù)據(jù)持久化到數(shù)據(jù)庫,保證最終一致。

如何保證緩存與數(shù)據(jù)庫的操作的同時成功或失?。ㄔ硬僮鲉栴})?

單體系統(tǒng),將緩存與數(shù)據(jù)庫操作放在一個事務(wù)。分布式系統(tǒng),利用tcc等分布式事務(wù)方案。

先操作緩存還是先操作數(shù)據(jù)庫(線程安全)?

先操作數(shù)據(jù)庫再刪除緩存的操作更保險,因為數(shù)據(jù)庫的操作時間一般要比緩存慢。

緩存穿透

緩存穿透是指客戶端請求的數(shù)據(jù)在緩存中和數(shù)據(jù)庫中都不存在,這樣緩存永遠(yuǎn)不會生效,這些請求都會達(dá)到數(shù)據(jù)庫。

緩存空對象(被動方案)

優(yōu)點:實現(xiàn)簡單,維護(hù)方便

缺點:額外的內(nèi)存消耗(緩存垃圾,設(shè)置ttl刪除)、可能造成短期的不一致(插入時更新緩存)

布隆過濾(被動方案)

在客戶端和redis之間加入布隆過濾器,如果不存在則拒絕,存在則放行。(用byte數(shù)組存儲,hashcode二進(jìn)制hash對應(yīng)位置,若為1存在,若為0不存在)

優(yōu)點:內(nèi)存占用少,沒有多余key

缺點:實現(xiàn)復(fù)雜,存在誤判可能

主動方案

增加id的復(fù)雜度,避免被猜測id規(guī)律(雪花算法)做好數(shù)據(jù)的基礎(chǔ)格式校驗加強(qiáng)用戶權(quán)限校驗做好熱點參數(shù)的限流

/**

* 緩存穿透

* @param id

* @return

*/

public Result queryWithPassThrough(int id){

String key = RedisConstants.CACHE_SHOP_KEY+id;

//從redis查詢企業(yè)用戶信息緩存

String userJson = stringRedisTemplate.opsForValue().get(key);

//判斷是否存在

if(StrUtil.isNotBlank(userJson)){

User user = JSONUtil.toBean(userJson,User.class);

return Result.ok(user);

}

//判斷命中的是否是空值

if(userJson!=null){

//返回錯誤信息

return Result.fail(UserErrorConstant.USER_NOEXIST_ERROR);

}

//不存在,根據(jù)id查詢數(shù)據(jù)庫

User user = userMapper.queryUserById(id);

//不存在,返回錯誤

if(user==null){

//緩存穿透-》將空值寫入redis

stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);

return Result.fail(UserErrorConstant.USER_NOEXIST_ERROR);

}

//存儲到redis中,設(shè)置了超時時間

stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(user),RedisConstants.CACHE_SHOP_TTL,TimeUnit.HOURS);

return Result.ok(user);

}

緩存雪崩

緩存雪崩是指同一時段大量的緩存key同時失效或者redis服務(wù)宕機(jī),導(dǎo)致大量請求到達(dá)數(shù)據(jù)庫,帶來巨大壓力。

解決方案

給不同的key的ttl添加隨機(jī)值(同一時間段大量key失效,有時候在做緩存的預(yù)熱時會在同一時間批量的數(shù)據(jù)導(dǎo)入)利用redis集群提高服務(wù)的可用性(主宕機(jī),從結(jié)點上還有數(shù)據(jù))給緩存業(yè)務(wù)添加降級限流策略(提前做好服務(wù)降級,比如快速失敗拒絕服務(wù))給業(yè)務(wù)添加多級緩存

緩存擊穿

緩存擊穿問題也叫熱點key問題,就是一個被高并發(fā)訪問并且緩存重建業(yè)務(wù)較復(fù)雜的key突然失效了,無數(shù)的請求訪問會在瞬間給數(shù)據(jù)庫帶來巨大的沖擊。(無數(shù)請求緩存重建)

解決方案

互斥鎖

未命中需要獲取鎖才能查詢數(shù)據(jù)庫重建緩存數(shù)據(jù),寫入緩存后再釋放鎖。

優(yōu)點:沒有額外內(nèi)存消耗,保證數(shù)據(jù)的一致性,實現(xiàn)簡單。

缺點:線程需要等待,性能受影響,可能有死鎖風(fēng)險。

/**

* 緩存擊穿->互斥鎖

* @param id

* @return

*/

public Result queryWithPassMutex(int id) {

String key = RedisConstants.CACHE_SHOP_KEY+id;

//從redis查詢企業(yè)用戶信息緩存

String userJson = stringRedisTemplate.opsForValue().get(key);

//判斷是否存在

if(StrUtil.isNotBlank(userJson)){

User user = JSONUtil.toBean(userJson,User.class);

return Result.ok(user);

}

//判斷命中的是否是空值

if(userJson!=null){

//返回錯誤信息

return Result.fail(UserErrorConstant.USER_NOEXIST_ERROR);

}

//緩存重建

String lockKey = RedisConstants.LOCK_SHOP_KEY+id;

User user =null;

try {

boolean isLock = tryLock(lockKey);

if(!isLock){

Thread.sleep(50);

//風(fēng)險!改成while(true)輪詢就好

return queryWithPassMutex(id);

}

//不存在,根據(jù)id查詢數(shù)據(jù)庫

user = userMapper.queryUserById(id);

//不存在,返回錯誤

if(user==null){

//緩存穿透-》將空值寫入redis

stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);

return Result.fail(UserErrorConstant.USER_NOEXIST_ERROR);

}

//存儲到redis中,設(shè)置了超時時間

stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(user),RedisConstants.CACHE_SHOP_TTL,TimeUnit.HOURS);

} catch (InterruptedException e){

throw new RuntimeException(e);

} finally {

//釋放互斥鎖

unlock(lockKey);

}

return Result.ok(user);

}

邏輯過期

發(fā)現(xiàn)邏輯時間已過期,未命中獲取互斥鎖,返回過期數(shù)據(jù),開啟新線程去做緩存重建并釋放鎖,如果有新線程來的時候還是會返回新數(shù)據(jù)。

優(yōu)點:線程無需等待,性能較好。

缺點:不保證一致性,有額外內(nèi)存消耗,實現(xiàn)復(fù)雜。

/**

* 緩存穿透

* @param id

* @return

*/

public Result queryWithPassThrough(int id){

String key = RedisConstants.CACHE_SHOP_KEY+id;

//從redis查詢企業(yè)用戶信息緩存

String userJson = stringRedisTemplate.opsForValue().get(key);

//判斷是否存在

if(StrUtil.isNotBlank(userJson)){

User user = JSONUtil.toBean(userJson,User.class);

return Result.ok(user);

}

//判斷命中的是否是空值

if(userJson!=null){

//返回錯誤信息

return Result.fail(UserErrorConstant.USER_NOEXIST_ERROR);

}

//不存在,根據(jù)id查詢數(shù)據(jù)庫

User user = userMapper.queryUserById(id);

//不存在,返回錯誤

if(user==null){

//緩存穿透-》將空值寫入redis

stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);

return Result.fail(UserErrorConstant.USER_NOEXIST_ERROR);

}

//存儲到redis中,設(shè)置了超時時間

stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(user),RedisConstants.CACHE_SHOP_TTL,TimeUnit.HOURS);

return Result.ok(user);

}

柚子快報邀請碼778899分享:數(shù)據(jù)庫 緩存 Redis實戰(zhàn)

http://yzkb.51969.com/

推薦鏈接

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://m.gantiao.com.cn/post/19286821.html

發(fā)布評論

您暫未設(shè)置收款碼

請在主題配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄