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

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

網(wǎng)站后臺(tái)如何做做搜索引擎優(yōu)化的企業(yè)

網(wǎng)站后臺(tái)如何做,做搜索引擎優(yōu)化的企業(yè),wordpress 安裝過(guò)程,wordpress 自媒體最核心的,包裝和準(zhǔn)備 個(gè)人項(xiàng)目,怎么包裝?一定要寫出代碼才可以嗎? 你可以在系統(tǒng)A中實(shí)現(xiàn)就可以,了解其中實(shí)現(xiàn)的細(xì)節(jié),怎么跟面試官對(duì)線等等,這些話術(shù)到位了之后,再把它融入到系統(tǒng)B&a…

最核心的,包裝和準(zhǔn)備

個(gè)人項(xiàng)目,怎么包裝?一定要寫出代碼才可以嗎?

你可以在系統(tǒng)A中實(shí)現(xiàn)就可以,了解其中實(shí)現(xiàn)的細(xì)節(jié),怎么跟面試官對(duì)線等等,這些話術(shù)到位了之后,再把它融入到系統(tǒng)B,這樣即可。

舉個(gè)例子

一個(gè)大前提,你要想好怎么跟面試官對(duì)線?

知道怎么對(duì)線后,自然就知道,怎么去提前準(zhǔn)備這塊內(nèi)容,舉例子:

你的簡(jiǎn)歷寫了這句話,那么你要怎么準(zhǔn)備?

  • 對(duì)熱點(diǎn)數(shù)據(jù)做緩存,針對(duì)可能的緩存穿透,同時(shí)使用緩存空值布隆過(guò)濾器解決;針對(duì)熱點(diǎn)數(shù)據(jù)過(guò)期,根據(jù)不同的數(shù)據(jù)一致性要求,采用不同的緩存構(gòu)建方案,防止緩存擊穿;

你的簡(jiǎn)歷寫了異步秒殺業(yè)務(wù),你又該怎么介紹?

1、業(yè)務(wù)大致邏輯的介紹

業(yè)務(wù)是用戶可以搶購(gòu)大額代金券,來(lái)抵扣購(gòu)買課程所需金額,一個(gè)用戶只能搶購(gòu)一張大額優(yōu)惠券

相關(guān)的表結(jié)構(gòu)

平價(jià)券表

自增id、代金券標(biāo)題、副標(biāo)題、使用規(guī)則、支付金額、抵扣金額、類型 0普通 1秒殺、狀態(tài) 1 2 3、創(chuàng)建時(shí)間、更新時(shí)間

秒殺券表

在平價(jià)優(yōu)惠券基礎(chǔ)上,秒殺優(yōu)惠券有其他字段,獨(dú)立成一張表。

關(guān)聯(lián)平價(jià)券的自增id、庫(kù)存、秒殺開(kāi)始時(shí)間、秒殺結(jié)束時(shí)間、創(chuàng)建時(shí)間、更新時(shí)間

訂單表

Id 訂單編號(hào)(全局id)、下單用戶id、購(gòu)買的優(yōu)惠券id、支付方式 1 2 3、訂單狀態(tài) 1 2 3 4 5 6 7、搶購(gòu)時(shí)間、支付時(shí)間、核銷時(shí)間、退款時(shí)間、更新時(shí)間

有啥難點(diǎn)?

一人一單、不超賣、保證并發(fā)量 等等

2、代碼一步步實(shí)現(xiàn)的過(guò)程介紹

方案的比較

選擇哪個(gè)鎖?

整體邏輯的 初步設(shè)計(jì)是怎么樣的?

使用基于數(shù)據(jù)庫(kù)的鎖 以及 JVM的鎖實(shí)現(xiàn)功能

初步設(shè)計(jì)存在什么問(wèn)題呢?

多集群部署時(shí),JVM不能看到同一把鎖

后續(xù)又基于什么、或者通過(guò)什么方式進(jìn)行完善優(yōu)化?

業(yè)務(wù)遷移到 redis 來(lái)做 、由最初的 JVM層面的隊(duì)列,到引入redis的stream,再到引入MQ等等

那么優(yōu)化了多少?

數(shù)據(jù)呈現(xiàn)!qps等等

怎么迭代優(yōu)惠券秒殺功能?

業(yè)務(wù)場(chǎng)景是:用戶可以搶購(gòu)數(shù)量有限的大額優(yōu)惠券,并且每個(gè)用戶最多只能搶一張。

怎么解決超賣問(wèn)題?方案對(duì)比,選擇樂(lè)觀鎖

