今天要说的是索引,很奇怪是吧,题目是设计,索引是用来优化查询的,似乎是跑题了。
其实不完全是。索引的确是用来优化查询的,但索引不能在出现查询性能糟糕的时候才考虑,而是应该在数据库设计的时候就预先予以考虑。
任何事物都是对立共同体,都具有两面性。数据库也不例外,查询和数据更新(这里的更新是 change 包括 插入 insert、修改 update、删除 delete)就是相互矛盾的。很多人可能都没有感觉到这对矛盾。在单一进程(或线程)情况下,这对矛盾并不明显;但在对数据库的并发访问时,这对矛盾就会凸显出来。
首先来考虑一下并发的修改数据库,数据库会如何处理。首先一个会话抢到了资源,然后对资源加锁(其他会话等待)、数据更新,最后释放资源(其他会话重新抢资源)。这样的流程,数据库的吞吐量会很低,解决办法就是将资源变碎,平衡每个资源的使用量。
通过上面的分析,可以得出:数据更新操作需要数据尽可能以分散的方式存储。
再来考虑一下查询操作,数据库会扫描表中的全部数据。相信有开发经验的人都知道,遍历数组要比遍历链表更快。这是因为连续的数据存储会节省重新寻址的开销。对于数据库也是一样,紧凑的数据存储会有利于查询。
查询操作需要数据以更紧凑的方式存储,而更新操作需要数据以更分散的方式存储,这就是矛盾。对于这样的矛盾,不同的数据库产品会选取各自不同的平衡方式。
说完数据库的矛盾,再来说索引的矛盾。索引的矛盾众所周知,它可以改善查询的性能,但增加了数据更新的开销。但和少有人注意过索引引起的开销,《SQL语言艺术》的作者进行了一个实验,得出了一个出乎意料的结论:增加两个索引所带来的开销相当于一个触发器。我并不是想说索引本身有什么不好,而是想说这样的开销是否值得。
究竟需要什么样的索引,的确和查询有很大的关系。但是,索引带来的开销也是惊人的,在增加索引之前一定要慎重。哪些索引可以合并?哪些索引会让另一些索引失效?哪些索引是必须的?哪些索引可有可无?这一系列的问题都需要做通盘的考虑,不能因为少数几个查询而导致整个应用的数据吞吐量下降。