mysql表字段超過多少影響性能 mysql表多少效率會下降

一直有傳言說,MySQL 表的數據只要超過 2000 萬行,其性能就會下降。而本文作者用實驗分析證明:至少在 2023 年,這已不再是 MySQL 表的有效軟限制。

傳言
互聯網上有一則傳言說,我們應該避免單個 MySQL 表中的數據超過 2000 萬行,否則表的性能就會下降——當數據量超過這個軟限制時,你就會發現 SQL 的查詢速度會比平時慢很多。這是多年前針對 HDD 做出的判斷。我想知道,時至 2023 年,SSD 上的 MySQL 是否仍然有此限制。如果真的有,那么原因是什么呢?

環境
數據庫

? MySQL 版本: 8.0.25

? 實例類型:AWS db.r5.large(2vCPUs, 16GiB RAM)

? EBS 存儲類型:General Purpose SSD(gp2)

測試客戶端

? Linux 內核版本:6.1

? 實例類型:AWS t2.micro(1 vCPU, 1GiB RAM)

實驗設計
創建具有相同結構、但大小不同的表。我一共創建了 9 個表,數據行數分別為:10 萬、20 萬、50 萬、100 萬、200 萬、500 萬、1000 萬、2000 萬、3000 萬、5000 萬和 6000 萬。

  1. 創建幾個具有相同結構的表:
CREATE TABLE row_test(
`id` int NOT NULL AUTO_INCREMENT,
`person_id` int NOT NULL,
`person_name` VARCHAR(200),
`insert_time` int,
`update_time` int,
PRIMARY KEY (`id`),
KEY `query_by_update_time` (`update_time`),
KEY `query_by_insert_time` (`insert_time`)
);
  1. 插入不同的數據。我使用了測試客戶端和表復制的方式創建了這些表。腳本可參考:https://github.com/gongyisheng/playground/blob/main/mysql/row_test/insert_data.py。
# test client
INSERT INTO {table} (person_id, person_name, insert_time, update_time) VALUES ({person_id}, {person_name}, {insert_time}, {update_time})
# copy
create table like <table>
insert into (`person_id`, `person_name`, `insert_time`, `update_time`)
select `person_id`, `person_name`, `insert_time`, `update_time` from

person_id、person_name、insert_time 和 update_time 的值是隨機的。

  1. 使用測試客戶端執行以下 sql 查詢來測試性能。腳本可參考:https://github.com/gongyisheng/playground/blob/main/mysql/row_test/select_test.py。
select count(*) from <table> -- full table scan
select count(*) from <table> where id = 12345 -- query by primary key
select count(*) from <table> where insert_time = 12345 -- query by index
select * from <table> where insert_time = 12345 -- query by index, but cause 2-times index tree lookup
  1. 查看 innodb 緩沖池狀態。
SHOW ENGINE INNODB STATUS
SHOW STATUS LIKE 'innodb_buffer_pool_page%

結果
查詢1:select count(*) from
在這里插入圖片描述

這種查詢會執行全表掃描,MySQL 并不擅長這種工作。

? 第一輪:沒有緩存。第一次執行查詢時,緩沖池中沒有緩存數據。

? 第二輪:有緩存。當緩沖池中已經有數據緩存時執行查詢,通常在第一次查詢執行完之后。

觀察結果:

1. 第一輪查詢的執行時間超出了后面幾次。

在這里插入圖片描述
原因是 MySQL 使用了 innodb_buffer_pool 來緩存數據頁。在第一次執行查詢之前,緩沖池是空的,所以 MySQL 必須進行大量的磁盤 I/O 才能從 .idb 文件加載表。但在第一次執行結束后,緩沖池中存儲了數據,后續查詢可以直接讀取內存,避免磁盤 I/O,因此速度更快。該過程稱為 MySQL 緩沖池預熱。

2. select count(*) from < table > 會設法將整個表加載到緩沖池。

在這里插入圖片描述

我比較了實驗前后 innodb_buffer_pool 的統計數據。運行查詢后,如果緩沖池足夠大,則其使用量變化等于表的大小。否則,只有部分表會緩存在緩沖池中。原因是查詢 select count(*) from table 會做全表掃描,并做逐行統計。如果沒有緩存,就需要將完整的表加載到內存中。為什么?因為 Innodb 支持事務,它不能保證事務在不同時間看到同一張表。全表掃描是獲得準確行數的唯一安全方法。