所以這個(gè)功能首先要完成的是:不要出現(xiàn)庫(kù)存超賣的情況,

有兩個(gè)解決方案:悲觀鎖syn & 樂(lè)觀鎖 cas

悲觀鎖的思想:認(rèn)為我在減庫(kù)存的時(shí)候,一定有其他用戶也在減,為了防止這種現(xiàn)象,減庫(kù)存時(shí),加了一個(gè)同步鎖synchronized,來(lái)解決并發(fā)問(wèn)題

樂(lè)觀鎖的思想:樂(lè)觀鎖是認(rèn)為我在減庫(kù)存的時(shí)候,不一定會(huì)發(fā)生并發(fā)問(wèn)題,就算有,我就放棄此次操作,再重新嘗試減一次。實(shí)現(xiàn)這一機(jī)制:

就是在減庫(kù)存的時(shí)候,判斷 庫(kù)存是否 > 0即可,只要是 > 0,就可以賣

當(dāng)出現(xiàn) <= 0時(shí),就減庫(kù)存失敗

基于樂(lè)觀鎖的性能比悲觀鎖要好,因?yàn)?/p>

悲觀鎖只允許一個(gè)線程在同步代碼塊執(zhí)行,其余線程必須等待鎖釋放,性能差

而基于庫(kù)存是否 > 0的樂(lè)觀鎖,只有在庫(kù)存真的 <= 0,才會(huì)并發(fā)失敗,性能遠(yuǎn)遠(yuǎn)比悲觀鎖好。

經(jīng)過(guò)以上方案的比較,項(xiàng)目采用樂(lè)觀鎖來(lái)解決超買問(wèn)題。

接下來(lái)是要解決每個(gè)用戶只能搶一張優(yōu)惠券的問(wèn)題

怎么保證每個(gè)用戶只能搶一張優(yōu)惠券呢?

項(xiàng)目是這樣解決的,首先確定無(wú)法使用樂(lè)觀鎖來(lái)解決

因?yàn)橛脩魮尩絻?yōu)惠券,在他沒(méi)搶到之前,數(shù)據(jù)庫(kù)并沒(méi)有記錄,無(wú)法根據(jù)字段進(jìn)行樂(lè)觀鎖。

所以采用悲觀鎖的方案,因?yàn)槟壳笆窃诮鉀Q單個(gè)用戶發(fā)起的并發(fā)請(qǐng)求,只需要針對(duì)單個(gè)用戶進(jìn)行加鎖,

確定鎖的粒度為每個(gè)用戶,鎖對(duì)象為用戶id,String 類型,為了防止加鎖的對(duì)象不是同一個(gè),采用的是toString().intern(),不同的請(qǐng)求,才會(huì)從字符串常量池中返回同一個(gè)對(duì)象,才能解決單個(gè)用戶并發(fā)問(wèn)題。

確定加鎖范圍判斷用戶是否已搶購(gòu) -> 樂(lè)觀鎖解決減庫(kù)存問(wèn)題 -> 把搶購(gòu)記錄,寫入數(shù)據(jù)庫(kù)

如果加鎖范圍只到樂(lè)觀鎖解決庫(kù)存問(wèn)題,是無(wú)法避免單個(gè)用戶的并發(fā)請(qǐng)求問(wèn)題的。

這是針對(duì)單個(gè)服務(wù)可用的方法,因?yàn)閟ynchronized鎖,基于JVM實(shí)例

如果部署多臺(tái)服務(wù),有多個(gè)JVM,synchronized無(wú)法做到分布式鎖,

所以在集群部署下,還會(huì)出現(xiàn)一人一單并發(fā)問(wèn)題

思考到集群下的JVM鎖問(wèn)題,采取分布式鎖優(yōu)化:

使用分布式鎖,解決集群下的一人一單問(wèn)題

為了解決上面說(shuō)到的問(wèn)題,決定使用跨JVM的鎖,即分布式鎖,redis就是很好的選擇。

首先自定義了一個(gè)比較簡(jiǎn)單的分布式鎖

存在的問(wèn)題是鎖超時(shí)釋放,但是業(yè)務(wù)還未執(zhí)行完畢

(想要更好的解決,可以使用redis分布式工具:redisson)

支持鎖重入:利用hash結(jié)構(gòu),通過(guò)記錄線程id、鎖的數(shù)量,來(lái)達(dá)到重入

鎖超時(shí)自動(dòng)續(xù)費(fèi):保證是業(yè)務(wù)執(zhí)行完畢,才釋放的鎖,不會(huì)被其他線程趁虛而入

