Saturday, August 28, 2010

โอ้! ขวางทาง Lock


ถ้าคุณเคยรับโทรศัพท์จากผู้ใช้ระบบที่อารมณ์บูดอันเนื่องมาจากธุรกรรมที่เขาหรือเธอกำลังทำอยู่บนโปรแกรมนั้นมันดูเหมือนแฮ็งก์ ๆ ช้า ๆ ไม่ไปไหนเลย หรือจากนักพัฒนาระบบที่ไม่เข้าใจว่าทำไม Session ทั้งหลายที่เกิดขึ้นจากการรันโปรแกรมที่เขาหรือเธอเขียนขึ้นนั้น มัน "Block" กันเอง คุณรู้ว่าจะมีประโยชน์มากที่สามารถระบุได้ว่าใคร (หรือ Session ใด) ไป Block ชาวบ้านเขา (หมายถึง Session อื่น) และจะมีประโยชน์ยิ่งขึ้นถ้ารู้ว่า Object ตัวใด (เช่น ตารางใด) กำลังถูก Block ไปจนกระทั่งรู้ว่าแถวใดกำลังถูก Block อยู่
ทดลองสร้าง Session ที่ล๊อคเรคคอร์ดที่ไป Block การล๊อคของเรคคอร์ดของ Session อื่น
{xtypo_info}ผู้แปล -- ปกติเมื่อ Session ต้องการทำการเปลี่ยนแปลงข้อมูล Session นั้นจะต้องทำการ Lock เรคคอร์ดที่ต้องการเปลี่ยนแปลงเสียก่อนโดยการใช้คำสั่ง SELECT ... FOR UPDATE, คำสั่ง DML บางอย่างเช่น Update, Delete จะทำการ Lock เรคคอร์ดที่มันไปกระทำไว้ทันที จนกว่าจะมีการ Commit หรือ Rollback คำสั่งนั้น ในขณะที่ Session กำลังรันคำสั่งเหล่านี้อยู่ (และยังไม่ได้ Commit หรือ Rollback) Session อื่น ๆ ที่รันคำสั่งที่จะทำการ Lock เรคคอร์ดเดียวกันกับเรคคอร์ดที่ถูก Lock อยู่ เราเรียกเหตุการณ์นี้ว่า Session แรกกำลัง Block Session หลังอยู่{/xtypo_info}

เริ่มต้นด้วยการสร้างสถานการณ์ที่ผู้ใช้ระบบคนหนึ่งไป Block ผู้ใช้ระบบอีกคนหนึ่ง ลองเปิด 2 Session แล้วรันคำสั่งต่อไปนี้ใน Session ที่ 1 เพื่อสร้างตารางทดสอบ

SQL> create table tstlock (foo varchar2(1), bar varchar2(1));

Table created.

SQL> insert into tstlock values (1,'a');

1 row created.

SQL> insert into tstlock values (2, 'b');

1 row created.

SQL> select * from tstlock ;

FOO BAR
--- ---
1   a
2   b

2 rows selected.

SQL> commit ;

Commit complete.


ต่อไปก็ทำการล๊อคข้อมูลทั้งตาราง โดยยังอยู่ใน Session 1
SQL> select * from tstlock for update;

และใน Session 2 ลองพยายามอัพเดทแถวดู
SQL> update tstlock set bar='a' where bar='a' ;

คำสั่งข้างบนนี้จะแฮงก์ไปเฉย ๆ โดยถูก Block จาก Session 1 ที่กำลังล๊อคข้อมูลทั้งตารางอยู่

การค้นหา Session ที่กำลัง Block Session อื่นอยู่

