索引是對數據庫表中一列或多列的值進行排序的一種結構。一個很是恰當的比喻就是書的目錄頁與書的正文內容之間的關係,爲了方便查找書中的內容,經過對內容創建索引造成目錄。索引是一個文件,它是要佔據物理空間的。mysql
主鍵索引: 數據列不容許重複,不容許爲NULL.一個表只能有一個主鍵。算法
惟一索引: 數據列不容許重複,容許爲NULL值,一個表容許多個列建立惟一索引。sql
能夠經過 ALTER TABLE table_name ADD UNIQUE (column);
建立惟一索引數據庫
能夠經過 ALTER TABLE table_name ADD UNIQUE (column1,column2);
建立惟一組合索引編程
普通索引: 基本的索引類型,沒有惟一性的限制,容許爲NULL值。服務器
能夠經過ALTER TABLE table_name ADD INDEX index_name (column);
建立普通索引併發
能夠經過ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);
建立組合索引分佈式
全文索引: 是目前搜索引擎使用的一種關鍵技術。函數
能夠經過ALTER TABLE table_name ADD FULLTEXT (column);
建立全文索引高併發
最左前綴
index(a,b,c) where a=3 只使用了a where a=3 and b=5 使用了a,b where a=3 and b=5 and c=4 使用了a,b,c where b=3 or where c=4 沒有使用索引 where a=3 and c=4 僅使用了a where a=3 and b>10 and c=7 使用了a,b where a=3 and b like 'xx%' and c=7 使用了a,b 複製代碼
索引算法有 BTree Hash
BTree是最經常使用的mysql數據庫索引算法,也是mysql默認的算法。由於它不只能夠被用在=,>,>=,<,<=和between這些比較操做符上,並且還能夠用於like操做符,只要它的查詢條件是一個不以通配符開頭的常量, 例如:
select * from user where name like 'jack%'; 若是一通配符開頭,或者沒有使用常量,則不會使用索引,例如: select * from user where name like '%jack'; 複製代碼
Hash Hash索引只能用於對等比較,例如=,<=>(至關於=)操做符。因爲是一次定位數據,不像BTree索引須要從根節點到枝節點,最後才能訪問到頁節點這樣屢次IO訪問,因此檢索效率遠高於BTree索引。
BTree索引是最經常使用的mysql數據庫索引算法,也是mysql默認的算法。由於它不只能夠被用在=,>,>=,<,<=和between這些比較操做符上,並且還能夠用於like操做符 例如:
只要它的查詢條件是一個不以通配符開頭的常量 select * from user where name like 'jack%'; 若是一通配符開頭,或者沒有使用常量,則不會使用索引,例如: select * from user where name like '%jack'; 複製代碼
Hash Hash索引只能用於對等比較,例如=,<=>(至關於=)操做符。因爲是一次定位數據,不像BTree索引須要從根節點到枝節點,最後才能訪問到頁節點這樣屢次IO訪問,因此檢索效率遠高於BTree索引。
對於低性能的SQL語句的定位,最重要也是最有效的方法就是使用執行計劃。 咱們知道,無論是哪一種數據庫,或者是哪一種數據庫引擎,在對一條SQL語句進行執行的過程當中都會作不少相關的優化,對於查詢語句,最重要的優化方式就是使用索引。 而執行計劃,就是顯示數據庫引擎對於SQL語句的執行的詳細狀況,其中包含了是否使用索引,使用什麼索引,使用的索引的相關信息等。
執行計劃包含的信息 id 有一組數字組成。表示一個查詢中各個子查詢的執行順序;
select_type 每一個子查詢的查詢類型,一些常見的查詢類型。
id | select_type | description |
---|---|---|
1 | SIMPLE | 不包含任何子查詢或union等查詢 |
2 | PRIMARY | 包含子查詢最外層查詢就顯示爲 PRIMARY |
3 | SUBQUERY | 在select或 where字句中包含的查詢 |
4 | DERIVED | from字句中包含的查詢 |
5 | UNION | 出如今union後的查詢語句中 |
6 | UNION RESULT | 從UNION中獲取結果集,例如上文的第三個例子 |
table 查詢的數據表,當從衍生表中查數據時會顯示 x 表示對應的執行計劃id partitions 表分區、表建立的時候能夠指定經過那個列進行表分區。 舉個例子:
create table tmp ( id int unsigned not null AUTO_INCREMENT, name varchar(255), PRIMARY KEY (id) ) engine = innodb partition by key (id) partitions 5; 複製代碼
type(很是重要,能夠看到有沒有走索引) 訪問類型
possible_keys 可能使用的索引,注意不必定會使用。查詢涉及到的字段上若存在索引,則該索引將被列出來。當該列爲 NULL時就要考慮當前的SQL是否須要優化了。
key 顯示MySQL在查詢中實際使用的索引,若沒有使用索引,顯示爲NULL。
TIPS:查詢中若使用了覆蓋索引(覆蓋索引:索引的數據覆蓋了須要查詢的全部數據),則該索引僅出如今key列表中
key_length 索引長度
ref 表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值
rows 返回估算的結果集數目,並非一個準確的值。
extra 的信息很是豐富,常見的有:
數據千萬級別之多,佔用的存儲空間也比較大,可想而知它不會存儲在一塊連續的物理空間上,而是鏈式存儲在多個碎片的物理空間上。可能對於長字符串的比較,就用更多的時間查找與比較,這就致使用更多的時間。
主要兩種拆分 垂直拆分,水平拆分。
垂直分表
也就是「大表拆小表」,基於列字段進行的。通常是表中的字段較多,將不經常使用的, 數據較大,長度較長(好比text類型字段)的拆分到「擴展表「。 通常是針對那種幾百列的大表,也避免查詢時,數據量太大形成的「跨頁」問題。
垂直分庫針對的是一個系統中的不一樣業務進行拆分,好比用戶User一個庫,商品Producet一個庫,訂單Order一個庫。 切分後,要放在多個服務器上,而不是一個服務器上。爲何? 咱們想象一下,一個購物網站對外提供服務,會有用戶,商品,訂單等的CRUD。沒拆分以前, 所有都是落到單一的庫上的,這會讓數據庫的單庫處理能力成爲瓶頸。按垂直分庫後,若是仍是放在一個數據庫服務器上, 隨着用戶量增大,這會讓單個數據庫的處理能力成爲瓶頸,還有單個服務器的磁盤空間,內存,tps等很是吃緊。 因此咱們要拆分到多個服務器上,這樣上面的問題都解決了,之後也不會面對單機資源問題。
數據庫業務層面的拆分,和服務的「治理」,「降級」機制相似,也能對不一樣業務的數據分別的進行管理,維護,監控,擴展等。 數據庫每每最容易成爲應用系統的瓶頸,而數據庫自己屬於「有狀態」的,相對於Web和應用服務器來說,是比較難實現「橫向擴展」的。 數據庫的鏈接資源比較寶貴且單機處理能力也有限,在高併發場景下,垂直分庫必定程度上可以突破IO、鏈接數及單機硬件資源的瓶頸。
水平分表
針對數據量巨大的單張表(好比訂單表),按照某種規則(RANGE,HASH取模等),切分到多張表裏面去。 可是這些表仍是在同一個庫中,因此庫級別的數據庫操做仍是有IO瓶頸。不建議採用。
水平分庫分表
將單張表的數據切分到多個服務器上去,每一個服務器具備相應的庫與表,只是表中數據集合不一樣。 水平分庫分表可以有效的緩解單機和單庫的性能瓶頸和壓力,突破IO、鏈接數、硬件資源等的瓶頸。
水平分庫分表切分規則
分庫分表後面臨的問題
事務支持 分庫分表後,就成了分佈式事務了。若是依賴數據庫自己的分佈式事務管理功能去執行事務,將付出高昂的性能代價; 若是由應用程序去協助控制,造成程序邏輯上的事務,又會形成編程方面的負擔。
跨庫join
只要是進行切分,跨節點Join的問題是不可避免的。可是良好的設計和切分卻能夠減小此類狀況的發生。解決這一問題的廣泛作法是分兩次查詢實現。在第一次查詢的結果集中找出關聯數據的id,根據這些id發起第二次請求獲得關聯數據。 分庫分表方案產品
跨節點的count,order by,group by以及聚合函數問題 這些是一類問題,由於它們都須要基於所有數據集合進行計算。多數的代理都不會自動處理合並工做。解決方案:與解決跨節點join問題的相似,分別在各個節點上獲得結果後在應用程序端進行合併。和join不一樣的是每一個結點的查詢能夠並行執行,所以不少時候它的速度要比單一大表快不少。但若是結果集很大,對應用程序內存的消耗是一個問題。
數據遷移,容量規劃,擴容等問題 來自淘寶綜合業務平臺團隊,它利用對2的倍數取餘具備向前兼容的特性(如對4取餘得1的數對2取餘也是1)來分配數據,避免了行級別的數據遷移,可是依然須要進行表級別的遷移,同時對擴容規模和分表數量都有限制。總得來講,這些方案都不是十分的理想,多多少少都存在一些缺點,這也從一個側面反映出了Sharding擴容的難度。
ID問題
一旦數據庫被切分到多個物理結點上,咱們將不能再依賴數據庫自身的主鍵生成機制。一方面,某個分區數據庫自生成的ID沒法保證在全局上是惟一的;另外一方面,應用程序在插入數據以前須要先得到ID,以便進行SQL路由. 一些常見的主鍵生成策略
UUID 使用UUID做主鍵是最簡單的方案,可是缺點也是很是明顯的。因爲UUID很是的長,除佔用大量存儲空間外,最主要的問題是在索引上,在創建索引和基於索引進行查詢時都存在性能問題。 Twitter的分佈式自增ID算法Snowflake 在分佈式系統中,須要生成全局UID的場合仍是比較多的,twitter的snowflake解決了這種需求,實現也仍是很簡單的,除去配置信息,核心代碼就是毫秒級時間41位 機器ID 10位 毫秒內序列12位。
mysql中的in語句是把外表和內表做hash 鏈接,而exists語句是對外表做loop循環,每次loop循環再對內表進行查詢。一直你們都認爲exists比in語句的效率要高,這種說法實際上是不許確的。這個是要區分環境的。