MySQL 性能調優

 

MySQL 性能調優

 

索引

索引是什麼

官方介紹索引是幫助MySQL高效獲取數據的數據結構。
筆者理解索引至關於一本書的目錄,經過目錄就知道要的資料在哪裏,不用一頁一頁查閱找出須要的資料。javascript

索引目的

索引的目的在於提升查詢效率,能夠類比字典,若是要查「mysql」這個單詞,咱們確定須要定位到m字母,而後從下往下找到y字母,再找到剩下的sql。
若是沒有索引,那麼你可能須要把全部單詞看一遍才能找到你想要的,若是我想找到m開頭的單詞呢?或者ze開頭的單詞呢?是否是以爲若是沒有索引,這個事情根本沒法完成?php


索引原理

除了詞典,生活中隨處可見索引的例子,如火車站的車次表、圖書的目錄等。
它們的原理都是同樣的,經過不斷的縮小想要得到數據的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是咱們老是經過同一種查找方式來鎖定數據。
數據庫也是同樣,但顯然要複雜許多,由於不只面臨着等值查詢,還有範圍查詢(>、<、between、in)、模糊查詢(like)、並集查詢(or)等等。
數據庫應該選擇怎麼樣的方式來應對全部的問題呢?css

咱們回想字典的例子,能不能把數據分紅段,而後分段查詢呢?
最簡單的若是1000條數據,1到100分紅第一段,101到200分紅第二段,201到300分紅第三段……這樣查第250條數據,只要找第三段就能夠了,一會兒去除了90%的無效數據。
但若是是1千萬的記錄呢,分紅幾段比較好?稍有算法基礎的同窗會想到搜索樹,其平均複雜度是lgN,具備不錯的查詢性能。
但這裏咱們忽略了一個關鍵的問題,複雜度模型是基於每次相同的操做成原本考慮的,
數據庫實現比較複雜,數據保存在磁盤上,而爲了提升性能,每次又能夠把部分數據讀入內存來計算,
由於咱們知道訪問磁盤的成本大概是訪問內存的十萬倍左右,因此簡單的搜索樹難以知足複雜的應用場景。html

磁盤IO與預讀

前面提到了訪問磁盤,那麼這裏先簡單介紹一下磁盤IO和預讀,
磁盤讀取數據靠的是機械運動,每次讀取數據花費的時間能夠分爲尋道時間、旋轉延遲、傳輸時間三個部分,尋道時間指的是磁臂移動到指定磁道所須要的時間,主流磁盤通常在5ms如下;
旋轉延遲就是咱們常常據說的磁盤轉速,好比一個磁盤7200轉,表示每分鐘能轉7200次,也就是說1秒鐘能轉120次,旋轉延遲就是1/120/2 = 4.17ms;
傳輸時間指的是從磁盤讀出或將數據寫入磁盤的時間,通常在零點幾毫秒,相對於前兩個時間能夠忽略不計。
那麼訪問一次磁盤的時間,即一次磁盤IO的時間約等於5+4.17 = 9ms左右,聽起來還挺不錯的,
但要知道一臺500 -MIPS的機器每秒能夠執行5億條指令,由於指令依靠的是電的性質,
換句話說執行一次IO的時間能夠執行40萬條指令,數據庫動輒十萬百萬乃至千萬級數據,每次9毫秒的時間,顯然是個災難。
下圖是計算機硬件延遲的對比圖,供你們參考:java

 

考慮到磁盤IO是很是高昂的操做,計算機操做系統作了一些優化,
當一次IO時,不光把當前磁盤地址的數據,而是把相鄰的數據也都讀取到內存緩衝區內,
由於局部預讀性原理告訴咱們,當計算機訪問一個地址的數據的時候,與其相鄰的數據也會很快被訪問到。
每一次IO讀取的數據咱們稱之爲一頁(page)。
具體一頁有多大數據跟操做系統有關,通常爲4k或8k,也就是咱們讀取一頁內的數據時候,實際上才發生了一次IO,這個理論對於索引的數據結構設計很是有幫助。mysql

索引的數據結構

前面講了生活中索引的例子,索引的基本原理,數據庫的複雜性,又講了操做系統的相關知識,目的就是讓你們瞭解,任何一種數據結構都不是憑空產生的,必定會有它的背景和使用場景。
咱們如今總結一下,咱們須要這種數據結構可以作些什麼,其實很簡單,那就是:每次查找數據時把磁盤IO次數控制在一個很小的數量級,最好是常數數量級。那麼咱們就想到若是一個高度可控的多路搜索樹是否能知足需求呢?就這樣,b+樹應運而生。git


詳解b+樹

