mysql 索引筆記

MyISAM引擎的B+Tree的索引

myisam

經過上圖能夠直接的看出, 在MyISAM對B+樹的運用中明顯的特色以下:node

  • 全部的非葉子節點中存儲的所有是索引信息
  • 在葉子節點中存儲的 value值實際上是 數據庫中某行數據的index

MyISAM引擎 索引文件的查看: mysql

在 /var/lib/mysql目錄中算法

.myd 即 my data , 數據庫中表的數據文件sql

.myi 即 my index , 數據庫中 索引文件數據庫

.log 即 mysql的日誌文件centos

InnoDB引擎 索引文件的查看: 性能

一樣在 /var/lib/mysql 目錄下面操作系統

InnoDB引擎的B+Tree的索引

InnoDB

InnoDB的實現方式業內也稱其爲聚簇索引, 什麼是聚簇索引呢? 就是相鄰的行的簡直被存儲到一塊兒, 對比上面的兩幅圖片就會發現, 在InnDB中, B+樹的葉子節點中存儲的是數據行中的一行行記錄, 缺點: 由於索引文件被存放在硬盤上, 因此很佔硬盤的空間.net

通常咱們會在每個表中添加一列 取名 id, 設置它爲primary key , 即將他設置成主鍵, 若是使用的存儲引擎也是InnoDB的話, 底層就會創建起主鍵索引, 也是聚簇索引, 而且會自動按照id的大小爲咱們排好序,(由於它的一個有序的樹)日誌

點擊查看 參考博文1

局部性原理

局部性原理是指CPU訪問存儲器時,不管是存取指令仍是存取數據,所訪問的存儲單元都趨於彙集在一個較小的連續區域中。 更進一步說, 當咱們經過程序向操做系統發送指令讓它讀取咱們指定的數據時, 操做系統會一次性讀取一頁(centos 每頁4kb大小,InnoDB存儲引擎中每一頁16kb)的數據, 它遵循局部性理論, 猜想當用戶須要使用某個數據時, 用戶極可能會使用這個數據周圍的數據,故而進行一次

InnoDB的頁格式

什麼是頁呢? 簡單說,就是一條條數據被的存儲在磁盤上, 使用數據時須要先將數據從磁盤上讀取到內存中, InnoDB每次讀出數據時一樣會遵循 局部性原理, 而不是一條條讀取, 因而InnoDB將數據劃分紅一個一個的頁, 以頁做爲和磁盤之間交互的基本單位

經過以下sql, 能夠看到,InnoDB中每一頁的大小是16kb

show global status like 'Innodb_page_size';

數據頁圖片

名稱 簡述
File Header 文件頭部, 存儲頁的一些通用信息
Page Header 頁面頭部, 存儲數據頁專有的信息
Infinum + supremum 最大記錄和最小記錄, 這是兩個虛擬的行記錄
User Records 用戶記錄, 用來實際存儲行記錄中的內容
Free Space 空閒空間, 頁中尚位使用的空間
Page Directory 頁面目錄, 存儲頁中某些記錄的位置
File Tailer 文件尾部 , 用來校驗頁是否完整

InnoDB的行格式 compact

每一頁中存儲的行數據越多. 總體的性能就會越強

compact的行格式以下圖所示

compact

能夠看到在行格式中在存儲真正的數據的前面會存儲一些其餘信息, 這些信息是爲了描述這條記錄而不得不添加的一些信息, 這些額外的信息就是上圖中的前三行

  • 變長字段的長度列表

在mysql中char是固定長度的類型, 同時mysql還支持諸如像 varchar這樣可變長度的類型, 不止varchar , 想 varbinary text blob這樣的變長數據類型, 由於 變長的數據類型的列存儲的數據的長度是不固定的, 因此說咱們在存儲真正的數據時, 也得將這些數據到底佔用了多大的長度也給保存起來

  • NULL標誌位

compact行格式會將值能夠爲NULL的列統一標記在 NULL標誌位中, 若是數據表中全部的字段都被標記上not null , 那麼就沒有NULL值列表

  • 記錄頭信息

記錄頭

記錄頭信息, 顧名思義就是用來描述記錄頭中的信息, 記錄頭信息由固定的5個字節組成, 一共40位, 不一樣位表明的意思也不一樣, 以下表

名稱 單位 bit 簡介
預留位1 1 未使用
預留位2 1 未使用
delete_mark 1 標記改行記錄是否被刪除了
min_rec_mark 1 標記在 B+樹中每層的非葉子節點中最小的node
n_owned 4 表示當前記錄擁有的記錄數
heap_no 13 表示當前記錄在堆中的位置
record_type 3 表示當前記錄的類型 , 0表示普通記錄, 1表示B+樹中非葉子節點記錄, 2表示最小記錄 ,3表示最大記錄
next_record 16 表示下一條記錄的相對位置

點擊查看 參考博文2

行溢出

在mysql中每一行, 能存儲的最大的字節數是65535個字節數, 此時咱們使用下面的sql執行時就會出現行溢出現象

CREATE TABLE test ( c VARCHAR(65535) ) CHARSET=ascii ROW_FORMAT=Compact;

給varchar申請最大65535 , 再加上compact行格式中還有前面三個非數據列佔用內存,因此一準溢出, 若是不想溢出, 能夠適當的將 65535 - 3

頁溢出

前面說了, InnoDB中數據的讀取按照頁爲單位, 每一頁的大小是 16kb, 換算成字節就是16384個字節, 可是每行最多存儲 65535個字節啊, 也就是說一行數據可能須要好幾個頁來存儲

