當前位置:首頁 » 數據倉庫 » 資料庫分庫分表集群
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

資料庫分庫分表集群

發布時間: 2023-04-04 04:10:24

A. 干貨來了,分庫分表的實戰案例分享

問題現狀

某系統, 訂單單表早就已經突破200G ,由於查詢維度較多,即使加了 兩個從庫,優化索引 等優化手段也無濟於事。因為資料庫達到瓶頸,應用只能通過 限速、非同步隊列等對其進行保護, 因此進行分庫分表的嘗試

整體思路

按照商戶ID進行分庫,用戶ID進行分表,同時通過數據同步等方式,把數據同步到一個運營庫, 同時滿足C端用戶、B端商戶、客服、運營等的需求。最終,通過 新老系統雙寫 逐漸從老庫過渡到新庫,完成業務的切換。

切分策略

1. 查詢切分

將ID和庫的Mapping關系記錄在一個單獨瞎仿的庫中,但是這樣 引入額外的伺服器來維護這個Mapping關系

2. 范圍切分

按照時間區間或ID區間來切分。但是 針對於某些大商戶來說,還是解決不了性能瓶頸的問題

3. Hash切分(最終方案)

我們分庫分表的方案是16*16的。

商戶Id後四位mod 16 分16個庫,攔螞 UserId後四位Mod 16 將每個庫分為16個表,共計分為256張表。

線上部署情況為 4個集群 ,每個集群4個庫( 1主3從 )。

場景一:資料庫性能達到瓶頸:擴大資料庫的集群數量,從16個資料庫變成32個資料庫。

場景二:單表容量達到瓶頸:擴大分簡神埋表的數量,從16切分變成32切分。

唯一ID方案

1. 利用資料庫自增ID(單點風險、單機性能瓶頸)

2. 利用資料庫集群並設置相應的步長( 需要單獨的資料庫集群 )

3. Twitter Snowflake( 需要獨立的集群以及ZK )

4. 採用了帶有業務屬性的方案:(時間戳+商戶ID+用戶ID+隨機數)

其他問題

數據遷移

第一階段

第二階段

第三階段

總結

B. newsql和nosql的區別和聯系

TiDB 是 PingCAP 公司設計的開源分布式 HTAP (Hybrid Transactional and Analytical Processing) 資料庫,結合了傳統的 RDBMS 和 NoSQL 的最佳特性。TiDB 兼容 MySQL,支持無限的水平擴展,具備強一致性和高可用性。TiDB 的目標是為 OLTP (Online Transactional Processing) 和 OLAP (Online Analytical Processing) 場景提供一站式的解決方案。

TiDB 具備如下特性:

  • 高度兼容 MySQL

    大多數情況下,無需修改代碼即可從 MySQL 輕松遷移至 TiDB,分庫分表後的 MySQL 集群亦可通過 TiDB 工具進行實時遷移。

  • 水平彈性擴展

    通過簡單地增加新節點即可實現 TiDB 的水平擴展,按需擴展吞吐或存儲,輕松應對高並發、海量數據場景。

  • 分布式事務

    TiDB 100% 支持標準的 ACID 事務。

  • 真正金融級高可用

    相比於傳統主從 (M-S) 復制方案,基於 Raft 的多數派選舉協議可以提供金融級的 100% 數據強一致性保證,且在不丟失大多數副本的前提下,可以實現故障的自動恢復 (auto-failover),無需人工介入。

  • 一站式 HTAP 解決方案

    TiDB 作為典型的 OLTP 行存資料庫,同時兼具強大的 OLAP 性能,配合 TiSpark,可提供一站式 HTAP 解決方案,一份存儲同時處理 OLTP & OLAP,無需傳統繁瑣的 ETL 過程。

  • 雲原生 SQL 資料庫

    TiDB 是為雲而設計的資料庫,支持公有雲、私有雲和混合雲,配合TiDB Operator 項目可實現自動化運維,使部署、配置和維護變得十分簡單。

  • TiDB 的設計目標是 100% 的 OLTP 場景和 80% 的 OLAP 場景,更復雜的 OLAP 分析可以通過TiSpark 項目來完成。

    TiDB 對業務沒有任何侵入性,能優雅地替換傳統的資料庫中間件、資料庫分庫分表等 Sharding 方案。同時它也讓開發運維人員不用關注資料庫 Scale 的細節問題,專注於業務開發,極大地提升研發的生產力。

C. 超詳細MySQL資料庫優化

資料庫優化一方面是找出系統的瓶頸,提高MySQL資料庫的整體性能,而另一方面需要合理的結構設計和參數調整,以提高用戶的相應速度,同時還要盡可能的節約系統資源,以便讓系統提供更大的負荷.

1. 優化一覽圖

2. 優化

筆者將優化分為了兩大類,軟優化和硬優化,軟優化一般是操作資料庫即可,而硬優化則是操作伺服器硬體及參數設置.

2.1 軟優化

2.1.1 查詢語句優化

1.首先我們可以用EXPLAIN或DESCRIBE(簡寫:DESC)命令分析一條查詢語句的執行信息.

2.例:

顯示:

其中會顯示索引和查詢數據讀取數據條數等信息.

2.1.2 優化子查詢

在MySQL中,盡量使用JOIN來代替子查詢.因為子查詢需要嵌套查詢,嵌套查詢時會建立一張臨時表,臨時表的建立和刪除都會有較大的系統開銷,而連接查詢不會創建臨時表,因此效率比嵌套子查詢高.

2.1.3 使用索引

索引是提高資料庫查詢速度最重要的方法之一,關於索引可以參高筆者<MySQL資料庫索引>一文,介紹比較詳細,此處記錄使用索引的三大注意事項:

2.1.4 分解表

對於欄位較多的表,如果某些欄位使用頻率較低,此時應當,將其分離出來從而形成新的表,

2.1.5 中間表

對於將大量連接查詢的表可以創建中間表,從而減少在查詢時造成的連接耗時.

2.1.6 增加冗餘欄位

類似於創建中間表,增加冗餘也是為了減少連接查詢.

2.1.7 分析表,,檢查表,優化表

分析表主要是分析表中關鍵字的分布,檢查表主要是檢查表中是否存在錯誤,優化表主要是消除刪除或更新造成的表空間浪費.

