不考虑索引,执行计划要走TABSCAN,问题转化为TABSCAN时扫描记录的顺序。 一个直观的想法是按照记录插入的顺序来扫描,然而这种想法是错误的。正确的答案是:和记录在表空间中的存储位置有关系,一个表的数据可能使用了多个数据页(page),编号为page 0, page 1, ... page n;而每个页中可能存放了多条记录,每条记录都占用一个Slot,Slot也是有编号的。TABSCAN扫描的顺序是按照page号从小到大、同一page中按照Slot编号从小到大,也是"SELECT * FROM TABLE"时返回记录的顺序。
先来看一个简单的例子,下面的脚本在数据库mydb中创建了一个4K大小的DMS类型的表空间userspace1和一张表TB1,往表TB1里插入6条记录,其中第2、3、5条记录较大,大小超过page的一半,这样可以确保插入时会分配新的数据页:
$ cat test1.sh
#!/bin/sh
#prepare a string of length 2402, will be used to insert into TB1
str=`awk 'BEGIN { for(i = 1; i <= 2400; i++) x=x"a"; print "'\''"x"'\''"}'`
db2 "terminate"
db2 "connect to mydb"
db2 "drop tablespace userspace1"
db2 "create tablespace userspace1 pagesize 4K managed by automatic storage"
db2 "CREATE TABLE TB1(id int, name VARCHAR(3990)) in userspace1"
db2 "insert into tb1 values(1,'abc')"
db2 "insert into tb1 values(123, $str)"
db2 "insert into tb1 values(234, $str)"
db2 "insert into tb1 values(2,'abc')"
db2 "insert into tb1 values(345,$str)"
db2 "insert into tb1 values(3,'abc')"
db2 "select id from tb1"
运行脚本,最后的select输出如下,我在每条记录后面补充了db2dart的检查结果以显示其存储位置:
$ sh test1.sh
ID
-----------
1 -> Page 0, Slot 4
123 -> Page 0, Slot 5
234 -> Page 1, Slot 0
2 -> Page 1, Slot 1
345 -> Page 2, Slot 0
3 -> Page 2, Slot 1
6 record(s) selected.
==================================
test1中,TABSCAN的结果和Insert的顺序完全一致。现在再做另一个测试,test2.sh里面的SQL语句内容、顺序和test1.sh中完全一样,只不过在每次insert之前,要断开数据库的连接,并重连:
$ cat test2.sh
#!/bin/sh
#prepare a string of length 2402, will be used to insert into TB1
str=`awk 'BEGIN { for(i = 1; i <= 2400; i++) x=x"a"; print "'\''"x"'\''"}'`
db2 "terminate"
db2 "connect to mydb"
db2 "drop tablespace userspace1"
db2 "create tablespace userspace1 pagesize 4K managed by automatic storage"
db2 "CREATE TABLE TB1(id int, name VARCHAR(3990)) in userspace1"
db2 "insert into tb1 values(1,'abc')"
db2 "connect reset"
db2 "connect to mydb"
db2 "insert into tb1 values(123, $str)"
db2 "connect reset"
db2 "connect to mydb"
db2 "insert into tb1 values(234, $str)"
db2 "connect reset"
db2 "connect to mydb"
db2 "insert into tb1 values(2,'abc')"
db2 "connect reset"
db2 "connect to mydb"
db2 "insert into tb1 values(345,$str)"
db2 "connect reset"
db2 "connect to mydb"
db2 "insert into tb1 values(3,'abc')"
db2 "select id from tb1"
运行结果如下:可以看到,比较短的三条记录都放到了Page 0上
$ sh test2.sh
ID
-----------
1 -> Page 0, Slot 4
123 -> Page 0, Slot 5
2 -> Page 0, Slot 6
3 -> Page 0, Slot 7
234 -> Page 1, Slot 0
345 -> Page 2, Slot 0
6 record(s) selected.
========================
通过上面两个测试可以看到,TABSCAN时扫描记录的顺序和记录插入顺序没有必然联系,而是与记录在表空间中的存储顺序有关,而存储顺序与 INSERT 的内部算法相关,如果想了进一步了解,请参考下面Db2专家郝庆运写的两篇文章:
注:本文之研究内容和结果,仅限于DMS表空间的情形并得到db2dart和db2trace工具的验证,对于SMS表空间未做研究!测试环境为linux, Db2 9.7 & Db2 10.5