怎麼辦呢?

  • compact行格式會在存儲真實數據的列中多存儲一部分數據, 這部分數據中存儲的就是下一頁的地址
  • dynamic行格式 中直接存儲數據所在的地址, 換句話說就是數據都被存儲在了其餘頁上
  • compressed行格式會使用壓縮算法對行格式進行壓縮處理

通常咱們都是將表中的id列設置爲主鍵, 這就會造成主鍵索引, 因而咱們須要注意了:

主鍵的佔用的空間越小,總體的檢索效率就會越高

爲何這麼說呢? 這就能夠結合頁的概念來解析, 在B+樹這種數據結果中, 葉子節點中用來存儲數據, 存儲數據的格式相似Key-value key就是索引值, value就是數據內容, 若是索引佔用的空間太大的話, 單頁16kb能存儲的索引就越小, 這就致使數據被分散在更多的頁上, 導致查詢的效率下降

創建索引的技巧

爲某一列創建索引

給text表中的title列建立索引, 索引名字 my_index
alter table text add index my_index (title);

雖然創建索引能提高查詢的效率, 根據前人的經驗看, 這並非必定的, 創建索引自己會直接消耗內存空間, 同時索, 插入,刪除, 這種寫操做就會打破B+樹的平衡面臨索引的重建, 通常出現以下兩種狀況時,是不推薦創建索引的

  1. 表中的數據自己就不多
  2. 咱們計算一下索引的選擇性很低

兼顧 - 索引的選擇性與前綴索引

所謂選擇性,其實就是說不重複出現的索引值(基數,Cardinality) 與 表中的記錄數的比值

即: 選擇性= 基數 / 記錄數

選擇性的取值範圍在(0,1]之間, 選擇性越接近1 , 說明創建索引的必要性就越強, 好比對sex列進行創建索引,這裏面非男即女, 若是對它創建索引的話, 實際上是沒意義的, 還不如直接進行全表掃描來的快

如何使用sql計算選擇性呢? 嚴格遵循上面的公式

SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM employees.titles;
count(基數/記錄數)
DISTINCT(title) / /count(*)

更詳細的例子看下面的鏈接

參考博文

索引失效問題

注意事項

  • 索引沒法存儲null值

  • 若是條件中有or, 即便條件中存在索引也不會使用索引,若是既想使用or,又想使用索引, 就給全部or條件控制的列加上索引

  • 使用like查詢時, 若是以%開頭,確定是進行全表掃描

  • 使用like查詢時, 若是%在條件後面

    • 對於主鍵索引, 索引失效
    • 對於普通索引, 索引不失效
  • 若是列的類型是字符串類型, 那麼必定要在條件中將數據用引號引發來,否則也會是索引失效

  • 若是mysql認爲全表掃描比用索引塊, 一樣不會使用索引

聯合索引

什麼是聯合索引

聯合索引, 也叫複合索引,說白了就是多個字段一塊兒組合成一個索引

像下面這樣使用 id + title 組合在一塊兒構成一個聯合索引

CREATE TABLE `text` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  PRIMARY KEY (`id`,`title`)
) ENGINE=InnoDB AUTO_INCREMENT=3691 DEFAULT CHARSET=utf8
  • 若是咱們像上圖那樣建立了索引,咱們只要保證咱們的 id+title 二者結合起來全局惟一就ok
  • 創建聯合索引一樣是須要進行排序的,排序的規則就是按照聯合索引全部列組成的字符串的之間的前後順序進行排序, 如a比b優先

左前原則

使用聯合索引進行查詢時必定要遵循左前綴原則, 什麼是左前綴原則呢? 就是說想讓索引生效的話,必定要添加上第一個索引, 只使用第二個索引進行查詢的話會致使索引失效

好比上面建立的聯合索引, 假如咱們的查詢條件是 where id = '1' 或者 where id = '1' and title = '唐詩宋詞' 索引都會不失效

可是若是咱們不使用第一個索引id, 像這樣 where title = '唐詩' , 結果就是致使索引失效

聯合索引的分組&排序

仍是使用這個例子:

CREATE TABLE `text` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  PRIMARY KEY (`id`,`title`)
) ENGINE=InnoDB AUTO_INCREMENT=3691 DEFAULT CHARSET=utf8

demo1: 當咱們像下面這樣寫sql時, 就會先按照id進行排序, 當id相同時,再按照title進行排序

select * form text order by id, title;

demo2: 當咱們像下面這樣寫sql時, 就會先將id相同的劃分爲一組, 再將title相同的劃分爲一組

select id,title form text group by id, title;

demo3: ASC和DESC混用, 其實你們都知道底層使用B+樹, 自己就是有序的, 要是不加限制的話,默認就是ASC, 反而是混着使用就使得索引失效

select * form text order by id ASC, title DESC;

如何定位慢查詢

相關參數

名稱 簡介
slow_query_log 慢查詢的開啓狀態
slow_query_log_file 慢查詢日誌存儲的位置
long_query_time 查詢超過多少秒才記錄下來

經常使用sql

# 查看mysql是否開啓了慢查詢
show variables like 'slow_query_log';   
# 將全局變量設置爲ON
set global slow_query_log ='on';
# 查看慢查詢日誌存儲的位置
show variables like 'slow_query_log_file';
# 查看規定的超過多少秒才被算做慢查詢記錄下來
show variables like 'long_query_time';
show variables like 'long_query%';
# 超過一秒就記錄 , 每次修改這個配置都從新創建一次連接
set global long_query_time=1;
相關文章
相關標籤/搜索