1. 分析表: 使用 ANALYZE 關鍵字,如ANALYZE TABLE user;

2. 檢查表: 使用 CHECK關鍵字,如CHECK TABLE user [option]

option 只對MyISAM有效,共五個參數值:

3. 優化表:使用OPTIMIZE關鍵字,如OPTIMIZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE user;

LOCAL|NO_WRITE_TO_BINLOG都是表示不寫入日誌.,優化表只對VARCHAR,BLOB和TEXT有效,通過OPTIMIZE TABLE語句可以消除文件碎片,在執行過程中會加上只讀鎖.

2.2 硬優化

2.2.1 硬體三件套

1.配置多核心和頻率高的cpu,多核心可以執行多個線程.

2.配置大內存,提高內存,即可提高緩存區容量,因此能減少磁碟I/O時間,從而提高響應速度.

3.配置高速磁碟或合理分布磁碟:高速磁碟提高I/O,分布磁碟能提高並行操作的能力.

2.2.2 優化資料庫參數

優化資料庫參數可以提高資源利用率,從而提高MySQL伺服器性能.MySQL服務的配置參數都在my.cnf或my.ini,下面列出性能影響較大的幾個參數.

2.2.3 分庫分表

因為資料庫壓力過大,首先一個問題就是高峰期系統性能可能會降低,因為資料庫負載過高對性能會有影響。另外一個,壓力過大把你的資料庫給搞掛了怎麼辦?所以此時你必須得對系統做分庫分表 + 讀寫分離,也就是把一個庫拆分為多個庫,部署在多個資料庫服務上,這時作為主庫承載寫入請求。然後每個主庫都掛載至少一個從庫,由從庫來承載讀請求。

2.2.4 緩存集群

如果用戶量越來越大,此時你可以不停的加機器,比如說系統層面不停加機器,就可以承載更高的並發請求。然後資料庫層面如果寫入並發越來越高,就擴容加資料庫伺服器,通過分庫分表是可以支持擴容機器的,如果資料庫層面的讀並發越來越高,就擴容加更多的從庫。但是這里有一個很大的問題:資料庫其實本身不是用來承載高並發請求的,所以通常來說,資料庫單機每秒承載的並發就在幾千的數量級,而且資料庫使用的機器都是比較高配置,比較昂貴的機器,成本很高。如果你就是簡單的不停的加機器,其實是不對的。所以在高並發架構里通常都有緩存這個環節,緩存系統的設計就是為了承載高並發而生。所以單機承載的並發量都在每秒幾萬,甚至每秒數十萬,對高並發的承載能力比資料庫系統要高出一到兩個數量級。所以你完全可以根據系統的業務特性,對那種寫少讀多的請求,引入緩存集群。具體來說,就是在寫資料庫的時候同時寫一份數據到緩存集群里,然後用緩存集群來承載大部分的讀請求。這樣的話,通過緩存集群,就可以用更少的機器資源承載更高的並發。

一個完整而復雜的高並發系統架構中,一定會包含:各種復雜的自研基礎架構系統。各種精妙的架構設計.因此一篇小文頂多具有拋磚引玉的效果,但是資料庫優化的思想差不多就這些了.

D. 資料庫集群、負載均衡、主從配置、動靜(讀寫)分離、分表分庫是dba還是運維還是開發乾的活,我一個開

誰懂迅猛,誰去做

~~~~~
想用、鉛戚沒專門的畝激橋人,就得自己弄

~

E. php怎麼處理高並發

以下內容轉載自徐漢彬大牛的博客億級Web系統搭建——單機到分布式集群

當一個Web系統從日訪問量10萬逐步增長到1000萬,甚至超過1億的過程中,Web系統承受的壓力會越來越大,在這個過程中,我們會遇到很多的問題。為了解決這些性能壓力帶來問題,我們需要在Web系統架構層面搭建多個層次的緩存機制。在不同的壓力階段,我們會遇到不同的問題,通過搭建不同的服務和架構來解決。

Web負載均衡

Web負載均衡(Load Balancing),簡單地說就是給我們的伺服器集群分配「工作任務」,而採用恰當的分配方式,對於保護處於後端的Web伺服器來說,非常重要。

負載均衡的策略有很多,我們從簡單的講起哈。

1.HTTP重定向

當用戶發來請求的時候,Web伺服器通過修改HTTP響應頭中的Location標記來返回一個新的url,然後瀏覽器再繼續請求這個新url,實際上就是頁面重定向。通過重定向,來達到「負載均衡」的目標。例如,我們在下載PHP源碼包的時候,點擊下載鏈接時,為了解決不同國家和地域下載速度的問題,它會返回一個離我們近的下載地址。重定向的HTTP返回碼是302

這個重定向非常容易實現,並且可以自定義各種策略。但是,它在大規模訪問量下,性能不佳。而且,給用戶的體驗也不好,實際請求發生重定向,增加了網路延時。

2. 反向代理負載均衡

反向代理服務的核心工作主要是轉發HTTP請求,扮演了瀏覽器端和後台Web伺服器中轉的角色。因為它工作在HTTP層(應用層),也就是網路七層結構中的第七層,因此也被稱為「七層負載均衡」。可以做反向代理的軟體很多,比較常見的一種是Nginx。

Nginx是一種非常靈活的反向代理軟體,可以自由定製化轉發策略,分配伺服器流量的權重等。反向代理中,常見的一個問題,就是Web伺服器存儲的session數據,因為一般負載均衡的策略都是隨機分配請求的。同一個登錄用戶的請求,無法保證一定分配到相同的Web機器上,會導致無法找到session的問題。

解決方案主要有兩種:

1.配置反向代理的轉發規則,讓同一個用戶的請求一定落到同一台機器上(通過分析cookie),復雜的轉發規則將會消耗更多的CPU,也增加了代理伺服器的負擔。

2.將session這類的信息,專門用某個獨立服務來存儲,例如redis/memchache,這個方案是比較推薦的。

反向代理服務,也是可以開啟緩存的,如果開啟了,會增加反向代理的負擔,需要謹慎使用。這種負載均衡策略實現和部署非常簡單,而且性能表現也比較好。但是,它有「單點故障」的問題,如果掛了,會帶來很多的麻煩。而且,到了後期Web伺服器繼續增加,它本身可能成為系統的瓶頸。

