來源 http://www.javashuo.com/article/p-ruynfosf-cu.htmlhtml
Redis分佈式鎖實現的三個核心要素nginx
1. 加鎖web
setnx(key,1)當一個線程執行setnx返回1,說明key本來不存在,該線程成功獲得了鎖,當其餘線程執行setnx返回0,說明key已經存在,該線程搶鎖失敗。redis
2.解鎖算法
當獲得鎖的線程執行完任務,須要釋放鎖,以便其餘線程能夠進入。釋放鎖的最簡單方式是執行del指令,del(key)釋放鎖以後,其餘線程就能夠繼續執行setnx命令來得到鎖。數據庫
3.鎖超時後端
若是一個獲得鎖的線程在執行任務的過程當中掛掉,來不及顯式地釋放鎖,這塊資源將會永遠被鎖住,別的線程再也別想進來。設計模式
因此,setnx的key必須設置一個超時時間,以保證即便沒有被顯式釋放,這把鎖也要在必定時間後自動釋放。setnx不支持超時參數,因此須要額外的指令,expire(key, 30)瀏覽器
if(setnx(key,1) == 1){ expire(key,30) try { do something ...... }catch() { } finally { del(key) } }
問題 1 setnx和expire的非原子性
緩存
設想一個極端場景,當某線程執行setnx,成功獲得了鎖:setnx剛執行成功,還將來得及執行expire指令,該節點掛了。這樣一來,這把鎖就沒有設置過時時間,變得「長生不老」,別的線程再也沒法得到鎖了。
解決:
setnx指令自己是不支持傳入超時時間的,Redis 2.6.12以上版本爲set指令增長了可選參數,僞代碼以下:set(key,1,30,NX),這樣就能夠取代setnx指令
問題 2 超時後使用del 致使誤刪其餘線程的鎖:
假如某線程成功獲得了鎖,而且設置的超時時間是30秒。
若是某些緣由致使線程B執行的很慢很慢,過了30秒都沒執行完,這時候鎖過時自動釋放,線程B獲得了鎖。隨後,線程A執行完了任務,線程A接着執行del指令來釋放鎖。但這時候線程B還沒執行完,線程A實際上刪除的是線程B加的鎖。
解決:
能夠在del釋放鎖以前作一個判斷,驗證當前的鎖是否是本身加的鎖。
至於具體的實現,能夠在加鎖的時候把當前的線程ID當作value,並在刪除以前驗證key對應的value是否是本身線程的ID。
1 加鎖: 2 String threadId = Thread.currentThread().getId() 3 set(key,threadId ,30,NX) 4 5 doSomething..... 6 7 解鎖: 8 if(threadId .equals(redisClient.get(key))){ 9 del(key) 10 }
可是,這樣作又隱含了一個新的問題,if判斷和釋放鎖是兩個獨立操做,不是原子性的
能夠經過使用Lua腳本 來實現兩個語句的原子性。
1. 在執行業務時,多個併發的請求,致使同個資源被訪問被屢次執行(加鎖)
2. 加鎖後,業務併發訪問這個線程大部分都返回了失敗,只有少部分獲取到鎖的線程才能處理,這種狀況下是很是不通情理的(使用reddison已經內部實現的自旋鎖來進行鎖的等待,獲取不到鎖就while循環一直嘗試加鎖,知道鎖的獲取成功才返回結果,可是這是悲觀鎖)
3. 加完鎖後,每次獲取完鎖時就對一個特定值+1,執行完後對特定值進行釋放,可是線程拿到鎖後,拋出異常時,沒法執行最後釋放鎖操做(加finally塊執行釋放鎖操做)
4. 加了finally塊後,這個塊中的業務失敗了,或者程序掛了,redis鏈接失敗了,沒法釋放鎖(對鎖加超時時間)
5. 加了超時時間後,在持有鎖這個業務中的執行時間比超時時間長,在業務執行的時候,鎖超時釋放了,這時,其餘的請求的線程就能獲取到這個鎖了,出現了線程的重複進入
(對redis鎖的key值用一個UUID來設計,同個線程內獲取這個鎖都須要生成一個惟一的id,釋放時只能讓同個線程內存放的id匹配釋放)
,第二個線程執行了業務,並且這個業務執行後,比第一個線程快,而且鎖超時解鎖解了第一個鎖
(獲取到這個鎖的時候,另外開闢一個線程,好比超時時間是10秒,這個線程就每5秒就查詢一下這個鎖是否失效,若是沒有失效就增長5秒中,保持這個鎖的有效性)
參考 http://www.javashuo.com/article/p-ngkmxygo-gt.html
http鏈接池+NIO+線程池(多生產者多消費者)(反向代理服務器,一致性哈希算法)+阻塞隊列+數據庫鏈接池+緩存(主從、集羣)+數據庫(集羣、分庫主從)。
細節:
一、設置http鏈接池,能夠下降延遲,提升客戶端響應時間。還能夠鏈接池複用,支持更大的併發量。
二、把一些靜態資源先加載到瀏覽器緩存裏面,減小服務器端的壓力
三、能夠對服務器端的數據進行壓縮
四、反向代理服務器能夠保護服務器的安全,來自互聯網的請求必需通過代理服務器。因此也能夠在代理服務器放一些靜態數據,當用戶第一次訪問靜態內容時,靜態內容就被緩存在反向代理服務器上,其餘用戶請求進來時,就能夠直接返回,減輕web服務器負載壓力。
五、NIO模型(是在Linux仍是Windows系統下,Windows建議用AIO,Linux系統下AIO的底層也是基於epoll多路複用,差異不大,LF的區別)
六、線程池(根據線程池處理不一樣性質的任務,要有不一樣性質的線程池,IO密集型,CPU*2。CPU密集型,CPU+1.多生產者多消費這模型)線程池還須要考慮:a.先設置一個最大線程數量和最小線程數量,進行性能評估,壓測。b.線程池阻塞隊列的大小要有界,不然服務器壓力過大。c.須考慮線程池的失敗策略,失敗後的補償。d.後臺批處理服務須與線上面向用戶的服務進行分離。
七、阻塞隊列,由於NIO第二個階段會引發用戶線程的阻塞,好比可能等待JDBC鏈接數據庫,所以在這裏用一個阻塞隊列,線程把請求放到阻塞隊列裏面,這個線程就能夠迴歸線程池,處理別的事情了。是一個生產者消費者模型
八、建一個數據庫鏈接池,主要是爲了減小資源的消耗、減小延遲。
九、數據存儲部分,1)根據實際狀況設置索引和優化SQL語句。2)冪等、樂觀、悲觀。3)防止SQL注入攻擊。4)一個事務當中操做不要過多,可能會阻塞,進而累積形成數據庫的故障。5)數據量太大,查詢的時間利用limit關鍵字進行分頁處理,防止結果集太大,讓應用OOM。
十、使用緩存,減小數據庫的訪問次數,提升併發量。1)緩存的結構,LRU,鏈表(集合類存放超時對象),大小,時間。2)核心業務和非核心業務進行分離,減小相互影響的可能性,不要使用共享緩存。3)不經常使用的數據不要使用緩存。4)夜間查詢一天之類搜索頻率比較高的詞彙,結合AI進行預測,預測的結果預先放到緩存裏面。5)考慮分佈式緩存數據庫:Redis、memcached,防止本地緩存內存溢出。Redis的主從同步,讀寫分離、負載均衡。主從+一級二級緩存+哨兵。哨兵是Redis 的高可用性解決方案:由一個或多個哨兵實例 組成的哨兵 系統能夠監視任意多個主服務器,以及這些主服務器屬下的全部從服務器,並在被監視的主服務器進入下線狀態時,自動將下線主服務器屬下的某個從服務器升級爲新的主服務器。
十一、主從有瓶頸,會有延遲、主服務器壓力過大。考慮集羣、分庫分表。考慮用一致性哈希算法實現分佈式緩存數據庫。數據遷移量小,引入虛擬節點、防止數據傾斜。
十二、 如有重複數據,布隆過濾器去重。
1三、數據庫存儲文件名之類的,服務器保存實際的數據。
保持數據一致性的方法:1. 消息隊列 2. 用同一個數據庫,A.B,C用同一個數據庫。 3。 用Redis緩存,把一些數據放到緩存。
來源 http://www.javashuo.com/article/p-rxyoqhdb-n.html
1、系統架構擴展
系統的擴展性能夠提供系統的性能。表明系統可以容納更高的負載、更大的數據集,而且系統是可維護的。擴展能夠分爲兩種:
垂直擴展(stade up),提升單一的機器性能配置,如添加內存、更換更強的處理器等等。
2.水平擴展(out),橫向添加新機器。
水平擴展比垂直擴展有更強大的擴展性,但水平擴展也來了更高的維護成本。實踐中須要根據具體狀況來尋求一個平衡點。
2、靜態化技術
採用預處理方式將頁面靜態化,存儲在磁盤,不須要鏈接數據庫讀取數據,能夠提升服務端性能
3、使用緩存服務器減小IO
使用Redis,Memache內存服務器,在內存中存儲數據,減小磁盤IO讀取量
4、引入微服務器框架Dubbo,SpringCloud
將業務模塊切分爲多個微服務,託管在微服務框架中,提升負載均衡與容錯處理
5、使用數據庫集羣技術
數據庫層面採用主從複製模式、集羣模式、採用分區表將數據平均分配到多個磁盤控制器。採用讀寫分離設計模式
6、圖片存儲在分佈文件系統或CDN服務中
靜態資源(圖片、視頻、網頁)採用分佈式存儲,或者使用CDN服務分發,系統須要設計爲先後端分離。
7、業務處理採用NIO技術
採用非阻塞IO技術,借鑑Dubbo使用Netty框架。
8、應用服務調優(Tomcat,Weblogic,Websphere)
通用的配置是設置JVM參數,內存各分區大小,垃圾回收線程多少。再根據不一樣應用服務器的配置參數,優化應用服務器。
9、負載均衡
使用了水平擴展之如何將大量的請求「均衡」到咱們的擴展機器上
兩種負載均衡模式:有狀態(若有攜帶session)和無狀態
兩種負載均衡方式:硬件均衡和軟件均衡
硬件均衡比較簡單,一般接入一個設備便可,以後的均衡和故障檢測等等都由硬件自動完成。成本較高。
軟件均衡則是經過軟件來轉發各類請求,更加容易的定義轉發規則,有較多的開源產品選擇。
第四層和第七層
常常在負載均衡中看到第四層和第七層這兩個名詞。它們其實是指它們各自工做時所處理的網絡協議的層數(使用ISO模型)。
第四層是數據傳輸層,包括TCP和UDP,第七層則是應用層,一般web中爲HTTP應用。如Apache、nginx等支持第七層的均衡,並且可配置性都至關強大,可以適應較複雜的應用。例如能夠簡單的將流量分擔到各個負載機上,也能夠定義一套業務規則,將應用劃分爲不一樣的池,每一個池處理某些固定規則的URL。
對比軟件均衡與硬件均衡,能夠發現它們各自的優缺點:
硬件均衡成本比較高,軟件均衡多數可使用免費的開源軟件來實現。
硬件均衡對於故障檢測比軟件均衡更增強大、快速。在採用硬件均衡時,一旦某臺機器出現故障,立刻就能夠檢測出來並當即屏蔽。
硬件均衡能夠快速的添加機器(接入硬件接口便可),而軟件均衡除了添加機器外還要添加一些配置信息,以將某些流量導入到新的機器。
軟件均衡能夠定義很是複雜的業務規則,而硬件均衡在這方面相對較弱。
多數的硬件均衡方案都有捆綁附加的一些服務如HTTPS加速、DOS防火牆等等。
10、操做系統優化虛擬內存調優、可用文件句柄多少配置,