每隔 1/3 的時(shí)間,會(huì)重置超時(shí)時(shí)間

支持鎖等待:即獲取不到鎖時(shí),利用發(fā)布訂閱 & 信號(hào)量的機(jī)制,等鎖釋放了 再去重試,對(duì)CPU友好。

到目前位置,業(yè)務(wù)流程為查詢優(yōu)惠券信息 ->加分布式鎖來(lái)解決同一用戶的并發(fā)請(qǐng)求-> 進(jìn)行一人一單的判斷,需要查詢數(shù)據(jù)庫(kù)->進(jìn)行樂(lè)觀鎖庫(kù)存超賣的判斷,需要更新數(shù)據(jù)庫(kù)->搶購(gòu)成功,創(chuàng)建訂單,寫入數(shù)據(jù)庫(kù)。

可以看到目前的流程存在大量的IO& 鎖,整體性能通過(guò)JMeter測(cè)試,

1000個(gè)用戶,200庫(kù)存的優(yōu)惠券,處理請(qǐng)求的平均耗時(shí)接近500ms

存在許多耗時(shí)的數(shù)據(jù)庫(kù)操作 & 鎖,還可以怎么提高性能呢?

基于redis:秒殺資格判斷異步寫入數(shù)據(jù)庫(kù)思路

通過(guò)定時(shí)任務(wù)把MySQL中參與秒殺的代金券,同步到Redis中做庫(kù)存的預(yù)扣減,基于Redis解決庫(kù)存超賣與一人一單,RocketMQ實(shí)現(xiàn)異步解耦,QPS從400提升至1200;

對(duì)業(yè)務(wù)進(jìn)行拆分,決定將耗時(shí)的數(shù)據(jù)庫(kù)操作,放到redis來(lái)做,具體為:秒殺資格的判斷

新增秒殺優(yōu)惠券的同時(shí),將優(yōu)惠券信息預(yù)熱在redis中

在redis中判斷用戶是否已經(jīng)下過(guò)單,

使用redis數(shù)據(jù)類型:Set,存放已經(jīng)下過(guò)單的用戶信息,

方便以O(shè)(1)復(fù)雜度判斷用戶是否下單sismember、sadd

key為:seckill:order:優(yōu)惠券id

如果還未下過(guò)單,使用redis判斷庫(kù)存是否充足,如果庫(kù)存充足,則需要減1

使用redis數(shù)據(jù)類型:hash,存儲(chǔ)優(yōu)惠券信息

get、incrby減庫(kù)存

key為:seckill:stock:優(yōu)惠券id

上述過(guò)程,是多條命令,無(wú)法保證這些命令執(zhí)行的原子性,會(huì)出現(xiàn)并發(fā)問(wèn)題,所以使用lua腳本

保證執(zhí)行上述命令的原子性

相當(dāng)于把之前的分布式鎖解決一人一單、樂(lè)觀鎖解決庫(kù)存超賣的問(wèn)題,通過(guò)基于內(nèi)存的redis解決了

大大提高性能


RocketMQ實(shí)現(xiàn)異步解耦,QPS從400提升至1200;


若判斷用戶有資格搶購(gòu),在這之前采用的是同步操作,同步等待信息寫入數(shù)據(jù)庫(kù),

即用戶請(qǐng)求需要等待搶購(gòu)信息寫入數(shù)據(jù)庫(kù),才可以返回

優(yōu)化的解決方案是:向消息隊(duì)列RocketMQ中添加消息(分布式id、優(yōu)惠券id、用戶id),立刻返回用戶請(qǐng)求,

開(kāi)啟異步線程,實(shí)現(xiàn)異步寫入數(shù)據(jù)庫(kù)的操作。減少響應(yīng)時(shí)間,提高用戶體驗(yàn)。

一開(kāi)始使用的是JDK自帶的阻塞隊(duì)列,耗時(shí)200ms

阻塞隊(duì)列在獲取消息時(shí),如果沒(méi)有消息,就阻塞住;等到有消息加入了,就被喚醒

使用jdk自帶的阻塞隊(duì)列缺點(diǎn)

  1. 使用的是JDK的阻塞隊(duì)列,用的是JVM的內(nèi)存,如果不加以限制,在高并發(fā)下,可能有無(wú)數(shù)的訂單放到阻塞隊(duì)列,可能會(huì)導(dǎo)致內(nèi)存溢出,也就是內(nèi)存受到限制。

  2. 消息一旦取出,就消失了,不能保證一定被消費(fèi)

  3. 不支持持久化,目前是基于內(nèi)存保存訂單信息,如果服務(wù)宕機(jī),內(nèi)存所有訂單信息都丟失;

選擇Stream消息隊(duì)列替代JDK自帶的阻塞隊(duì)列

耗時(shí)100ms

比較redis 不同方式實(shí)現(xiàn)消息隊(duì)列之間的優(yōu)缺點(diǎn),即為什么選擇Stream而不是List?

最重要的是記住Stream的優(yōu)點(diǎn)(持久化、全局ID、解決消息漏讀、pendin-list保證消息至少消費(fèi)一次、獨(dú)立于JVM的內(nèi)存、支持消費(fèi)者組消費(fèi),減少消息擠壓、可以阻塞讀取)

理解內(nèi)部實(shí)現(xiàn),來(lái)說(shuō)明為什么有這些優(yōu)點(diǎn)。

Stream相關(guān)的八股

具體落實(shí)到項(xiàng)目中,怎么實(shí)現(xiàn)?

創(chuàng)建一個(gè)Stream消息隊(duì)列,不指定上限

lua腳本判斷有資格后,向消息隊(duì)列添加消息

項(xiàng)目啟動(dòng)時(shí),開(kāi)啟異步線程,阻塞讀取Stream消息隊(duì)列中的消息,完成寫入數(shù)據(jù)庫(kù)操作

如果成功消費(fèi),那么發(fā)送ack確認(rèn)給消息隊(duì)列,消息才會(huì)從pending隊(duì)列中移除

如果消費(fèi)出現(xiàn)問(wèn)題,就到該消費(fèi)者的pending隊(duì)列中,再次消費(fèi)

專業(yè)消息隊(duì)列RocketMQ

RocketMQ使用并發(fā)消費(fèi)模式,并設(shè)置合理的線程數(shù)量(IO類型,寫庫(kù)存),快速處理隊(duì)列中堆積的消息,使

用Redis的分布式鎖+自旋鎖,對(duì)商品的庫(kù)存進(jìn)行并發(fā)控制,把并發(fā)壓力轉(zhuǎn)移到Redis中,緩解DB壓力;

因?yàn)椴l(fā)消費(fèi),對(duì)數(shù)據(jù)庫(kù)減庫(kù)存操作,是不安全的

除非直接利用數(shù)據(jù)庫(kù)樂(lè)觀鎖減

而不是先去讀再減 ,直接減

但是對(duì)DB壓力大

使用redis樂(lè)觀鎖 + sleep + 自旋來(lái)解決

3、未來(lái)展望 or 再次迭代 or 這個(gè)功能有什么可以完善的地方?

如果沒(méi)下單,庫(kù)存怎么還回去?

使用延時(shí)隊(duì)列? 那么又引出 - 延時(shí)隊(duì)列怎么實(shí)現(xiàn)的?

其實(shí)redis 的 stream同樣的,又引出八股文,這些都是需要準(zhǔn)備的 Stream相關(guān)的八股

.....

自定義的分布式鎖,相比官方提供的,存在缺陷,如:

最嚴(yán)重的 業(yè)務(wù)未結(jié)束,鎖先超時(shí)釋放了,其他線程趁虛而入、

不支持 鎖重入:用hash即可、

不支持 阻塞等待:用信號(hào)量、發(fā)布/訂閱機(jī)制 即可、

在多redis實(shí)例下,即主從模式下因?yàn)槭钱惒綇?fù)制的,導(dǎo)致分布式鎖不可靠性:官方提供的 紅鎖 解決

redisson 針對(duì)前面三個(gè)缺陷、RedLock 紅鎖

4、實(shí)現(xiàn)過(guò)程中遇到了什么難點(diǎn)?什么bug?

@Transational失效,因?yàn)椴皇谴韺?duì)象調(diào)用。深入了理解Spring事務(wù)原理 -- Aop。

怎么解決?

  • 比較笨方法:新開(kāi)一個(gè)類

  • 或者 自己注入自己,進(jìn)行調(diào)用,也是代理對(duì)象的調(diào)用

  • 獲取代理對(duì)象來(lái)解決。

JVM的syn悲觀鎖解決一人一單問(wèn)題的時(shí)候:

用的是用戶的id,忘記intern放到字符串常量池,

導(dǎo)致獲取String對(duì)象的時(shí)候,每次都是新的對(duì)象,即 加 對(duì)象鎖出現(xiàn)問(wèn)題

還有syn鎖范圍設(shè)置的不夠大,釋放鎖之后,事務(wù)還未寫入,導(dǎo)致數(shù)據(jù)庫(kù)記錄還未變更,存在并發(fā)問(wèn)題