3. 如果緩沖池不能容納全表,則會爆發查詢延遲。

在這里插入圖片描述
我注意到 innodb_buffer_pool 的大小會極大地影響查詢性能,因此我嘗試在不同的配置下運行查詢。當使用 11G 緩沖區,而表的大小達到 5000 萬行時,就會爆發查詢延遲。接著,我將緩沖區縮減到 7G,當表的大小達到 3000 萬行時,爆發了查詢延遲。最后,我將緩沖區縮減到 3G,當表的大小僅為 2000 萬行時,就爆發了查詢延遲。很明顯,如果表中的數據無法緩存在緩沖池中,則 select count(*) from

必須執行昂貴的磁盤 I/O,這會導致查詢運行時間直線上升。

4. 對于沒有緩存的查詢,查詢花費的時間與表的大小呈線性關系,與緩沖池大小無關。

在這里插入圖片描述
當沒有緩存時,查詢花費的時間由磁盤 I/O 決定,與緩沖池大小無關。在 IOPS 相同的情況下,是否使用 select count(*) 預熱緩沖池并沒有區別。

5. 如果無法完整地緩存整個表,則有無緩存的查詢運行時間差異是恒定的。

另請注意,如果無法完整地緩存整個表,雖然查詢運行時會突然上升,但運行時是可預測的。無論表的大小如何,有無緩存的時間差異是恒定的。原因是表的部分數據緩存在緩沖區中,這里的時間差異來自從緩沖區讀取數據節省的時間。

查詢2,3:select count(*) from where = 12345
在這里插入圖片描述
這個查詢使用了索引。由于不是范圍查詢,MySQL 只需要利用 B+ 樹的路徑從上到下查找頁面,并將這些頁面緩存到 innodb 緩沖池中即可。

我創建的表的 B+ 樹的深度都是 3,因此前面的 3~4 次 I/O 都被拿來預熱緩沖區,平均耗時 4~6 毫秒。之后,再次運行相同的查詢,MySQL 就會直接從內存中查找結果,耗時為 0.5 毫秒,約等于網絡 RTT。如果緩存頁面長時間未命中,并從緩沖池中逐出,則必須再次從磁盤加載該頁面,這樣就需要磁盤 I/O(最多 4 次)。

查詢4:select * from where = 12345
在這里插入圖片描述
這個查詢涉及兩次索引查找。由于 select * 需要查詢獲取的 person_name、person_id 字段并不在索引中,因此在查詢執行期間,數據庫引擎必須查找 2 個 B+ 樹。它首先查找 insert_time B+ 樹,獲取目標行的主鍵,然后查找主鍵 B+ 樹,獲取該行的完整數據,如下圖所示:

在這里插入圖片描述
這就是我們應該在生產中避免 select * 的原因。此次實驗證實,此查詢加載的頁面塊比查詢 2 或 3 多出了 2 倍,且最高可達 8 倍。查詢的平均運行時間為 6~10 毫秒,也是查詢 2 或 3 的 1.5~2 倍。

傳言是怎么來的
在這里插入圖片描述
首先,我們需要知道 innodb 索引頁的物理結構。默認頁面大小為 16k,由頁眉、系統記錄、用戶記錄、頁面導向器和尾部組成。只有剩下的 14~15k 用來存儲數據。

假設你使用 INT 作為主鍵(4 字節),每行 1KB 的有效負載。每個葉頁可以存儲 15 行,一個指向該頁的指針需要 4+8=12 字節。因此,每個非葉頁最多可以容納 15k / 12 字節 = 1280 個指針。如果你有一個 4 層的 B+ 樹,它最多可以容納 1280128015 = 24.6M 行數據。

回到 HDD 占據市場主導地位,且 SSD 對于數據庫而言過于昂貴的時代,4 次隨機 I/O 可能是我們可以容忍的最壞情況,而使用 2 次索引樹查找的查詢甚至會使情況變得更糟。當時的工程師想要控制索引樹的深度,不希望它們太深。而如今 SSD 越來越流行,隨機 I/O 比以前便宜了,因此我們應該反思一下 10 年前的規則。