3. IP負載均衡

IP負載均衡服務是工作在網路層(修改IP)和傳輸層(修改埠,第四層),比起工作在應用層(第七層)性能要高出非常多。原理是,他是對IP層的數據包的IP地址和埠信息進行修改,達到負載均衡的目的。這種方式,也被稱為「四層負載均衡」。常見的負載均衡方式,是LVS(Linux Virtual Server,Linux虛擬服務),通過IPVS(IP Virtual Server,IP虛擬服務)來實現。

在負載均衡伺服器收到客戶端的IP包的時候,會修改IP包的目標IP地址或埠,然後原封不動地投遞到內部網路中,數據包會流入到實際Web伺服器。實際伺服器處理完成後,又會將數據包投遞回給負載均衡伺服器,它再修改目標IP地址為用戶IP地址,最終回到客戶端。

上述的方式叫LVS-NAT,除此之外,還有LVS-RD(直接路由),LVS-TUN(IP隧道),三者之間都屬於LVS的方式,但是有一定的區別,篇幅問題,不贅敘。

IP負載均衡的性能要高出Nginx的反向代理很多,它只處理到傳輸層為止的數據包,並不做進一步的組包,然後直接轉發給實際伺服器。不過,它的配置和搭建比較復雜。

4. DNS負載均衡

DNS(Domain Name System)負責域名解析的服務,域名url實際上是伺服器的別名,實際映射是一個IP地址,解析過程,就是DNS完成域名到IP的映射。而一個域名是可以配置成對應多個IP的。因此,DNS也就可以作為負載均衡服務。

這種負載均衡策略,配置簡單,性能極佳。但是,不能自由定義規則,而且,變更被映射的IP或者機器故障時很麻煩,還存在DNS生效延遲的問題。

5. DNS/GSLB負載均衡

我們常用的CDN(Content Delivery Network,內容分發網路)實現方式,其實就是在同一個域名映射為多IP的基礎上更進一步,通過GSLB(Global Server Load Balance,全局負載均衡)按照指定規則映射域名的IP。一般情況下都是按照地理位置,將離用戶近的IP返回給用戶,減少網路傳輸中的路由節點之間的跳躍消耗。

「向上尋找」,實際過程是LDNS(Local DNS)先向根域名服務(Root Name Server)獲取到頂級根的Name Server(例如.com的),然後得到指定域名的授權DNS,然後再獲得實際伺服器IP。

CDN在Web系統中,一般情況下是用來解決大小較大的靜態資源(html/Js/Css/圖片等)的載入問題,讓這些比較依賴網路下載的內容,盡可能離用戶更近,提升用戶體驗。

例如,我訪問了一張imgcache.gtimg.cn上的圖片(騰訊的自建CDN,不使用qq.com域名的原因是防止http請求的時候,帶上了多餘的cookie信息),我獲得的IP是183.60.217.90。

這種方式,和前面的DNS負載均衡一樣,不僅性能極佳,而且支持配置多種策略。但是,搭建和維護成本非常高。互聯網一線公司,會自建CDN服務,中小型公司一般使用第三方提供的CDN。

Web系統的緩存機制的建立和優化

剛剛我們講完了Web系統的外部網路環境,現在我們開始關注我們Web系統自身的性能問題。我們的Web站點隨著訪問量的上升,會遇到很多的挑戰,解決這些問題不僅僅是擴容機器這么簡單,建立和使用合適的緩存機制才是根本。

最開始,我們的Web系統架構可能是這樣的,每個環節,都可能只有1台機器。

我們從最根本的數據存儲開始看哈。

一、 MySQL資料庫內部緩存使用

MySQL的緩存機制,就從先從MySQL內部開始,下面的內容將以最常見的InnoDB存儲引擎為主。

1. 建立恰當的索引

最簡單的是建立索引,索引在表數據比較大的時候,起到快速檢索數據的作用,但是成本也是有的。首先,佔用了一定的磁碟空間,其中組合索引最突出,使用需要謹慎,它產生的索引甚至會比源數據更大。其次,建立索引之後的數據insert/update/delete等操作,因為需要更新原來的索引,耗時會增加。當然,實際上我們的系統從總體來說,是以select查詢操作居多,因此,索引的使用仍然對系統性能有大幅提升的作用。

2. 資料庫連接線程池緩存

如果,每一個資料庫操作請求都需要創建和銷毀連接的話,對資料庫來說,無疑也是一種巨大的開銷。為了減少這類型的開銷,可以在MySQL中配置thread_cache_size來表示保留多少線程用於復用。線程不夠的時候,再創建,空閑過多的時候,則銷毀。

其實,還有更為激進一點的做法,使用pconnect(資料庫長連接),線程一旦創建在很長時間內都保持著。但是,在訪問量比較大,機器比較多的情況下,這種用法很可能會導致「資料庫連接數耗盡」,因為建立連接並不回收,最終達到資料庫的max_connections(最大連接數)。因此,長連接的用法通常需要在CGI和MySQL之間實現一個「連接池」服務,控制CGI機器「盲目」創建連接數。

建立資料庫連接池服務,有很多實現的方式,PHP的話,我推薦使用swoole(PHP的一個網路通訊拓展)來實現。

3. Innodb緩存設置(innodb_buffer_pool_size)

innodb_buffer_pool_size這是個用來保存索引和數據的內存緩存區,如果機器是MySQL獨占的機器,一般推薦為機器物理內存的80%。在取表數據的場景中,它可以減少磁碟IO。一般來說,這個值設置越大,cache命中率會越高。

4. 分庫/分表/分區。

MySQL資料庫表一般承受數據量在百萬級別,再往上增長,各項性能將會出現大幅度下降,因此,當我們預見數據量會超過這個量級的時候,建議進行分庫/分表/分區等操作。最好的做法,是服務在搭建之初就設計為分庫分表的存儲模式,從根本上杜絕中後期的風險。不過,會犧牲一些便利性,例如列表式的查詢,同時,也增加了維護的復雜度。不過,到了數據量千萬級別或者以上的時候,我們會發現,它們都是值得的。

二、 MySQL資料庫多台服務搭建

