作为开发人员数据库查询语句我们经常用到,但是你是否想过为什么大厂都会强制开发人员禁止使用 SELECT * 语句?你一定会说因为效率低啊,多差除了一些无用的数据。如果是这么想的,那就继续听我来说。
我们先来看一下使用 SELECT * 会产生什么样的后果。首先它会增加查询分析器的解析成本,数据库不仅仅会解析字段内容,还会解析对象、权限、属性等更多的相关内容,这些多余的东西往往会给数据库带来很大的负担。其次如果数据库进行了字段的删除/增加就会造成与 resultMap 不一致进而导致程序报错无法正确运行。接着在查询数据量很大的情况下还会增加网络开销和数据传输时长,这时如果表中存在日志内容字段或者二进制字段的情况下,数据传输的量将以几何倍数增长,不管数据库和应用程序是否位于同一台服务器(或集群),都会感觉到明显的卡顿,而且数据表中一般会包含 varchar 、blob 和 text 这样的大文本/超大文本类型的字段,这样就更近一步的增加了 IO 开销。在 MySQL 的数据库引擎是 InnoDB 的情况下,遇到这种大文本/超大文本类型字段的情况会将超过 728 字节的数据序列化到另一个地方存储,每次读取相对应的记录的时候就会增加一次操作步骤。
上面所说的只是从表象层面来说的,这些我们都能看得到感受得到。下面我们从 MySQL 的索引层面来说一下。使用 SELECT * 会造成 MySQL 优化器的 覆盖索引 策略失效,这个策略在业内又是被公认为速度和效率都是极好的,那么这样一来这个优秀的策略就无用武之地了。那么为什么说覆盖索引策略是极好的呢,下面我们通过一个例子来讲解一下。
我们的数据库中存在一张表 emp,表中包含 id,name,sex,birthday 字段,其中 id 是主键,sex 存在索引。这时磁盘上就存在两个 B+ 树,一棵是主键索引树(聚集索引)保整行内容,另一棵是 sex 索引树(辅助索引)保存 id 和 sex内容。当我们通过希望 where 语句查询出 sex 等于男的数据时,那么where 条件可以通过 sex 索引过滤掉不符合要求的数据,如果用户只想获取 sex 和 id 字段的数据的话,sex 索引就可以直接返回所需的数据,这种情况时极好的。如果我们的查询语句是 SELECT * 的话,那么 MySQL 通过 sex 索引树过滤出数据后,还要再去主键索引树上去拿到符合条件的所有字段的内容,这样就多了一次索引树的查询,进而降低了查询速度。在这里需要注意的是:我们在开发中辅助索引的数量往往大于聚集索引的数量,那么利用辅助索引去进行查询的时候是从内存中直接读取数据的,聚集索引有时会去读取磁盘这样就出现了拆查询缓慢的问题。
前面我们提到了辅助索引,辅助索引一般包含单列索引和联合索引。单列索引就是我们前面所讲的 sex 索引,下面我们重点讲解一下联合索引。所谓的联合索引就是多个字段联合在一起,这是我们所知道的。但是大部分开发人员所不知道的是联合多因其实是建立了多个索引,比如我们将上例中的 name、sex、birthday 三个字段做一个联合索引,那么在数据库中实际上是创建了三个索引,分别是:name 索引,sex_name 索引、sex_name_birthday 索引 (注意:这里只是为了看起来易读,实际数据库索引命名不是这样的)。
讲到这里我问各位读者一个小问题:where name='xxx' and birthday ='2019-01-01'
这个语句使用了上面三个索引中的哪些?肯定会有读者说用到了 name 索引和sex_name_birthday 索引,真的是这样吗?我们不妨先来看看 MySQL 中索引的使用规则,我们可以把索引看成一本词典,name、sex、birthday 分别是这本辞海的字母、字和词,如果我们想要查询字(sex)的时候就需要知道这个字的以什么字母(name)开头,如果我们要查询词(birthday )的时候就需要知道这个此是以什么字(sex)开头,这个开头的字(sex)是以什么字母(name) 开头的。这么一说你应该就明白了吧,前面所问的那个问题的答案就是只用到了 name 索引,这是因为 birthday 位于 sex_name_birthday 索引 我们无法跨过 sex_name 索引去查询它,就像我们无法通过字母去查询某个词一样。
讲了这么多我们来总结一下使用联合索引的好处。首先它可以减少开销,当我们建立了一个联合索引就相当于创建了多个索引,我们知道多创建一个索引就会增加磁盘和操作开销,但是使用联合索引则会减少这些开销。其次可以实现索引覆盖,MySQL 可以通过遍历索引直接获取到数据,进而减少了回表的操作和 IO 操作。最后通过联合索引可以在筛选出符合条件的数据的同时提高查询效率。
当然并不是说任何情况下都适合建立索引,下面列出了不适合建立索引的情况。
这篇文章从 SELECT * 讲起,最后以联合索引结尾,内容稍显复杂但是只要记住:避免使用 SELECT * ,会使覆盖索引策略失效,多索引情况下尽量使用联合索引减少开销。