## 聊一下MySQL 關於mysql關係型資料庫的一些分析: 1、從性能上:如果我們碰到需要執行耗時特別久,並且執行結果不是很頻繁變動的SQL語句,我們就沒有必要每次都去查詢資料庫,因為每次操作資料庫都很耗時。 2、從併發上:在大併發的情況下(比如618秒殺活動,你敢讓千萬級的請求直接打到資料庫上 ...
聊一下MySQL
關於mysql關係型資料庫的一些分析:
1、從性能上:如果我們碰到需要執行耗時特別久,並且執行結果不是很頻繁變動的SQL語句,我們就沒有必要每次都去查詢資料庫,因為每次操作資料庫都很耗時。
2、從併發上:在大併發的情況下(比如618秒殺活動,你敢讓千萬級的請求直接打到資料庫上嗎?)所有的請求直接訪問資料庫,資料庫就由可能造成宕機。
Redis
針對第一種情況,可以將不那麼頻繁變換的結果放入基於緩存的資料庫中,每次查詢就去緩存中讀數據,這樣就提升了查詢效率,也減輕了資料庫的壓力。
針對第二種情況,可以讓這些高併發的請求從資料庫中分離出來,不去直接訪問資料庫,另外找一個適合高併發的來處理請求。
那就是Redis!!!
是什麼?
- 基於記憶體的K/V存儲中間件(關於K/V你還能想到誰?)
- NoSQL(非關係型)資料庫
幹啥用?
- 緩存
(將熱點數據放到記憶體中,設置記憶體的最大使用量以及淘汰策略來保證緩存的命中率)
- 設置定時過期數據
- 分散式鎖
(在分散式場景下,無法使用單機環境下的鎖來對多個節點上的進程進行同步。可以使用 Redis 自帶的 SETNX 命令實現分散式鎖)
- 抽獎功能
- 實現排行榜
- .......
面試題:1、redis常見的數據結構有哪些?
2、Redis持久化瞭解麽?
秒殺場景
618預熱早已開啟,其中必不可少的秒殺是一個非常典型的活動場景。比如,在雙 11、618 等電商促銷活動中,都會有秒殺場景。秒殺場景的業務特點是限時限量,業務系統要處理瞬時的大量高併發請求。在秒殺活動中,大量用戶在同一時間視窗內搶購限量商品,這往往會給系統帶來極高的併發壓力。使用傳統的關係型資料庫來處理併發請求往往會導致性能瓶頸和系統崩潰。而Redis作為一種高性能的記憶體資料庫,能夠快速處理大量的讀寫請求,非常適合用於秒殺場景。
秒殺的特征一:瞬時併發訪問量非常高。一般資料庫只能支撐千級別的併發請求,而redis的併發處理能力能達到萬級別,甚至更高。在秒殺時,我們需要使用redis攔截大部分請求,避免大量請求直接發送給資料庫,造成資料庫宕機。
秒殺的特征二:讀多寫少,都是簡單的查詢操作。對於簡單的查詢操作並且結果相對固定我們可以先將其查詢出來放入redis中,減輕資料庫的壓力。秒殺活動中只有少部分用戶能成功下單,所以,商品庫存查詢操作(讀操作)要遠多於庫存扣減和下單操作(寫操作)。
秒殺的特征三:實時性和響應時間要求。 秒殺活動通常是實時進行的,用戶希望儘快得知搶購結果。系統需要具備高性能和低延遲,及時響應用戶的請求。
三個階段
秒殺前
秒殺前,用戶會不斷的刷新商品詳情頁,那麼就會導致對詳情頁的請求量急劇增加。這個階段的應對方案,一般是儘量把商品詳情頁的頁面元素靜態化,然後使用 CDN 或是瀏覽器把這些靜態化的元素緩存起來。這樣一來,秒殺前的大量請求可以直接由 CDN 或是瀏覽器緩存服務,不會到達伺服器端了。
秒殺時
用戶點擊秒殺按鈕---->大量併發請求查詢庫存--->有庫存(下單){沒有則返回,可能會點擊其他秒殺按鈕,繼續查詢庫存}--->庫存扣減---->生成實際訂單---->後續處理(物流、訂單支付等)
這個階段的操作是:庫存查驗、庫存扣減、訂單處理。而最大的併發力都在庫存查驗操作上。這個階段為了支撐大量高併發的庫存查驗請求,我們需要在這個環節使用 Redis 保存庫存量,這樣一來,請求可以直接從 Redis 中讀取庫存併進行查驗。
為什麼訂單處理操作可以在資料庫中執行?而庫存扣減操作不能在資料庫中執行?
1、在資料庫中進行庫存扣減操作會帶來額外的開銷。我們已經事先在redis中保存好了庫存量,如果交給資料庫來處理扣減,那麼就需要資料庫將最新的值再同步到redis中,這樣反而繁瑣也帶來了額外的開銷。
2、可能會出現超售的情況。資料庫的處理速度很慢,而秒殺又是一個快速高併發的場景,可能資料庫更新完一個庫值,秒殺就結束了,可能會導致用戶查詢到舊的庫存值,再進行下單,出現超售的情況。
所以需要在redis中把庫存扣減和庫存查驗兩個操作一氣呵成,實現原子操作。
秒殺後
用戶量減少,伺服器完全可以獨立進行訂單處理等操作。
實現
我們可以使用Redis的原子操作和分散式鎖來支撐秒殺時的場景。(本文介紹分散式鎖)
簡單聊聊分散式
簡單理解:類比衛生間的隔間:
- 隔離性: 衛生間的隔間將每個使用者隔離開來,使每個人都能獨立使用空間,不會相互干擾。同樣,分散式系統中的各個節點也是相互隔離的,它們可以獨立運行,不會相互影響。
- 資源共用: 衛生間的隔間提供共用的基礎設施,如水源和排水管道等。類似地,分散式系統中的節點可以共用一些資源,如共用存儲、共用資料庫等。
- 容錯性: 如果一個衛生間隔間出現故障或需要維護,其他隔間仍然可以繼續使用。同樣,分散式系統中的節點之間可以具備容錯能力,如果某個節點發生故障,其他節點可以接替其工作。
- 擴展性: 如果需要更多的衛生間隔間,可以增加更多的隔間來滿足需求。同樣,分散式系統可以通過增加更多的節點來實現擴展性,以處理更大的負載和流量。
Redis分散式鎖(秒殺場景)
先讓客戶端向 Redis 申請分散式鎖,只有拿到鎖的客戶端才能執行庫存查驗和庫存扣減。這樣一來,大量的秒殺請求就會在爭奪分散式鎖時被過濾掉。而且,庫存查驗和扣減也不用使用原子操作了,因為多個併發客戶端只有一個客戶端能夠拿到鎖,已經保證了客戶端併發訪問的互斥性。大部分秒殺請求本身就會因為搶不到鎖而被攔截。
為啥要實現分散式鎖:
- 在有限的資源情況下,控制同一時間(段)只有某些線程(用戶/伺服器)能訪問到資源。(比如秒殺時)
- 單個鎖只對單個jvm有限(好比用廁所只能用專屬於自己的哪個)
關鍵:怎麼保證同一時間內只有一個伺服器搶到鎖。
核心思想是:先來的人先把數據改成自己的標識(ip)後來的人發現標識已存在,就搶鎖失敗,繼續等待,等先來的人執行方法結束,把標識清空,其他的人繼續搶鎖。(類比搶廁所/上廁所 )
註意事項:
- 要註意解鎖(你不可能一直占著廁所吧!!!)。
- 一定要註意加上過期時間(否則如果因為伺服器掛掉,鎖就會一直存在了)。
使用分散式鎖可能存在的問題
1、鎖提前過期:分散式鎖設置的時間提前過期,會導致其他方法加入一塊同時執行。(好比你拉*沒拉完,外面大哥把廁所門給撬開了)導致多個方法同時執行了。
2、還有可能導致連鎖效應(解鎖時不小心釋放掉別人的鎖)同樣導致多個方法同時執行。
3、解鎖的時候,判斷好了是自己的鎖,但是當要執行解鎖的時候,就在這個時刻,自己的鎖過期了(不需要刪了)有其他的方法見縫插針進來了,導致A刪了B的鎖。(就像是你用完廁所,你自己的門鎖時間過期開了,你手裡還有把解鎖的“鑰匙”,結果你打開了旁邊大哥的門)這裡其實是破壞了原子性。(需要保證A的操作是原子性的,不能有其他操作打斷。(也就是說A的上鎖和解鎖中間不能有其他的鎖操作)跟操作系統的PV操作好像)。
4、鎖提前過期,任務還沒執行完,可以進行續期。(就像你上廁所,外面大哥等了你10min,結果你還沒完事,然後你跟大哥說再給我10min)進行續期。
關於redis在秒殺場景中的應用還有很多,這隻是一丟丟~
場景題
1、如何防止超賣問題?
2、如何保證訂單的順序和可靠性?
3、如何解決重覆下單的問題?
4、做了什麼限流削峰的措施?
5、如果緩存中的數據突然失效,導致請求全部打到了資料庫,怎麼辦?
6、秒殺系統面臨的問題有哪些?
........
博客參考