29 January 2020

Calculate Number of Transactions

การนับจำนวน transactions ใน Database
มีวิธีดังนี้
- วิธีหาจำนวน transactions จาก V$SYSSTAT
- วิธีหาจำนวน transactions จาก History Snapshot
- วิธีหาจำนวน transactions จาก Load profile section ของ Statspack หรือ AWR

วิธีหาจำนวน transactions จาก V$SYSSTAT

อธิบาย
- V$SYSSTAT ใช้สำหรับ system statistics
- จะเก็บจำนวน transactions ตั้งแต่เริ่มต้น startup Database ถ้า restart คือ เริ่มนับ 0 ใหม่

หาจำนวน transactions
SQL> select sum(s.value) transactions
from v$sysstat s ,v$instance i 
where s.name in ('user commits','transaction rollbacks');
TRANSACTIONS
----------------------
11

หาจำนวน transactions ต่อ second
จาก https://community.oracle.com/thread/3725596

sysdate - startup_time คือ จำนวนวัน นับจาก startup Database ถึงปัจจุบัน
86400 * จำนวนวัน คือ แปลงจำนวนวันเป็นวินาที โดย 86400 คือ จำนวนวินาทีใน 1 วัน

SQL> select round(sum(s.value / (86400 * (sysdate - startup_time))),3) "transactions/sec" 
from v$sysstat s ,v$instance i
where s.name in ('user commits','transaction rollbacks');
transactions/sec
----------------
            .064

หาจำนวน transactions โดยการกำหนดจุดเริ่มต้นและจุดสุดท้าย ในการนับจำนวน transactions ที่ต่างกัน
จาก http://ba6.us/?q=book/export/html/26

Query กำหนดให้เป็นค่าจำนวน transaction เริ่มต้น
select sysdate, sum(value) 
into begindate, beginval
from v$sysstat 
where name in ('user commits','user_rollbacks');
SYSDATE  SUM(VALUE)
------------------ -----------
20191128 16:50:00          11

Query กำหนดให้เป็นค่าจำนวน transaction สุดท้าย
select sysdate, sum(value) 
into enddate, endval
from v$sysstat
where name in ('user commits','user_rollbacks');
SYSDATE SUM(VALUE)
----------------- -------------
20191128 16:55:00          11

dbms_lock.sleep(จำนวนวินาที) ใช้กำหนดระยะเวลาห่างของ transactions เริ่มต้น และ transactions สุดท้าย หน่วยวินาที
endval - beginval คือ ผลต่างหรือที่เพิ่มเข้ามาใหม่ของจำนวน transactions ในช่วงเวลาที่กำหนด (เริ่มต้นถึงครั้งสุดท้าย)

diff transactions คือ ผลต่างของจำนวน transactions เริ่มต้นกับสุดท้าย
diff seconds คือ ผลต่างของเวลาเริ่มต้นกับสุดท้าย
diff transactions per second คือ ผลต่างของจำนวน transactions เริ่มต้นกับสุดท้าย ต่อ second

SQL> set serveroutput on
declare
    begindate date;
    enddate date;
    beginval number;
    endval number;
begin

    select sysdate, sum(value) 
    into begindate, beginval
    from v$sysstat 
    where name in ('user commits','user_rollbacks');

    dbms_lock.sleep(3000);

    select sysdate, sum(value) 
    into enddate, endval
    from v$sysstat
    where name in ('user commits','user_rollbacks');

    dbms_output.put_line((endval - beginval) || ' diff transactions');
    
    dbms_output.put_line(((enddate - begindate) * 86400) || ' diff seconds');

    dbms_output.put_line((endval - beginval) / ((enddate - begindate) * 86400) || ' diff transactions per second');

end;
/
0 diff transactions
300 diff seconds
0 diff transactions per second

วิธีหาจำนวน transactions จาก History Snapshot

อธิบาย
- DBA_HIST_SYSSTAT ใช้สำหรับ แสดง information ของ historical system statistics
- DBA_HIST_SNAPSHOT ใช้สำหรับ แสดง information เกี่ยวกับ snapshots ใน Workload Repository