1台MySQL機器,實際上是高風險的單點,因為如果它掛了,我們Web服務就不可用了。而且,隨著Web系統訪問量繼續增加,終於有一天,我們發現1台MySQL伺服器無法支撐下去,我們開始需要使用更多的MySQL機器。當引入多台MySQL機器的時候,很多新的問題又將產生。

1. 建立MySQL主從,從庫作為備份

這種做法純粹為了解決「單點故障」的問題,在主庫出故障的時候,切換到從庫。不過,這種做法實際上有點浪費資源,因為從庫實際上被閑著了。

2. MySQL讀寫分離,主庫寫,從庫讀。

兩台資料庫做讀寫分離,主庫負責寫入類的操作,從庫負責讀的操作。並且,如果主庫發生故障,仍然不影響讀的操作,同時也可以將全部讀寫都臨時切換到從庫中(需要注意流量,可能會因為流量過大,把從庫也拖垮)。

3. 主主互備。

兩台MySQL之間互為彼此的從庫,同時又是主庫。這種方案,既做到了訪問量的壓力分流,同時也解決了「單點故障」問題。任何一台故障,都還有另外一套可供使用的服務。

不過,這種方案,只能用在兩台機器的場景。如果業務拓展還是很快的話,可以選擇將業務分離,建立多個主主互備。

三、 MySQL資料庫機器之間的數據同步

每當我們解決一個問題,新的問題必然誕生在舊的解決方案上。當我們有多台MySQL,在業務高峰期,很可能出現兩個庫之間的數據有延遲的場景。並且,網路和機器負載等,也會影響數據同步的延遲。我們曾經遇到過,在日訪問量接近1億的特殊場景下,出現,從庫數據需要很多天才能同步追上主庫的數據。這種場景下,從庫基本失去效用了。

於是,解決同步問題,就是我們下一步需要關注的點。

1. MySQL自帶多線程同步

MySQL5.6開始支持主庫和從庫數據同步,走多線程。但是,限制也是比較明顯的,只能以庫為單位。MySQL數據同步是通過binlog日誌,主庫寫入到binlog日誌的操作,是具有順序的,尤其當SQL操作中含有對於表結構的修改等操作,對於後續的SQL語句操作是有影響的。因此,從庫同步數據,必須走單進程。

2. 自己實現解析binlog,多線程寫入。

以資料庫的表為單位,解析binlog多張表同時做數據同步。這樣做的話,的確能夠加快數據同步的效率,但是,如果表和表之間存在結構關系或者數據依賴的話,則同樣存在寫入順序的問題。這種方式,可用於一些比較穩定並且相對獨立的數據表。

國內一線互聯網公司,大部分都是通過這種方式,來加快數據同步效率。還有更為激進的做法,是直接解析binlog,忽略以表為單位,直接寫入。但是這種做法,實現復雜,使用范圍就更受到限制,只能用於一些場景特殊的資料庫中(沒有表結構變更,表和表之間沒有數據依賴等特殊表)。

四、 在Web伺服器和資料庫之間建立緩存

實際上,解決大訪問量的問題,不能僅僅著眼於資料庫層面。根據「二八定律」,80%的請求只關注在20%的熱點數據上。因此,我們應該建立Web伺服器和資料庫之間的緩存機制。這種機制,可以用磁碟作為緩存,也可以用內存緩存的方式。通過它們,將大部分的熱點數據查詢,阻擋在資料庫之前。

1. 頁面靜態化

用戶訪問網站的某個頁面,頁面上的大部分內容在很長一段時間內,可能都是沒有變化的。例如一篇新聞報道,一旦發布幾乎是不會修改內容的。這樣的話,通過CGI生成的靜態html頁面緩存到Web伺服器的磁碟本地。除了第一次,是通過動態CGI查詢資料庫獲取之外,之後都直接將本地磁碟文件返回給用戶。

在Web系統規模比較小的時候,這種做法看似完美。但是,一旦Web系統規模變大,例如當我有100台的Web伺服器的時候。那樣這些磁碟文件,將會有100份,這個是資源浪費,也不好維護。這個時候有人會想,可以集中一台伺服器存起來,呵呵,不如看看下面一種緩存方式吧,它就是這樣做的。

2. 單台內存緩存

通過頁面靜態化的例子中,我們可以知道將「緩存」搭建在Web機器本機是不好維護的,會帶來更多問題(實際上,通過PHP的apc拓展,可通過Key/value操作Web伺服器的本機內存)。因此,我們選擇搭建的內存緩存服務,也必須是一個獨立的服務。

內存緩存的選擇,主要有redis/memcache。從性能上說,兩者差別不大,從功能豐富程度上說,Redis更勝一籌。

3. 內存緩存集群

當我們搭建單台內存緩存完畢,我們又會面臨單點故障的問題,因此,我們必須將它變成一個集群。簡單的做法,是給他增加一個slave作為備份機器。但是,如果請求量真的很多,我們發現cache命中率不高,需要更多的機器內存呢?因此,我們更建議將它配置成一個集群。例如,類似redis cluster。

Redis cluster集群內的Redis互為多組主從,同時每個節點都可以接受請求,在拓展集群的時候比較方便。客戶端可以向任意一個節點發送請求,如果是它的「負責」的內容,則直接返回內容。否則,查找實際負責Redis節點,然後將地址告知客戶端,客戶端重新請求。

對於使用緩存服務的客戶端來說,這一切是透明的。

內存緩存服務在切換的時候,是有一定風險的。從A集群切換到B集群的過程中,必須保證B集群提前做好「預熱」(B集群的內存中的熱點數據,應該盡量與A集群相同,否則,切換的一瞬間大量請求內容,在B集群的內存緩存中查找不到,流量直接沖擊後端的資料庫服務,很可能導致資料庫宕機)。

4. 減少資料庫「寫」

上面的機制,都實現減少資料庫的「讀」的操作,但是,寫的操作也是一個大的壓力。寫的操作,雖然無法減少,但是可以通過合並請求,來起到減輕壓力的效果。這個時候,我們就需要在內存緩存集群和資料庫集群之間,建立一個修改同步機制。

先將修改請求生效在cache中,讓外界查詢顯示正常,然後將這些sql修改放入到一個隊列中存儲起來,隊列滿或者每隔一段時間,合並為一個請求到資料庫中更新資料庫。