順便說一句,5 層 B+ 樹可以容納 128012801280*15 = 31.4B 行數據,超過了 INT 所能容納的最大數據量。對每行大小的不同假設將導致不同的軟限制,或小于或大于 2000 萬行。例如,在我的實驗中,每一行大約是 816 字節(我使用 utf8mb4 字符集,所以每個字符占用 4 個字節),4 層 B+ 樹可以容納的軟限制是 29.5M。

結論
? Innodb 緩存池的大小、表的大小決定了是否會出現性能降級。

? 判斷是否需要拆分 MySQL 表的一個更有意義的指標是查詢運行時/緩沖池命中率。如果查詢總是命中緩沖區,則不會有任何性能問題。2000 萬行只是一個經驗值。

? 除了拆分 MySQL 表之外,增加 Innodb 緩存池的大小和數據庫的內存也是一個選擇。

? 如果可能,請避免在生產中使用 select *,這類語句在最壞的情況下會導致 2 次索引樹查找。

? (我個人的意見)考慮到 SSD 現在越來越流行,2000 萬行不再是 MySQL 表的有效軟限制。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/20778.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20778.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20778.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

內網滲透-在HTTP協議層面繞過WAF

進入正題&#xff0c;隨著安全意思增強&#xff0c;各企業對自己的網站也更加注重安全性。但很多web應用因為老舊&#xff0c;或貪圖方便想以最小代價保證應用安全&#xff0c;就只僅僅給服務器安裝waf。 本次從協議層面繞過waf實驗用sql注入演示&#xff0c;但不限于實際應用…

[數據集][目標檢測]輪胎檢測數據集VOC+YOLO格式439張1類別

數據集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路徑的txt文件&#xff0c;僅僅包含jpg圖片以及對應的VOC格式xml文件和yolo格式txt文件) 圖片數量(jpg文件個數)&#xff1a;439 標注數量(xml文件個數)&#xff1a;439 標注數量(txt文件個數)&#xff1a;439 標注類別…

mysql怎么部署雙機

MySQL的雙機部署是為了實現數據的高可用性和容錯性。以下是MySQL雙機熱備部署的基本步驟&#xff0c;我會盡量清晰地分點表示和歸納&#xff1a; 1. 環境準備 安裝MySQL&#xff1a;在兩臺服務器上分別安裝MySQL數據庫。確保版本兼容。 網絡配置&#xff1a;確保兩臺服務器之…

題目:判斷一個素數能被幾個9整除

題目&#xff1a;判斷一個素數能被幾個9整除 There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all parallel goods. Those who are worried about being cheated should …

顛仆流離學二叉樹2 (Java篇)

本篇會加入個人的所謂魚式瘋言 ??????魚式瘋言:??????此瘋言非彼瘋言 而是理解過并總結出來通俗易懂的大白話, 小編會盡可能的在每個概念后插入魚式瘋言,幫助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能說的不是那么嚴謹.但小編初心是能讓更多人…

泛型知識匯總

演示代碼&#xff1a; package exercise;import java.util.Arrays;public class MyArrayList<E> {Object[] obj new Object[10];int size;public boolean add(E e) {obj[size] e;size;return true;}public E get(int index) {return (E) obj[index];}//沒有這個函數&a…

現代信號處理12_譜估計的4種方法(CSDN_20240602)

Slepian Spectral Estimator(1950) 做譜估計的目標是盡可能看清楚信號功率譜在某一個頻率上的情況&#xff0c;假設我們想了解零頻時的分布&#xff0c;最理想的情況是濾波器的傳遞函數H(ω) 是一個沖激函數&#xff0c;這樣就沒有旁瓣&#xff0c;也就沒有泄漏&#xff1b;其次…

【OpenHarmony】TypeScript 語法 ③ ( 條件語句 | if else 語句 | switch case 語句 )

文章目錄 一、條件語句1、if else 語句2、switch case 語句 參考文檔 : <HarmonyOS第一課>ArkTS開發語言介紹 一、條件語句 1、if else 語句 TypeScript 中的 if 語句 / if else 語句 用法 , 與 JavaScript 語言中的 if 語句 / if else 語句 語法 基本相同 ; if else 語…

使用Java構建RESTful API:實現靈活、可擴展的Web服務

RESTful API已經成為構建現代Web應用的標準之一&#xff0c;它通過簡單的HTTP協議進行通信&#xff0c;提供了一種輕量級、靈活、可擴展的方式來構建和管理Web服務。Java作為一種強大的編程語言&#xff0c;提供了許多框架和庫來幫助開發者構建高效的RESTful API。本文將探討如…