如上圖,是一顆b+樹,關於b+樹的定義能夠參見B+樹
這裏只說一些重點,淺藍色的塊咱們稱之爲一個磁盤塊,能夠看到每一個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示),
如磁盤塊1包含數據項17和35,包含指針P一、P二、P3,P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。
真實的數據存在於葉子節點即三、五、九、十、1三、1五、2八、2九、3六、60、7五、7九、90、99。
非葉子節點只不存儲真實的數據,只存儲指引搜索方向的數據項,
如1七、35並不真實存在於數據表中。github

b+樹的查找過程

如圖所示,若是要查找數據項29,
那麼首先會把磁盤塊1由磁盤加載到內存,此時發生一次IO,
在內存中用二分查找肯定29在17和35之間,鎖定磁盤塊1的P2指針,內存時間由於很是短(相比磁盤的IO)能夠忽略不計,
經過磁盤塊1的P2指針的磁盤地址把磁盤塊3由磁盤加載到內存,發生第二次IO,
29在26和30之間,鎖定磁盤塊3的P2指針,經過指針加載磁盤塊8到內存,發生第三次IO,
同時內存中作二分查找找到29,結束查詢,總計三次IO。算法

真實的狀況是,3層的b+樹能夠表示上百萬的數據,若是上百萬的數據查找只須要三次IO,性能提升將是巨大的,
若是沒有索引,每一個數據項都要發生一次IO,那麼總共須要百萬次的IO,顯然成本很是很是高。sql

b+樹性質

經過上面的分析,咱們知道IO次數取決於b+數的高度h,假設當前數據表的數據爲N,每一個磁盤塊的數據項的數量是m,則有h=㏒(m+1)N,當數據量N必定的狀況下,m越大,h越小;
而m = 磁盤塊的大小 / 數據項的大小,磁盤塊的大小也就是一個數據頁的大小,是固定的,若是數據項佔的空間越小,數據項的數量越多,樹的高度越低。
這就是爲何每一個數據項,即索引字段要儘可能的小,好比int佔4字節,要比bigint8字節少一半。
這也是爲何b+樹要求把真實的數據放到葉子節點而不是內層節點,一旦放到內層節點,磁盤塊的數據項會大幅度降低,致使樹增高。
當數據項等於1時將會退化成線性表。

當b+樹的數據項是複合的數據結構,好比(name,age,sex)的時候,
b+數是按照從左到右的順序來創建搜索樹的,
好比當(張三,20,F)這樣的數據來檢索的時候,
b+樹會優先比較name來肯定下一步的所搜方向,若是name相同再依次比較age和sex,最後獲得檢索的數據;
但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪一個節點,
由於創建搜索樹的時候name就是第一個比較因子,必需要先根據name來搜索才能知道下一步去哪裏查詢。
好比當(張三,F)這樣的數據來檢索時,b+樹能夠用name來指定搜索方向,
但下一個字段age的缺失,因此只能把名字等於張三的數據都找到,而後再匹配性別是F的數據了,
這個是很是重要的性質,即索引的最左匹配特性。


索引是否是越多越好?

索引可以極大的提升數據檢索效率,也可以改善排序分組操做的性能,可是咱們不能忽略的一個問題就是索引是徹底獨立於基礎數據以外的一部分數據。
假設咱們在更新表中有字段的同時,也更新索引數據,調整由於更新所帶來鍵值變化後的索引信息。
而若是咱們沒有對字段進行索引的話,MySQL 所須要作的僅僅只是更新表中字段 的信息。
這樣,所帶來的最明顯的資源消耗就是增長了更新所帶來的IO量和調整索引所致的計算量。

此外,索引是須要佔用存儲空間的,並且隨着數據量的增加,所佔用的空間也會不斷增加。
因此索引還會帶來存儲空間資源消耗的增加。


什麼場景應該加索引?加索引的四個原則

1. 較頻繁的做爲查詢條件的字段應該建立索引

提升數據查詢檢索的效率最有效的辦法就是減小須要訪問的數據量,從上面所瞭解到的索引的益處中咱們知道了,索引正是咱們減小經過索引鍵字段做爲查詢條件的Query 的IO 量的最有效手段。因此通常來講咱們應該爲較爲頻繁的查詢條件字段建立索引。

2. 惟一性太差的字段不適合單首創建索引,即便頻繁做爲查詢條件

惟一性太差的字段主要是指哪些呢?
如狀態字段,類型字段等等,這些字段中存方的數據可能總共就是那麼幾個幾十個值重複使用,每一個值都會存在於成千上萬或是更多的記錄中。
對於這類字段,咱們徹底沒有必要建立單獨的索引的。
由於即便咱們建立了索引,MySQL Query Optimizer 大多數時候也不會去選擇使用,
若是何時MySQL Query Optimizer 抽了一下風選擇了這種索引,那麼很是遺憾的告訴你,這可能會帶來極大的性能問題。
因爲索引字段中每一個值都含有大量的記錄,那麼存儲引擎在根據索引訪問數據的時候會帶來大量的隨機IO,甚至有些時候可能還會出現大量的重複IO。