除了上述通過改變系統架構的方式提升寫的性能外,MySQL本身也可以通過配置參數innodb_flush_log_at_trx_commit來調整寫入磁碟的策略。如果機器成本允許,從硬體層面解決問題,可以選擇老一點的RAID(Rendant Arrays of independent Disks,磁碟列陣)或者比較新的SSD(Solid State Drives,固態硬碟)。

5. NoSQL存儲

不管資料庫的讀還是寫,當流量再進一步上漲,終會達到「人力有窮時」的場景。繼續加機器的成本比較高,並且不一定可以真正解決問題的時候。這個時候,部分核心數據,就可以考慮使用NoSQL的資料庫。NoSQL存儲,大部分都是採用key-value的方式,這里比較推薦使用上面介紹過Redis,Redis本身是一個內存cache,同時也可以當做一個存儲來使用,讓它直接將數據落地到磁碟。

這樣的話,我們就將資料庫中某些被頻繁讀寫的數據,分離出來,放在我們新搭建的Redis存儲集群中,又進一步減輕原來MySQL資料庫的壓力,同時因為Redis本身是個內存級別的Cache,讀寫的性能都會大幅度提升。

國內一線互聯網公司,架構上採用的解決方案很多是類似於上述方案,不過,使用的cache服務卻不一定是Redis,他們會有更豐富的其他選擇,甚至根據自身業務特點開發出自己的NoSQL服務。

6. 空節點查詢問題

當我們搭建完前面所說的全部服務,認為Web系統已經很強的時候。我們還是那句話,新的問題還是會來的。空節點查詢,是指那些資料庫中根本不存在的數據請求。例如,我請求查詢一個不存在人員信息,系統會從各級緩存逐級查找,最後查到到資料庫本身,然後才得出查找不到的結論,返回給前端。因為各級cache對它無效,這個請求是非常消耗系統資源的,而如果大量的空節點查詢,是可以沖擊到系統服務的。

在我曾經的工作經歷中,曾深受其害。因此,為了維護Web系統的穩定性,設計適當的空節點過濾機制,非常有必要。

我們當時採用的方式,就是設計一張簡單的記錄映射表。將存在的記錄存儲起來,放入到一台內存cache中,這樣的話,如果還有空節點查詢,則在緩存這一層就被阻擋了。

異地部署(地理分布式)

完成了上述架構建設之後,我們的系統是否就已經足夠強大了呢?答案當然是否定的哈,優化是無極限的。Web系統雖然表面上看,似乎比較強大了,但是給予用戶的體驗卻不一定是最好的。因為東北的同學,訪問深圳的一個網站服務,他還是會感到一些網路距離上的慢。這個時候,我們就需要做異地部署,讓Web系統離用戶更近。

一、 核心集中與節點分散

有玩過大型網游的同學都會知道,網游是有很多個區的,一般都是按照地域來分,例如廣東專區,北京專區。如果一個在廣東的玩家,去北京專區玩,那麼他會感覺明顯比在廣東專區卡。實際上,這些大區的名稱就已經說明了,它的伺服器所在地,所以,廣東的玩家去連接地處北京的伺服器,網路當然會比較慢。

當一個系統和服務足夠大的時候,就必須開始考慮異地部署的問題了。讓你的服務,盡可能離用戶更近。我們前面已經提到了Web的靜態資源,可以存放在CDN上,然後通過DNS/GSLB的方式,讓靜態資源的分散「全國各地」。但是,CDN只解決的靜態資源的問題,沒有解決後端龐大的系統服務還只集中在某個固定城市的問題。

這個時候,異地部署就開始了。異地部署一般遵循:核心集中,節點分散。

·核心集中:實際部署過程中,總有一部分的數據和服務存在不可部署多套,或者部署多套成本巨大。而對於這些服務和數據,就仍然維持一套,而部署地點選擇一個地域比較中心的地方,通過網路內部專線來和各個節點通訊。

·節點分散:將一些服務部署為多套,分布在各個城市節點,讓用戶請求盡可能選擇近的節點訪問服務。

例如,我們選擇在上海部署為核心節點,北京,深圳,武漢,上海為分散節點(上海自己本身也是一個分散節點)。我們的服務架構如圖:

需要補充一下的是,上圖中上海節點和核心節點是同處於一個機房的,其他分散節點各自獨立機房。
國內有很多大型網游,都是大致遵循上述架構。它們會把數據量不大的用戶核心賬號等放在核心節點,而大部分的網游數據,例如裝備、任務等數據和服務放在地區節點里。當然,核心節點和地域節點之間,也有緩存機制。

二、 節點容災和過載保護

節點容災是指,某個節點如果發生故障時,我們需要建立一個機制去保證服務仍然可用。毫無疑問,這里比較常見的容災方式,是切換到附近城市節點。假如系統的天津節點發生故障,那麼我們就將網路流量切換到附近的北京節點上。考慮到負載均衡,可能需要同時將流量切換到附近的幾個地域節點。另一方面,核心節點自身也是需要自己做好容災和備份的,核心節點一旦故障,就會影響全國服務。

過載保護,指的是一個節點已經達到最大容量,無法繼續接接受更多請求了,系統必須有一個保護的機制。一個服務已經滿負載,還繼續接受新的請求,結果很可能就是宕機,影響整個節點的服務,為了至少保障大部分用戶的正常使用,過載保護是必要的。

解決過載保護,一般2個方向:

·拒絕服務,檢測到滿負載之後,就不再接受新的連接請求。例如網游登入中的排隊。

·分流到其他節點。這種的話,系統實現更為復雜,又涉及到負載均衡的問題。

小結

Web系統會隨著訪問規模的增長,漸漸地從1台伺服器可以滿足需求,一直成長為「龐然大物」的大集群。而這個Web系統變大的過程,實際上就是我們解決問題的過程。在不同的階段,解決不同的問題,而新的問題又誕生在舊的解決方案之上。

系統的優化是沒有極限的,軟體和系統架構也一直在快速發展,新的方案解決了老的問題,同時也帶來新的挑戰。

F. 架構師的技術升級之路

這篇文章更多的是從溝通角度分析架構師的升級之道。但我們知道,架構師更多是靠技術拿高薪。

在本文里,我將列些我見到的技術架構平時需要解決的問題,有技術的,也有溝通協調方面的,以這些實實在在的案例,來列舉些技術架構需要具備的技能,以此來分析下高級開發如何更高效地升級到技術架構。好了,開場白結束,正文開始。