.....

5、如果你的簡(jiǎn)歷 關(guān)鍵字出現(xiàn),分布式id、分布式鎖、qps等等

心里就要思考到,哪些是會(huì)被提問(wèn)的?

怎么進(jìn)行壓力測(cè)試的?

QPS、并發(fā)量、平均花費(fèi)時(shí)間 等的關(guān)系:QPS和并發(fā)數(shù)和平均耗時(shí)的關(guān)系以及壓測(cè)思路_qps和并發(fā)數(shù)的關(guān)系-CSDN博客

分布式id相關(guān)的準(zhǔn)備

為什么不采用數(shù)據(jù)庫(kù)自增id?

單一表的存儲(chǔ)容量有上限

當(dāng)分表存儲(chǔ)時(shí),會(huì)存在重復(fù)的id

規(guī)律性明顯,容易看出訂單銷量等狀態(tài)

分布式ID是什么?

是應(yīng)用在分布式系統(tǒng)中,保證全局唯一的自增id。

它可以讓一個(gè)業(yè)務(wù),不管有多少個(gè)服務(wù)、多少?gòu)埍?/strong>,都可以擁有唯一的自增id。

全局唯一的分布式ID怎么實(shí)現(xiàn)?

使用redisString數(shù)據(jù)類型的incr自增命令,來(lái)幫助生成全局唯一id,有以下好處:

因?yàn)閞edis執(zhí)行命令是單線程的,所以在執(zhí)行自增命令生成自增id時(shí),

不存在并發(fā)問(wèn)題,自然不會(huì)導(dǎo)致id重復(fù)的問(wèn)題;

并且是自增的,符合分布式id要求;

并且redis基于內(nèi)存操作,性能極高

為了保證生成的id安全性,具體如下操作:

采用long類型存儲(chǔ)id,long類型64位

· 第一個(gè)符號(hào)位,永遠(yuǎn)為0

· 接下來(lái)的31bit,采用精確到的時(shí)間戳進(jìn)行存儲(chǔ)

o 時(shí)間戳如何計(jì)算得來(lái):定義一個(gè)初始時(shí)間,用當(dāng)前下單時(shí)間減去初始時(shí)間,得到31bit

· 后面的32bit,是為了解決在一秒內(nèi)重復(fù)的下單,足夠容納一秒內(nèi)的訂單量

如何運(yùn)算?

先得到當(dāng)前時(shí)間 - 初始時(shí)間的時(shí)間戳,然后左移32位,給一天的訂單量讓出32位bit

使用自增命令,得到自增值,要保證不會(huì)超過(guò)32bit,然后直接進(jìn)行或運(yùn)算

return timestamp << COUNT_BITS | count;

時(shí)間戳的代碼

/** * 初始時(shí)間的時(shí)間戳,本質(zhì)是從1970-01-01 00:00:00 到2022-01-01 00:00:00 經(jīng)過(guò)多少秒 */ private static final long BEGIN_TIMESTAMP = 1640995200L;

//測(cè)試時(shí)間戳 public static void main(String[] args) { LocalDateTime time = LocalDateTime.of(2022, 1, 1, 0, 0, 0); System.out.println( time.toEpochSecond(ZoneOffset.UTC)); }

自增命令的key怎么設(shè)置比較好?

在自增中,采用的是32bit來(lái)存儲(chǔ)自增值,也就是說(shuō)自增值超過(guò)32bit存儲(chǔ)容量,就會(huì)不符合我們的要求。

所以在設(shè)置key時(shí),采用一天一個(gè)key,一天訂單量很難超過(guò)32bit,也就是自增值不會(huì)超過(guò)

o 如:("icr:" + keyPrefix + ":"+"2022:03:20"),keyPrefix 為業(yè)務(wù)名稱

o 還帶來(lái)統(tǒng)計(jì)方便的好處

§ 比如某天的訂單數(shù),直接看對(duì)應(yīng)key的自增數(shù)字就可以。這樣做統(tǒng)計(jì)簡(jiǎn)單很多。

自增id生成器代碼

