12、表的访问方式(索引)_2

表的访问方式

以t1表为例来看表的访问方式
首先创建了一个用户,建立了一张表t1,按照object_id列排序的:

SQL> create user u1 identified by u1;    -- 创建用户u1
User created.

SQL> grant connect,resource,dba to u1;   -- 给u1授权
Grant succeeded.

SQL> connect u1/u1    -- 连接用户u1
Connected.

SQL> create table t1 as select * from dba_objects order by object_id;   -- 创建t1表
Table created.

SQL> exec dbms_stats.gather_table_stats('U1','T1');     -- 收集t1表的统计信息
PL/SQL procedure successfully completed.

SQL>

查询t1表的统计信息:
1、t1表的块数:

select * from dba_tables s where s.table_name='T1';


占1261个块

2、object_id列的唯一值数量:

select * from dba_tab_columns a where a.OWNER='U1' and a.TABLE_NAME='T1';


object_id列有86315个唯一值

对于一个表,我们收集完统计信息以后,关注的几个东西:
1、表的行数
2、表的块数
3、重点列的唯一值的数量

查询从t1表里取object_id = 150的数据SQL语句的执行计划(无索引,全表扫描):

SQL> set autotrace trace
SQL> set linesize 150
SQL> set pagesize 0

SQL> select * from t1 where object_id = 150;

Execution Plan   -- 语句select * from t1 where object_id = 150;的执行计划
----------------------------------------------------------
Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation	      | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |	     |     1 |    98 |   344   (1)| 00:00:05 |
|*  1 |  TABLE ACCESS FULL| T1      |     1 |    98 |   344   (1)| 00:00:05 | --TABLE ACCESS FULL:全表扫描,因为没有索引,所以只能全表扫描
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_ID"=150)    -- 过滤条件

Statistics
----------------------------------------------------------
	 18  recursive calls
	  0  db block gets
     1244  consistent gets   -- 内存读
	  0  physical reads    -- 物理读
	  0  redo size
     1625  bytes sent via SQL*Net to client
	524  bytes received via SQL*Net from client
	  2  SQL*Net roundtrips to/from client
	  2  sorts (memory)
	  0  sorts (disk)
	  1  rows processed

SQL> /   -- 再次执行一下

Execution Plan
----------------------------------------------------------
Plan hash value: 3617692013

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    98 |   344   (1)| 00:00:05 |
|*  1 |  TABLE ACCESS FULL| T1    |     1 |    98 |   344   (1)| 00:00:05 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_ID"=150)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1235  consistent gets
       1232  physical reads
          0  redo size
       1625  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

在t1表的object_id列上建一个索引:

SQL> create index i_t1_id on t1(object_id);
Index created.

建完索引之后,再次统计一下t1表的信息:

SQL> exec dbms_stats.gather_table_stats('U1','T1');    -- 再次收集t1表的统计信息

查询t1表关于索引的相关信息:

select * from dba_indexes s where s.table_name='T1' and s.owner='U1';


索引的叶子数量191,唯一值数量86315

查询索引在哪个列上:

select * from dba_ind_columns x where x.table_name='T1' and x.table_owner='U1';

查询t1表索引的集群因子:

select * from dba_indexes s where s.table_name='T1' and s.owner='U1';


集群因子:1233

关于索引需要关注的几个东西:
1、唯一值的数量
2、集群因子

查询有索引时从t1表里取object_id = 150的数据SQL语句的执行计划(走索引):

SQL> set autotrace trace
SQL> set linesize 150
SQL> set pagesize 0

SQL> select * from t1 where object_id = 150;

Execution Plan
----------------------------------------------------------
Plan hash value: 1111474805

---------------------------------------------------------------------------------------
| Id  | Operation		            | Name    | Rows  | Bytes | Cost (%CPU)| Time  |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	      |	    |     1 |    98 |     2(0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1      |     1 |    98 |     2(0)| 00:00:01 |    -- 先访问索引之后,在通过INDEX ROWID访问表
|*  2 |   INDEX RANGE SCAN	      | I_T1_ID |     1 |       |     1(0)| 00:00:01 |    -- INDEX RANGE SCAN:走索引了
---------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=150)