1 技術本身不產生價值,業務才會,論技術和業務的整合

一般會把架構分為技術架構和業務架構,這里我無意對比這兩類的優劣,但我只想說,在公司里,是靠業務價值創造盈利點的,所以技術,比如消息運橋隊列,內存優化,以及分庫分表資料庫集群等,只有嵌入到業務里,才能通過提升業務的可擴展性或性能,從而產生價值。

上述似乎是廢話,但恰恰是架構師工作的難點,大家可以想像一下,比如通過MyCat搭建個分庫分表架構不難,甚至把分庫分表組件通過負載均衡搭建成集群也不難,這些網上都有現成的案例。但如何要凱知在當前的業務系統里實現分庫分表,難度就不小了。具體來講,因為業務系統里或許有冗餘數據,而且有各類帶join, group by等的查詢語句,如何在分庫分表系統里兼容這些 歷史 問題,而且在上線新分庫系統後遷移 歷史 數據,又如,在產線切換到分庫分表時,萬一有問題如何回退,這些絕非是知道些Demo案例的高級開發能解決的問題。

所以在技術和業務方面,我自己的感受是(包括我見到的和聽到的) :只有接觸到業務了,才能用技術解決實際問題,才能更了解這個技術用起來的各類坑,像剛才提到的分庫分表是這樣,其它的諸如日誌組件,消息隊列組件都這樣。通過下面部分給出的架構師平時要解決的實際問題的講述,大家能更深刻地體會到這點。

2 資深架構師平時要解決的問題

如下的問題均是來源於實際,出於項目保密的原則,本人隱去了關鍵性的業務描述,但大家都能看懂,並能感受到架構師平時要解決問題的難度。

問題一,A公司有財務管理人事管理等10個左右的項目,它們在產線上,需要標准化管理,比如用同一個Maven倉庫,不論功能業務如何,得用同一套配置管理服務,用同一套日誌管理和分析組件,還得用同一套大數據組件來根據不同的業務維度來分析數據。

如果是重新搭建一套系統,這個難度也不小,更何況,對資深架構師的要求是,在 歷史 項目的技術上做標准化管理,否則每個項目各管各的,維護成本大不算,不同項目間的庫還很容易產生沖突。架構師要在保持業務穩定的前提下實現這點,大家可以考慮下難度。

問題二,隨著B公司業務量的上升,資料庫里的數據達到了T級,所以需要通過分庫分表來實現優化。這本身不難,但如何在升級的過程中保持業務的穩定?不能說上個功能點,關鍵業務就掛了,而且,萬一上線後出現問題,得提供應急的回退方案。

問題三,C公司是個創業型公司,剛開始的時候,通過SSM外加Oracle,能滿足大多數的業務需求,但隨著業務量的提升,需盯悄消要資深架構在短時間里實現針對高並發和大數據的方案,比如並發量高了,系統至少不能垮,而且針對每筆訂單,處理可以稍作延遲,但不能丟數據。

問題四,D公司需要在linux上搭建一套和產線一樣的測試環境,在平時的開發過程中,各業務組可以通過工具,在測試環境上部署或回退本項目的組件,這里,不僅要搭建測試環境,更要通過jenkins等工具給各業務組搭建一套能便捷部署系統的工具。

除了上述的問題之外,資深架構更像一個救火隊員,比如在公司的業務體系裡,任何一個團隊報出的和架構相關的問題,比如調消息隊列有延遲,調分庫分表時報內存OOM異常了,或者因Dubbo底層而導致的延遲或OOM,資深架構得能親自或帶領手下解決具體的問題。

3 和高級開發相比,資深架構一定得精通的技能(或素質)

其實高級開發和資深架構在需要掌握的技能方面,並沒太大的差別,具體而言,能幫助實現性能優化的分布式組件和資料庫組件(或者叫中間件)也就這么多,linux下的操作命令也就這么些,一些系統管理的工具,比如Maven,Jenkins,ant等的用法也不難。但和高級開發相比,資深架構的差別在於如下幾點。

1 資深架構解決的問題種類和數量要比高級開發多很多,所謂神槍手得靠子彈喂出來,有些問題,比如針對Kafka消息中間件的問題,資深架構一看日誌就知道該怎麼改,或者一看log4j錯誤信息就知道和其它哪些類有沖突了,又如,在搭建線程池時遇到了OOM問題,資深架構估計也能通過簡單地看日誌,也能快速定位問題所在。

也就是說,資深架構已經積累了很多處理問題的經驗,遇到一般問題時,無需再通過比較耗時的debug看問題根源,往往在腦子里已經存儲了大量可能會導致問題的原因,再通過查看關鍵日誌即可定位到具體的代碼點,然後就能很快地給出解決方案。

2 在給出解決方案時,比如要上個分布式redis集群,或者上個消息中間件,對高級開發而言,往往會有很多試錯的時間,比如上線後有某些功能點沒調通,得通過Debug或查日誌來逐一解決問題,或上線某個基於python的大數據分析系統後,雖然能滿足基本的功能,但在某個場景(比如寫日誌線程並發量太多)里,可能會導致OOM異常。

而對資深架構來說,往往之前已經做過同類事情,所以能避免很多坑(少了很多試錯成本和時間),而且由於對底層代碼比較熟悉,所以哪怕出現比較疑難的問題(比如不能穩定重現),資深架構能通過看日誌很快定位到具體的底層類,(而高級開發一般對此就束手無策了)。相比之下,資深架構的中流砥柱效應就能體現出來。

3 資深架構一般具有對各組件的差別非常了解,比如做分布式隊列,該先用Kafka還是rabbitMQ,或者搭建資料庫集群時,該用MySQL里的哪種引擎。

這樣,在選型時,由於知道了各種方案的優缺點,所以能知道哪類方案更適合本業務系統,或者說,通過重寫哪類組件的底層代碼,能很快地搭建起滿足本系統的中間件組件。這點,高級開發未必能做到。

總結一下,資深架構得對關鍵組件的底層非常了解,並且精通針對某些組件(比如消息組件,分庫組件)的實施和排查問題的能力,此外,資深架構的基本功也得非常扎實。

1 debug能力就不用說了,得能熟練地通過linux命令,從各類日誌中發現並解決問題。

