为什么不建议用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 * 放一些无用的列,只会白白的占用缓冲空间。浪费本可以提高性能的机会。

目前就这些,欢迎各位大佬批评

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