Statistics
----------------------------------------------------------
	  0  recursive calls
	  0  db block gets
	  4  consistent gets    -- 内存读从原来全表扫描的1244降低到4了
	  0  physical reads
	  0  redo size
     1628  bytes sent via SQL*Net to client
	524  bytes received via SQL*Net from client
	  2  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
	  1  rows processed

查询从t1表里取object_id 1到8000的数据SQL语句的执行计划(走索引):

SQL> select * from t1 where object_id between 1 and 8000;
7960 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 1111474805

---------------------------------------------------------------------------------------
| Id  | Operation		            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	      |	    |  7879 |   754K|   132(0)| 00:00:02 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T1      |  7879 |   754K|   132(0)| 00:00:02 |
|*  2 |   INDEX RANGE SCAN	      | I_T1_ID |  7879 |       |    19(0)| 00:00:01 |    -- 走的还是索引
---------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID">=1 AND "OBJECT_ID"<=8000)

Statistics
----------------------------------------------------------
	  1  recursive calls
	  0  db block gets
       1173  consistent gets     -- 内存读:1173
	  0  physical reads
	  0  redo size
     838468  bytes sent via SQL*Net to client
       6354  bytes received via SQL*Net from client
	532  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
     7960  rows processed

建另外一张表:t2表,按照object_name排序的

SQL> create table t2 as select * from dba_objects order by object_name;
Table created.

在t2表的object_id列上建一个索引:

SQL> create index i_t2_id on t2(object_id);
Index created.

收集t2表的统计信息:

SQL> exec dbms_stats.gather_table_stats('U1','T2');
PL/SQL procedure successfully completed.

查询t2表索引的集群因子:

select * from dba_indexes s where s.table_name='T2' and s.owner='U1';


集群因子:46382,相对于t1表的1233来说,这个集群因子就很大了,这个索引就不是很好了

查询从t2表里取object_id 1到8000的数据SQL语句的执行计划(全表扫描,有索引,但是没有走索引):

SQL> select * from t2  where object_id between 1 and 8000;
7960 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 1513984157

--------------------------------------------------------------------------
| Id  | Operation	      | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |	     |  7879 |   754K|   344   (1)| 00:00:05 |
|*  1 |  TABLE ACCESS FULL| T2          |  7879 |   754K|   344   (1)| 00:00:05 |  -- 全表扫描
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJECT_ID"<=8000 AND "OBJECT_ID">=1)

Statistics
----------------------------------------------------------
	  1  recursive calls
	  0  db block gets
       1762  consistent gets    -- 内存读:1762
	  0  physical reads
	  0  redo size
     452436  bytes sent via SQL*Net to client
       6354  bytes received via SQL*Net from client
	532  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
     7960  rows processed

查询从t2表里取object_id 1到8000的数据SQL语句的执行计划(强制走索引):

SQL> select /*+ index(t2,i_t2_id) */ * from t2 where object_id between 1 and 8000;
7960 rows selected.

Execution Plan
----------------------------------------------------------
Plan hash value: 3661687773

---------------------------------------------------------------------------------------
| Id  | Operation		    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	        |	    |  7879 |   754K|  4254(1)| 00:00:52 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T2      |  7879 |   754K|  4254(1)| 00:00:52 |
|*  2 |   INDEX RANGE SCAN	        | I_T2_ID |  7879 |       |    19(0)| 00:00:01 |
---------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID">=1 AND "OBJECT_ID"<=8000)

Statistics
----------------------------------------------------------
	  1  recursive calls
	  0  db block gets
       5750  consistent gets   -- 内存读:5750
	  0  physical reads
	  0  redo size
     838468  bytes sent via SQL*Net to client
       6354  bytes received via SQL*Net from client
	532  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
     7960  rows processed

这时候,走索引的效果比全表扫描的效果更差,因为受集群因子的影响

查询从t2表里取object_id = 150的数据SQL语句的执行计划(走了索引):

SQL> select * from t2  where object_id = 150;