2 無需了解所有組件的底層代碼(這太難了,也做不到),但需要了解一些常用組件的關鍵底層實現(比如Spring IOC或常用中間件) 方式,更得具備到組件內部jar里debug排查問題的能力。

3 學習能力更不說了,和高級開發相比,資深架構更得了解哪類組件該學,而且,每個組件內部的知識太多,比如Kafka的知識就能寫至少一本書,對於資深架構而言,首先需要用較短的時間了解該組件(比如kafka)的架構以及和其它分布式組件(比如Flume)的整合方式,而且還得具備過濾知識的能力,即知道哪些知識不用學。這樣一旦有需求,就可以較快地搭建出系統原型骨架,隨後再逐步完善功能效果。

4 對於程序員而言,如何高效地升級到架構或資深架構?

當我還處在一般開發和高級開發的中間水平時,我認為我能很快地升級到架構師的水平,所謂無知者無畏。當我邁出升級的步伐時,剛開始,我突然發現升級的難度很大,從而無處下手,因為平時我缺乏實踐架構師技能的實戰機會。現在,通過一些努力,我雖然沒有自信說自己一定達到了架構師的水平,但大多數架構師能乾的活,我勉強能做好。而且我平時也在不斷揣摩身邊技術架構的思考方式和解決問題的方法,所以在這方面我自認為給出的建議不會耽誤大家。

首先是鞏固自己基本功方面的建議。

1 學再多的視頻和材料,也不及動手實踐一個案例。

比如,大家在學習消息隊列時,一定得動手搭建個環境,最好用虛擬機模式分布式的場景,這時可能就有同學說了,環境太難搭建,怎麼辦?自己查資料,這種動手能力對架構師而言就屬於基本功,如果這也做不好,那麼也沒希望升級到架構師了。

類似這樣,大家可列個學習列表,網上升級到架構師的系列視頻很多,質量高的也不少,都是別人的經驗之談,但如果就看理論,或者看關鍵點,這連架構師的面試都通過不了,更何況做實際的架構師的活。

2 平時不能畏難,一定得多解決問題。

在平時工作中,一定會出很多問題,而且不少是出在核心代碼和底層代碼里,這時就一定得通過看日誌等方式去排查問題。 我知道,對很多想升級的高級開發而言,剛開始的時候一定很難,比如linux命令都不熟,或者效率很慢,別人都找出問題點了,自己才剛打開日誌。其實大家都這樣過來的,多查多練,最多三個月,動手能力一定能提升。

3 得鍛煉自己在linux里(或在分布式環境里)部署系統部署組件的能力,尤其是部署集群的能力,在此基礎上,通過各種工具能進行壓力測試。

比如還是拿kafka來說,搭建好集群後,就可以用kafka自帶的Performance來做壓測。其實如果是自己練習,壓測的結果沒太大的意義,但這個流程走下來,一定能對搭建環境,使用工具和看日誌等技巧就非常熟悉了。

4 盡量培養自己的調優意識。說這個話很虛,具體而言,自己得能通過各種資料庫日誌(比如各sql的運行時間)來找出長sql,並在此基礎上通過執行計劃來優化,又如,可以通過mp文件和GC日誌來看虛擬機的內存使用曲線,看內存主要耗在哪些方面,如果是自己代碼沒寫好那還好辦,如果是耗在(中間件的)底層jar包里的代碼里,那也得知道解決方案。

以上只是架構師所需要的基礎技能, 其實如果能真正做到上述4點的話,大家離開架構師的水準也不遠了,在此基礎上,大家還得繼續鍛煉整合的能力。

從縱向來講,需要進一步深化搭建集群的技能,比如能從底層代碼的角度,了解集群的組成方式,這樣的話,就能很清晰地了解到集群的擴展方式和性能調優點。

從橫向來講,需要進一步了解多種組件的整合方式,比如系統如何同日誌組件整合,大數據分析工具如何同日誌組件整合等。

剩下的就是不斷積累經驗技能了。

5 在升級路上,如何避免一些坑

我在平時還有機會接觸一些大神,這些其實都是大神們的經驗之談。下面分享下在升級過程中應當避免哪些坑。

1 就像大家以前准備政治考試時,先准備大點,在保證大點不拉下的基礎上,再詳細復習每個大點里的細節。比如,可以先了解Spring Cloud里有哪些組件,比如Ribbon可以用來負載均衡,Hystrix可以用來容錯等,先把Spring Cloud里諸多組件先了解個大概,能用它們搭建成一個微服務體系後,再深入了解其中每個組件的細節,比如Spring Cloud Stream里Kafka配置細節。

但我經過和多位架構師溝通,他們在升級時,多少都在這方面走過彎路,我自己有時候也會不知不覺陷入技術細節之中,而忘記我學這個技術的初衷。這里給大家的建議是,在明確學習目標後(比如要學Spring Cloud),剛開始別先自己閉門造車地為自己制定學習目標,可以先借鑒現有的視頻講解等的學習路線。制定學習計劃時,以兩到三天為單位,給自己定好一個短期目標,等到Spring Cloud組件全都了解後,再通過運行通若干個案例來深入了解組件的細節,這樣就能控制住自己的學習步驟。

2 千萬別理論和實際脫節。這似乎是廢話,但我見過很多高級開發,平時就看視頻和書,也不運行代碼,結果進步的速度很慢。

如果沒機會實踐架構技能怎麼辦?看自己組里有沒有架構的活。如果也沒有怎麼辦?(別嫌我啰嗦)回家自己准備環境,按視頻里的搭建架構環境。必要時,你甚至可以通過跳槽來換得一個架構師的實踐機會。

3 架構師可以是技術控,但絕不能是完美主義,畢竟解決方案得和實際業務切合,並得考慮解決問題的成本。而且,架構師不能過於拘泥於細節,不能什麼都事必躬親,很多時候,得給出方向,或者把問題拆分成開發能理解的子問題,然後讓手下人去干。 這似乎和技術沒有關系,這就要求架構師更具備和人打交道的能力了,這點將在本文的第6部分詳細說明。

6 指導技術難於自己實現功能,再論資深架構的協調(或者說扯皮)能力的煉成