หาจำนวน transactions ต่อ second ในแต่ละช่วงเวลา
จาก https://grepora.com/2016/05/25/oracle-tps-evaluating-transaction-per-second/

ช่วงเวลาใช้ snap time คือ BEGIN_INTERVAL_TIME ถึง BEGIN_INTERVAL_TIME ย้อนหลัง

SQL> with 
hist_snaps as 
(select instance_number,
    snap_id,
    round(begin_interval_time,'MI') datetime,
    (begin_interval_time + 0 - lag(begin_interval_time + 0) over (partition by dbid, instance_number order by snap_id)) 
* 86400 diff_time
    from dba_hist_snapshot
), 
hist_stats as 
(select dbid,
    instance_number,
    snap_id,
    stat_name,
    value - lag(value) over (partition by dbid,instance_number,stat_name order by snap_id)delta_value
    from dba_hist_sysstat
    where stat_name in ('user commits', 'user rollbacks')
)
select to_char(datetime,'yyyymmdd hh24:mi:ss')datetime,
round (sum (delta_value) / 3600, 4) "transactions/sec"
from hist_snaps sn, hist_stats st
where st.instance_number = sn.instance_number
and st.snap_id = sn.snap_id
and diff_time is not null
group by datetime
order by 1 desc;
DATETIME          transactions/sec
----------------- ----------------
20191128 22:01:00            .2481
20191128 21:46:00            .0058
20191128 21:31:00            .0039
20191128 21:16:00            .0033
20191128 21:01:00            .0039

หาจำนวน transactions ต่อ second หรือ minute หรือ hour หรือ day ในแต่ละช่วงเวลา
จาก https://dbaclass.com/article/find-user-commits-per-minute-oracle-database/

ช่วงเวลาใช้ snap time คือ BEGIN_INTERVAL_TIME ถึง END_INTERVAL_TIME

VALUE คือ จำนวน transactions ในช่วงเวลา snap time นั้นๆ
VALUE_PREVOUS คือ จำนวน transactions ในช่วงเวลา snap time ก่อนหน้าช่วงนั้นๆ
VALUE_DIFF คือ VALUE - VALUE_PREVOUS
  ถ้าค่าเป็นบวก แสดงว่า จำนวน transactions ในช่วงเวลา snap time นั้นๆ มีค่ามากกว่า snap time ก่อนหน้าช่วงนั้นๆ
  ถ้าค่าเป็นลบ แสดงว่า จำนวน transactions ในช่วงเวลา snap time นั้นๆ มีค่าน้อยกว่า snap time ก่อนหน้าช่วงนั้นๆ

STAT_PER_SEC คือ จำนวน transactions ต่อ seconds ในช่วงเวลา snap time   
STAT_PER_MIN คือ จำนวน transactions ต่อ minutes ในช่วงเวลา snap time
STAT_PER_HOURS คือ จำนวน transactions ต่อ hours ในช่วงเวลา snap time
STAT_PER_DAY คือ จำนวน transactions ต่อ days ในช่วงเวลา snap time

col stat_name for a20
col value_diff for 9999,999,999
col stat_per_min for 9999,999,999
set lines 200 pages 1500 long 99999999
col begin_interval_time for a30
col end_interval_time for a30
set linesize 1000
set pagesize 40
set pause on
SQL> select hsys.snap_id,
       hsnap.begin_interval_time,
       hsnap.end_interval_time,
           hsys.stat_name,
           hsys.value,
           lag(hsys.value,1,0) over (order by hsys.snap_id) as "value_prevous",
           hsys.value - lag(hsys.value,1,0) over (order by hsys.snap_id) as "value_diff",
           extract(second from (hsnap.end_interval_time - hsnap.begin_interval_time))sec_diff,
           extract(minute from (hsnap.end_interval_time - hsnap.begin_interval_time))min_diff,
           extract(hour from (hsnap.end_interval_time - hsnap.begin_interval_time))hr_diff,
           extract(day from (hsnap.end_interval_time - hsnap.begin_interval_time))day_diff,
           round
           (
            (hsys.value - lag(hsys.value,1,0) over (order by hsys.snap_id)) /
            round
            (   abs
                (extract(hour from (hsnap.end_interval_time - hsnap.begin_interval_time))*60*60 +
                 extract(minute from (hsnap.end_interval_time - hsnap.begin_interval_time))*60 +
                 extract(second from (hsnap.end_interval_time - hsnap.begin_interval_time)) +
                 extract(day from (hsnap.end_interval_time - hsnap.begin_interval_time))*24*60*60
                )
                ,4
            )
            ,4
           )"stat_per_sec",
           round
           (
            (hsys.value - lag(hsys.value,1,0) over (order by hsys.snap_id)) /
            round
            (   abs
                (extract(hour from (hsnap.end_interval_time - hsnap.begin_interval_time))*60 +
                 extract(minute from (hsnap.end_interval_time - hsnap.begin_interval_time)) +
                 extract(second from (hsnap.end_interval_time - hsnap.begin_interval_time))/60 +
                 extract(day from (hsnap.end_interval_time - hsnap.begin_interval_time))*24*60
                )
                ,4
            )
            ,4
           )"stat_per_min",
           round
           (
            (hsys.value - lag(hsys.value,1,0) over (order by hsys.snap_id)) /
            round
            (   abs
                (extract(hour from (hsnap.end_interval_time - hsnap.begin_interval_time))+
                 extract(minute from (hsnap.end_interval_time - hsnap.begin_interval_time))/60 +
                 extract(second from (hsnap.end_interval_time - hsnap.begin_interval_time))/60/60 +
                 extract(day from (hsnap.end_interval_time - hsnap.begin_interval_time))*24
                )
                ,4
            )
            ,4
           )"stat_per_hr",
           round
           (
            (hsys.value - lag(hsys.value,1,0) over (order by hsys.snap_id)) /
            round
            (   abs
                (extract(hour from (hsnap.end_interval_time - hsnap.begin_interval_time))/24+
                 extract(minute from (hsnap.end_interval_time - hsnap.begin_interval_time))/60/24 +
                 extract(second from (hsnap.end_interval_time - hsnap.begin_interval_time))/60/60/24 +
                 extract(day from (hsnap.end_interval_time - hsnap.begin_interval_time))
                )
                ,4
            )
            ,4
           )"stat_per_day"
from dba_hist_sysstat hsys, dba_hist_snapshot hsnap
where hsys.snap_id = hsnap.snap_id
and hsnap.instance_number in (select instance_number from v$instance)
and hsnap.instance_number = hsys.instance_number
and hsys.stat_name in ('user commits','user rollbacks')
order by 1 desc;

   SNAP_ID BEGIN_INTERVAL_TIME            END_INTERVAL_TIME              STAT_NAME       
---------- ------------------------------ ------------------------------ --------------------
      1597 11/28/2019 10:00:50.865 PM     11/28/2019 10:15:06.272 PM     user commits        
      1597 11/28/2019 10:00:50.865 PM     11/28/2019 10:15:06.272 PM     user rollbacks      
      1596 11/28/2019 9:45:45.151 PM      11/28/2019 10:00:50.865 PM     user commits        
      1596 11/28/2019 9:45:45.151 PM      11/28/2019 10:00:50.865 PM     user rollbacks      

      VALUE value_prevous    value_diff   SEC_DIFF   MIN_DIFF    HR_DIFF   DAY_DIFF 
 ---------- ------------- ------------- ---------- ---------- ---------- ---------- 
       1156            26         1,130     15.407         14          0          0 
         26           264          -238     15.407         14          0          0 
        264            25           239      5.714         15          0          0 
         25           243          -218      5.714         15          0          0 

stat_per_sec  stat_per_min stat_per_hr stat_per_day
------------ ------------- ----------- ------------
       1.321            79   4755.8923   114141.414
     -0.2782           -17  -1001.6835   -24040.404
       .2639            16    949.9205   22761.9048
     -0.2407           -14   -866.4547   -20761.905

วิธีหาจำนวน transactions จาก Load profile section ของ Statspack หรือ AWR
จาก https://timurakhmadeev.wordpress.com/2012/02/21/load-profile/

อธิบาย
- V$SYSMETRIC ใช้สำหรับแสดง system metric ที่เก็บจากช่วงเวลาปัจจุบันสุด โดย long duration คือ 60 second และ short duration คือ 15 second
- ในช่วงเวลา 15 หรือ 60 second ที่ผ่านมา

หาจำนวน transactions ต่อ second
col short_name  format a20              heading 'Load Profile'
col per_sec     format 999,999,999.9    heading 'Per Second'
col per_tx      format 999,999,999.9    heading 'Per Transaction'
set colsep '   '
SQL> select lpad(short_name, 20, ' ') short_name
     , per_sec
     , per_tx from
    (select short_name
          , max(decode(typ, 1, value)) per_sec
          , max(decode(typ, 2, value)) per_tx
          , max(m_rank) m_rank 
        from
        (select /*+ use_hash(s) */
                m.short_name
              , s.value * coeff value
              , typ
              , m_rank
           from v$sysmetric s,
               (select 'Database Time Per Sec'                      metric_name, 'DB Time' short_name, .01 coeff, 1 typ, 1 m_rank from dual union all
                select 'CPU Usage Per Sec'                          metric_name, 'DB CPU' short_name, .01 coeff, 1 typ, 2 m_rank from dual union all
                select 'Redo Generated Per Sec'                     metric_name, 'Redo size' short_name, 1 coeff, 1 typ, 3 m_rank from dual union all
                select 'Logical Reads Per Sec'                      metric_name, 'Logical reads' short_name, 1 coeff, 1 typ, 4 m_rank from dual union all
                select 'DB Block Changes Per Sec'                   metric_name, 'Block changes' short_name, 1 coeff, 1 typ, 5 m_rank from dual union all
                select 'Physical Reads Per Sec'                     metric_name, 'Physical reads' short_name, 1 coeff, 1 typ, 6 m_rank from dual union all
                select 'Physical Writes Per Sec'                    metric_name, 'Physical writes' short_name, 1 coeff, 1 typ, 7 m_rank from dual union all
                select 'User Calls Per Sec'                         metric_name, 'User calls' short_name, 1 coeff, 1 typ, 8 m_rank from dual union all
                select 'Total Parse Count Per Sec'                  metric_name, 'Parses' short_name, 1 coeff, 1 typ, 9 m_rank from dual union all
                select 'Hard Parse Count Per Sec'                   metric_name, 'Hard Parses' short_name, 1 coeff, 1 typ, 10 m_rank from dual union all
                select 'Logons Per Sec'                             metric_name, 'Logons' short_name, 1 coeff, 1 typ, 11 m_rank from dual union all
                select 'Executions Per Sec'                         metric_name, 'Executes' short_name, 1 coeff, 1 typ, 12 m_rank from dual union all
                select 'User Rollbacks Per Sec'                     metric_name, 'Rollbacks' short_name, 1 coeff, 1 typ, 13 m_rank from dual union all
                select 'User Transaction Per Sec'                   metric_name, 'Transactions' short_name, 1 coeff, 1 typ, 14 m_rank from dual union all
                select 'User Rollback UndoRec Applied Per Sec'      metric_name, 'Applied urec' short_name, 1 coeff, 1 typ, 15 m_rank from dual union all
                select 'Redo Generated Per Txn'                     metric_name, 'Redo size' short_name, 1 coeff, 2 typ, 3 m_rank from dual union all
                select 'Logical Reads Per Txn'                      metric_name, 'Logical reads' short_name, 1 coeff, 2 typ, 4 m_rank from dual union all
                select 'DB Block Changes Per Txn'                   metric_name, 'Block changes' short_name, 1 coeff, 2 typ, 5 m_rank from dual union all
                select 'Physical Reads Per Txn'                     metric_name, 'Physical reads' short_name, 1 coeff, 2 typ, 6 m_rank from dual union all
                select 'Physical Writes Per Txn'                    metric_name, 'Physical writes' short_name, 1 coeff, 2 typ, 7 m_rank from dual union all
                select 'User Calls Per Txn'                         metric_name, 'User calls' short_name, 1 coeff, 2 typ, 8 m_rank from dual union all
                select 'Total Parse Count Per Txn'                  metric_name, 'Parses' short_name, 1 coeff, 2 typ, 9 m_rank from dual union all
                select 'Hard Parse Count Per Txn'                   metric_name, 'Hard Parses' short_name, 1 coeff, 2 typ, 10 m_rank from dual union all
                select 'Logons Per Txn'                             metric_name, 'Logons' short_name, 1 coeff, 2 typ, 11 m_rank from dual union all
                select 'Executions Per Txn'                         metric_name, 'Executes' short_name, 1 coeff, 2 typ, 12 m_rank from dual union all
                select 'User Rollbacks Per Txn'                     metric_name, 'Rollbacks' short_name, 1 coeff, 2 typ, 13 m_rank from dual union all
                select 'User Transaction Per Txn'                   metric_name, 'Transactions' short_name, 1 coeff, 2 typ, 14 m_rank from dual union all
                select 'User Rollback Undo Records Applied Per Txn' metric_name, 'Applied urec' short_name, 1 coeff, 2 typ, 15 m_rank from dual) m
          where m.metric_name = s.metric_name
            and s.intsize_csec > 5000
            and s.intsize_csec < 7000
)
      group by short_name
)
 order by m_rank;
