mysql 字段值分布很少的字段要不要加索引

2021-10-09 11:37:00 字數 3588 閱讀 1420

在我還是個mysql新手的時候,看到有的同事給字段值分布很少的字段也加索引,這違背了我看過的大部分mysql索引優化的文章內容,甚是疑惑。

例如:訂單狀態字段只有6個值: 0 待確認,1 已確認,2 已收貨,3 已取消,4 已完成,5 已關閉

在我理解mysql b+tree的原理後,很有必要去實戰這種情況到底有沒有必要加索引。

建立帶索引的表

drop table if exists `bool_index`;

create table `bool_index` (

`id` int (11) not null auto_increment,

`rand_id` varchar (200) comment '隨機數',

`order_status` tinyint (1) not null default '0' comment '訂單狀態.0待確認,1已確認,2已收貨,3已取消,4已完成,5已作廢',

`created_at` datetime not null,

primary key (`id`),

key `idx_order_status` (`order_status`)

) engine = innodb default charset = utf8;

建立不帶索引的表

drop table if exists `bool_no_index`;

create table `bool_no_index` (

`id` int (11) not null auto_increment,

`rand_id` varchar (200) comment '隨機數',

`order_status` tinyint (1) not null default '0' comment '訂單狀態.0待確認,1已確認,2已收貨,3已取消,4已完成,5已作廢',

`created_at` datetime not null,

primary key (`id`)

) engine = innodb default charset = utf8;

通過儲存過程造一些測試資料

delimiter $$

drop procedure if exists `proc_index`$$

create procedure proc_index()

begin

declare rand_id varchar(120);

declare order_status int(1);

declare i int default 0;

declare createtime datetime;

-- 除錯過程, 插入一些資料

while i < 10000 do

set rand_id= substring(md5(rand()),1,28);

-- 生成 訂單狀態值.0待確認,1已確認,2已收貨,3已取消,4已完成,5已關閉

set order_status = floor(rand()*10)%6;

set createtime = now();

insert into `bool_index`(`rand_id`,`order_status`,`created_at`) values(rand_id,order_status,createtime);

insert into `bool_no_index`(`rand_id`,`order_status`,`created_at`) values(rand_id,order_status,createtime);

set i=i+1;

end while;

end$$

call proc_index();

表資料量/耗時

select * from bool_index

where order_status=3 and rand_id='bd0bcd23960dbe0140bea563e7bd';

select * from bool_no_index

where order_status=3 and rand_id='bd0bcd23960dbe0140bea563e7bd';

order_status=3資料總量

1w0.002s

0.002s

約2000

4w0.011s

0.009s

約8000

8w0.021s

0.021s

約1.6w

16w0.059s

0.040s

約3.2w

32w0.142s

0.110s

約6.3w

64w1.194s

0.383s

約12w

100w

2.761s

0.563s

約20w

200w

7.025s

1.158s

約40w

通過比較,在資料量小於16w時,加索引和不加索引查詢速度差別不大,資料大於16w時,隨著資料量的增大,加索引的查詢速度相對會越來越慢。

如:第20001萬條記錄rand_id='56079ad22da839c1a00bd812a191'  order_status=3

通過explain分析執**況

加索引掃瞄的資料rows=366798,不加索引rows=997976 (全表掃瞄),明明加索引的掃瞄條目更少,為何反而變慢了呢?

舉乙個非常好理解的場景(通過索引讀取表中20%的資料)解釋一下這個有趣的概念:(例子** 

假設一張表含有10萬行資料--------100000行

我們要讀取其中20%(2萬)行資料----20000行

表中每行資料大小80位元組----------80bytes

資料庫中的資料塊大小8k----------8000bytes

所以有以下結果:

每個資料塊包含100行資料---------100行

這張表一共有1000個資料塊--------1000塊

上面列出了一系列淺顯易懂的資料,我們挖掘一下這些資料後面的故事:

通過索引讀取20000行資料 = 約20000個table access by rowid = 需要處理20000個塊來執行這個查詢

但是,請大家注意:整個表只有1000個塊!

所以:如果按照索引讀取全部的資料的20%相當於將整張表平均讀取了20次!!so,這種情況下直接讀取整張表的效率會更高。)(索引還涉及多次回表查詢問題)

總結:禁止在更新十分頻繁、區分度不高的屬性上建立索引

具體深層次的原因請先了解b+tree的底層原理

mysql字段值連線語法

問題 現有一批人員組織名稱需要組合查詢,表userinfo如下 employee company branch department usergroup hall salepoint 0288000001 廣州分公司 工程建設中心 0221000002 廣州分公司 從化分公司 政企客戶中心 0221...

根據字段值取字段別名

描述 根據某一字段的不同值,為另一字段取不同的別名。背景 建乙個學生缺勤表,其中有乙個欄位是缺勤狀態status,取值為0 5,分別代表曠課 早到 遲到 病假 事假 公假。求出各系 各缺勤狀態的人數。select 系別,sum case when 缺勤狀態 0 then 1 else 0 end a...

Mysql 新增字段 修改字段 刪除字段

alter table 表名 add 欄位名 字段型別 字段長度 default 預設值 comment 注釋 例如 alter table order add code char 6 default null comment 優惠碼 2 修改字段 修改欄位名 字段型別 長度 a 修改欄位名 alt...