100w的數據表比1000w的數據表查詢更快嗎

「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!
當咱們對一張表發起查詢的時候,是否是這張表的數據越少,查詢的就越快?
答案是不必定,這和mysql B+數索引結構有必定的關係。前端

innodb邏輯存儲結構

從Innodb存儲引擎的邏輯存儲結構來看,全部數據都被邏輯的放在一個表空間(tablespace)中,默認狀況下,全部的數據都放在一個表空間中,固然也能夠設置每張表單獨佔用一個表空間,經過innodb_file_per_table來開啓。mysql

mysql> show variables like 'innodb_file_per_table';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_file_per_table | ON    |
+-----------------------+-------+
1 row in set (0.00 sec)
複製代碼

表空間又是由各個段組成的,常見的有數據段,索引段,回滾段等。由於innodb的索引類型是b+樹,那麼數據段就是葉子結點,索引段爲b+的非葉子結點。sql

段空間又是由區組成的,在任何狀況下,每一個區的大小都爲1M,innodb引擎通常默認頁的大小爲16k,通常一個區中有64個連續的頁(64*16k=1M)。後端

經過段咱們知道,還存在一個最小的存儲單元頁。它是innodb管理的最小的單位,默認是16K,固然也能夠經過innodb_page_size來設置爲4K、8K...,咱們的數據都是存在頁中的markdown

mysql> show variables like 'innodb_page_size';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.00 sec)
複製代碼

因此innodb的數據結構應該大體以下:數據結構

image.png

B+ 樹

b+樹索引的特色就是數據存在葉子結點上,而且葉子結點之間是經過雙向鏈表方式組織起來的。
假設存在這樣一張表:post

CREATE TABLE `user` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL DEFAULT '',
  `age` int(10) NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
複製代碼

彙集索引

對於主鍵索引id,假設它的b+樹結構可能以下:spa

image.png

  1. 此時樹的高度是2
  2. 葉子節點之間雙向鏈表鏈接
  3. 葉子結點除了id外,還存了name、age字段(葉子結點包含整行數據)

咱們來看看 select * from user where id=30 是如何定位到的。指針

  1. 首先根據id=30,判斷在第一層的25-50之間
  2. 經過指針找到在第二層的p2中
  3. 把p2再加載到內存中
  4. 經過二分法找到id=30的數據

總結:能夠發現一共發起兩次io,最後加載到內存檢索的時間忽略不計。總耗時就是兩次io的時間。code

非彙集索引

經過表結構咱們知道,除了id,咱們還有name這個非彙集索引。因此對於name索引,它的結構可能以下:

image.png

  1. 此時樹的高度是2
  2. 葉子節點之間雙向鏈表鏈接
  3. 葉子結點除了name外,還有對應的主鍵id

咱們來看看 select * from user where name=jack 是如何定位到的。

  1. 首先根據 name=jack,判斷在第一層的mary-tom之間
  2. 經過指針找到在第二層的p2中
  3. 把p2再加載到內存中
  4. 經過二分法找到name=jack的數據(只有name和id)
  5. 由於是select *,因此經過id再去主鍵索引查找
  6. 一樣的原理最終在主鍵索引中找到全部的數據

總結:name查詢兩次io,而後經過id再次回表查詢兩次io,加載到內存的時間忽略不計,總耗時是4次io。

一棵樹能存多少數據

以上面的user表爲例,咱們先看看一行數據大概須要多大的空間:經過show table status like 'user'\G

mysql> show table status like 'user'\G
*************************** 1. row ***************************
           Name: user
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 10143
 Avg_row_length: 45
    Data_length: 458752
Max_data_length: 0
   Index_length: 311296
      Data_free: 0
 Auto_increment: 10005
    Create_time: 2021-07-11 17:22:56
    Update_time: 2021-07-11 17:31:52
     Check_time: NULL
      Collation: utf8mb4_general_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)
複製代碼

咱們能夠看到Avg_row_length=45,那麼一行數據大概佔45字節,由於一頁的大小是16k,那麼一頁能夠存儲的數據是16k/45b = 364行數據,這是葉子結點的單page存儲量。
以主鍵索引id爲例,int佔用4個字節,指針大小在InnoDB中佔6字節,這樣一共10字節,從root結點出來多少個指針,就能夠知道root的下一層有多少個頁。由於root結點只有一頁,因此此時就是16k/10b = 1638個指針。

  1. 若是樹的高度是2,那麼能存儲的數據量就是1638 * 364 = 596232條
  2. 若是樹的高度是3,那麼能存儲的數據量就是1638 * 1638 * 364 = 976628016條

image.png

如何知道一個索引樹的高度

innodb引擎中,每一個頁都包含一個PAGE_LEVEL的信息,用於表示當前頁所在索引中的高度。默認葉子節點的高度爲0,那麼root頁的PAGE_LEVEL + 1就是這棵索引的高度。

image.png 那麼咱們只要找到root頁的PAGE_LEVEL就好了。

經過如下sql能夠定位user表的索引的page_no:

mysql> SELECT b.name, a.name, index_id, type, a.space, a.PAGE_NO  FROM information_schema.INNODB_SYS_INDEXES a,  information_schema.INNODB_SYS_TABLES b  WHERE a.table_id = b.table_id AND a.space <> 0 and b.name='test/user';
+-----------+---------+----------+------+-------+---------+
| name      | name    | index_id | type | space | PAGE_NO |
+-----------+---------+----------+------+-------+---------+
| test/user | PRIMARY |      105 |    3 |    67 |       3 |
| test/user | name    |      106 |    0 |    67 |       4 |
+-----------+---------+----------+------+-------+---------+
2 rows in set (0.00 sec)
複製代碼

能夠看到主鍵索引的page_no=3,由於PAGE_LEVEL在每一個頁的偏移量64位置開始,佔用兩個字節。因此算出它在文件中的偏移量:16384*3 + 64 = 49152 + 64 =49216,再取前兩個字節就是root的PAGE_LEVEL了。
經過如下命令找到ibd文件目錄

show global variables like "%datadir%" ;
+---------------+-----------------------+
| Variable_name | Value                 |
+---------------+-----------------------+
| datadir       | /usr/local/var/mysql/ |
+---------------+-----------------------+
1 row in set (0.01 sec)
複製代碼

user.ibd在 /usr/local/var/mysql/test/下。
經過hexdump來分析data文件。

hexdump -s 49216 -n 10  user.ibd
000c040 00 01 00 00 00 00 00 00 00 69
000c04a
複製代碼

000c040 00 01 00 00 00 00 00 00 00 69
00 01就是說明PAGE_LEVEL=1,那麼樹的高度就是1+1=2。

回到題目

100w的數據表比1000w的數據表查詢更快嗎?經過查詢的過程咱們知道,查詢耗時和樹的高度有很大關係。若是100w的數據若是和1000w的數據的樹的高度是同樣的,那其實它們的耗時沒什麼區別。

相關文章
相關標籤/搜索