Load Profile                  Per Second     Per Transaction
--------------------   -----------------   -----------------
             DB Time               .0068                    
              DB CPU               .0067                    
           Redo size         40,574.6338         43,530.7857
       Logical reads          1,035.7357          1,111.1964
       Block changes            312.5999            335.3750
      Physical reads             24.6172             26.4107
     Physical writes              4.6272              4.9643
          User calls               .3995               .4286
              Parses             45.5226             48.8393
         Hard Parses              6.7410              7.2321
              Logons               .0666               .0714
            Executes            143.5253            153.9821
           Rollbacks               .3995                    
        Transactions               .9321                    
        Applied urec              0.0000              0.0000

11 January 2020

Connecting to a Hung Database

เมื่อ Database เกิดอาการ hang และเราไม่สามารถ login เพื่อเข้าไปตรวจสอบหรือแก้ไข
จะมีวิธีการที่แนะนำดังนี้ (เรียงตามลำดับผลกระทบจากน้อยไปมาก)
- วิธีที่ 1 เข้า SQLPlus ด้วย option Prelim และ  เก็บ diagnostic information ต่างๆ เพื่อวิเคราะห์ problem
   หลังจากได้ diagnostic information ต่างๆแล้ว สามารถใช้วิธีที่ 2 หรือ 3 หรือ 4 เพื่อให้ Database เริ่มทำงานใหม่
- วิธีที่ 2 เข้า SQLPlus ด้วย option Prelim และ shutdown abort
- วิธีที่ 3 Kill Oracle background process ทั้งหมด ของ Database ที่ต้องการ
- วิธีที่ 4 Restart Server

ตัวอย่าง เหตุการณ์ทดสอบ

Database ชื่อ dbtest1

Session ที่ต้องการสังเกตุ
SQL> begin
  loop
    null;
  end loop;
end;
/

พบ process ใช้งาน %CPU = 99.4
--> top
   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                       
 31006 oracle    20   0 1346m  58m  53m R 99.4  3.1   2:26.03 oracle_31006_db                                                                 
 23328 oracle    20   0 1328m  44m  41m S  0.4  2.4   0:27.50 ora_mmnl_dbtest                                                                 
 23302 oracle    20   0 1330m  34m  28m S  0.2  1.8   0:43.55 ora_dia0_dbtest                                                                 
 23364 oracle    20   0 1350m  25m  21m S  0.2  1.3   0:00.76 ora_arc3_dbtest                                                                 
 26573 root      20   0 99.7m 3640 2652 S  0.2  0.2   0:01.21 sshd       

