擴容
垂直擴容(縱向擴展)
提高單個服務(服務器藕溅、數(shù)據(jù)庫)自身能力
但會增大單個服務中其他軟件設施的依賴與管理雌芽、服務內部復雜度
水平擴容(橫向擴展)
增加更多服務成員
但會增加網(wǎng)絡挥转、數(shù)據(jù)庫IO開銷蒙揣、管理多個服務器的難度
對數(shù)據(jù)庫的擴容方案
確定業(yè)務類型
- 讀操作多:采用垂直擴容方案(redis锻全、CDN)庆亡。采用水平擴容沒有太大的意義匾乓,因為性能的瓶頸不在寫操作,所以不需要實時去完成又谋,用更多的服務器來分擔壓力性價比太低拼缝。所以針對單個系統(tǒng)去強化它的讀性能就可以了
- 寫操作多:采用水平擴容方案(HBase娱局、增加服務器、數(shù)據(jù)庫)咧七。也可以考慮垂直擴容提升單個數(shù)據(jù)庫的性能衰齐,但會發(fā)現(xiàn)資金與硬盤的IO能力是有限的,所以需要增加更多數(shù)據(jù)庫來分擔寫的壓力猪叙。
緩存
應用需要支撐大量并發(fā)量娇斩,但數(shù)據(jù)庫的性能有限,所以使用緩存來減少數(shù)據(jù)庫壓力與提高訪問性能
特征
- 命中率 = 命中數(shù) / (命中數(shù) + 沒有命中數(shù))
- 最大空間:緩存最大空間
- 清空策略:FIFO/LFU/LRU/過期時間/隨機
FIFO:最先進入緩存的數(shù)據(jù)穴翩,在緩存空間不足時被清除犬第,為了保證最新數(shù)據(jù)可用
LFU(Least Frequently Used):最近最不常用,基于訪問次數(shù)芒帕,去除命中次數(shù)最少的元素 LRU(Least Recently Used):最近最少使用歉嗓,基于訪問時間,在被訪問過的元素中去除最久未使用的元素
本地緩存Guava Cache
其實編程時候寫的成員變量背蟆、靜態(tài)變量鉴分、常亮也被看做緩存
分布式緩存Redis
高并發(fā)緩存問題
緩存一致性
緩存并發(fā)
當大量請求訪問同一個沒有被緩存的數(shù)據(jù)的時候带膀,會發(fā)送大量請求給數(shù)據(jù)庫志珍,導致數(shù)據(jù)庫壓力過大,還會導致一致性問題垛叨,所以解決方式就是在緩存獲取的時候加上針對單個數(shù)據(jù)的鎖伦糯,直到緩存被重建成功得到最新數(shù)據(jù)
緩存擊穿/穿透
查詢一個數(shù)據(jù)庫中不存在的數(shù)據(jù),比如商品詳情嗽元,查詢一個不存在的ID敛纲,每次都會訪問DB,如果有人惡意破壞剂癌,很可能直接對DB造成過大地壓力淤翔。
解決方案:當通過某一個key去查詢數(shù)據(jù)的時候,如果對應在數(shù)據(jù)庫中的數(shù)據(jù)都不存在佩谷,我們將此key對應的value設置為一個默認的值旁壮。
緩存失效
在高并發(fā)的環(huán)境下,如果此時key對應的緩存失效谐檀,此時有多個進程就會去同時去查詢DB抡谐,然后再去同時設置緩存。這個時候如果這個key是系統(tǒng)中的熱點key或者同時失效的數(shù)量比較多時稚补,DB訪問量會瞬間增大童叠,造成過大的壓力框喳。
解決方案:
- 將系統(tǒng)中key的緩存失效時間均勻地錯開
- 當我們通過key去查詢數(shù)據(jù)時课幕,首先查詢緩存厦坛,如果此時緩存中查詢不到,就通過分布式鎖進行加鎖
熱點key
緩存中的某些Key(可能對應用與某個促銷商品)對應的value存儲在集群中一臺機器乍惊,使得所有流量涌向同一機器杜秸,成為系統(tǒng)的瓶頸,該問題的挑戰(zhàn)在于它無法通過增加機器容量來解決润绎。
解決方案:
- 客戶端熱點key緩存:將熱點key對應value并緩存在客戶端本地撬碟,并且設置一個失效時間。
- 將熱點key分散為多個子key莉撇,然后存儲到緩存集群的不同機器上呢蛤,這些子key對應的value都和熱點key是一樣的。
消息隊列
消息隊列是為了解決生產(chǎn)和消費的速度不一致導致的問題棍郎,有以下好處:
- 減少請求響應時間其障。比如注冊功能需要調用第三方接口來發(fā)短信,如果等待第三方響應可能會需要很多時間
- 服務之間解耦涂佃。主服務只關心核心的流程励翼,其他不重要的、耗費時間流程是否如何處理完成不需要知道辜荠,只通知即可
- 流量削鋒汽抚。對于不需要實時處理的請求來說,當并發(fā)量特別大的時候伯病,可以先在消息隊列中作緩存造烁,然后陸續(xù)發(fā)送給對應的服務去處理
如果想要實現(xiàn)一個消息隊列,可以參考這里
最簡單的消息隊列就是一個消息轉發(fā)器狱从,基本功能只有三個:消息存儲膨蛮、消息發(fā)送、消息刪除季研,可使用LinkedBlockingQueue敞葛、ConcurrentLinkedQueue實現(xiàn)
應用拆分
限流
限流是為了解決高并發(fā)情況下,大量請求導致數(shù)據(jù)庫或服務器壓力過大出現(xiàn)延遲或出錯的方式与涡,在圖中惹谐,如果一次性將100多萬數(shù)據(jù)發(fā)送給master庫,那么服務器與數(shù)據(jù)庫的IO性能將會被大量占用驼卖,導致其他服務對數(shù)據(jù)庫的不可用氨肌,master庫還需要很久的時間將數(shù)據(jù)同步給slave庫
控制某段代碼在一定時間內的執(zhí)行次數(shù),可通過Guava或Semaphore實現(xiàn)
降級與熔斷
數(shù)據(jù)庫切庫酌畜、分庫怎囚、分表
切庫
切庫:數(shù)據(jù)庫讀寫分離導致的數(shù)據(jù)庫切換操作
當單個數(shù)據(jù)庫的讀寫性能達到瓶頸的時候,可根據(jù)業(yè)務來判斷讀與寫的比重,然后通過將數(shù)據(jù)庫設置為Master-Slave模式完成讀寫分離并配置好所有庫的讀寫權限恳守。
當查詢業(yè)務多余讀取業(yè)務的時候考婴,通過負載均衡,將查詢的操作分擔給不同的從庫催烘,從而減輕主庫的壓力沥阱。
分庫分表
當單庫的性能達到瓶頸,或當單表容量達到瓶頸伊群,通過SQL與索引的優(yōu)化之后還是很慢考杉,那么就需要分表
水平分表:表結構保持不變,根據(jù)固定的ID將數(shù)據(jù)劃分到不同表中舰始,需要在寫入與查詢的時候進行ID的路由
垂直分表:將表結構根據(jù)數(shù)據(jù)的活躍度拆分成多個表崇棠,來分別提高不同的單表處理能力
問題:
- 事務問題。在執(zhí)行分庫之后丸卷,由于數(shù)據(jù)存儲到了不同的庫上易茬,數(shù)據(jù)庫事務管理出現(xiàn)了困難。如果依賴數(shù)據(jù)庫本身的分布式事務管理功能去執(zhí)行事務及老,將付出高昂的性能代價抽莱;如果由應用程序去協(xié)助控制,形成程序邏輯上的事務骄恶,又會造成編程方面的負擔食铐。
- 跨庫跨表的join問題。在執(zhí)行了分庫分表之后僧鲁,難以避免會將原本邏輯關聯(lián)性很強的數(shù)據(jù)劃分到不同的表虐呻、不同的庫上,我們無法join位于不同分庫的表寞秃,也無法join分表粒度不同的表斟叼,結果原本一次查詢能夠完成的業(yè)務,可能需要多次查詢才能完成春寿。
- 額外的數(shù)據(jù)管理負擔和數(shù)據(jù)運算壓力朗涩。額外的數(shù)據(jù)管理負擔,最顯而易見的就是數(shù)據(jù)的定位問題和數(shù)據(jù)的增刪改查的重復執(zhí)行問題绑改,這些都可以通過應用程序解決谢床,但必然引起額外的邏輯運算。
PS:給大家推薦一個java資源共享厘线,學習交流群《957734884》