鏈接:https://www.cnblogs.com/zhuzhen/p/9340941.html
海量數據的解決方案:
- 使用緩存。
- 頁面靜態化技術。
- 數據庫優化。
- 分類數據庫中活躍的數據。
- 批量讀取和延遲修改。
- 讀寫分離。
- 適應nosql和Hadoop技術。
- 分布式部署數據庫。
- 應用服務和數據服務分離。
- 使用搜索引擎搜索數據庫中的數據。
- 進行業務拆分。
一、使用緩存。
網站訪問數據的特點大多數呈現為“二八定律”:80%的業務訪問集中在20%的數據上。例如:在某一段時間內百度的搜索熱詞可能集中在少部分的熱門詞匯上;新浪微博某一時期也可能大家廣泛關注的主題也是少部分事件。
總的來說就是用戶只用到了總數據條目的一小部分,當網站發展到一定規模,數據庫IO操作成為性能瓶頸的時候,使用緩存將這一小部分的熱門數據緩存在內存中是一個很不錯的選擇,不但可以減輕數據庫的壓力,還可以提高整體網站的數據訪問速度。
使用緩存的方式可以通過程序代碼將數據直接保存到內存中,例如通過使用Map或者ConcurrentHashMap;另一種,就是使用緩存框架:redis、Ehcache、Memcache等。 使用緩存框架的時候,我們需要關心的就是什么時候創建緩存和緩存失效策略。
注意:使用緩存的時候還要考慮到緩存服務器發生故障時候如何進行容錯處理,是使用N多臺服務器緩存相同的數據,通過分布式部署的方式對緩存數據進行控制,當一臺發生故障的時候自動切換到其他的機器上去;還是通過Hash一致性的方式,等待緩存服務器恢復正常使用的時候重新指定到該緩存服務器。Hash一致性的另一個作用就是在分布式緩存服務器下對數據進行定位,將數據分布在不用緩存服務器上。
二、頁面靜態化技術
使用傳統的JSP界面,前端界面的顯示是通過后臺服務器進行渲染后返回給前端游覽器進行解析執行?,F在提倡前后端分離,前端界面基本都是HTML網頁代碼,通過Angular JS或者NodeJS提供的路由向后端服務器發出請求獲取數據,然后在游覽器對數據進行渲染,這樣在很大程度上降低了后端服務器的壓力。還可以將這些靜態的HTML、css、JS、圖片資源等放置在緩存服務器上或者CDN服務器上,一般使用最多的應該是CDN服務器或者Nginx服務器提供的靜態資源功能。
優化前端應該遵循以下幾點:
- 盡量減少HTTP請求。
- 使用cdn(Content Delivery Network,即內容分發網絡)。
- 添加expire頭,控制緩存的失效日期。
- 采用Gzip壓縮組件。
- 將樣式表放在頭部。
- 將腳本放在底部。
- 避免使用css表達式。
- 使用外部的JAVAScript和css。
- 減少DNS查詢。
- 精簡JavaScript。
- 避免重定向。
- 使用ajax可以緩存。
三、數據庫優化
大多數網站性能的瓶頸都是開在數據庫IO操作上。對于數據庫的優化來說,是一種用技術換金錢的方式。數據庫優化的方式很多,常見的可以分為:
- 數據庫表結構優化。
- SQL語句優化。
- 分區。
- 分表。
- 索引優化。
- 使用存儲過程代替直接操作。
1、表結構優化
1.1、命名規范
1.庫名、表名、字段名必須使用小寫字母,并采用下劃線分割。
- MySQL有配置參數lower_case_table_names,不可動態更改,linux系統默認為 0,即庫表名以實際情況存儲,大小寫敏感。如果是1,以小寫存儲,大小寫不敏感。如果是2,以實際情況存儲,但以小寫比較。
- 如果大小寫混合使用,可能存在abc,Abc,ABC等多個表共存,容易導致混亂。
- 字段名顯示區分大小寫,但實際使?用不區分,即不可以建立兩個名字一樣但大小寫不一樣的字段。
- 為了統一規范, 庫名、表名、字段名使用小寫字母。
2、庫名、表名、字段名禁止超過32個字符
庫名、表名、字段名支持最多64個字符,但為了統一規范、易于辨識以及減少傳輸量,禁止超過32個字符。
3、使用INNODB引擎。
INNODB引擎是MySQL5.5版本以后的默認引擘,支持事務、行級鎖,有更好的數據恢復能力、更好的并發性能,同時對多核、大內存、SSD等硬件支持更好,支持數據熱備份等,因此INNODB相比MyISAM有明顯優勢。
- InnoDB支持事務,MyISAM不支持。
- InnoDB支持行級鎖,MyISAM支持表級鎖。
- InnoDB支持MVCC,MyISAM不支持。(MVCC (Multiversion Concurrency Control),即多版本并發控制技術,它使得大部分支持行鎖的事務引擎,不再單純的使用行鎖來進行數據庫的并發控制,取而代之的是把數據庫的行鎖與行的多個版本結合起來,只需要很小的開銷,就可以實現非鎖定讀,從而大大提高數據庫系統的并發性能。 )
- InnoDB支持外鍵,而MyISAM不支持 。
- InnoDB不支持全文索引,而MyISAM支持。
innodb引擎的4大特性 :
- 插入緩沖(insert buffer)。
- 二次寫(double write)。
- 自適應哈希索引(ahi)。
- 預讀(read ahead)
4、庫名、表名、字段名禁止使用MySQL保留字。
當庫名、表名、字段名等屬性含有保留字時,SQL語句必須用反引號引用屬性名稱,這將使得SQL語句書寫、SHELL腳本中變量的轉義等變得?非常復雜。
5、禁止使用分區表。
分區表對分區鍵有嚴格要求;分區表在表變大后,執?行DDL、SHARDING、單表恢復等都變得更加困難。因此禁止使用分區表,并建議業務端手動SHARDING。
6.建議使用UNSIGNED存儲非負數值。
同樣的字節數,非負存儲的數值范圍更大。如TINYINT有符號為 -128-127,無符號為0-255。
7.建議使用INT UNSIGNED存儲IPV4
用UNSINGED INT存儲IP地址占用4字節,CHAR(15)則占用15字節。另外,計算機處理整數類型比字符串類型快。使用INT UNSIGNED而不是CHAR(15)來存儲IPV4地址,通過MySQL函數inet_ntoa和inet_aton來進行轉化。IPv6地址目前沒有轉化函數,需要使用DECIMAL或兩個BIGINT來存儲。
8.強烈建議使用TINYINT來代替ENUM類型。
ENUM類型在需要修改或增加枚舉值時,需要在線DDL,成本較高;ENUM列值如果含有數字類型,可能會引起默認值混淆。
9.使用VARBINARY存儲大小寫敏感的變長字符串或二進制內容。
VARBINARY默認區分大小寫,沒有字符集概念,速度快。
10.INT類型固定占用4字節存儲
例如INT(4)僅代表顯示字符寬度為4位,不代表存儲長度。數值類型括號后面的數字只是表示寬度而跟存儲范圍沒有關系,比如INT(3)默認顯示3位,空格補齊,超出時正常顯示,Python、java客戶端等不具備這個功能。
11.區分使用DATETIME和TIMESTAMP。
存儲年使用YEAR類型。存儲日期使用DATE類型。存儲時間(精確到秒)建議使用TIMESTAMP類型。
DATETIME和TIMESTAMP都是精確到秒,優先選擇TIMESTAMP,因為TIMESTAMP只有4個字節,而DATETIME8個字節。同時TIMESTAMP具有自動賦值以及?自動更新的特性。注意:在5.5和之前的版本中,如果一個表中有多個timestamp列,那么最多只能有一列能具有自動更新功能。
12.所有字段均定義為NOT NULL。
- 對表的每一行,每個為NULL的列都需要額外的空間來標識。
- B樹索引時不會存儲NULL值,所以如果索引字段可以為NULL,索引效率會下降。
- 建議用0、特殊值或空串代替NULL值。
2、SQL優化
1、當只要一行數據時使用LIMIT 1
2、為搜索字段建索引
3、在Join表的時候使用相當類型的列,并將其索引
4、千萬不要ORDER BY RAND()
5、SELECT只獲取必要的字段、避免SELECT *
6、用IN代替OR。SQL語句中IN包含的值不應過多,應少于1000個。
7、SQL中避免出現now()、rand()、sysdate()、current_user()等不確定結果的函數。
8、避免使用存儲過程、觸發器、視圖、自定義函數等。(這些高級特性有性能問題,以及未知BUG較多。業務邏輯放到數據庫會造成數據庫的DDL、SCALE OUT、SHARDING等變得更加困難。)
9、不要在MySQL數據庫中存放業務邏輯。
3、索引優化
MySQL的優化主要分為結構優化(Scheme optimization)和查詢優化(Query optimization)。
3.1、聯合索引及最左前綴原理
聯合索引(復合索引)
首先介紹一下聯合索引。聯合索引其實很簡單,相對于一般索引只有一個字段,聯合索引可以為多個字段創建一個索引。它的原理也很簡單,比如,我們在(a,b,c)字段上創建一個聯合索引,則索引記錄會首先按照A字段排序,然后再按照B字段排序然后再是C字段,因此,聯合索引的特點就是:
- 第一個字段一定是有序的
- 當第一個字段值相等的時候,第二個字段又是有序的,比如下表中當A=2時所有B的值是有序排列的,依次類推,當同一個B值得所有C字段是有序排列的、
| A | B | C |
| 1 | 2 | 3 |
| 1 | 4 | 2 |
| 1 | 1 | 4 |
| 2 | 3 | 5 |
| 2 | 4 | 4 |
| 2 | 4 | 6 |
| 2 | 5 | 5 |
其實聯合索引的查找就跟查字典是一樣的,先根據第一個字母查,然后再根據第二個字母查,或者只根據第一個字母查,但是不能跳過第一個字母從第二個字母開始查。這就是所謂的最左前綴原理。
最左前綴原理
我們再來詳細介紹一下聯合索引的查詢。還是上面例子,我們在(a,b,c)字段上建了一個聯合索引,所以這個索引是先按a 再按b 再按c進行排列的,所以:
以下的查詢方式都可以用到索引:
select * from table where a=1;
select * from table where a=1 and b=2;
select * from table where a=1 and b=2 and c=3;
上面三個查詢按照 (a ), (a,b ),(a,b,c )的順序都可以利用到索引,這就是最左前綴匹配。
如果查詢語句是:
select * from table where a=1 and c=3;那么只會用到索引a。
如果查詢語句是:
select * from table where b=2 and c=3;因為沒有用到最左前綴a,所以這個查詢是用不到索引的。
如果用到了最左前綴,但是順序顛倒會用到索引碼?
比如:
select * from table where b=2 and a=1; select * from table where b=2 and a=1 and c=3;
如果用到了最左前綴而只是顛倒了順序,也是可以用到索引的,因為mysql查詢優化器會判斷糾正這條sql語句該以什么樣的順序執行效率最高,最后才生成真正的執行計劃。但我們還是最好按照索引順序來查詢,這樣查詢優化器就不用重新編譯了。
3.2、索引優化策略
- 最左前綴匹配原則
- 主鍵外檢一定要建索引
- 對 where,on,group by,order by 中出現的列使用索引
- 盡量選擇區分度高的列作為索引,區分度的公式是count(distinct col)/count(*),表示字段不重復的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0
- 對較小的數據列使用索引,這樣會使索引文件更小,同時內存中也可以裝載更多的索引鍵
- 索引列不能參與計算,保持列“干凈”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’);
- 為較長的字符串使用前綴索引
- 盡量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那么只需要修改原來的索引即可
- 不要過多創建索引, 權衡索引個數與DML之間關系,DML也就是插入、刪除數據操作。這里需要權衡一個問題,建立索引的目的是為了提高查詢效率的,但建立的索引過多,會影響插入、刪除數據的速度,因為我們修改的表數據,索引也需要進行調整重建
- 對于like查詢,”%”不要放在前面。
- 查詢where條件數據類型不匹配也無法使用索引 ,字符串與數字比較不使用索引;
高并發情況下的解決方案
- 應用程序和靜態資源文件進行分離
- 頁面緩存
- 集群與分布式
- 反向代理
- CDN
1、應用程序和靜態資源文件進行分離
所謂的靜態資源就是我們網站中用到的Html、Css、Js、Image、Video、Gif等靜態資源。應用程序和靜態資源文件進行分離也是常見的前后端分離的解決方案,應用服務只提供相應的數據服務,靜態資源部署在指定的服務器上(Nginx服務器或者是CDN服務器上),前端界面通過Angular JS或者Node JS提供的路由技術訪問應用服務器的具體服務獲取相應的數據在前端游覽器上進行渲染。這樣可以在很大程度上減輕后端服務器的壓力。例如,百度主頁使用的圖片就是單獨的一個域名服務器上進行部署的
2、頁面緩存
頁面緩存是將應用生成的很少發生數據變化的頁面緩存起來,這樣就不需要每次都重新生成頁面了,從而節省大量CPU資源,如果將緩存的頁面放到內存中速度就更快。
可以使用Nginx提供的緩存功能,或者可以使用專門的頁面緩存服務器Squid。
3、集群與分布式
4、反向代理
5、CDN