爲什麼不建議用select * ?

不論是自己寫還是看公司的祖傳代碼,是不是都會遇到這種SQL:

select * from table;

“經驗”豐富後,可能你看到了這樣的SQL後,大腦中本能的搜索出了網上的各種說使用*不好的文章,但具體是爲啥不好呢?

業務表象原因:

  1. 有這樣的場景,一個表T被A,B兩個功能使用,兩個功能查詢表T用的都是select *,然後有一天晚上一道閃電從產品經歷的眼前閃過,於是產品經理決定改一下B功能。經過一番商議之後,發現此功能必須(就必須,不要在此糾結)得在原有的表上加一個字段。然後問題就來了,加了字段後A功能某名奇妙的也加了個字段,並且沒有任何卵用。
  2. 還有一個這樣的場景,在你即將離開公司的前一天。領導讓你加班到深夜,此刻也許你的大腦中有一行命令在盤旋:
rm -rf /

但是,你終究是慫的,只是在臨走前留下了 select * from table代碼,然後第二天,新人入職,想看下這個功能取的是什麼表的什麼字段。一陣歡快的查找和搜索,定位到了這段SQL。隨後可能就是一句MMP。

業務方面無非就是此類問題,到此爲止

數據庫原理方面原因(此處以MySql爲例):

  1. 使用*無法利用index類型的單表查詢。在mysql中不同的查詢條件,經mysql優化器優化後可能會利用不同的查詢類型。比如下面這張表:
create table person(
	id int not null auto_increment,
	name varchar(50) not null,
	job1 varchar(50) not null,
	job2 varchar(50) not null,
	job3 varchar(50) not null,
	
	primary key id,
	key p_idx_j1(job1,job2,job3)
);

數據是:

1,ermao,programmer,doctor,teacher
2,haha,engineer,boss,star

這種數據結構,如果想查詢job1,job2,job3,用 select * from person where job2 = 'boss'是不會走二級索引的,只能在聚集索引的那顆B+樹上做全表掃描,也就是最慢的一種查詢方式。

如果是select job1,job2,job3 from person where job2 = 'boss'這種方式查詢呢?是不是認爲job2在聯合索引的中間,所以並不會走二級索引。其實它是會走二級索引的那個B+樹的。我們知道在mysql中(Innodb引擎)聚集索引是一定存在的,如果表結構中定義了主鍵,聚集索引就根據主鍵建立,否則如果有唯一列就用唯一列,否則就會自動在每條記錄中生成一個隱藏的ID列並以此建立聚集索引,聚集索引的葉子節點就是真實的數據。
比如此數據結構,就會有兩棵B+樹,一顆是聚集索引,另一顆是job1,job2,job3定義的聯合索引(二級索引)。二級所索引的葉子節點是索引列加ID,所以走索引的大致流程分兩步,一是先根據索引列在二級索引樹中找到ID,然後根據ID在聚集索引中找到對應的記錄(這一步也稱爲回表)。
有沒有發現問題?也就是二級索引中其實已經包含了job1,job2,job3這三列的數據。那如果我查詢語句中查詢的就是這三列的數據,並且搜索條件中也是這三列中的一列,那我還有必要分兩步操作嗎?還有必要進行回表操作嗎?我只需要在二級索引樹中進行搜索即可。所以對於這種情況,如果用select job1,job2,job3 from person where job2 = 'boss'這種指定列的查詢只需走二級索引,無需回表,並且二級索引的每條記錄不含有隱藏列,加載內存的操作會更快。而如果用select * 的話,因爲二級索引樹中並沒有name那一列,所以根本走不了二級索引,只能對聚集索引進行全表掃描,在數據兩大的情況下,性能影響還是很可觀的。

  1. 連接查詢時,* 無法進入緩衝池
    mysql中連接查詢的原理是先對驅動表進行查詢操作,然後再用從驅動表得到的數據作爲條件,逐條的到被驅動表進行查詢。
    每次驅動表加載一條數據到內存中,然後被驅動表所有的數據都需要往內存中加載一遍進行比較。效率很低,所以mysql中可以指定一個緩衝池的大小,緩衝池大的話可以同時加載多條驅動表的數據進行比較,放的數據條數越多性能io操作就越少,性能也就越好。所以,如果此時使用select * 放一些無用的列,只會白白的佔用緩衝空間。浪費本可以提高性能的機會。

目前就這些,歡迎各位大佬批評

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章