@Component
public class RedisIdWorker {/**\* 初始時(shí)間的時(shí)間戳,本質(zhì)是從1970-01-01 00:00:00 到2022-01-01 00:00:00 經(jīng)過(guò)多少秒*/private static final long BEGIN_TIMESTAMP = 1640995200L;//測(cè)試時(shí)間戳public static void main(String[] args) {LocalDateTime time = LocalDateTime.of(2022, 1, 1, 0, 0, 0);System.out.println( time.toEpochSecond(ZoneOffset.UTC));}/**\* 序列號(hào)的位數(shù)*/private static final int COUNT_BITS = 32;@Resourceprivate StringRedisTemplate stringRedisTemplate;/**\* @param keyPrefix key前綴,不同業(yè)務(wù)有不同的key\* @return long型,作為id,占用更少空間,有利于索引建立*/public long nextId(String keyPrefix) {
//   符號(hào)位不用管,只要保證正數(shù)就可以,怎么保證? 時(shí)間戳中,當(dāng)前時(shí)間 - 初始時(shí)間,當(dāng)前時(shí)間要 > 初始時(shí)間?    // 1.生成當(dāng)前時(shí)間的 時(shí)間戳
?    LocalDateTime now = LocalDateTime.now();
?    long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
//     當(dāng)前時(shí)間 - 初始時(shí)間
?    long timestamp = nowSecond - BEGIN_TIMESTAMP;?    // 2.生成序列號(hào)
?    // 2.1.獲取當(dāng)前日期,精確到天
?    String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
?    
?    long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);?    // 3.拼接并返回,如果直接拼接得到的是字符串,返回要long。所以這里采用位運(yùn)算
//     先把時(shí)間戳挪到高位,在這里 左移32位。 再跟序列號(hào)進(jìn)行 或運(yùn)算
?    return timestamp << COUNT_BITS | count;}
}
你還了解哪些分布式ID生成算法?

除了基于redis生成的分布式id,還了解雪花算法、uuid、數(shù)據(jù)庫(kù)自增id

雪花算法 同樣采用64bit存儲(chǔ)

o 第一位表示符號(hào)位,為0

o 接下來(lái)的41bit,用于表示精確到毫秒的時(shí)間戳

o 接下來(lái)的10bit,(這一部分可以靈活調(diào)整)

§ 前5位表示機(jī)器id,后5位表示機(jī)房id

o 剩下的12bit,用來(lái)表示一毫秒內(nèi),能夠生成的id數(shù)量

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

生成速度快,有序遞增、易于再此基礎(chǔ)上改造

缺點(diǎn):

依賴于時(shí)間,當(dāng)機(jī)器的時(shí)間對(duì)應(yīng)不上時(shí),可能導(dǎo)致重復(fù)id

uuid 基于時(shí)間、機(jī)器id的生成方案

缺點(diǎn)是:

占用內(nèi)存大,128bit

時(shí)間問(wèn)題,導(dǎo)致id重復(fù)

可以保證唯一,但是不是自增的

若redis服務(wù)宕機(jī),分布式id如何生成?

采用redis主從復(fù)制 + 哨兵機(jī)制,來(lái)達(dá)到服務(wù)的高可用

當(dāng)主節(jié)點(diǎn)宕機(jī)時(shí),自動(dòng)故障轉(zhuǎn)移

主從復(fù)制保證數(shù)據(jù)同步

6、分布式鎖相關(guān)的準(zhǔn)備

分布式鎖是什么?

滿足分布式或集群模式下,多線程可見(jiàn)互斥的鎖。

怎么基于redis實(shí)現(xiàn)?

使用redis的 setnx命令,來(lái)實(shí)現(xiàn)分布式鎖,非阻塞,獲取失敗,直接返回

加鎖操作:setnx

因?yàn)閞edis執(zhí)行命令是單線程,不會(huì)并發(fā)安全問(wèn)題

并且為了防止死鎖,加了key的過(guò)期時(shí)間

并且將value設(shè)置唯一標(biāo)識(shí),是為了防止鎖誤刪的現(xiàn)象

解鎖操作:基于lua腳本,因?yàn)椴恢挂粭l命令

首先判斷該鎖是不是自己加的,即檢查唯一標(biāo)識(shí)get

如果是,才可以進(jìn)行解鎖del

鎖誤刪現(xiàn)象是什么?

比如目前線程A,持有鎖,當(dāng)時(shí)因?yàn)樽枞?#xff0c;導(dǎo)致業(yè)務(wù)沒(méi)執(zhí)行完,鎖超時(shí)釋放了

此時(shí)線程B重新持有鎖,進(jìn)行業(yè)務(wù)處理,

在線程B還沒(méi)處理完業(yè)務(wù)時(shí),線程A處理好了,并且二話不說(shuō),直接把鎖刪除了

這就導(dǎo)致線程B的鎖,被線程A刪掉的情況。導(dǎo)致鎖誤刪

這時(shí),其他線程又可以趁虛而入了。

唯一標(biāo)識(shí)怎么設(shè)置?