3. 更新很是頻繁的字段不適合建立索引

上面在索引的弊端中咱們已經分析過了,索引中的字段被更新的時候,不只僅須要更新表中的數據,同時還要更新索引數據,以確保索引信息是準確的。
這個問題所帶來的是IO 訪問量的較大增長,不只僅影響更新Query 的響應時間,還會影響整個存儲系統的資源消耗,加大整個存儲系統的負載。

4. 不會出如今WHERE子句中的字段不建立索引

查詢時,不會命中索引。那麼索引就沒有存在的意義了。

建立索引的舉例說明

CREATE TABLE `v9_member_menu` (
  `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, # 主鍵標識 `name` char(40) NOT NULL DEFAULT '', # 菜單名稱 `parentid` smallint(6) NOT NULL DEFAULT '0', # 父級ID `m` char(20) NOT NULL DEFAULT '', # 模型名稱 `c` char(20) NOT NULL DEFAULT '', # 控制器名 `a` char(20) NOT NULL DEFAULT '', # 方法名 `data` char(100) NOT NULL DEFAULT '', # 附加數據 `listorder` smallint(6) unsigned NOT NULL DEFAULT '0',# 排序值 `display` enum('1','0') NOT NULL DEFAULT '1', # 是否顯示 `isurl` enum('1','0') NOT NULL DEFAULT '0', # 是不是一個連接 `url` char(255) NOT NULL DEFAULT '', # 連接地址 PRIMARY KEY (`id`), KEY `listorder` (`listorder`), KEY `parentid` (`parentid`), KEY `module` (`m`,`c`,`a`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; 

關於 菜單的使用場景, 我作出以下整理

  1. 會根據 url分割出 m,c,a 而後進行查詢菜單ID,再關聯權限表,查詢是否有權限。
  2. 根據 菜單ID 獲取菜單信息,例如 刪,改,查的應用場景
  3. 會根據菜單的父級ID 查詢父級信息, 或者同本身的ID 查詢子級信息。
  4. 顯示菜單時,一般會進行排序。
  • 第一個狀況 就符合 ,建立複合索引的條件,在where中常常會一塊兒出現,
    例如 m=home and c=index and a=login

  • 第二個狀況 可使用主鍵索引,主鍵自己就自帶索引屬性。

  • 第三個狀況,在查詢子級時 一般會使用到。

  • 第四個狀況: 排序也常用到。

  • data 和 url 爲什麼不加索引?
    data 和 url 屬於詳細內容, 通常只用於展現,不會加入到where條件查詢中,因此不須要加索引。

  • display 和 isurl 爲什麼不加索引
    display 和 isurl 同樣 他的數值很單一,不是1就是0,不必加索引,並且符合條件的數據有不少,給mysql帶來大量的隨機IO。


索引的類型

聚簇索引和非聚簇索引

索引分爲聚簇索引和非聚簇索引兩種,聚簇索引是按照數據存放的物理位置爲順序的,而非聚簇索引就不同了;
聚簇索引能提升多行檢索的速度,而非聚簇索引對於單行的檢索很快。

聚簇索引是一種數據存儲方式,它其實是在同一個結構中保存了B+樹索引和數據行,InnoDB表是按照聚簇索引組織的(相似於Oracle的索引組織表)。
InnoDB經過主鍵聚簇數據,若是沒有定義主鍵,會選擇一個惟一的非空索引代替,若是沒有這樣的索引,會隱式定義個主鍵做爲聚簇索引。

下圖形象說明了聚簇索引表(InnoDB)和非聚簇索引(MyISAM)的區別:

 

對於非聚簇索引表來講(右圖),表數據和索引是分紅存儲的,主鍵索引和二級索引存儲上沒有任何區別。

而對於聚簇索引表來講(左圖),表數據是和主鍵一塊兒存儲的,主鍵索引的葉結點存儲行數據,二級索引的葉結點存儲行的主鍵值。

聚簇索引表最大限度地提升了I/O密集型應用的性能,但它也有如下幾個限制:

1)插入速度嚴重依賴於插入順序,按照主鍵的順序插入是最快的方式,不然將會出現頁分裂,嚴重影響性能。
所以,對於InnoDB表,咱們通常都會定義一個自增的ID列爲主鍵。

2)更新主鍵的代價很高,由於將會致使被更新的行移動。所以,對於InnoDB表,咱們通常定義主鍵爲不可更新。

3)二級索引訪問須要兩次索引查找,第一次找到主鍵值,第二次根據主鍵值找到行數據。
二級索引的葉節點存儲的是主鍵值,而不是行指針(非聚簇索引存儲的是指針或者說是地址),這是爲了減小當出現行移動或數據頁分裂時二級索引的維護工做,但會讓二級索引佔用更多的空間。

聚簇索引的葉節點就是數據節點,而非聚簇索引的頁節點仍然是索引檢點,並保留一個連接指向對應數據塊。
**聚簇索引主鍵的插入速度要比非聚簇索引主鍵的插入速度慢不少。 **

相比之下,聚簇索引適合排序,非聚簇索引不適合用在排序的場合。
由於聚簇索引自己已是按照物理順序放置的,排序很快。
非聚簇索引則沒有按序存放,須要額外消耗資源來排序。
當你須要取出必定範圍內的數據時,用聚簇索引也比用非聚簇索引好。


主鍵索引(PRIMARY KEY )

主鍵自帶索引屬性。 無論是 修改查詢刪除 基本都會用到它。


普通索引(Normal)

這是最基本的索引,它沒有任何限制,好比上文中爲listorder字段建立的索引就是一個普通索引,MyIASM中默認的BTREE類型的索引,也是咱們大多數狀況下用到的索引。

實例

–直接建立索引
CREATE INDEX index_name ON table(column(length))

–修改表結構的方式添加索引
ALTER TABLE table_name ADD INDEX index_name ON (column(length))

–建立表的時候同時建立索引
CREATE TABLE `table` (

    `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL , `time` int(10) NULL DEFAULT NULL , PRIMARY KEY (`id`), INDEX index_name (title(length)) ) –刪除索引 DROP INDEX index_name ON table 