項目質量管理

目錄 1.概述 2.三個關鍵過程 2.1.規劃質量管理&#xff08;Plan Quality Management&#xff09; 2.2.管理質量&#xff08;Manage Quality&#xff09; 2.3.控制質量&#xff08;Control Quality&#xff09; 3.應用場景 3.1.十個應用場景 3.2.產品設計與開發 4.小結…

使用PyCharm 開發工具創建工程

一. 簡介 前面學習了 安裝 python解釋器。如何安裝python的一種開發工具 PyCharm。 本文來簡單學習一下&#xff0c;如何使用 PyCharm 開發工具創建一個簡單的 python工程。 二. PyCharm 開發工具創建一個工程 1. 首先&#xff0c;首先打開PyCharm 開發工具。選擇 創建一…

Docker部署SiYuan筆記-Unraid

使用unraid的docker部署SiYuan筆記&#xff0c;簡單記錄 筆記說明 Siyuan筆記是一款基于markdown語法的筆記工具&#xff0c;具有活躍的社區和多設備支持。大部分功能都是免費&#xff0c;源代碼開源&#xff0c;支持插件安裝&#xff0c;具有很不錯的使用體驗。 Docker地址&a…

linux---生產者和消費者模型

生產者消費者模式就是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊&#xff0c;而通過阻塞隊列來進行通訊&#xff0c;所以生產者生產完數據之后不用等待消費者處理&#xff0c;直接扔給阻塞隊列&#xff0c;消費者不找生產者要數據&#…

2024年海南省三支一扶報名指南,照片要求

2024年海南省三支一扶報名指南&#xff0c;照片要求 一、考試時間安排&#xff1a; 報名時間&#xff1a;6月1日8:00至6月7日18:00 準考證打印時間&#xff1a;6月17日8:00 考試時間&#xff1a;6月22日 二、招聘人數 海南省計劃招募390名高校畢業生

STM32_IIC

1、IIC簡介 I2C&#xff0c;即Inter IC Bus。是由Philips公司開發的一種串行通用數據總線&#xff0c;主要用于近距離、低速的芯片之間的通信&#xff1b;有兩根通信線&#xff1a;SCL&#xff08;Serial Clock&#xff09;用于通信雙方時鐘的同步、SDA&#xff08;Serial Data…

JVM之【執行引擎】

執行引擎 執行引擎是JVM的核心組件之一&#xff0c;它負責將Java字節碼文件轉換為機器指令并執行。這一過程涉及多個組成部分&#xff0c;各部分協同工作來完成字節碼到機器指令的轉換和執行。以下是執行引擎的主要組成部分及其作用&#xff1a; 1. 解釋器&#xff08;Interp…

vue.js框架快速入門

Vue.js是一個漸進式JavaScript框架&#xff0c;用于構建用戶界面和單頁應用程序。以下是Vue.js快速入門的基本步驟和概念&#xff1a; 1. 環境準備 確保你的計算機上安裝了Node.js&#xff0c;它包括npm&#xff08;Node Package Manager&#xff09;&#xff0c;用于管理項目…

友善RK3399v2平臺利用rkmpp實現硬件編解碼加速

測試VPU 編譯mpp sudo apt update sudo apt install gcc g cmake make cd ~ git clone https://github.com/rockchip-linux/mpp.git cd mpp/build/linux/aarch64/ sed -i s/aarch64-linux-gnu-gcc/gcc/g ./arm.linux.cross.cmake sed -i s/aarch64-linux-gnu-g/g/g ./arm.lin…

如何學習ai agent?

如何學習Agent&#xff0c;推薦閱讀《動手做AI Agent》這本書。 推薦理由&#xff1a; 1&#xff1a;一本書能夠全方位了解并探索Agent的奧秘&#xff01; &#xff08;1&#xff09;Agent的發展進程。 &#xff08;2&#xff09;可以幫我們做哪些事&#xff1a;自動辦公&am…

TypeScript 中的迭代器和生成器

1. 迭代器 迭代器是一種對象&#xff0c;它提供了一種統一的方式來訪問集合中的元素&#xff0c;而不暴露集合的內部結構。在 TypeScript 中&#xff0c;迭代器通過實現 Iterator 接口來定義。 interface Iterator<T> {next(): IteratorResult<T>; }interface It…