Execution Plan
----------------------------------------------------------
Plan hash value: 3661687773

---------------------------------------------------------------------------------------
| Id  | Operation		            | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	      |	    |     1 |    98 |        2(0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T2      |     1 |    98 |        2(0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN	      | I_T2_ID |     1 |       |        1(0)| 00:00:01 |
---------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=150)

Statistics
----------------------------------------------------------
	  1  recursive calls
	  0  db block gets
	  4  consistent gets    -- 内存读:4
	  0  physical reads
	  0  redo size
     1628  bytes sent via SQL*Net to client
	524  bytes received via SQL*Net from client
	  2  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
	  1  rows processed

访问一行,原来是4,现在也是4,访问一行数据时走索引还不错

arraysize参数
每次返回给用户数据行数的限制

查询arraysize大小:

SQL> show arraysize

arraysize 15

一般情况:全表扫描最大的内存读取量不会超过表的总的block的数量,但是由于参数arraysize(每次读取返回给用户数据的行数)的设置,可能一个块要读取多次,比如一个块有100行数据,arraysize的大小设置为了25,一个块就要读取4次了

在程序里面叫做:FetchSize
JAVA程序:(设置fetch size)

public class FetchSize {
    static final String driver_class  = "oracle.jdbc.driver.OracleDriver";
    static final String connectionURL = "jdbc:oracle:thin:@linux1:1521:orcl1";
    static final String userID        = "scott";
    static final String userPassword  = "tiger";
                                        
    public FetchSize() {
    }

    public void runTest() {

        Connection  con = null;
        Statement   stmt = null;
        ResultSet   rset = null;
        String      query_string = "SELECT * FROM tables WHERE rownum < 200 ORDER BY owner, table_name";
        int         newFetchSize = 50;

        try {

            System.out.println("+-------------------------------+");
            System.out.println("| SETUP CONNECTION              |");
            System.out.println("+-------------------------------+");

            System.out.println("Loading JDBC Driver  -> " + driver_class);
            Class.forName (driver_class).newInstance();

            System.out.println("Connecting to        -> " + connectionURL);
            con = DriverManager.getConnection(connectionURL, userID, userPassword);
            System.out.println("Connected as         -> " + userID);

            System.out.println("Turning Off AutoCommit...");
            con.setAutoCommit(false);

            /*
            **  EXECUTE GENERIC QUERY
            */
            System.out.println("+-------------------------------+");
            System.out.println("| EXECUTE GENERIC QUERY         |");
            System.out.println("+-------------------------------+");

            System.out.println("Executing Generic (SYSDATE) Query...");

            System.out.println("Creating Statement...");
            stmt = con.createStatement ();

            System.out.println("Get Default Fetch Size:" + stmt.getFetchSize());
            System.out.println("Manually Set Default Fetch Size to " + newFetchSize);
            
                          stmt.setFetchSize(newFetchSize);
            System.out.println("Get New Fetch Size:" + stmt.getFetchSize());

            System.out.println("Opening ResultsSet...");
            rset = stmt.executeQuery (query_string);

            while (rset.next ()) {
                System.out.println("  RESULTS            -> " + rset.getString (2));
            }

            System.out.println("Closing ResultSet...");
            rset.close();

            System.out.println("Closing Statement...");
            stmt.close();

        }  catch (SQLException e) {

            e.printStackTrace();

            if (con != null) {
                try {
                    con.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
        } catch (Exception e) {

            e.printStackTrace();

        } finally {
            if (con != null) {

                try {
        
                              System.out.println("+-------------------------------+");
                              System.out.println("| CLOSE DOWN ALL CONNECTIONS    |");
                              System.out.println("+-------------------------------+");

                             System.out.println("Closing down all connections...");
                             con.close();

                         } catch (SQLException e) {

                              e.printStackTrace();
                                    
                                  }
                }

      } // FINALLY
    }

    public static void main(String[] args) {
        FetchSize fetchSize = new FetchSize();
        fetchSize.runTest();
    }
}
posted @ 2024-11-20 15:32  一只c小凶许  阅读(1)  评论(0编辑  收藏  举报