当前位置: 首页>数据库>正文

SQL 临时表加索引 临时表建立索引




SQL 临时表加索引 临时表建立索引,SQL 临时表加索引 临时表建立索引_数据,第1张


建索引,别看是 CREATE INDEX 那么简单。建好了,可真不是简单的事儿。

你看,为建索引,还有作者专门写了一本书。唯一一本能从索引本质说起来的书,从磁盘随机读,顺序读说起,从数据库索引结构剖析,从浅入深,从局部入手,全盘考量,最终选择合适的索引,说它是系统工程,可真一点没错。

当然,数据库设计与开发中,类似的系统工程思维,真有不少,看我前面一篇帖子,就是最好的证明:

作为面试官,我最常用的两类SQL数据库面试题

今天,我要分享的,是针对千万级别的表,应该慎重考虑的一种索引及其原理。

举个例子:


DECLARE @ORDER_ID VARCHAR(15) = 'A-14RX7',
        @ATTR NVARCHAR(128) = 'PRODUCT_NAME'

SELECT A.EVENT_ID,
    A.ORDER_ID,
    A.ATTR,
    A.VAL1,
    A.VAL2
FROM EVENT B 
    INNER JOIN EVENT_LOG A 
        ON A.EVENT_ID = B.ID 
WHERE A.ORDER_ID = @ORDER_ID 
    AND A.ATTR = @ATTR
ORDER BY B.BEGIN_DATE ASC


其中 EVENT 的数据量有上百万,EVENT_LOG 的数据量有2000多万

此时的索引情况是 EVENT 在 ID 上有唯一索引,而 EVENT_LOG 上有 针对 EVENT_ID 做了非唯一索引。

就上面这段脚本,运行时间超过了20秒。

很可能大家看到这样一个 Join, 两边都建立了索引,认为查询效率应该没有问题。你看,索引都建了,也都按照索引去走了。那么为什么效率还那么慢呢?其实,就是索引无效。

比如我第一次建索引的时候,就犯了这么个错误:


CREATE INDEX IDX_EVT_ATTR_VAL 
ON EVENT_LOG(ORDER_ID,ATTR) 
INCLUDE(EVENT_ID,VAL1,VAL2)


众所周知,索引呈树结构组织,最底层的数据页,存储索引真实的数据,还有指向堆表的地址或者主键值。


SQL 临时表加索引 临时表建立索引,SQL 临时表加索引 临时表建立索引_SQL 临时表加索引_02,第2张


上图就是典型的索引结构图。索引的键值,都存储在 Page3, Page4(当然实际的索引页复杂的多),俗称叶子页,而中间的那些数据页,存储的数据,都是到达最终叶子页的路径,俗称中间页。最顶层的那个页,称为顶点(root)页。

本次查询中,从 EVENT_LOG 查询的字段不多,因此把这些字段都存到索引叶子结点,明显能够减少回表的成本。我真就这么干了。

潇洒的回车执行后,去泡了杯茶回来,结果一看,我楞眼了,索引还没建完。于是赶紧停掉,好险。都已经 2 分钟过去了。

仔细看了下 EVENT_LOG 表结构,才发现 VAL1,VAL2 居然是 2 个超长字符串,将其放在叶子页,明显加大了索引的叶子页的空间,原本1000个页搞定的事情,说不定就长成了10万个页,使得索引无效。

再看下表统计,有2000多万条数据。而真实逻辑是,1 个 EVENT_ID 在 EVENT_LOG 中对应的数据,可能有上百条,但在 EVENT_ID 加 ATTR 的限定下,数据条数不会超过 10 条。

于是,果断放弃覆盖索引,改成一般索引:


CREATE INDEX IDX_EVT_ATTR_VAL 
ON EVENT_LOG(ORDER_ID,ATTR)


果然,不到 40 秒,索引建立完毕,最开始的查询,也秒出。

所以,大家千万别以为,索引建了就一定 有用,建错索引,带来的后果很严重。


https://www.xamrdz.com/database/6vw1926437.html

相关文章: