底辺SEの備忘録

スキル無しの底辺です。参考になりません。

実行計画でNESTED LOOPSを選択した際の外部表(駆動表)の選択について

事前準備

--table作成
create table tbl1(col1 number,col2 number,col3 number);
create table tbl2(col1 number,col2 number,col3 number);

--インデックス作成
CREATE INDEX tbl1_idx ON tbl1(col1);
CREATE INDEX tbl2_idx ON tbl2(col1);

--tbl1には10件投入
begin
for i in 1..10 loop
insert into tbl1 values(i,i,i);
commit;
end loop;
end;
/

--tbl1には1000000件投入
begin
for i in 1..1000000 loop
insert into tbl2 values(i,i,i);
commit;
end loop;
end;
/

確認(読み込みブロック数と実行時間)

SQL> --tbl1(件数が少ない方を駆動表にする)
SQL> select /*+ USE_NL(tbl2) LEADING(tbl1) */count(1) from tbl1,tbl2 where tbl1.col2=tbl2.col2;

  COUNT(1)
----------
        10

経過: 00:00:00.55

実行計画
----------------------------------------------------------
Plan hash value: 1872982815

----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |     1 |    26 |  7508   (1)| 00:00:01 |
|   1 |  SORT AGGREGATE     |      |     1 |    26 |            |          |
|   2 |   NESTED LOOPS      |      |    10 |   260 |  7508   (1)| 00:00:01 |
|   3 |    TABLE ACCESS FULL| TBL1 |    10 |   130 |     2   (0)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| TBL2 |     1 |    13 |   751   (1)| 00:00:01 |
----------------------------------------------------------------------------

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

   4 - filter("TBL1"."COL2"="TBL2"."COL2")

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)


統計
----------------------------------------------------------
          9  recursive calls
          0  db block gets
      27616  consistent gets
          0  physical reads
          0  redo size
        573  bytes sent via SQL*Net to client
        451  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL> --tbl2(件数が多い方を駆動表にする)
SQL> select /*+ USE_NL(tbl1) LEADING(tbl2) */count(1) from tbl1,tbl2 where tbl1.col2=tbl2.col2;

  COUNT(1)
----------
        10

経過: 00:00:06.35

実行計画
----------------------------------------------------------
Plan hash value: 835256089

----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |     1 |    26 |   301K  (1)| 00:00:12 |
|   1 |  SORT AGGREGATE     |      |     1 |    26 |            |          |
|   2 |   NESTED LOOPS      |      |    10 |   260 |   301K  (1)| 00:00:12 |
|   3 |    TABLE ACCESS FULL| TBL2 |  1109K|    13M|   751   (1)| 00:00:01 |
|*  4 |    TABLE ACCESS FULL| TBL1 |     1 |    13 |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------

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

   4 - filter("TBL1"."COL2"="TBL2"."COL2")

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)


統計
----------------------------------------------------------
          7  recursive calls
          0  db block gets
    3002836  consistent gets
          0  physical reads
          0  redo size
        573  bytes sent via SQL*Net to client
        451  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          1  rows processed

SQL>

まとめ

実はあまり理解できていないのですが、どちらを駆動表にしてもレコードの組み合わせの個数は同じになります。 それでもtbl1の駆動表にした方が読み込みブロック数(処理時間も)が少なくなります。

docs.oracle.com 等を確認すると駆動表をフェッチしている(最初に対象データを絞る)らしいのでその分駆動表が小さい方が速いと勝手に理解しました。

Linuxのプロセス生成forkとexeclの違い

fork関数は呼び出したプロセスのコピープロセスを作成する

確認したプログラム

#include <stdio.h>
#include <unistd.h>

int main(void){
 pid_t pid;
 int i;
 printf("start\n");
 fork();
 pid=getpid();
 for(i=0;i<5;i++){
  printf("pid=%d i=%d\n",pid,i);
 }
}

実行結果

start
pid=6438 i=0
pid=6438 i=1
pid=6438 i=2
pid=6438 i=3
pid=6438 i=4
pid=6439 i=0
pid=6439 i=1
pid=6439 i=2
pid=6439 i=3
pid=6439 i=4

execl関数は別のプログラムを実行する

確認したプログラム

#include <stdio.h>
#include <unistd.h>

int main(void){
 printf("start\n");
 execl("/bin/ls","/bin/ls","-l",NULL);
 printf("finish\n");
 return 0;
}

実行結果
lsコマンドが実行される。別のプロセスに置換されるため「printf("finish\n");」は実行されない

start
合計 480
-rw-------. 1 root root   1795  4月 21 22:59 anaconda-ks.cfg
-rwxr-xr-x  1 root root   8456  5月  3 15:42 exe
-rw-r--r--  1 root root    149  5月  3 15:42 exe.c
-rw-r--r--  1 root root   7039  5月  3 15:45 exec.log.2943
-rw-r--r--  1 root root  28569  5月  3 15:44 exec.log.5652
-rwxr-xr-x  1 root root   8560  5月  1 21:44 flk
-rw-r--r--  1 root root    177  5月  1 21:44 flk.c
-rw-r--r--. 1 root root   1843  4月 21 23:00 initial-setup-ks.cfg
-rw-r--r--  1 root root 391626  4月 30 20:51 listener.log
-rw-r--r--  1 root root   7041  5月  3 15:23 test.log.2943
-rw-r--r--  1 root root   4542  5月  3 15:23 test.log.4314
-rw-r--r--  1 root root    292  5月  3 15:23 test.log.4315

sqlplusのプロセスを確認する

ローカル接続(リスナーを経由しない場合)

sqlplusとサーバプロセスの間に親子関係がある

[root@localhost ~]# ps -ef |grep 9433| grep -v grep
oracle    9433  3998  0 21:03 pts/0    00:00:00 sqlplus   as sysdba
oracle    9434  9433  0 21:03 ?        00:00:00 oracleorcl (DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))
[root@localhost ~]#

リモート接続(リスナーを経由する場合)

sqlplusとサーバプロセスの間に親子関係がない。 リスナーからfork()??exec()??されているかららしい。

[root@localhost ~]# ps -ef |grep 9811|grep -v grep
oracle    9811     1  0 21:09 ?        00:00:00 oracleorcl (LOCAL=NO)
[root@localhost ~]# ps -ef |grep sqlplus|grep -v grep
oracle    9809  3998  0 21:09 pts/0    00:00:00 sqlplus
[root@localhost ~]#

LinuxでCPU使用率を上げる(使用率を上げた後にsarコマンドで確認)

CPUの数を確認

# cat /proc/cpuinfo | grep processor
processor       : 0
processor       : 1
#

2つ存在することを確認

一方のCPUに負荷をかける

実行コマンドyes >> /dev/null &

sarコマンドで確認

12:54:20 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
12:54:25 PM     all     29.70      0.00     20.40      0.00      0.00     49.90
12:54:25 PM       0     59.28      0.00     40.72      0.00      0.00      0.00
12:54:25 PM       1      0.00      0.00      0.00      0.00      0.00    100.00

参考にさせていただいたサイト

LinuxでCPU使用率を上げるコマンド - ablog

oerrコマンドでエラーの内容を調べることが可能

[oracle@localhost ~]$ oerr ora 1555
01555, 00000, "snapshot too old: rollback segment number %s with name \"%s\" too small"
// *Cause: rollback records needed by a reader for consistent read are
//         overwritten by other writers
// *Action: If in Automatic Undo Management mode, increase undo_retention
//          setting. Otherwise, use larger rollback segments
[oracle@localhost ~]$ oerr ora 0001
00001, 00000, "unique constraint (%s.%s) violated"
// *Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
//         For Trusted Oracle configured in DBMS MAC mode, you may see
//         this message if a duplicate entry exists at a different level.
// *Action: Either remove the unique restriction or do not insert the key.
[oracle@localhost ~]$

sarコマンドでネットワーク使用状況を確認

コマンド

sar -n DEV 1

ループバックアドレスpingした状態で実行

実行コマンド
sar -n DEV 1|grep -e lo -e IFACE

実行結果

222759秒     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s
222800秒        lo      2.00      2.00      0.16      0.16      0.00      0.00      0.00

シェルスクリプトの中でsqlplusを実行

内容

ユーザ名(第1引数)、パスワード(第2引数)を渡し、test.sqlを10秒ごとに10回実行する。

#/bin/bash

username="$1"
password="$2"

SLEEP_TIME=10
MAX_LOOP_COUNT=10
LOOP_COUNT=0
script_file="test.sql"

while [ $LOOP_COUNT -lt $MAX_LOOP_COUNT ]
do
 cat "${script_file}" |sqlplus -s ${username}/${password}
 LOOP_COUNT=`expr $LOOP_COUNT + 1`
done