惟一索引(Unique)

與普通索引相似,不一樣的就是:索引列的值必須惟一,但容許有空值(注意和主鍵不一樣)。
若是是組合索引,則列值的組合必須惟一,建立方法和普通索引相似。

例如:用戶表的 用戶名 和 郵箱 均可以進行惟一索引

實例
–建立惟一索引
CREATE UNIQUE INDEX indexName ON table(column(length))

–修改表結構
ALTER TABLE table_name ADD UNIQUE indexName ON (column(length))

–建立表的時候直接指定
CREATE TABLE `table` (

    `id` int(11) NOT NULL AUTO_INCREMENT , `title` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL , `time` int(10) NULL DEFAULT NULL , PRIMARY KEY (`id`), UNIQUE indexName (title(length)) ); 

全文索引(Full Text)

MySQL從3.23.23版開始支持全文索引和全文檢索,FULLTEXT索引僅可用於 MyISAM 表;
他們能夠從CHAR、VARCHAR或TEXT列中做爲CREATE TABLE語句的一部分被建立,
或是隨後使用ALTER TABLE 或CREATE INDEX被添加。

對於較大的數據集,將你的資料輸入一個沒有FULLTEXT索引的表中,而後建立索引,其速度比把資料輸入現有FULLTEXT索引的速度更爲快。
不過切記對於大容量的數據表,生成全文索引是一個很是消耗時間很是消耗硬盤空間的作法。

在數據量不是很大的狀況下 能夠利用 全文索引作 站內搜索。
可是得先分詞,才能進行全文檢索。檢索時 是經過 空格來分割詞彙。
最好是 新建一個關聯表(其中 存儲分詞的字段 用全文索引),把分詞後的內容 用 空格分割 存儲到 關聯表,而後對應原始表。

查詢流程以下

  1. 查詢關聯表
  2. 獲取全部能查到的 文章ID
  3. 根據文章ID 獲取文章數據

也能夠配合第三方的檢索插件 來進行全文檢索
packagist.org 搜索中文分詞

小項目可使用 結巴分詞


單列索引 和 複合索引

多個單列索引與單個多列索引的查詢效果不一樣,由於執行查詢時,MySQL只能使用一個索引,會從多個索引中選擇一個限制最爲嚴格的索引。
即 mysql 底層本身會判斷 使用那個索引 速度會更快


組合索引(最左前綴)

平時用的SQL查詢語句通常都有比較多的限制條件,因此爲了進一步榨取MySQL的效率,就要考慮創建組合索引。
例如上表中針對title和time創建一個組合索引:

ALTER TABLE article ADD INDEX index_titme_time (title(50),time(10))

創建這樣的組合索引,實際上是至關於分別創建了下面兩組組合索引:
–title,time
–title

爲何沒有time這樣的組合索引呢?這是由於MySQL組合索引「最左前綴」的結果。
簡單的理解就是隻從最左面的開始組合。
並非只要包含這兩列的查詢都會用到該組合索引,以下面的幾個SQL所示:

–使用到上面的索引
SELECT * FROM article WHREE title='測試' AND time=1234567890; SELECT * FROM article WHREE utitle='測試'; –不使用上面的索引 SELECT * FROM article WHREE time=1234567890; 