因?yàn)槟壳坝懻摰氖琼?xiàng)目在集群部署的環(huán)境下,線程id可能重復(fù)

所以基于每個(gè)線程的id + UUID來(lái)進(jìn)行唯一標(biāo)識(shí)的設(shè)置。

為什么解鎖要使用lua腳本

因?yàn)榻怄i是兩個(gè)操作get、del,必須保證解鎖的原子性,否則可能出現(xiàn)以下現(xiàn)象:鎖誤刪

判斷該鎖是我之前加的

進(jìn)行解鎖時(shí),阻塞了

知道鎖超時(shí)釋放,接著其他線程進(jìn)行加鎖

自己從阻塞狀態(tài)恢復(fù),執(zhí)行業(yè)務(wù),dek把別人的鎖又給刪除了

自定義的分布式鎖,存在什么問(wèn)題?

鎖誤刪問(wèn)題解決了,但是還存在一個(gè)比較嚴(yán)重的問(wèn)題,就是鎖超時(shí)時(shí)間的設(shè)置

如果設(shè)置的太短,可能業(yè)務(wù)還沒(méi)執(zhí)行完 或者 業(yè)務(wù)阻塞,導(dǎo)致鎖超時(shí)釋放

其他線程趁虛而入,又導(dǎo)致了一人不止下一單問(wèn)題的出現(xiàn)。

不支持鎖重入、鎖超時(shí)自動(dòng)續(xù)費(fèi)、鎖等待、

主從模式下因?yàn)槭钱惒綇?fù)制的,導(dǎo)致分布式鎖不可靠性

怎么解決自定義分布式鎖問(wèn)題?

使用redis分布式工具:redisson

· 支持鎖重入:利用hash結(jié)構(gòu),通過(guò)記錄線程id、鎖的數(shù)量,來(lái)達(dá)到重入

· 鎖超時(shí)自動(dòng)續(xù)費(fèi):保證是業(yè)務(wù)執(zhí)行完畢,才釋放的鎖,不會(huì)被其他線程趁虛而入

o 每隔 1/3 的時(shí)間,會(huì)重置超時(shí)時(shí)間

· 支持鎖等待:即獲取不到鎖時(shí),利用發(fā)布訂閱 & 信號(hào)量的機(jī)制,等鎖釋放了 再去重試,對(duì)CPU友好。

Redis 如何解決集群情況下分布式鎖的可靠性?

redis官方是實(shí)現(xiàn)了紅鎖RedLock,專門來(lái)解決集群模式下分布式鎖不可靠的問(wèn)題,

redis推薦使用5個(gè)獨(dú)立的redis主服務(wù)器

加鎖的過(guò)程如下:

記錄開(kāi)始訪問(wèn)的時(shí)間t1,線程依次訪問(wèn)5個(gè)主服務(wù)器,進(jìn)行set nx px的操作,

會(huì)帶上唯一標(biāo)識(shí)

加上超時(shí)時(shí)間,是為了鎖一定會(huì)被釋放

并且還設(shè)定了獲取鎖的時(shí)間,一般設(shè)置為幾十毫秒,

如果在時(shí)間內(nèi)獲取不到,那么就返回,不會(huì)再某個(gè)redis服務(wù)耗費(fèi)太多的獲取鎖時(shí)間

最后統(tǒng)計(jì)線程成功獲取了幾把鎖,要獲取到一半以上,并且將獲取鎖的總時(shí)間 與 設(shè)置的鎖過(guò)期時(shí)間對(duì)比

如果 獲取鎖的總時(shí)間>設(shè)置的鎖過(guò)期時(shí)間,那么加鎖失敗

如果沒(méi)有獲取到一半以上的鎖,在這里是3把鎖,也是加鎖失敗

加鎖成功要同時(shí)滿足兩個(gè)條件

· 獲取到超過(guò)半數(shù)以上的鎖

· 加鎖的總耗時(shí),不大于 鎖的過(guò)期時(shí)間

并且在執(zhí)行業(yè)務(wù)時(shí),真正能夠利用的鎖時(shí)間為:設(shè)置的鎖超時(shí)時(shí)間 - 獲取鎖的總耗時(shí)

如果覺(jué)得鎖的時(shí)間已經(jīng)來(lái)不及完成業(yè)務(wù)執(zhí)行,那么可以直接釋放全部鎖,讓下一個(gè)線程來(lái)操作

避免業(yè)務(wù)還沒(méi)執(zhí)行完,就出現(xiàn)釋放鎖的現(xiàn)象

解鎖操作:

