zoukankan      html  css  js  c++  java
  • Oracle 列顺序测试

    列顺序测试

    大家在做表设计的时候通常对表中列的排列顺序没有过多注意,但是其实越常用的列,它的位置越靠前,则查询速度越快。

    因为每个block里面存储了row directory (每行数据在块中的位移地址)而没有存放column的位移
    所以只能根据column#通过扫描row header 以及 column data 来获取相应的列值
    越靠后的列,需要计算的越多

     

    建立测试表col_test

    1. declare
    2.   v_sql varchar2(4000) ;
    3. begin
    4.   v_sql := 'create table col_test (' ;
    5.   for i in 1 .. 100 loop
    6.     v_sql := v_sql || 'id'||i||' number ,' ;
    7.   end loop ;
    8.   v_sql := substr(v_sql,1,length(v_sql)-1) || ') ' ;
    9.   execute immediate v_sql ;
    10. end ;
    11. /
    12. _dexter@DAVID> desc col_test ;
    13. Name                                                  Null?    Type
    14. ----------------------------------------------------- -------- --------------
    15. ID1                                                            NUMBER
    16. ID2                                                            NUMBER
    17. ID3                                                            NUMBER
    18. ID4                                                            NUMBER
    19. ID5                                                            NUMBER
    20. ....
    21. ID99                                                           NUMBER
    22. ID100                                                          NUMBER
    复制代码


     

    初始化数据100w

    1. declare
    2.   v_sql varchar2(4000) ;
    3. begin
    4.   v_sql := 'insert into  col_test select ' ;
    5.   for i in 1 .. 100 loop
    6.     v_sql := v_sql || ' 1 ,' ;
    7.   end loop ;
    8.   v_sql := substr(v_sql,1,length(v_sql)-1) || ' from dual connect by level <= 1000000 ' ;
    9.   execute immediate v_sql ;
    10.   commit ;
    11. end ;
    12. /
    复制代码


    测试耗时

    1. declare
    2.   n       number ;
    3.   begin_t pls_integer ;
    4.   end_t   pls_integer ;
    5.   v_Sql   varchar2(4000) ;
    6. begin
    7.   for i in 1 .. 101 loop
    8.      begin_t := dbms_utility.get_time ;
    9.      if i = 1 then
    10.        v_sql := 'select count(*) from col_test' ;
    11.      else
    12.        v_sql := 'select count(id'||(i-1)||') from col_test' ;
    13.      end if ;
    14.      execute immediate v_sql into n ;
    15.      end_t   := dbms_utility.get_time ;
    16.      dbms_output.put_line('Col'||(i-1)||' : '||(end_t-begin_t)*10);
    17.   end loop ;
    18. end ;
    19. /
    20. --下面的单位为毫秒
    21. _dexter@DAVID> /
    22. Col0 : 150
    23. Col1 : 140
    24. Col2 : 110
    25. Col3 : 100
    26. Col4 : 100
    27. Col5 : 110
    28. Col6 : 110
    29. Col7 : 110
    30. Col8 : 110
    31. Col9 : 120
    32. Col10 : 130
    33. Col11 : 120
    34. Col12 : 110
    35. Col13 : 120
    36. Col14 : 120
    37. Col15 : 120
    38. Col16 : 130
    39. Col17 : 120
    40. Col18 : 160
    41. Col19 : 130
    42. Col20 : 140
    43. Col21 : 130
    44. Col22 : 140
    45. Col23 : 140
    46. Col24 : 140
    47. Col25 : 130
    48. Col26 : 160
    49. Col27 : 150
    50. Col28 : 140
    51. Col29 : 150
    52. Col30 : 150
    53. Col31 : 160
    54. Col32 : 180
    55. Col33 : 160
    56. Col34 : 160
    57. Col35 : 160
    58. Col36 : 160
    59. Col37 : 170
    60. Col38 : 180
    61. Col39 : 170
    62. Col40 : 170
    63. Col41 : 180
    64. Col42 : 180
    65. Col43 : 170
    66. Col44 : 210
    67. Col45 : 190
    68. Col46 : 180
    69. Col47 : 180
    70. Col48 : 190
    71. Col49 : 210
    72. Col50 : 190
    73. Col51 : 190
    74. Col52 : 200
    75. Col53 : 200
    76. Col54 : 230
    77. Col55 : 200
    78. Col56 : 210
    79. Col57 : 200
    80. Col58 : 210
    81. Col59 : 220
    82. Col60 : 210
    83. Col61 : 210
    84. Col62 : 220
    85. Col63 : 240
    86. Col64 : 220
    87. Col65 : 220
    88. Col66 : 220
    89. Col67 : 230
    90. Col68 : 270
    91. Col69 : 220
    92. Col70 : 230
    93. Col71 : 230
    94. Col72 : 270
    95. Col73 : 230
    96. Col74 : 240
    97. Col75 : 240
    98. Col76 : 250
    99. Col77 : 240
    100. Col78 : 260
    101. Col79 : 260
    102. Col80 : 280
    103. Col81 : 250
    104. Col82 : 250
    105. Col83 : 250
    106. Col84 : 250
    107. Col85 : 260
    108. Col86 : 260
    109. Col87 : 260
    110. Col88 : 290
    111. Col89 : 260
    112. Col90 : 270
    113. Col91 : 280
    114. Col92 : 280
    115. Col93 : 280
    116. Col94 : 270
    117. Col95 : 310
    118. Col96 : 270
    119. Col97 : 310
    120. Col98 : 290
    121. Col99 : 290
    122. Col100 : 280

    123. PL/SQL procedure successfully completed.
    复制代码


     

    可以看到耗时是线性增加的,所以在做表设计的时候,应该注意建表时的列的排列顺序,将常用的列放在靠前的位置

    null值测试

    关于null值的存储,可以查看我的另外一篇帖子http://www.itpub.net/thread-1718328-1-1.html
    因为基于它的存储原理,一个rowpiece中,如果列后面的值都为空,则不再占用存储空间,并且 rowheader cc(Column Count) 也不会将其记录到上面,
    也就是说如果你的列个数为5,但是这一行的rowpiece中第二列不为空,第三、四、五列为空
    cc会记为2
    但是如果第三、四列都为空,但是第五列不为空,则cc=5 ,第三列第四列使用都使用ff表示,占用了2个字节的存储空间
    null
    值在表中的位置除了影响存储空间,还会影响查询,因为在扫描数据的时候
    可以根据cc的个数来确定是否需要扫描column data的数据
    感兴趣的可以做一下测试
    100
    列,第50列为有值,其他都为null

     

    下面是测试内容:

    建立测试表null_test

    1. declare
    2.   v_sql varchar2(4000) ;
    3. begin
    4.   v_sql := 'create table null_test (' ;
    5.   for i in 1 .. 100 loop
    6.     v_sql := v_sql || 'id'||i||' number ,' ;
    7.   end loop ;
    8.   v_sql := substr(v_sql,1,length(v_sql)-1) || ') ' ;
    9.   execute immediate v_sql ;
    10. end ;
    11. /
    复制代码


    初始化数据100w

    1. insert into null_test(id50) select 100 from dual connect by level <= 1000000 ; commit ;
    复制代码


    测试

    1. declare
    2.   n       number ;
    3.   begin_t pls_integer ;
    4.   end_t   pls_integer ;
    5.   v_Sql   varchar2(4000) ;
    6. begin
    7.   for i in 1 .. 101 loop
    8.      begin_t := dbms_utility.get_time ;
    9.      if i = 1 then
    10.        v_sql := 'select count(*) from null_test' ;
    11.      else
    12.        v_sql := 'select count(id'||(i-1)||') from null_test' ;
    13.      end if ;
    14.      execute immediate v_sql into n ;
    15.      end_t   := dbms_utility.get_time ;
    16.      dbms_output.put_line('Col'||(i-1)||' : '||(end_t-begin_t)*10);
    17.   end loop ;
    18. end ;
    19. /
    20. --测试结果
    21. dexter@FAKE> /
    22. Col0 : 60
    23. Col1 : 80
    24. Col2 : 80
    25. Col3 : 90
    26. Col4 : 90
    27. Col5 : 100
    28. Col6 : 100
    29. Col7 : 100
    30. Col8 : 120
    31. Col9 : 110
    32. Col10 : 120
    33. Col11 : 130
    34. Col12 : 130
    35. Col13 : 140
    36. Col14 : 150
    37. Col15 : 160
    38. Col16 : 160
    39. Col17 : 160
    40. Col18 : 170
    41. Col19 : 170
    42. Col20 : 170
    43. Col21 : 180
    44. Col22 : 180
    45. Col23 : 180
    46. Col24 : 190
    47. Col25 : 190
    48. Col26 : 200
    49. Col27 : 200
    50. Col28 : 200
    51. Col29 : 200
    52. Col30 : 200
    53. Col31 : 210
    54. Col32 : 210
    55. Col33 : 220
    56. Col34 : 220
    57. Col35 : 230
    58. Col36 : 220
    59. Col37 : 220
    60. Col38 : 230
    61. Col39 : 240
    62. Col40 : 240
    63. Col41 : 230
    64. Col42 : 240
    65. Col43 : 250
    66. Col44 : 250
    67. Col45 : 250
    68. Col46 : 260
    69. Col47 : 260
    70. Col48 : 270
    71. Col49 : 260
    72. Col50 : 270
    73. Col51 : 70
    74. Col52 : 80
    75. Col53 : 70
    76. Col54 : 70
    77. Col55 : 80
    78. Col56 : 70
    79. Col57 : 70
    80. Col58 : 80
    81. Col59 : 70
    82. Col60 : 70
    83. Col61 : 70
    84. Col62 : 70
    85. Col63 : 80
    86. Col64 : 80
    87. Col65 : 70
    88. Col66 : 70
    89. Col67 : 70
    90. Col68 : 80
    91. Col69 : 70
    92. Col70 : 70
    93. Col71 : 70
    94. Col72 : 80
    95. Col73 : 70
    96. Col74 : 70
    97. Col75 : 70
    98. Col76 : 80
    99. Col77 : 70
    100. Col78 : 70
    101. Col79 : 70
    102. Col80 : 80
    103. Col81 : 70
    104. Col82 : 70
    105. Col83 : 80
    106. Col84 : 70
    107. Col85 : 70
    108. Col86 : 70
    109. Col87 : 80
    110. Col88 : 70
    111. Col89 : 70
    112. Col90 : 80
    113. Col91 : 70
    114. Col92 : 70
    115. Col93 : 70
    116. Col94 : 80
    117. Col95 : 70
    118. Col96 : 70
    119. Col97 : 70
    120. Col98 : 70
    121. Col99 : 70
    122. Col100 : 80

    123. PL/SQL procedure successfully completed.
    复制代码



    可以看到前50列的查询耗时是线性增加,但是从第51列开始到最后都维持到一个稳定的值
    线性增加是因为Oracle 数据库会不断扫描row header 以及 column data 计算要查找的列的位移值
    而从第51列开始,则可以直接扫描row header CC 个数即可拿到要查找的列的值(null),所以节省了大量扫描column data部分的时间,也证实了上面提出的观点
    所以设计表的时候,尽量把会存储null值的列放在表的末尾

     

    为了加深印象,我们使用bbed抽出一条数据来看一下它在block中到底是怎样存储的,加深一下印象

    1. dexter@FAKE> select dbms_rowid.rowid_relative_fno(t.rowid) as "FNO#",
    2.   2         dbms_rowid.rowid_block_number(t.rowid) as "BLK#",
    3.   3         dbms_rowid.rowid_row_number(t.rowid) as "ROW#"
    4.   4    from dexter.null_test t
    5.   5   where rownum<2 ;

    6.       FNO#       BLK#       ROW#
    7. ---------- ---------- ----------
    8.          6        284          0

    9. BBED> set dba 6,284
    10.         DBA             0x0180011c (25166108 6,284)
    11. BBED> p *kdbr[0]
    12. rowdata[4675]
    13. -------------
    14. ub1 rowdata[4675]                           @5878     0x2c

    15. BBED> set offset 5878
    16.         OFFSET          5878

    17. BBED> dump /v offset 5878 count 58
    18. File: /u01/apps/oracle/oradata/fake/gg_trans201.dbf (6)
    19. Block: 284     Offsets: 5878 to 5935  Dba:0x0180011c
    20. -------------------------------------------------------
    21. 2c0032ff ffffffff ffffffff ffffffff l ,.2.............
    22. ffffffff ffffffff ffffffff ffffffff l ................
    23. ffffffff ffffffff ffffffff ffffffff l ................
    24. ffffffff 02c2022c 0032              l .....Â.,.2

    25. <16 bytes per line>
    复制代码


    大家一定对count 58 产生疑问,请继续看下去

    row header 2c0032
    它包括:
    2c=flag=00101100=--H-FL--=header+first+last
    00=lb itl slot 0x00
    32=cc column count = 16x3+2=50
    因为这是一个普通的堆积表,所以后面就是rowpiececolumn data了,包括前4949个字节的ff和第50列的列长度02和列值c202

    1. dexter@FAKE> select dump(100,16) from dual ;


    2. DUMP(100,16)
    3. -----------------
    4. Typ=2 Len=2: c2,2
    复制代码


    so 3+49+1+2=55
    后面3个字节是下一行的row header : 2c 0032

     

      

    LB

      
      

    ITL

      
      

    CC

      
      

    COL#1 value

      
      

    COL#2-49 value

      
      

    COL#50 length

      
      

    COL#50 value

      
      

    COL#51 value

      
      

    COL#52-100 value

      
      

    2C

      
      

    00

      
      

    32

      
      

    FF

      
      

    FF

      
      

    02

      
      

    C202

      
      

    FF

      
      

    FF

      

     

    附录:

    rowpiece构成

    可以参考官方文档中的说明
    12-7

    1.jpg


    row piece header
    包括columns个数,other row piece的位置(当出现row chain或者列个数大于253的时候),cluster key cluster table
    通常一个row header 至少包含3个字节(状态、itl槽位、column个数)


    cluster table
    的存储情况会在其他章节中介绍
    关于row chain的存储,可以我的另外一篇帖子,超过253列的存储

    http://www.itpub.net/thread-1719026-1-1.html


    LB说明


    KCHDFLPN
    K=Cluster key                    (用来表示cluster key)
    C=Cluster table member    (当表中拥有cluster时使用此标识)
    H=Head of row piece         (含有Rowpiece header)
    D=Delete row                   (表示已经删除)
    F=First data piece             (Rowpiece 的第一个piece)
    L=Last data piece              (Rowpiece 的最后一个piece,Rowchain或者列个数大于253时候会出现)
    P=First column continues from previous piece  (没有重现过)
    N=Last column continues in next piece            (没有重现过)

  • 相关阅读:
    一、Git安装
    三、ASP.NET Core 部署Linux 第二篇(Core SDK 3.1)
    .net core 3.1
    一、GitHub创建仓并提交项目
    二、GitHub创建仓并提交项目
    django xdmin使用
    python logging一个通用的使用模板
    一则小脚本(工作中用)
    jmeter 压测最近的心得体会
    jmeter 分布式实战
  • 原文地址:https://www.cnblogs.com/peter-peng/p/3505969.html
Copyright © 2011-2022 走看看