自創索引表

若是又須要能夠自創 索引表(關聯表)。

例如 如今有一個文章表, 須要作一個文章的站內搜索
那麼 咱們須要新建一個文章表

CREATE TABLE `article` (
  `id` int(11) unsigned NOT NULL COMMENT '主鍵', `title` varchar(255) NOT NULL COMMENT '標題', `author` varchar(255) NOT NULL DEFAULT '' COMMENT '做者', `content` text NOT NULL COMMENT '內容', `create_time` int(11) unsigned NOT NULL COMMENT '建立時間', `update_time` int(11) unsigned DEFAULT NULL COMMENT '修改時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

建立一個 分詞索引表

CREATE TABLE `article_participle` ( `id` int(11) NOT NULL, `article_id` int(11) unsigned NOT NULL COMMENT '文章表ID ', `participle` varchar(1000) NOT NULL COMMENT '關鍵詞 以空格分隔', PRIMARY KEY (`id`), UNIQUE KEY `article_id` (`article_id`) USING BTREE COMMENT '文章ID', FULLTEXT KEY `participle` (`participle`) COMMENT '中文分詞存儲' ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

先根據 搜索的關鍵詞 搜索 分詞索引表
而後在根據搜索出的結果 (article_id 文章ID) 搜索文章表


索引方法

BTree 索引特徵

BTree索引能夠被用在像=,>,>=,<,<=和BETWEEN這些比較操做符上。並且還能夠用於LIKE操做符,只要它的查詢條件是一個不以通配符開頭的常量。像下面的語句就可使用索引:

SELECT * FROM tbl_name WHERE key_col LIKE 'Patrick%'; SELECT * FROM tbl_name WHERE key_col LIKE 'Pat%_ck%'; 

下面這兩種狀況不會使用索引:

SELECT * FROM tbl_name WHERE key_col LIKE '%Patrick%'; SELECT * FROM tbl_name WHERE key_col LIKE other_col; 

第一條是由於它以通配符開頭,第二條是由於沒有使用常量。

假如你使用... LIKE '%string%'並且string超過三個字符,MYSQL使用Turbo Boyer-Moore algorithm算法來初始化查詢表達式,而後用這個表達式來讓查詢更迅速。

一個這樣的查詢col_name IS NULL是可使用col_name的索引的。

任何一個沒有覆蓋全部WHERE中AND級別條件的索引是不會被使用的。也就是說,要使用一個索引,這個索引中的第一列須要在每一個AND組中出現。

下面的WHERE條件會使用索引:

... WHERE index_part1=1 AND index_part2=2 AND other_column=3 /* index = 1 OR index = 2 */ ... WHERE index=1 OR A=10 AND index=2 /* 優化成 "index_part1='hello'" */ ... WHERE index_part1='hello' AND index_part3=5 /* 可使用 index1 的索引可是不會使用 index2 和 index3 */ ... WHERE index1=1 AND index2=2 OR index1=3 AND index3=3; 

下面的WHERE條件不會使用索引:

/* index_part1 沒有被使用到 */ ... WHERE index_part2=1 AND index_part3=2 /* 索引 index 沒有出如今每一個 where 子句中 */ ... WHERE index=1 OR A=10 /* 沒有索引覆蓋全部列 */ ... WHERE index_part1=1 OR index_part2=10 

有時候mysql不會使用索引,即便這個在可用的狀況下。
例如當mysql預估使用索引會讀取大部分的行數據時。(在這種狀況下,一次全表掃描可能比使用索引更快,由於它須要更少的檢索)。
然而,假如語句中使用LIMIT來限定返回的行數,mysql則會使用索引。
由於當結果行數較少的狀況下使用索引的效率會更高。

位圖索引 (HASH)

Hash類型的索引有一些區別於以上所述的特徵:

1.相對於BTree索引,佔用的空間很是小,建立和使用很是快。
位圖索引因爲只存儲鍵值的起止Rowid和位圖,佔用的空間很是少。

2.不適合鍵值較多的列。

3.不適合update、insert、delete頻繁的列。

4.能夠存儲null值。
BTree索引因爲不記錄空值,當基於is null的查詢時,會使用全表掃描。
而對位圖索引列進行is null查詢時,則可使用索引。

5.當select count(XX) 時,能夠直接訪問索引中一個位圖就快速得出統計數據。

6.當根據鍵值作and,or或 in(x,y,..)查詢時,直接用索引的位圖進行或運算,快速得出結果行數據統計。

7.它們只能用於對等比較,例如=和<=>操做符(可是快不少)。它們不能被用於像<這樣的範圍查詢條件。假如系統只須要使用像「鍵值對」的這樣的存儲結構,儘可能使用hash類型索引。

8.優化器不能用hash索引來爲ORDER BY操做符加速。(這類索引不能被用於搜索下一個次序的值)

9.mysql不能判斷出兩個值之間有多少條數據(這須要使用範圍查詢操做符來決定使用哪一個索引)。假如你將一個MyISAM錶轉爲一個依靠hash索引的MEMORY表,可能會影響一些語句(的性能)。

10.只有完整的鍵才能被用於搜索一行數據。(假如用B-tree索引,任何一個鍵的片斷均可以用於查找)。


去索引化

爲了更好的提升併發量,又產生了另外一個思想!去索引化。
去索引化,並非真正的去掉索引。只是經過異步操做把索引 像關係表那樣存起來。
這樣能夠提高,高併發寫入的性能,又能夠提高數據查詢的性能。


SQL語句優化

常常用到的須要條件字段 須要創建索引
避免在 where 子句中對字段進行 null 值判斷(全表掃描)
避免 !=或<>操做
避免 in 和 not in 可用(between)
避免 '%c%' 考慮使用全文檢索。
避免使用 參數,子句,函數操做
避免表達式操做 如 num/2=100; 優化後 num=100*2;
查詢時 把條件中 有索引的 放在最左邊 (最左前綴)
exists 代替 in


分佈式架構 和集羣架構的區別

簡單說,分佈式是以縮短單個任務的執行時間來提高效率的,而集羣則是經過提升單位時間內執行的任務數來提高效率。

例如:
若是一個任務由10個子任務組成,每一個子任務單獨執行需1小時,則在一臺服務器上執行改任務需10小時。

採用分佈式方案,提供10臺服務器,每臺服務器只負責處理一個子任務,不考慮子任務間的依賴關係,執行完這個任務只需一個小時。(這種工做模式的一個典型表明就是Hadoop的Map/Reduce分佈式計算模型)

而採用集羣方案,一樣提供10臺服務器,每臺服務器都能獨立處理這個任務。假設有10個任務同時到達,10個服務器將同時工做,10小後,10個任務同時完成,這樣,整身來看,仍是1小時內完成一個任務!


分表

爲何要分表?

數據庫中的數據量不必定是可控的,在未進行分庫分表的狀況下,隨着時間和業務的發展,庫中的表會愈來愈多,表中的數據量也會愈來愈大,相應地,數據操做,增刪改查的開銷也會愈來愈大;

另外,因爲沒法進行分佈式式部署,而一臺服務器的資源(CPU、磁盤、內存、IO等)是有限的,最終數據庫所能承載的數據量、數據處理能力都將遭遇瓶頸。

分表的方式?

水平切分(橫向切分)
垂直切分(縱向切分)
聯合切分(橫向切分 和縱向切分)


垂直分表

何爲垂直分表?

即將表按照功能模塊、關係密切程度劃分出來,部署到不一樣的數據表上。
好比user(用戶表 主要存用戶名 和密碼)表和user_details(用戶詳情 頭像,地址等)表。
好比博客表中的title和content表。(大字段 拆到另一個表裏)

大字段垂直切分

什麼樣的字段適合於從表中拆分:
首先要確定是大字段。爲何?緣由很簡單,就是由於他的大。
大字段通常都是存放着一些較長的Detail 信息,如文章的內容,帖子的內容,產品的介紹等等。

其次是和表中其餘字段相比訪問頻率明顯要少不少。
若是咱們要查詢某些記錄的某幾個字段,數據庫並非只須要訪問咱們須要查詢的哪幾個字段,而是須要讀取其餘全部字段這樣,咱們就不得不讀取包括大字段在內的不少並不相干的數據。
而因爲大字段所佔的空間比例很是大,天然所浪費的IO 資源也就很是之大了。

實際上,在有些時候,咱們甚至都不必定非要大字段才能進行垂直分拆。
在有些場景下,有的表中大部分字段平時都不多訪問,而其中的某幾個字段倒是訪問頻率很是高。
對於這種表,也很是適合經過垂直分拆來達到優化性能的目的。

垂直切分的優勢

  • 數據庫的拆分簡單明瞭,拆分規則明
  • 應用程序模塊清晰明確,整合容易
  • 數據維護方便易行,容易定位

垂直切分的缺點

  • 部分表關聯沒法在數據庫級別完成,須要在程序中完成
  • 對於訪問極其頻繁且數據量超大的表仍然存在性能瓶頸,不必定能知足要求
  • 事務處理相對更爲複雜
  • 切分達到必定程度以後,擴展性會遇到限制
  • 過分切分可能會帶來系統過渡複雜而難以維護

水平分表

何爲水平切分?

當一個表中的數據量過大時,咱們能夠把該表的數據按照某種規則,進行劃分,而後存儲到多個結構相同的表,和不一樣的庫上。
依據的條件能夠是時間、地域、功能等比較清晰的條件

好比財務報表、薪資發放就能夠用時間進行水平分割;

好比商品庫存就能夠用地域進行分割

好比用戶表的普通用戶、商戶就能夠用功能來進行劃分

水平通用分表策略

以uuid做爲全局惟一標識,爲每個新生成的用戶生成uuid
將uuid進行md5加密,生成16進制隨機字符串,取隨機字符串前兩位進行10進制轉換,對分表數量的取餘,獲取插入的表後綴名。
好比創建8張表,對8取餘,則會生成user_0...user_7,每一個用戶會隨機插入這8張表中

分表後,如何統計數據?

id uuid addtime

全部統計數據都是根據業務需求而來的,原始數據存在的狀況,咱們能夠進行自建索引,實現具體的業務需求。
好比根據添加時間自建索引,其結構以下:

id uuid addtime

那麼根據addtime 咱們就能夠得出總數,最新個數。

分表後查詢效率的問題?

根據自建索引表,獲取uuid,再根據uuid獲取數據每一行的數據。   
只不過多了一個10次的for循環而已,而php的10for循環能夠說是微秒級的。 結果集存儲的是指針 在經過 mysql_fetch_row()讀取磁盤文件 

水平切分的優勢

  • 表關聯基本可以在數據庫端所有完成
  • 不會存在某些超大型數據量和高負載的表遇到瓶頸的問題
  • 應用程序端總體架構改動相對較少
  • 事務處理相對簡單
  • 只要切分規則可以定義好,基本上較難遇到擴展性限制

水平切分的缺點

  • 切分規則相對更爲複雜,很難抽象出一個可以知足整個數據庫的切分規則
  • 後期數據的維護難度有所增長,人爲手工定位數據更困難
  • 應用系統各模塊耦合度較高,可能會對後面數據的遷移拆分形成必定的困難

垂直與水平聯合切分的使用 (聯合切分)

若是大部分業務邏輯稍微複雜一點,系統負載大一些的系統,
都沒法經過上面任何一種數據的切分方法來實現較好的擴展性,
而須要將上述兩種切分方法結合使用,不一樣的場景使用不一樣的切分方法。

 

聯合切分的優勢

  • 能夠充分利用垂直切分和水平切分各自的優點而避免各自的缺陷
  • 讓系統擴展性獲得最大化提高

聯合切分的缺點

  • 數據庫系統架構比較複雜,維護難度更大;
  • 應用程序架構也相對更復雜;

MySQL 數據庫鎖定機制

數據庫鎖定機制簡單來講就是數據庫爲了保證數據的一致性而使各類共享資源在被併發訪問訪問變得有序所設計的一種規則。對於任何一種數據庫來講都須要有相應的鎖定機制。

爲了保證數據的一致完整性,任何一個數據庫都存在鎖定機制。鎖定機制的優劣直接影響到一個數據庫系統的併發處理能力和性能,因此鎖定機制的實現也就成爲了各類數據庫的核心技術之一。

總的來講,MySQL 各存儲引擎使用了三種類型(級別)的鎖定機制:**行級鎖定,頁級鎖定和表級鎖定。

行級鎖定(row-level)

優勢 :併發優點 
缺點 :耗費資源,容易形成死鎖

行級鎖定最大的特色就是鎖定對象的顆粒度很小,也是目前各大數據庫管理軟件所實現的鎖定顆粒度最小的。因爲鎖定顆粒度很小,因此發生鎖定資源爭用的機率也最小,可以給予應用程序儘量大的併發處理能力而提升一些須要高併發應用系統的總體性能。

雖然可以在併發處理能力上面有較大的優點,可是行級鎖定也所以帶來了很多弊端。因爲鎖定資源的顆粒度很小,因此每次獲取鎖和釋放鎖須要作的事情也更多,帶來的消耗天然也就更大了。此外,行級鎖定也最容易發生死鎖。

表級鎖定(table-level)

優勢 :簡單, 避免死鎖 
缺點 :併發劣勢

和行級鎖定相反,表級別的鎖定是MySQL 各存儲引擎中最大顆粒度的鎖定機制。該鎖定機制最大的特色是實現邏輯很是簡單,帶來的系統負面影響最小。因此獲取鎖和釋放鎖的速度很快。因爲表級鎖一次會將整個表鎖定,因此能夠很好的避免困擾咱們的死鎖問題。

固然,鎖定顆粒度大所帶來最大的負面影響就是出現鎖定資源爭用的機率也會最高,導致併發度大打折扣。

頁級鎖定(page-level)

BerkeleyDB 引擎使用
頁級鎖定是MySQL 中比較獨特的一種鎖定級別,在其餘數據庫管理軟件中也並非太常見。頁級鎖定的特色是鎖定顆粒度介於行級鎖定與表級鎖之間,因此獲取鎖定所須要的資源開銷,以及所能提供的併發處理能力也一樣是介於上面兩者之間。另外,頁級鎖定和行級鎖定同樣,會發生死鎖。

使用場景

在MySQL 數據庫中,
使用表級鎖定的主要是MyISAM,Memory,CSV 等一些非事務性存儲引擎,
而使用行級鎖定的主要是Innodb 存儲引擎和NDB Cluster 存儲引擎,
頁級鎖定主要是BerkeleyDB 存儲引擎的鎖定方式。

MyISAM 表鎖優化

縮短鎖定時間,

短短几個字,提及來確實聽容易的,但實際作起來恐怕就並不那麼簡單了。如何讓鎖定時間儘量的短呢?惟一的辦法就是讓咱們的Query 執行時間儘量的短。

a) 儘可能減小大的複雜Query,將複雜Query 分拆成幾個小的Query 分步進行;
b) 儘量的創建足夠高效的索引,讓數據檢索更迅速;
c) 儘可能讓MyISAM 存儲引擎的表只存放必要的信息,控制字段類型;

