在現代的軟件系統中,幾乎全部的系統都使用到了數據庫,不管是關係型數據,例如MySql、SQLite、Oracle、SQLServer等,仍是非關係性數據,例如mongoDB、redis等。本文已web系統爲例來闡述爲何要下降數據庫的壓力,在提出具體方案以前先大體講解一下如今web系統的架構,要了解web系統的架構和演變過程具體能夠參考大型網站架構演變和知識體系這片文章。web
如今的大型web系統多采用分佈式的架構,分佈式系統面臨的最大挑戰就是如何在複雜的、併發的狀況下保證數據的一致性問題。一般爲了不因爲保證數據一致性問題而帶來的困難,一般狀況下都是採用多個實例,單個數據源的架構模式,簡化模式如圖。
在這個架構中,經過不斷的增長實例(webserver)能夠下降應用服務器的壓力,因此只要保證應用代碼的質量、應用之間的低耦合性、可擴展性和可維護性等,應用服務器的壓力就再也不會成爲總體架構中性能瓶頸,可是隨着業務量的不斷增長增加,或者時間的積累,沉澱下來的數據變得愈來愈來多,隨着而來的數據庫的壓力變得愈來愈大,慢慢的性能的瓶頸主要集中在數據庫上。redis
表索引能夠加快對錶中數據的檢索速度,可是會下降表中數據的更新速度,因此增長表的索引必定控制在合理範圍內,過多的索引不但不會下降數據庫的壓力,反而可能增大數據庫的壓力,表索引的創建通常要從具體業務場景出發,對於讀多寫少的場景,能夠經過適當的增長索引來提升效率,對錶的那些列創建索引?創建單獨索引仍是創建複合索引?要根據具體的業務場景來決定,創建索引以後能夠針對索引對業務邏輯中使用的SQL進行優化,創建索引是最基礎的手段,這裏不錯過多的介紹。sql
通常狀況下,業務中所處理的數據的都具備必定的時間間隔,因此能夠經過對業務進行梳理,將當前時間間隔以外的數據進行截轉,截轉到歷史數據庫中,經過對業務進行拆分,當須要歷史數據時,能夠轉到歷史數據庫中進行查詢,或者修改,經過減小當前數據庫的數據量,來減輕當前業務數據的壓力。數據截轉通常狀況下是按照時間來進行,因此在業務員數據庫設計的時候就要考慮到時間這個因素。
數據截轉能夠進行間隔一段時間作一次手工的數據截轉,也能夠啓動一個定時器,每一個一段時間進行一次數據截轉,推薦的方式是準實時截轉,及天天在業務量較小的時間,啓動任務實時截轉。
數據截轉須要注意的幾個問題:(1)外鍵關聯關係(特別是有主鍵ID的關聯的)注意在截轉的歷史數據庫中的關聯關係是否正確。(2)保證生產庫和歷史庫的業務關聯關係,從而避免歷史庫的數據須要關聯生產庫中的數據。數據庫
緩存是下降數據壓力一個強有力的手段,基本是全部系統大型web系統中都會使用到,因此現代的大型web系統的架構通常如圖。
請求1到達webserver以後,首先執行2訪問緩存,若是hit則返回,miss則執行3訪問數據庫,在執行4同步到緩存中,再返回。可是不是緩存並非萬能的,緩存也有其使用的業務場景,通常在讀多寫少,數據重複查詢比較集中的場景下,緩存能夠大大提升性能,緩存操做順序很是重要,不合理的操做順序,在併發場景下經常會致使數據的不一致,緩存的具體操做能夠參考緩存架構設計細節二三事這邊文章。緩存
有些業務例如報表、數據彙總等須要數據量較多,此時可能須要進行多表聯合查詢,聯合查詢操做很是消耗數據庫的性能,因此在這種業務場景下爲了不過大的性能消耗,每每須要將查詢時的多個表按照關聯條件進行關聯,生成一張含有冗餘信息的包含全部表的多個字段的大寬表,這樣在進行查詢時,只在一張表中進行查詢,性能明顯獲得提高。大寬表的生成是在業務流程中生成仍是經過異步化任務來生成,根據具體的業務邏輯來定。服務器
非關係型數據庫,也就是咱們一般說的NoSQL數據,最多見就是key/value類型的數據庫,這類數據庫不強調錶的關係,可是查詢速度很是快,所在某些具體場景下,咱們應該優先選擇NoSQL數據庫,例如字典信息表的查詢。架構
若是採用單點數據數據庫,就算對數據進行上述的相關優化,可是因爲其自己的單點性,因此隨着流量的激增,數據庫仍然會成爲系統的瓶頸,如何對數據進行拆分來解決這個問題了,讀寫分離就是最經常使用的方法,讀寫分離的原理以下圖。
讀寫分離技術如今已經應用的很成熟,經過將數據拆分爲兩個實例,讀寫分離操做改善了數據單點的瓶頸,分攤了數據庫壓力,並且當主數據庫宕機以後能夠迅速的切換到從庫,而不會致使業務不可用,同時也起到數據備份的做用,因爲存在兩個數據實例,因此數據怎麼由主庫同步到從庫、主從之間延遲引起的數據不一致問題,以及怎麼來分離業務中讀和寫操做成爲要解決的問題成爲要解決的問題。主從同步能夠參考Mysql主從架構的複製原理及配置這篇文章,從主數據一致能夠參考DB主從一致性的幾種解決方法這篇文章。併發
採用讀寫分離以後,數據庫已經變爲兩份實例,數據庫的壓力已經獲得分攤,若是數據庫的壓力仍是過大時,這是就要從業務方面着手,將具體業務細分,將業務對應的表分拆到不一樣的數據庫當中,以下圖。
業務變更較大,同時要對系統內部之間的相互調用提供接口,調用方式能夠選用RPC、Restful、JMQ消息等方式。通常狀況下,數據庫垂直拆分作的足夠細分的話,加上讀寫分離技術,加上適當的數據截轉就能夠知足通常的大型業務系統對性能的需求。異步
數據庫能夠進行垂直拆分,固然也能夠對數據庫中的表進行垂直拆分,對錶進行拆分就是對數據拆分的再拆分,如圖。種解決方法只適用於一些特定的場景,例如對錶進行垂直拆分,經過異步化調用將全部任務異步化,前提是總的任務能夠進行分佈的異步化操做,在實際應用比較少,由於設計的表只要複合三範式的要求,通常是很難在進行拆分的,應用較可能是對錶進行水平拆分。 數據庫設計
若是已經作了數據庫拆分,而且進行了讀寫分離,數據壓力仍是過大,主要緣由就是數據庫表中的記錄太多,或者對數據進行了截轉,可是對歷史數據的操做仍是比較頻繁的,且隨着截轉的歷史數據愈來愈多,歷史數據庫的壓力也邊的也變的愈來愈大,這時有兩種解決方案:第一種方案就是對數據庫中的表進行垂直拆分,從而不用在截轉數據,經過不斷對錶進行水平拆分,保證數據數據庫中單表的記錄數保持在一個高性能合理的範圍之類,經過擴容將不一樣分配到不一樣的數據中(分庫分表)來保證數據庫的壓力,應用在訪問時,經過分庫分表的條件進行路由,就能夠取到數據。第二種就是仍舊對數據進行截轉,當歷史數據信息過多從而致使數據庫壓力過大時,採用搜索引擎的方式來解決。相比於第一種操做第二種方案適用於讀操做上,對與寫操做,具備必定的侷限性,第一種方案具備必定的通用性。對錶進行水平拆分的過程如圖所示。
在進行進行具體水平拆分以前,咱們須要考慮這樣幾個問題