Oracle ได้เตรียม View ชื่อ DBA_BLOCKERS ซึ่งจะแสดงรายการ SID ซึ่งกำลัง Block SID อื่นอยู่ แต่ว่าจากประสบการณ์ของตัวผู้เขียน พบว่าจะช้ากว่าการคิวรีจาก V$LOCK และไม่ได้ให้ข้อมูลอื่นใดเพิ่มเติมนอกจาก SID ที่กำลัง Block คนอื่นอยู่ ในขณะที่ V$LOCK คิวรีได้เร็วกว่าทำให้ง่ายในการระบุ Session ที่ Block คนอื่น ๆ อยู่พร้อมกับข้อมูลอื่น ๆ ที่เป็นประโยชน์ในการวิเคราะห์
SQL> select * from v$lock ;

ADDR     KADDR    SID TY ID1    ID2   LMODE REQUEST CTIME BLOCK
-------- -------- --- -- ------ ----- ----- ------- ----- -----
AF9E2C4C AF9E2C60 479 TX 131078 16739     0       6   685     0
ADDF7EC8 ADDF7EE0 422 TM 88519  0         3       0   697     0
ADDF7F74 ADDF7F8C 479 TM 88519  0         3       0   685     0
ADEBEA20 ADEBEB3C 422 TX 131078 16739     6       0   697     1
....     ....     ... .. ...... ..        .       .   ...     .


สังเกตคอลัมน์ BLOCK ถ้า Session ใด ๆ กำลัง Block Session อื่นอยู่ ค่า BLOCK จะเท่ากับ 1, นอกจากนั้นคุณสามารถจะชี้ Session ที่กำลังถูก Block ได้โดยเปรียบเทียบค่าในคอลัมน์ ID1 และ ID2, Session ที่ถูก Block จะมีค่าของ ID1 และ ID2 เหมือนกับ Session ที่ Block มันอยู่ และตัว Session ที่ถูก Block นี้จะมีค่าในคอลัมน์ REQUEST > 0 เสมอ เพื่อแสดงว่ามันกำลังร้องขออยู่ (แต่ยังไม่ได้)
ในคิวรีข้างบนเราจะเห็นว่า SID 422 กำลัง Block SID 479, SID 422 มีลักษณะคล้าย ๆ กับ Session 1 ในตัวอย่างของเรา และ SID 479 ก็คล้ายกับ Session 2
เพื่อให้ดูง่ายขึ้นเราอาจจะเขียนคิวรีแบบนี้
SQL> select l1.sid, ' IS BLOCKING ', l2.sid
2 from v$lock l1, v$lock l2
3 where l1.block =1 and l2.request > 0
4 and l1.id1=l2.id1
5 and l1.id2=l2.id2;

SID 'ISBLOCKING' SID
--- ------------ ---
422 IS BLOCKING  479

1 row selected.


และเราอาจจะเพิ่มข้อมูลบางอย่างเข้าไป โดยการ Join กับ V$SESSION เพื่อให้ดูง่ายขึ้น
SQL> select s1.username || '@' || s1.machine
2 || ' ( SID=' || s1.sid || ' ) is blocking '
3 || s2.username || '@' || s2.machine || ' ( SID=' || s2.sid || ' ) ' AS blocking_status
4 from v$lock l1, v$session s1, v$lock l2, v$session s2
5 where s1.sid=l1.sid and s2.sid=l2.sid
6 and l1.BLOCK=1 and l2.request > 0
7 and l1.id1 = l2.id1
8 and l2.id2 = l2.id2 ;

BLOCKING_STATUS
--------------------------------------------------------------------
BULKLOAD@yttrium ( SID=422 ) is blocking BULKLOAD@yttrium ( SID=479 )

1 row selected.


ยังคงมีข้อมูลในวิว V$LOCK อีกมาก ก่อนที่จะสามารถอ่านข้อมูลเหล่านี้ เราจำเป็นจะต้องเข้าใจเพิ่มเติมเกี่ยวกับประเภทของการล๊อค และความหมายของคอลัมน์ ID1 และ ID2 เสียก่อน ขอบคุณที่ให้ความสนใจ โปรดติดตามตอนต่อไปนะคร๊าบ

แปลจาก What's blocking my lock? โดย Natalka Roshak on Sat, 2006-04-01 18:00 www.orafaq.com/blog/natalka_roshak

No comments:

Post a Comment