分離能並行的操做

MyISAM 的存儲引擎還有一個很是有用的特性,Concurrent Insert(併發插入)的特性。MyISAM 存儲引擎有一個控制是否打開Concurrent Insert 功能的參數選項:concurrent_insert,能夠設置爲0,1 或者2。三個值的具體說明以下:

a) concurrent_insert=2,不管MyISAM 存儲引擎的表數據文件的中間部分是否存在由於刪除數據而留下的空閒空間,都容許在數據文件尾部進行Concurrent Insert;

b) concurrent_insert=1,當MyISAM 存儲引擎表數據文件中間不存在空閒空間的時候,能夠從文件尾部進行Concurrent Insert;

c) concurrent_insert=0,不管MyISAM 存儲引擎的表數據文件的中間部分是否存在由於刪除數據而留下的空閒空間,都不容許Concurrent Insert。

Innodb 行鎖優化

a) 儘量讓全部的數據檢索都經過索引來完成,從而避免Innodb 由於沒法經過索引鍵加鎖而升級爲表級鎖定;

b) 合理設計索引,讓Innodb 在索引鍵上面加鎖的時候儘量準確,儘量的縮小鎖定範圍,避免形成沒必要要的鎖定而影響其餘Query 的執行;

c) 儘量減小基於範圍的數據檢索過濾條件,避免由於間隙鎖帶來的負面影響而鎖定了不應鎖定的記錄;