加鎖失敗后,會(huì)向所有redis主節(jié)點(diǎn)發(fā)起解鎖操作,執(zhí)行l(wèi)ua腳本保證解鎖的原子性

完整代碼,要稍微注意一下lua腳本怎么寫

//   在項(xiàng)目一啟動(dòng)類加載時(shí)就加載static代碼塊,只加載一次,性能最好。
// DefaultRedisScript是實(shí)現(xiàn)類,泛型為腳本的返回值類型
private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
static {
//     因?yàn)橐獙懖恢挂恍?#xff0c;所以放到代碼塊UNLOCK_SCRIPT = new DefaultRedisScript<>();
//     去類路徑下找UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
//     設(shè)置返回值類型UNLOCK_SCRIPT.setResultType(Long.class);
}@Override
public void unlock() {
//     釋放鎖
//     stringRedisTemplate.delete(KEY_PREFIX + name);/*// 獲取線程標(biāo)示String threadId = ID_PREFIX + Thread.currentThread().getId();// 獲取鎖中的標(biāo)示String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);// 判斷標(biāo)示是否一致if(threadId.equals(id)) {// 釋放鎖stringRedisTemplate.delete(KEY_PREFIX + name);}*/// 調(diào)用lua腳本stringRedisTemplate.execute(UNLOCK_SCRIPT,// 生成單元素的集合:singletonList方法Collections.singletonList(KEY_PREFIX + name),ID_PREFIX + Thread.currentThread().getId());
}

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

相關(guān)文章:

  • 門戶網(wǎng)站開(kāi)發(fā)報(bào)價(jià)單企業(yè)的網(wǎng)絡(luò)推廣
  • 文山州住房建設(shè)網(wǎng)站企業(yè)網(wǎng)絡(luò)推廣計(jì)劃
  • 微信小程序模版廣州seo網(wǎng)站推廣公司
  • 南通高端網(wǎng)站建設(shè)公司培訓(xùn)網(wǎng)頁(yè)
  • 承德做網(wǎng)站優(yōu)化搜狗網(wǎng)址大全
  • 國(guó)內(nèi)產(chǎn)品推廣網(wǎng)站廣州市疫情最新情況
  • 編程和做網(wǎng)站有關(guān)系嗎招商外包
  • 微信api文檔徐州seo代理計(jì)費(fèi)
  • 泉州市住房與城鄉(xiāng)建設(shè)局網(wǎng)站seo外鏈?zhǔn)鞘裁匆馑?/a>
  • 手機(jī)網(wǎng)站 pc網(wǎng)站模板百度近日收錄查詢
  • 深圳疫情防控最新通知seo網(wǎng)站關(guān)鍵詞優(yōu)化
  • 怎么判斷網(wǎng)站建設(shè)年齡新媒體運(yùn)營(yíng)主要做什么
  • 網(wǎng)站寶 西部數(shù)碼網(wǎng)站管理助手廣告最多的網(wǎng)站
  • 淘寶客優(yōu)惠券網(wǎng)站建設(shè)加盟官網(wǎng)怎么做百度推廣運(yùn)營(yíng)
  • 網(wǎng)站模板可視化編輯百度霸屏培訓(xùn)
  • php企業(yè)網(wǎng)站源代碼如何自己搭建網(wǎng)站
  • 懷化三中網(wǎng)站營(yíng)銷活動(dòng)
  • 類似于wordpress搜索引擎seo推廣
  • 3合1網(wǎng)站建設(shè)公司網(wǎng)絡(luò)營(yíng)銷策劃的具體流程是
  • 網(wǎng)站建設(shè)qianhaiyouseo是什么職位的簡(jiǎn)稱
  • web網(wǎng)站開(kāi)發(fā)歷史河南網(wǎng)站關(guān)鍵詞優(yōu)化代理
  • 工程施工合同協(xié)議書范本什么是seo營(yíng)銷
  • 網(wǎng)站建設(shè)一意見(jiàn)搜索引擎入口google
  • 阿里云oss做網(wǎng)站白度
  • 百度做公司網(wǎng)站有用嗎淘寶seo搜索引擎原理
  • 有規(guī)范seo 關(guān)鍵詞優(yōu)化
  • 重慶渝中區(qū)企業(yè)網(wǎng)站建設(shè)哪家好百度高級(jí)搜索首頁(yè)
  • 怎么做淘客手機(jī)網(wǎng)站網(wǎng)站關(guān)鍵詞優(yōu)化方法
  • 可以做任務(wù)的網(wǎng)站有哪些營(yíng)銷網(wǎng)站都有哪些
  • 湖南建筑公司網(wǎng)站百度廣告客服電話