--> ps -eo user,pid,ppid,cmd |grep 31006
oracle    31006      1 oracledbtest1 (DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))
oracle    32065  31824 grep 31006

และตอนนี้ไม่สามารถ login เข้า SQLPlus เพราะ Database hung

วิธีที่ 1

--> export ORACLE_SID=dbtest1
--> sqlplus -prelim / as sysdba

ตัวอย่าง การใช้ oradebug ตรวจสอบ process PID = 31006 ว่าตอนนี้กำลังใช้ query อะไรทำงานอยู่หรือไม่

Debug PID ที่ต้องการตรวจสอบ
SQL> oradebug setospid 31006

แสดง trace file
SQL> oradebug tracefile_name
/oracle/diag/rdbms/dbtest1/dbtest1/trace/dbtest1_ora_31006.trc

แสดง SQL ที่ process PID = 31006 กำลังใช้งานอยู่
SQL> oradebug current_sql
begin
  loop
    null;
  end loop;
end;

Close trace
SQL> oradebug close_trace

อ่าน trace file ด้วยคำสั่งของ OS
--> vi /oracle/diag/rdbms/dbtest1/dbtest1/trace/dbtest1_ora_31006.trc
Trace file /oracle/diag/rdbms/dbtest1/dbtest1/trace/dbtest1_ora_31452.trc
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
Build label:    RDBMS_12.2.0.1.0_LINUX.X64_170125
ORACLE_HOME:    /oracle/product/12.2.0/dbhome_1
System name:    Linux
Node name:      oradb12c.localdomain
Release:        2.6.32-573.el6.x86_64
Version:        #1 SMP Thu Jul 23 15:44:03 UTC 2015
Machine:        x86_64
Instance name: dbtest1
Redo thread mounted by this instance: 1
Oracle process number: 24
Unix process pid: 31452, image: oracle@oradb12c.localdomain (TNS V1-V3)

*** 2019-11-26T04:30:42.158025+07:00
*** SESSION ID:(68.10681) 2019-11-26T04:30:42.158049+07:00
*** CLIENT ID:() 2019-11-26T04:30:42.158054+07:00
*** SERVICE NAME:(SYS$USERS) 2019-11-26T04:30:42.158059+07:00
*** MODULE NAME:(sqlplus@oradb12c.localdomain (TNS V1-V3)) 2019-11-26T04:30:42.158064+07:00
*** ACTION NAME:() 2019-11-26T04:30:42.158068+07:00
*** CLIENT DRIVER:(SQL*PLUS) 2019-11-26T04:30:42.158072+07:00

Received ORADEBUG command (#2) 'current_sql' from process '31226'

*** 2019-11-26T04:30:42.158105+07:00
Finished processing ORADEBUG command (#2) 'current_sql'
...
....
.....

:q

สามารถดู option การใช้งานอื่นๆของ oradebug ได้จาก เปิด help
SQL> oradebug help

วิธีที่ 2

--> export ORACLE_SID=dbtest1
--> sqlplus -prelim / as sysdba

สั่ง Shutdown abort
SQL> shutdown abort

วิธีที่ 3

แสดง PID ของ process ทั้งหมด ตามชื่อ Database
GREP -V GREP เป็นคำสั่งสำหรับเอา process จากคำสั่ง GREP ออก
--> ps -ef | grep dbtest1 | grep -v grep | awk '{print $2}'
33738
33740
33746
....
......
........

Kill PID ของ process ทั้งหมด ตามชื่อ Database
--> kill -9 `ps -ef | grep dbtest1 | grep -v grep | awk '{print $2}'`

Remove RAM segment ที่ใช้งานอยุ่
--> ipcs -pmb

วิธีที่ 4

ใช้คำสั่ง shutdown หรือ restart OS

คำสั่ง shutdown
--> init 0

คำสั่ง restart
--> init 6