不少開發者,尤其是資深開發者,或許都有這樣的體會,對於一些功能,我寧可自己做,而不是把它們拆分成若干個子功能再安排手下人去做。或者我寧可去攻克一些技術的難題,也不願意去和人扯皮,從而去制定架構里組件的選型方案。

可以這樣說,架構師30%的價值來自他擁有的專業技能,30%的價值來自他分析和解決問題的能力,而40%的價值(甚至更高)來自於指導和協調能力。除去最後40%的價值,架構師其實和高級開發沒什麼差別。比如通過下面的例子,我們能看到架構師為什麼還得具備指導和協調的能力。

案例1:當架構師被要求改善本公司系統(比如是個應用網站)的調用性能時,他就得和多個組打交道,往往是,有些組未必肯支持(畢竟現有系統用得不錯誰都不願改),或者具體的改善點需要一些組來落實,這就相當於增加該組的工作量了。

案例2:當架構師搭建好一套分布式緩存系統後,就得培訓其它組的開發人員,讓他們合理使用這套系統。

案例3:又如架構師幫一個組解決了一個典型的OOM問題後,得把解決這個問題的思路向其他組推廣,以便節省解決同類問題的時間。

從上述案例中,我們一定能感受到在溝通,協調方面架構師需要掌握的技能水準。這方面說難不難,多練就行,但對IT開發而言,動嘴要比動手寫代碼要難。下面也給出些提升「動嘴」能力的技巧。

1 首先得提升自己綜合邏輯思維的能力,這點可以靠多寫博客,甚至寫書來提升。其實寫的時候,就相當於把自己要講的內容用文字整理了一遍,這樣無形中也提升了自己綜合表達能力。

2 在組內要多分享技術。其實剛開始分享時,一定不知道該說什麼,甚至講完後沒人能懂(當然自己一定能懂),但多講幾次後,口頭表達和與別人的交流能力也上去了。

3 在遇到和其它組交流時(比如聯調或溝通介面),一定得抓住機會多開口,剛開始的時候,估計很難讓別人能接受自己的觀點,或者自己有理也未必能講清楚,但經過多次協調後,就能讓別人接受自己的觀點,或者大家能達成彼此能接受的妥協方案。

對本文感興趣的Java工程師的朋友們可以私信我【Java架構】進我的粉絲群領取一些架構資料 電子書籍在群里也會有面試上的一些建議交流

最後一個小要求,可以幫我轉發下!

G. 資料庫為什麼要分庫分表

1 基本思想之什麼是分庫分表?
從字面上簡單理解,就是把原本存儲於一個庫的數據分塊存儲到多個庫上,把原本存儲於一個表的數據分塊存儲到多個表上。
2 基本思想之為什麼要分庫分表?


據庫中的數據量不一定是可控的,在未進行分庫分表的情況下,隨著時間和業務的發展,庫中的表會越來越多,表中的數據量也會越來越大,相應地,數據操作,增
刪改查的開銷也會越來越大;另外,由於無法進行分布式式部署,而一台伺服器的資源(CPU、磁碟、內存、IO等)是有限的,最終資料庫所能承載的數據量、
數據處理能力都將遭遇瓶頸。
3 分庫分表的實施策略。

分庫分表有垂直切分和水平切分兩種。
3.1
何謂垂直切分,即將表按照功能模塊、關系密切程度劃分出來,部署到不同的庫上。例如,我們會建立定義資料庫workDB、商品資料庫payDB、用戶數據
庫userDB、日誌資料庫logDB等,分別用於存儲項目數據定義表、商品定義表、用戶數據表、日誌數據表等。
3.2
何謂水平切分,當一個表中的數據量過大時,我們可以把該表的數據按照某種規則,例如userID散列,進行劃分,然後存儲到多個結構相同的表,和不同的庫
上。例如,我們的userDB中的用戶數據表中,每一個表的數據量都很大,就可以把userDB切分為結構相同的多個userDB:part0DB、
part1DB等,再將userDB上的用戶數據表userTable,切分為很多userTable:userTable0、userTable1等,
然後將這些表按照一定的規則存儲到多個userDB上。
3.3 應該使用哪一種方式來實施資料庫分庫分表,這要看資料庫中數據量的瓶頸所在,並綜合項目的業務類型進行考慮。
如果資料庫是因為表太多而造成海量數據,並且項目的各項業務邏輯劃分清晰、低耦合,那麼規則簡單明了、容易實施的垂直切分必是首選。

如果資料庫中的表並不多,但單表的數據量很大、或數據熱度很高,這種情況之下就應該選擇水平切分,水平切分比垂直切分要復雜一些,它將原本邏輯上屬於一體
的數據進行了物理分割,除了在分割時要對分割的粒度做好評估,考慮數據平均和負載平均,後期也將對項目人員及應用程序產生額外的數據管理負擔。
在現實項目中,往往是這兩種情況兼而有之,這就需要做出權衡,甚至既需要垂直切分,又需要水平切分。我們的游戲項目便綜合使用了垂直與水平切分,我們首先對資料庫進行垂直切分,然後,再針對一部分表,通常是用戶數據表,進行水平切分。
4 分庫分表存在的問題。

4.1 事務問題。
在執行分庫分表之後,由於數據存儲到了不同的庫上,資料庫事務管理出現了困難。如果依賴資料庫本身的分布式事務管理功能去執行事務,將付出高昂的性能代價;如果由應用程序去協助控制,形成程序邏輯上的事務,又會造成編程方面的負擔。
4.2 跨庫跨表的join問題。
在執行了分庫分表之後,難以避免會將原本邏輯關聯性很強的數據劃分到不同的表、不同的庫上,這時,表的關聯操作將受到限制,我們無法join位於不同分庫的表,也無法join分表粒度不同的表,結果原本一次查詢能夠完成的業務,可能需要多次查詢才能完成。
4.3 額外的數據管理負擔和數據運算壓力。

外的數據管理負擔,最顯而易見的就是數據的定位問題和數據的增刪改查的重復執行問題,這些都可以通過應用程序解決,但必然引起額外的邏輯運算,例如,對於
一個記錄用戶成績的用戶數據表userTable,業務要求查出成績最好的100位,在進行分表之前,只需一個order
by語句就可以搞定,但是在進行分表之後,將需要n個order
by語句,分別查出每一個分表的前100名用戶數據,然後再對這些數據進行合並計算,才能得出結果。