d) 儘可能控制事務的大小,減小鎖定的資源量和鎖定時間長度;

e) 在業務環境容許的狀況下,儘可能使用較低級別的事務隔離,以減MySQL 由於實現事務隔離級別所帶來的附加成本;

減小死鎖產生機率建議

a) 相似業務模塊中,儘量按照相同的訪問順序來訪問,防止產生死鎖;

b) 在同一個事務中,儘量作到一次鎖定所須要的全部資源,減小死鎖產生機率;

c) 對於很是容易產生死鎖的業務部分,能夠嘗試使用升級鎖定顆粒度,經過表級鎖定來減小死鎖產生的機率

死鎖解決

查看正在鎖的事務

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 

查看等待鎖的事務

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; 

數據類型優化

儘可能選擇佔用空間小的類型

由於小的類型不管是在磁盤,仍是在內存中佔用的空間都是小的,在進行查詢或者排序是臨時表要求的空間也會相對較少。在數據量比較小的時候可能感受不到,可是當數據量比較大時,這個原則的重要性可能就會獲得顯現。

儘可能選擇簡單/恰當的類型

在對錶進行選擇以及排序的時候,對於簡單的類型每每只須要消耗較少的CPU時鐘週期。例如,對於MySql server而言,整數類型值的Compare每每會比字符串類型值的Compare簡單且快,因此當你須要對特定的表進行排序時應該儘可能選擇整數類型做爲排序的依據

儘可能將字段設置爲NOTNULL

通常狀況下,若是你沒有顯示的制定一個字段爲NULL,那麼這個字段將會被數據庫系統認爲是NULLABLE, 系統的這種默認行爲將會致使如下三個問題
(1) Mysql服務器自身的 查詢優化功能將會受影響
(2) Mysql針對null值的字段須要額外的存儲空間以及處理
(3) 若是一個null值是索引的一部分,那麼索引的效果也會收到影響

因爲這個原則對於數據庫性能提高的做用不是很大,因此對於已經存在的DB schema,其存在NULLABLE字段或者是索引爲NULLABLE的,也不用專門的去修改它,可是對於新設計的DB或者索引須要儘可能遵照這個原則。

相關文章
相關標籤/搜索