MySQL中Varchar(50)和varchar(500)區別是什麼?

👉 這是一個或許對你有用的社群
🐱 一對一交流/面試小冊/簡歷最佳化/求職解惑,歡迎加入芋道快速開發平臺知識星球。下面是星球提供的部分資料:
👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERPCRMAI 大模型等等功能:
  • Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud
  • 影片教程:https://doc.iocoder.cn
【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本 

一. 問題描述

我們在設計表結構的時候,設計規範裡面有一條如下規則:
  • 對於可變長度的欄位,在滿足條件的前提下,儘可能使用較短的變長欄位長度。
為什麼這麼規定?我在網上查了一下,主要基於兩個方面
  • 基於儲存空間的考慮
  • 基於效能的考慮
網上說Varchar(50)varchar(500)儲存空間上是一樣的,真的是這樣嗎?
基於效能考慮,是因為過長的欄位會影響到查詢效能?
本文我將帶著這兩個問題探討驗證一下
基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 影片教程:https://doc.iocoder.cn/video/

二.驗證儲存空間區別

1.準備兩張表

CREATETABLE`category_info_varchar_50`

 (

`id`bigint

(

20

NOTNULL

 AUTO_INCREMENT 

COMMENT'主鍵'

,

`name`varchar

(

50

NOTNULLCOMMENT'分類名稱'

,

`is_show`tinyint

(

4

NOTNULLDEFAULT'0'COMMENT'是否展示:0 停用,1啟用'

,

`sort`int

(

11

NOTNULLDEFAULT'0'COMMENT'序號'

,

`deleted`tinyint

(

1

DEFAULT'0'COMMENT'是否刪除'

,

`create_time`

 datetime 

NOTNULLCOMMENT'建立時間'

,

`update_time`

 datetime 

NOTNULLCOMMENT'更新時間'

,

  PRIMARY 

KEY

 (

`id`

USING

 BTREE,

KEY`idx_name`

 (

`name`

USING

 BTREE 

COMMENT'名稱索引'

ENGINE

=

InnoDBDEFAULTCHARSET

=utf8mb4 

COMMENT

=

'分類'

;

CREATETABLE`category_info_varchar_500`

 (

`id`bigint

(

20

NOTNULL

 AUTO_INCREMENT 

COMMENT'主鍵'

,

`name`varchar

(

500

NOTNULLCOMMENT'分類名稱'

,

`is_show`tinyint

(

4

NOTNULLDEFAULT'0'COMMENT'是否展示:0 停用,1啟用'

,

`sort`int

(

11

NOTNULLDEFAULT'0'COMMENT'序號'

,

`deleted`tinyint

(

1

DEFAULT'0'COMMENT'是否刪除'

,

`create_time`

 datetime 

NOTNULLCOMMENT'建立時間'

,

`update_time`

 datetime 

NOTNULLCOMMENT'更新時間'

,

  PRIMARY 

KEY

 (

`id`

USING

 BTREE,

KEY`idx_name`

 (

`name`

USING

 BTREE 

COMMENT'名稱索引'

ENGINE

=

InnoDB

 AUTO_INCREMENT=

288135DEFAULTCHARSET

=utf8mb4 

COMMENT

=

'分類'

;

2.準備資料

給每張表插入相同的資料,為了凸顯不同,插入100萬條資料

DELIMITER $$

CREATE PROCEDURE batchInsertData(IN total INT)

BEGIN

    DECLARE start_idx INT DEFAULT 1;

    DECLARE end_idx INT;

    DECLARE batch_size INT DEFAULT 500;

    DECLARE insert_values TEXT;
    SET end_idx = LEAST(total, start_idx + batch_size - 1);
    WHILE start_idx <= total DO

        SET insert_values = 

''

;

        WHILE start_idx <= end_idx DO

            SET insert_values = CONCAT(insert_values, CONCAT(

'(\'

name

', start_idx, '

\

', 0, 0, 0, NOW(), NOW()),'

));

            SET start_idx = start_idx + 1;

        END WHILE;

        SET insert_values = LEFT(insert_values, LENGTH(insert_values) - 1); -- Remove the trailing comma

        SET @sql = CONCAT(

'INSERT INTO category_info_varchar_50 (name, is_show, sort, deleted, create_time, update_time) VALUES '

, insert_values, 

';'

);
        PREPARE stmt FROM @sql;

        EXECUTE stmt;

       SET @sql = CONCAT(

'INSERT INTO category_info_varchar_500 (name, is_show, sort, deleted, create_time, update_time) VALUES '

, insert_values, 

';'

); 

       PREPARE stmt FROM @sql;

        EXECUTE stmt;
        SET end_idx = LEAST(total, start_idx + batch_size - 1);

    END WHILE;

END$$

DELIMITER ;
CALL batchInsertData(1000000);

3.驗證儲存空間

查詢第一張表SQL
SELECT

    table_schema 

AS"資料庫"

,

    table_name 

AS"表名"

,

    table_rows 

AS"記錄數"

,

TRUNCATE

 ( data_length / 

1024

 / 

1024

2

 )  

AS"資料容量(MB)"

,

TRUNCATE

 ( index_length / 

1024

 / 

1024

2

 )  

AS"索引容量(MB)"
FROM

    information_schema.TABLES 

WHERE

    table_schema = 

'test_mysql_field'
and

 TABLE_NAME = 

'category_info_varchar_50'
ORDERBY

    data_length 

DESC

,

    index_length 

DESC

;

查詢結果
查詢第二張表SQL
SELECT

    table_schema 

AS"資料庫"

,

    table_name 

AS"表名"

,

    table_rows 

AS"記錄數"

,

TRUNCATE

 ( data_length / 

1024

 / 

1024

2

 )  

AS"資料容量(MB)"

,

TRUNCATE

 ( index_length / 

1024

 / 

1024

2

 )  

AS"索引容量(MB)"
FROM

    information_schema.TABLES 

WHERE

    table_schema = 

'test_mysql_field'
and

 TABLE_NAME = 

'category_info_varchar_500'
ORDERBY

    data_length 

DESC

,

    index_length 

DESC

;

查詢結果

4.結論

兩張表在佔用空間上確實是一樣的,並無差別
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/yudao-cloud
  • 影片教程:https://doc.iocoder.cn/video/

三.驗證效能區別

1.驗證索引覆蓋查詢

selectnamefrom

 category_info_varchar_50 

wherename

 = 

'name100000'
-- 耗時0.012s
selectnamefrom

 category_info_varchar_500 

wherename

 = 

'name100000'
-- 耗時0.012s
selectnamefrom

 category_info_varchar_50 

orderbyname

;

-- 耗時0.370s
selectnamefrom

 category_info_varchar_500 

orderbyname

;

-- 耗時0.379s

透過索引覆蓋查詢效能差別不大

1.驗證索引查詢

select

 * 

from

 category_info_varchar_50 

wherename

 = 

'name100000'
--耗時 0.012s
select

 * 

from

 category_info_varchar_500 

wherename

 = 

'name100000'
--耗時 0.012s
select

 * 

from

 category_info_varchar_50 

wherenamein

(

'name100'

,

'name1000'

,

'name100000'

,

'name10000'

,

'name1100000'

,

'name200'

,

'name2000'

,

'name200000'

,

'name20000'

,

'name2200000'

,

'name300'

,

'name3000'

,

'name300000'

,

'name30000'

,

'name3300000'

,

'name400'

,

'name4000'

,

'name400000'

,

'name40000'

,

'name4400000'

,

'name500'

,

'name5000'

,

'name500000'

,

'name50000'

,

'name5500000'

,

'name600'

,

'name6000'

,

'name600000'

,

'name60000'

,

'name6600000'

,

'name700'

,

'name7000'

,

'name700000'

,

'name70000'

,

'name7700000'

,

'name800'

,

'name8000'

,

'name800000'

,

'name80000'

,

'name6600000'

,

'name900'

,

'name9000'

,

'name900000'

,

'name90000'

,

'name9900000'

-- 耗時 0.011s -0.014s 
-- 增加 order by name 耗時 0.012s - 0.015s

select

 * 

from

 category_info_varchar_50 

wherenamein

(

'name100'

,

'name1000'

,

'name100000'

,

'name10000'

,

'name1100000'

,

'name200'

,

'name2000'

,

'name200000'

,

'name20000'

,

'name2200000'

,

'name300'

,

'name3000'

,

'name300000'

,

'name30000'

,

'name3300000'

,

'name400'

,

'name4000'

,

'name400000'

,

'name40000'

,

'name4400000'

,

'name500'

,

'name5000'

,

'name500000'

,

'name50000'

,

'name5500000'

,

'name600'

,

'name6000'

,

'name600000'

,

'name60000'

,

'name6600000'

,

'name700'

,

'name7000'

,

'name700000'

,

'name70000'

,

'name7700000'

,

'name800'

,

'name8000'

,

'name800000'

,

'name80000'

,

'name6600000'

,

'name900'

,

'name9000'

,

'name900000'

,

'name90000'

,

'name9900000'

-- 耗時  0.012s -0.014s 
-- 增加 order by name 耗時 0.014s - 0.017s

索引範圍查詢效能基本相同, 增加了order By後開始有一定效能差別;

3.驗證全表查詢和排序

全表無排序

全表有排序

select

 * 

from

 category_info_varchar_50 

orderbyname

 ;

--耗時 1.498s
select

 * 

from

 category_info_varchar_500 

orderbyname

  ;

--耗時 4.875s

結論:

全表掃描無排序情況下,兩者效能無差異,在全表有排序的情況下, 兩種效能差異巨大;

分析原因

varchar50 全表執行sql分析
我發現86%的時花在資料傳輸上,接下來我們看狀態部分,關注Created_tmp_files和sort_merge_passes
Created_tmp_files為3
sort_merge_passes為95
varchar500 全表執行sql分析
增加了臨時表排序
Created_tmp_files 為 4
sort_merge_passes為645
關於sort_merge_passes, Mysql給出瞭如下描述:
Number of merge passes that the sort algorithm has had to do. If this value is large, you may want to increase the value of the sort_buffer_size.
其實sort_merge_passes對應的就是MySQL做歸併排序的次數,也就是說,如果sort_merge_passes值比較大,說明sort_buffer和要排序的資料差距越大,我們可以透過增大sort_buffer_size或者讓填入sort_buffer_size的鍵值對更小來緩解sort_merge_passes歸併排序的次數。

四.最終結論

至此,我們不難發現,當我們最該欄位進行排序操作的時候,Mysql會根據該欄位的設計的長度進行記憶體預估, 如果設計過大的可變長度, 會導致記憶體預估的值超出sort_buffer_size的大小, 導致mysql採用磁碟臨時檔案排序,最終影響查詢效能;

歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,長按”或“掃描”下方二維碼噢
星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。
文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)

相關文章