دیتابیس اوراکل برای پیاده سازی مفاهیمی چون Data Concurrency و Read Consistency، مشخصات تراکنشها را در قسمتی از هدر بلاک ثبت می کند. این قسمت از هدر بلاک که به آن (Interested Transaction List(ITL هم گفته می شود، می تواند شامل تعدادی slot باشد.
تراکنشها برای تصاحب یک رکورد، باید slotای را در هدر بلاکی که رکورد در آن قرار دارد، در اختیار بگیرند و اطلاعاتی نظیر (transaction ID(XID و (undo block address(UBA و همچنین تعداد رکوردهایی که تراکنش در این بلاک قفل کرده را در این قسمت ثبت کنند البته هر slot صرفا به یک transaction تعلق دارد و هر transaction در هر بلاک، تنها می تواند یک slot را اشغال کند.
با انجام commit و rollback و متعاقب آن، پایان یافتن تراکنش، itl slot می تواند توسط تراکنش دیگری استفاده شود.
حجم هر transaction slot حدودا 24 بایت می باشد البته قرار نیست آدرس فیزیکی رکوردها یا همان rowid در این فضا ذخیره شود:
SQL> select type_size from v$type_size where description=‘TRANSACTION VARIABLE HEADER’
24
تعداد اولیه Transaction Slotها برای هر بلاک، با خصیصه INITRANS قابل تنظیم است(در سطح هر شی). مقدار پیش فرض INITRANS برای جداول برابر با یک و برای ایندکسها به دو تنظیم شده است. زمانی که به ITL slot بیشتری نیاز باشد، به صورت خودکار اوراکل ITL slot جدیدی را رزرو خواهد کرد مگر انکه تعداد ITL slotها به عدد تعیین شده برای خصیصه MAXTRANS برسد و یا اینکه فضای کافی در بلاک موجود نباشد.
SQL> create table MYTBL (id number,text varchar2(4000)) ;
Table created
SQL> select p.INI_TRANS,p.MAX_TRANS from user_tables p where p.TABLE_NAME=’MYTBL’;
INI_TRANS MAX_TRANS
———- ———-
1 255
همانطور که می بینید، مقدار پیش فرض خصیصه max_trans برابر با 255 می باشد پس بطور همزمان و در صورتی که فضای بلاک اجازه دهد، می توان حداکثر 255 تراکنش بر روی هر بلاک در حال اجرا داشت البته این عدد قابل توجه است زیرا که تعداد رکوردها حاضر در یک بلاک، در بسیاری از مواقع به این عدد نمی رسد.
توضیح 1: از اوراکل 9i، با وجود تنظیم initrans به عدد یک، اوراکل برای هر دیتابلاک دو transaction slot در نظر می گیرد.
توضیح 2: خصیصه Maxtrans از اوراکل 10gR1 غیرقابل تنظیم است و در صورت تنظیم، اوراکل از همان عدد 255 استفاده خواهد کرد.
آشنایی بیشتر با ساختار ITL SLOTها
برای آشنایی بیشتر با ساختار transaction slotها، قصد داریم از بلاکهای یک جدول دامپی گرفته و با مشاهده محتویات آن، به بررسی اطلاعات موجود در آن بپردازیم.
برای این کار، ابتدا جدولی را ایجاد می کنیم که تعداد initrans آن برابر با عدد دو تنظیم شده است:
SQL> create table mytbl (id number,text varchar2(4000)) PCTFREE 0 INITRANS 2;
Table created
برای تهیه دامپ از بلاکهای این جدول، باید شماره بلاکها را مشخص کنیم، به این جهت به ویوی dba_extents رجوع می کنیم:
SQL> select file_id,BLOCK_ID,blocks from dba_extents p where owner=’USEF’ and segment_name=’MYTBL’;
FILE_ID BLOCK_ID BLOCKS
———- ———- ———-
10 136 8
همانطور که می بینید، جدول mytbl شامل 8 بلاک می باشد(از شماره 136 تا 143). با کمک دستور زیر، اوراکل از این بلاکها dump گرفته و خروجی را در یک فایل تریس قرار خواهد داد:
SQL> alter system dump datafile 10 block min 136 block max 143;
System altered
پرس و جوی زیر، آدرس فایل تریس مربوط به session جاری را مشخص می کند.
SQL> SELECT p.tracefile FROM v$session s JOIN v$process p ON s.paddr = p.addr WHERE s.sid =(SELECT sid FROM v$mystat WHERE ROWNUM = 1);
TRACEFILE
——————————————————————————–
/18c/base/diag/rdbms/db18c/db18c/trace/db18c_ora_11978.trc
در قسمت زیر، بخشی از محتویات فایل تریس که مربوط به ITL Slot یکی از بلاکهای این جدول می باشد، را مشاهده می کنید:
[root@ol7 ~]# less /18c/base/diag/rdbms/db18c/db18c/trace/db18c_ora_11978.trc
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0000.000.00000000 0x00000000.0000.00 —- 0 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 —- 0 fsc 0x0000.00000000
قصد داریم با انجام تراکنشی، بررسی کنیم که چه تغییری در این ساختار ایجاد می شود:
–session 1:
SQL> insert into mytbl values(1,’v.u’);
1 row inserted
SQL> SELECT dbms_rowid.rowid_block_number(rowid) block_number FROM mytbl;
BLOCK_NUMBER
————
141
همانطور که می بینید، این رکورد در بلاک شماره 141 ثبت شده است پس با اجرای دستور زیر، از این بلاک دامپی را تهیه می کنیم:
SQL> alter system dump datafile 10 block 141;
System altered
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x003e.00d.00000698 0x034000e3.03d7.1d —- 1 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 —- 0 fsc 0x0000.00000000
همانطور که انتظار می رود، تراکنش در حال اجرا، یکی از slotها را در اختیار گرفته است با توجه به این که این تراکنش صرفا یک رکورد را در این بلاک درگیر کرده است، مقدار ستون LCK برای 0x01 از عدد 0 به عدد 1 تغییر کرده است.
مجددا در همین session، بدون انکه دستور insert قبلی را commit یا rollback کرده باشیم، دستور insert دیگری را اجرا می کنیم:
–session 1:
SQL> insert into mytbl values(2,’v.u’);
1 row inserted
–session 2:
SQL> alter system dump datafile 10 block 141;
System altered
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x003e.00d.00000698 0x034000e3.03d7.1e —- 2 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 —- 0 fsc 0x0000.00000000
همانطور که می بینید، عدد یک برای ستون LCK از 1 به 2 که به معنی دو رکورد از بلاک شماره 141 می باشد، تغییر کرده است.این اتفاق برای insertهای بعدی هم رخ می دهد:
SQL> insert into mytbl values(3,’v.u’);
1 row inserted
SQL> insert into mytbl values(4,’v.u’);
1 row inserted
SQL> insert into mytbl values(5,’v.u’);
1 row inserted
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x003e.00d.00000698 0x034000e3.03d7.21 —- 5 fsc 0x0000.00000000
0x02 0x0000.000.00000000 0x00000000.0000.00 —- 0 fsc 0x0000.00000000
در نهایت با اجرای دستور commit، به اجرای این تراکنش خاتمه می دهیم.
SQL> commit;
Commit complete
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x003e.00d.00000698 0x034000e3.03d7.21 –U- 5 fsc 0x0000.a7d3633b
0x02 0x0000.000.00000000 0x00000000.0000.00 —- 0 fsc 0x0000.00000000
قصد داریم در sessionهای مجزا، دستور حذف رکوردهای درج شده را صادر کنیم:
–session 1:
SQL> delete mytbl where id=1;
1 row deleted
–session 2:
SQL> delete mytbl where id=2;
1 row deleted
–session 3:
SQL> delete mytbl where id=3;
1 row deleted
با اجرای دستورات فوق، خواهیم دید که مجموع تعداد transaction slotها از دو به سه تغییر کرده است که هر تراکنش صرفا اطلاعات یک رکورد را تغییر می دهد:
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x003b.001.0000052d 0x04000ddf.0292.1d —- 1 fsc 0x0008.00000000
0x02 0x003c.007.000006ba 0x0400039b.03ea.01 —- 1 fsc 0x0008.00000000
0x03 0x0042.001.0000066e 0x04000e52.03f3.01 —- 1 fsc 0x0008.00000000
با اجرای دستور حذف دو رکورد بعدی، تعداد transaction slot، به عدد پنج خواهد رسید:
–session 4:
SQL> delete mytbl where id=4;
1 row deleted
–session 5:
SQL> delete mytbl where id=5;
1 row deleted
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x003b.001.0000052d 0x04000ddf.0292.1d —- 1 fsc 0x0008.00000000
0x02 0x003c.007.000006ba 0x0400039b.03ea.01 —- 1 fsc 0x0008.00000000
0x03 0x0042.001.0000066e 0x04000e52.03f3.01 —- 1 fsc 0x0008.00000000
0x04 0x003a.016.0000052c 0x04000142.0328.1b —- 1 fsc 0x0008.00000000
0x05 0x0039.01e.000004fc 0x0400031d.0377.0c —- 1 fsc 0x0008.00000000
نکته: با کمک ویوی v$transaction و ستون XID موجود در فایل dump، می توان مشخصات دقیق تراکنشها را مشاهده کرد:
SQL> select utl_raw.reverse(substr(xid,1,4)) COL1,utl_raw.reverse(substr(xid,5,4)) COL2,utl_raw.reverse(substr(xid,9,8)) COL3 from v$transaction;
بررسی enq: TX – allocate ITL entry
زمانی که امکان ایجاد ITL slot جدیدی برای یک transaction در یک بلاک وجود نداشته باشد، sessionای که آن transaction را اجرا کرده است، با wait eventای بنام enq: TX – allocate ITL entry در حالت انتظار باقی خواهد ماند.
به دو دلیل ممکن است امکان ایجاد ITL slot جدید از بین برود:
1.رسیدن تعداد ITL slotها به عدد تعیین شده برای MAXTRANS
2.پر شدن فضای بلاک
در این قسمت قصد داریم با پر کردن فضای یک بلاک، sessionای را با این ITL wait در حالت انتظار قرار دهیم.
با اجرای دستورات زیر، جدول mytbl ایجاد می شود و با درج 6 رکورد در این جدول، فضای خالی یکی از بلاکهای این جدول، به صورت کامل پر می شود:
SQL> create table mytbl (id number,text varchar2(4000)) PCTFREE 0 INITRANS 1;
Table created
توجه: برای بلاکهای جدول mytbl فضای خالی در نظر گرفته نشده است(PCTFREE=0).
SQL> insert into mytbl values(1,rpad(‘v’,2000,’v’));
1 row inserted
SQL> insert into mytbl values(2,rpad(‘v’,2000,’v’));
1 row inserted
SQL> insert into mytbl values(3,rpad(‘v’,2000,’v’));
1 row inserted
SQL> insert into mytbl values(4,rpad(‘v’,2000,’v’));
1 row inserted
SQL> insert into mytbl values(5,rpad(‘v’,2,’v’));
1 row inserted
SQL> insert into mytbl values(6,rpad(‘v’,6,’v’));
1 row inserted
SQL> commit;
Commit complete
SQL> SELECT dbms_rowid.rowid_block_number(rowid) block# FROM mytbl;
BLOCK#
———-
228
228
228
228
228
228
6 rows selected
همانطور که می بینید، همه این 6 رکورد در یک بلاک درج شده اند همچنین رکورد بعدی به دلیل پر بودن بلاک شماره 228، در بلاک بعدی ثبت می شود:
SQL> insert into mytbl values(7,”);
1 row inserted
SQL> SELECT dbms_rowid.rowid_block_number(rowid) block# FROM mytbl;
BLOCK#
———-
228
228
228
228
228
228
229
7 rows selected
SQL> rollback;
Rollback complete
همانطور که ملاحظه شد، بلاک 228 گنجایش بیشتری ندارد با این وجود، در صورتی که روش ما برای بررسی پر بودن بلاک، روش درستی باشد(که هست)، امکان انجام بیش از دو تراکنش همزمان بر روی بلاک 228 وجود نخواهد داشت.
توجه: همانطور که قبلا هم ذکر شد، به صورت پیش فرض اوراکل در هر بلاک دو ITL slot را ایجاد خواهد کرد حتی اگر مقدار INITRANS برابر با یک باشد.
در ادامه با اجرای سه دستور delete از سه session مختلف، این مسئله را بررسی می کنیم:
–session 1(transaction 1):
SQL> delete mytbl where id=1;
1 row deleted
Transaction شماره یک، هم slot و هم رکورد را در اختیار گرفته و بدون مشکل در حال اجراست.
–session 2(transaction 2):
SQL> delete mytbl where id=2;
1 row deleted
همانند تراکنش شماره یک، تراکنش شماره دو هم slot دوم را مشغول کرده و بدون انتظار در حالت اجرا قرار گرفته است.
–session 3(transaction 3):
SQL> select sid from v$mystat where rownum<=1;
SID
———-
382
SQL> delete mytbl where id=3;
Executing…
با اجرای تراکنش سوم، دستور delete در حالت انتظار باقی خواهد ماند. با رجوع به ویوی v$session_wait خواهیم دید که این session گرفتار wait eventای بنام enq: TX – allocate ITL entry شده است:
SQL> select event from v$session_wait where sid=382;
EVENT
—————————————————————-
enq: TX – allocate ITL entry
با خاتمه یافتن یکی از تراکنشهای شماره یک یا دو، session شماره سه هم از این حالت انتظار خارج می شود.
دو راهکاری برای جلوگیری ITL WAIT
با افزایش pct_free جدول و همچنین افزایش خصیصه INITRANS در جدول و ایندکس، از تعداد دفعات رخ دادن این نوع از wait eventها کاسته می شود البته این کار نیازمند بازسازی جداول و ایندکسها خواهد بود:
SQL> alter table mytbl INITRANS 50;
Table altered
Deadlock و ITL WAIT
زمانی را تصور کنید که session شماره یک transaction slot آخر بلاک A از جدول mytbl1 را تصرف کرده است همچنین session شماره دو، آخرین slot بلاک B از جدول mytbl2 را در اختیار دارد. با توجه به اینکه ایجاد slot جدیدی در هیچ کدام از این دو بلاک امکان پذیر نمی باشد، اگر session شماره یک slotای را از بلاک B درخواست کند و session شماره دو هم slotای را از بلاک A در خواست کند، Deadlock رخ خواهد داد.
برای مثال، دستور زیر به دلیل زخ دادن deadlock متوقف شده است:
–session 1:
SQL> delete mytbl1 where id=3;
1 row deleted
–session 2:
SQL> delete mytbl2 where id=4;
1 row deleted
–session 2:
SQL>delete mytbl1 where id=5
waiting…
–session 1:
SQL> delete mytbl2 where id=6;
ORA-00060: deadlock detected while waiting for resource
با رجوع به فایل تریسی که در alert log آدرس آن موجود است، اطلاعات کاملتری را در مورد این deadlock مشاهده خواهیم کرد:
2020-02-16T13:38:08.719469+03:30
ORA-00060: Deadlock detected. See Note 60.1 at My Oracle Support for Troubleshooting ORA-60 Errors. More info in file /18c/base/diag/rdbms/db18c/db18c/trace/db18c_ora_29336.trc.
قسمتی از محتویات فایل تریس را در قسمت زیر می بینید:
Deadlock graph:
————Blocker(s)———– ————Waiter(s)————
Resource Name process session holds waits serial process session holds waits serial
TX-0042001D-00000710-00000000-00000000 89 137 X 31007 92 510 S 27570
TX-003E0011-0000075C-00000000-00000000 92 510 X 27570 89 137 S 31007
—– VKTM Time Drifts Circular Buffer —–
session 137: DID 0001-0059-00000030 session 510: DID 0001-005C-00000006
session 510: DID 0001-005C-00000006 session 137: DID 0001-0059-00000030
—– Information for waiting sessions —–
Session 137:
sid: 137 ser: 31007 audsid: 570616 user: 104/USEF
flags: (0x100041) USR/- flags2: (0x40009) -/-/INC
flags_idl: (0x1) status: BSY/-/-/- kill: -/-/-/-
current SQL:
delete mytbl2 where id=6
Session 510:
sid: 510 ser: 27570 audsid: 570619 user: 104/USEF
flags: (0x100041) USR/- flags2: (0x40009) -/-/INC
flags_idl: (0x1) status: BSY/-/-/- kill: -/-/-/-
pid: 92 O/S info: user: oracle, term: UNKNOWN, ospid: 30324
application name: PL/SQL Developer, hash value=1190136663
action name: Command Window – New, hash value=254318129
current SQL:
delete mytbl1 where id=5
—– End of information for waiting sessions —–
*** 2020-02-16T13:38:06.735658+03:30
dbkedDefDump(): Starting a non-incident diagnostic dump (flags=0x0, level=1, mask=0x0)
—– Error Stack Dump —–
—– Current SQL Statement for this session (sql_id=91q18bnn0zm8b) —–
delete mytbl2 where id=6
The history is displayed in reverse chronological order.
Current Wait Stack:
0: waiting for ‘enq: TX – allocate ITL entry’
name|mode=0x54580004, usn<<16 | slot=0x3e0011, sequence=0x75c
wait_id=119 seq_num=120 snap_id=1
wait times: snap=10.214547 sec, exc=10.214547 sec, total=10.214547 sec
wait times: max=32.000000 sec, heur=10.214547 sec
wait counts: calls=3 os=3
in_wait=1 iflags=0x15a0
There is at least one session blocking this session.
Dumping 1 direct blocker(s):
inst: 1, sid: 510, ser: 27570
Dumping final blocker:
inst: 1, sid: 510, ser: 27570
There are 1 sessions blocked by this session.
Dumping one waiter:
inst: 1, sid: 510, ser: 27570
wait event: ‘enq: TX – allocate ITL entry’
p1: ‘name|mode’=0x54580004
p2: ‘usn<<16 | slot’=0x42001d
p3: ‘sequence’=0x710
row_wait_obj#: 124758, block#: 175, row#: 0, file# 10
min_blocked_time: 0 secs, waiter_cache_ver: 35252
Wait State:
fixed_waits=0 flags=0x22 boundary=(nil)/-1
Session Wait History:
elapsed time of 0.000066 sec since current wait
0: waited for ‘enq: TX – allocate ITL entry’
name|mode=0x54580004, usn<<16 | slot=0x41001a, sequence=0x777
wait_id=118 seq_num=119 snap_id=1
wait times: snap=5.000921 sec, exc=5.000921 sec, total=5.000921 sec
wait times: max=5.000000 sec
wait counts: calls=2 os=2
occurred after 0.000075 sec of elapsed time
1: waited for ‘enq: TX – allocate ITL entry’
name|mode=0x54580004, usn<<16 | slot=0x3e0011, sequence=0x75c
wait_id=117 seq_num=118 snap_id=1
wait times: snap=15.999972 sec, exc=15.999972 sec, total=15.999972 sec
wait times: max=16.000000 sec
wait counts: calls=6 os=6
occurred after 0.000045 sec of elapsed time
2: waited for ‘enq: TX – allocate ITL entry’
name|mode=0x54580004, usn<<16 | slot=0x41001a, sequence=0x777
wait_id=116 seq_num=117 snap_id=1
wait times: snap=4.999940 sec, exc=4.999940 sec, total=4.999940 sec
wait times: max=5.000000 sec
wait counts: calls=2 os=2
occurred after 0.000072 sec of elapsed time
itl wait صرفا برای update و delete رخ می دهد و در زمان insert این نوع از event wait اتفاق نمی افتد زیرا که اگر itlای در بلاک خالی نباشد، اوراکل برای insert به سراغ بلاک بعدی می رود.
درود بر همت شما