- 論壇徽章:
- 0
|
INFORMIX鎖機制及如何分析其鎖沖突(第一部分)
本文講述INFORMIX數據庫鎖的基本原理,由2部分組成。IDS是OLTP應用及內嵌式系統的最佳解決方案。通過本文可以幫助你理解數據
庫鎖的使用方法,便于你及時處理、分析鎖沖突。
介紹
在多用戶數據庫系統中擁有成千上萬的并發(fā)用戶在同時訪問數據,因此我們需要有某種機制來保護數據以維護其數據一致性。除了事
物日志機制外,鎖機制就是我們采用的另一個主要手段。
然而,鎖機制經常會導致沖突及等待現象的發(fā)生。這通常是一個DBA在日常管理工作中會碰到的一個普遍問題。如果缺乏一些適當的腳
本或手段,往往會導致在分析鎖問題時變得越來越復雜并出現錯誤。
本文將闡述IDS的鎖機制,幫助你分析出現的鎖沖突及鎖等待的情況。以下的示例基于數據庫stores_demo,該數據庫可通過以下命令
創(chuàng)建:
dbaccessdemo stores_demo -log
鎖類型
IDS包含幾種不同的鎖。通常有:
共享鎖
共享鎖可以被放置在沒有設置排他鎖的記錄上。其他用戶可以在相同的記錄上放置共享鎖或更新鎖,但是不能再放置排他鎖。
更新鎖
更新鎖是一種在使用更新游標時產生的特殊類型的鎖。更新鎖只能被放置在當前沒有設置更新鎖或排他鎖的記錄上。一旦記錄被加上
更新鎖,它的數據在被修改的同時就會立刻升級為排他鎖。
排他鎖
排他鎖只能被放置在沒有任何鎖的記錄上。一旦排他鎖被放置在該記錄上,則其他鎖也就不能在增加到該記錄了。該記錄將被被會話
獨占使用。
內部鎖
內部鎖包含多種特殊類型的鎖。舉個例子,當一條記錄被修改時,該記錄上被放置一個排他鎖,同時所涉及的數據庫表上也被放置一
個排他的內部鎖。這是為了確保當該表的數據在排他使用時其他的會話不能在該表上再設置共享或排他鎖。
鎖粒度
IDS允許應用開發(fā)人員在不同的對象上放置鎖。有以下對象。
數據庫鎖
數據庫可以以排他或共享的方式被鎖住。在排他的情況下將防止其他任何人再訪問該數據庫。在共享的情況下允許并發(fā)用戶讀取或修
改該數據庫的數據,但是不允許在該數據庫上再放置排他鎖。
數據庫共享鎖:
數據庫共享鎖在你打開數據庫的同時將被自動設置。這將防止沒有其他用戶可以在該數據庫上再放置排他鎖或刪除數據庫。一些數據
庫工具:onunload也會在數據庫上放置共享鎖
SQL語句:
database stores_demo
Listing 1. Share lock on a database
Output from onstat -k:
----------------------
Locks
address wtlist owner lklist type tblsnum rowid key#/bsiz
300583dc 0 400d6998 0 HDR+S 100002 207 0
在這里你可以看到數據庫上加有共享鎖HDR+S。tblsnum=100002表明了數據庫的表空間。rowid=207是該數據庫信息存儲在數據庫
sysmaster,表sysdatabases中記錄的16進制信息。數據庫表sysmaster:sysdatabases通常也被稱作數據庫的表空間。
數據庫及他們的16進制rowid信息可以通過以下sql語句獲得:
Listing 2. Query on database sysmaster retrieving the row ID of a database tablespace entry
Query:
------
database sysmaster;
select name, hex(rowid) hex_rowid
from sysdatabases;
Result:
-------
name hex_rowid
linux_mag 0x00000204
onpload 0x00000206
stores_demo 0x00000207
sysmaster 0x00000201
sysuser 0x00000203
sysutils 0x00000202
tpcb 0x00000208
數據庫排他鎖:
一個排他鎖可以被排他的設置在數據庫上。數據庫工具:dbexport在倒出數據的時候就會在數據庫上放置一個排他鎖。
SQL 語句:
database stores_demo exclusive
Listing 3. Exclusive lock on a database
Output from onstat -k:
----------------------
Locks
address wtlist owner lklist type tblsnum rowid key#/bsiz
300583dc 0 400d63d8 0 HDR+X 100002 207 0
在這里你可以看到一個排他鎖(HDR+X)被放置在數據庫上,其對應的rowid為207
表鎖:
與數據庫鎖類似,數據庫表也可以以排他或共享的形式被鎖住。排他鎖可以防止其他用戶讀取或修改該表信息。一種例外是該會話運
行在臟讀的隔離級下。這樣就可以從已設置排他鎖的表中讀取數據,但是該數據可能是不準確的(正在修改中,沒有真正提交)。在表上設置
共享鎖可以允許其他用戶讀取該表數據,但是不允許修改數據。
表共享鎖:
共享鎖可以以共享的形式放置在數據庫表上。數據庫工具:onunload、oncheck在使用的時候會在數據庫表上設置共享鎖(依賴于表的
鎖模式是頁還是記錄)
SQL 語句:
begin work; lock table orders in share mode
Listing 4. Share lock on a table
Output from onstat -k:
----------------------
Locks
address wtlist owner lklist type tblsnum rowid key#/bsiz
300582e0 0 400d63d8 300583dc HDR+S 10013b 0 0
你可以看到一個共享鎖(HDR+S)和rowid被設置為0(rowid=0)。rowid為0表明是一個表鎖。tablespace number (tblsnum=10013b)構成
該表的16進制partition number。
會導致這種情形的2種可能:
oncheck命令
Data Dictionary查詢命令
Listing 5. Identifying a table through its hexadecimal partition number
Dictionary Query:
-----------------
database stores_demo;
select st.tabname, dbinfo("dbspace", st.partnum), hex(st.partnum)
from systables st
where hex(st.partnum) = "0x0010013B";
Result:
------
tabname dbspace hex_partnum
orders rootdbs 0x0010013B
oncheck command:
----------------
oncheck -pt 0x0010013b
Result:
-------
TBLspace Report for stores_demo:informix.orders
Physical Address 1:1988
Creation date 08/21/2006 20:07:41
TBLspace Flags 801 Page Locking
TBLspace use 4 bit bit-maps
Maximum row size 80
Number of special columns 0
...
...
注意到你需要使用0x00對通過onstat -k獲得的16進制信息進行補足,使其形成8位完整的16進制數。同時需將小寫字母轉換為大寫字
母,例如0x10013b-》0x0010013B。
如果是分片表的partnum,你需要在sysfragments表里面查找相應信息:
Listing 6. Identifying a fragmented table through its hexadecimal partition number
Query:
------
database stores_demo;
select st.tabname, dbinfo("dbspace", sf.partn), hex(sf.partn)
from systables st, sysfragments sf
where st.tabid = sf.tabid
and sf.fragtype = "T"
and hex(sf.partn) = "0x0010013B";
Result:
-------
No rows found.
本例中的partition number 0x0010013B不是分片表的一個分片,因此在sysfragments表中不存在對應信息。但是對于分片表則會有信
息記錄在sysfragments表中,請使用前面提供的語句進行查詢。
oncheck -pt <hex_partnum>不能對分片表信息進行查詢,它不會顯示真實的分片表名,僅有分片信息。
表排他鎖:
排他鎖可以被顯式的或隱式的被設置在對應的表上。例如:alter table
SQL語句:
begin; lock table orders in exclusive mode
Listing 7. Exclusive lock on table
Output from onstat -k:
----------------------
Locks
address wtlist owner lklist type tblsnum rowid key#/bsiz
300582e0 0 400d63d8 300583dc HDR+X 10013b 0 0
在這里一個排他鎖(HDR+X)被設置在表上,對應的tblsnum為10013b
頁或記錄級鎖:
鎖的級別(頁或記錄鎖)可以在創(chuàng)建表的時候進行指定,或使用alter table對已有表進行修改。
建表:
reate table t1 (f1 int) lock mode (row)
修改表:
alter table t1 modify lock mode (page)
缺省鎖模式,在創(chuàng)建表的時候采取何種鎖模式,可以通過在配置文件中配置DEF_TABLE_LOCKMODE來進行指定或通過環(huán)境變量
IFX_DEF_TABLE_LOCKMODE來指定。在建表時所指定的鎖模式會優(yōu)先于通過DEF_TABLE_LOCKMODE或IFX_DEF_TABLE_LOCKMODE所指定的鎖模式。
使用頁鎖意味著即使只修改一條記錄也會將該記錄所在的整個數據頁進行加鎖。依賴于記錄的大小及數據頁的大小,采用這種方式會
降低數據庫的并發(fā)處理性能。特別需要注意的是采取頁鎖不僅會對相關數據頁有影響,而且對索引頁同樣也會有影響。這樣會進一步降低數據
庫的并發(fā)處理性能,因為索引頁相對于數據頁來說通常會包含更多的記錄。
你可以通過以下2種方式來確定當前表采取了何種鎖模式:
Listing 8. Determining the lock mode of a table using oncheck (a) or a dictionary query (b)
a) Command:
-----------
oncheck -pt stores_demo:eherber.orders
Output:
-------
TBLspace Report for stores_log:informix.orders
Physical Address 1:3905
Creation date 05/08/2006 16:47:43
TBLspace Flags 801 Page Locking
...
...
b) Query:
---------
database stores_demo;
select tabname, locklevel
from systables
where tabname = "orders";
Result:
-------
tabname locklevel
orders P
以下為IDS中鎖模式的縮寫標志:
B 視圖(需要檢查其依賴的鎖模式)
P 頁級鎖
R 記錄鎖
可以通過以下SQL語句產生相應的修改已存在表的鎖模式的語句:
Listing 9. Using meta SQL to convert all tables to row locking
Meta SQL:
---------
database stores_demo;
output to alter_table.sql without headings
select "alter table '" || trim(owner) || "'." || trim(tabname) || " lock mode(row);"
from 'informix'.systables
where tabid > 99
and tabtype = 'T'
and locklevel = 'P';
Generated output file 'alter_table.sql':
----------------------------------------
alter table 'informix'.customer lock mode(row);
alter table 'informix'.orders lock mode(row);
...
...
頁鎖:
對于配置為使用頁鎖的表,在數據處理的過程中頁鎖會被設置在對應的數據頁上。鎖的類型可能為排他鎖(插入、修改、刪除)或共
享鎖(設置隔離級為重復讀)
SQL語句:
begin; update orders set ship_charge = (ship_charge*1.2) where order_num = 1005
Listing 10. Exclusive lock on a page
Output from onstat -k:
----------------------
Locks
address wtlist owner lklist type tblsnum rowid key#/bsiz
440cf9bc 0 4506c3d0 0 HDR+S 100002 207 0
440cfa14 0 4506c3d0 440cf9bc HDR+IX 10013b 0 0
440d082c 0 4506c3d0 440cfa14 HDR+X 10013b 100 0
本例出現了3中類型的鎖:
1.數據庫上的共享鎖(HDR+S)。tblsnum=100002是該數據庫的partnum。
該共享鎖可以防止其他用戶在該數據庫上設置排他鎖
2.表上的內部排他鎖(HDR+IX)。tblsnum=0表明這是一個表鎖。
內部排他鎖可以防止其他用戶在該表上設置共享或排他鎖。
3.數據頁上的排他鎖(rowid=100,如果rowid的后2位為00,則表明該鎖為頁鎖)
排他鎖可以防止其他用戶在該數據頁上放置共享鎖或排他鎖。
記錄鎖:
如果數據庫表被設置為記錄級鎖,則在數據的處理過程中,只有相關的記錄上被設置上鎖。鎖的類型可能為排他鎖(插入、修改、刪
除)或共享鎖(設置隔離級為重復讀)
SQL語句:
begin; set isolation to repeatable read; select * from customer where customer_num = 110
Listing 11. Share lock on a row
Output from onstat -k:
----------------------
Locks
address wtlist owner lklist type tblsnum rowid key#/bsiz
440cf9bc 0 4506c3d0 0 HDR+S 100002 207 0
440cfa14 0 4506c3d0 440cf9bc HDR+IS 100139 0 0
440cfd84 0 4506c3d0 440d082c HDR+S 100139 10a 0
440d082c 0 4506c3d0 440cfa14 HDR+SR 10013a 10a K- 1
本例出現了4種類型的鎖:
1、數據庫上的共享鎖(HDR+S)。tblsnum=100002是該數據庫的partnum。
該共享鎖可以防止其他用戶在該數據庫上設置排他鎖
2、表上的內部排他鎖(HDR+IX)。tblsnum=0表明這是一個表鎖。
內部排他鎖可以防止其他用戶在該表上設置共享或排他鎖。
3、在記錄上設置的共享鎖(rowid=10a)
記錄上設置的共享鎖可以防止其他用戶在該記錄上設置排他鎖。
4、索引上設置的共享鎖(K-1)
在索引上設置的共享鎖可以防止其他用戶在該索引值上設置排他鎖。
你可以通過以下SQL查詢到被設置記錄鎖的相關記錄:
Listing 12. Identifying a locked row
Query:
------
database stores_demo;
set isolation to dirty read;
select *
from customer
where hex(rowid) = "0x0000010A";
Result:
-------
customer_num 110
fname Roy
...
...
索引鍵鎖:
類似于數據記錄上的鎖,IDS同樣在索引鍵上設置對應的鎖,以保護其內容。鍵值鎖可以確保唯一索引保持其唯一性,在事物未提交以
前不允許相同值的記錄插入到同樣的索引位置。
索引鍵鎖可以通過在onstat -k的輸出中于key#/bsiz字段中由K來表示出來。
數據庫的日志模式
IDS中可以使用的數據庫日志模式為以下4種:
無日志模式
緩沖日志模式
非緩沖日志模式
ANSI日志模式
這些日志模式在后續(xù)章節(jié)中進行闡述。
無日志模式:
運行在無日志模式下的數據庫。不能執(zhí)行begin、rollback、commit操作,事物信息不會被記錄在邏輯日志中。每條SQL語句的執(zhí)行,
自動提交。所涉及的鎖只會影響當前正在執(zhí)行的語句。
缺省隔離級:
臟讀。這是在采用無日志模式的數據庫上唯一可以使用的隔離級
SQL語句:
create database stores_demo
緩沖日志模式:
事物信息被記錄在邏輯日志中。如果你想包含多條SQL操作于一個事物中,則顯式的begin操作是必須的。事物的結束以commit或
rollback為標志。如果沒有顯式的begin作為事物的開始,則每條SQL語句將被看為獨立的事物。
如果commit命令被執(zhí)行,整個事物的信息并不是立即被寫入磁盤上的邏輯日志。這些信息將會被保存在共享內存中的log buffer里。
當log buffer滿或使用非緩沖日志方式的數據庫事物、使用ANSI日志模式的數據庫事物在事物提交的時候這些信息才真正的被寫入到磁盤上。
你應該衡量由此帶來的對于數據庫事物吞吐率提升的好處與當數據庫宕機時造成的數據丟失風險之間的利弊。如果你需要較高的事物
吞吐率,同時需要保存關鍵的業(yè)務數據,使用高速磁盤或使用電池的ramdisk是一個不錯的選擇。
缺省隔離級:
提交讀
使用該日志模式的數據庫缺省隔離級為提交讀,但是用戶可以使用set isolation to <isol_level> 命令設置適當的其他隔離級
SQL命令:
create database stores_demo with buffered log
非緩沖日志模式:
與緩沖日志模式類似,但是使用該日志模式的數據庫在事物提交時將會立即將相關的事物信息寫入磁盤。你不會丟失任何已提交的事
物信息。
缺省隔離級:
提交讀
使用該日志模式的數據庫缺省隔離級為提交讀,但是用戶可以使用set isolation to <isol_level> 命令設置適當的其他隔離級
SQL命令:
create database stores_demo with log
ANSI日志模式:
使用該日志模式的數據庫的缺省隔離級為重復讀,這意味著在處理的每條記錄上將會被設置上共享鎖。這會導致鎖沖突及所等待現象
的出現。隔離級別可以通過set isolation to <isolation_level> 命令進行切換。
缺省隔離級:
重復讀
使用該日志模式的數據庫可以使用set isolation to <isol_level>命令來進行隔離級的切換
SQL命令:
create database stores_demo with log mode ansi
Database logging modes and default isolation levels
Database logging mode Create statement Default isolation level
No logging create database stores_demo Dirty read
Buffered logging create database stores_demo with buffered log Committed read
Unbuffered logging create database stores_demo with log Committed read
Mode ANSI (unbuffered logging) create database stores_demo with log mode ansi Repeatable read
當前數據庫日志模式可以通過onmonitor工具或查詢sysmaster數據庫來獲得:
onmonitor (status-databases)
N -- 無日志
B -- 緩沖日志模式
U -- 非緩沖日志模式
U* -- ANSI模式 (unbuffered logging)
sysmaster query
Listing 13. Determining the logging mode of a database
Query:
------
database sysmaster;
select name, is_logging, is_buff_log, is_ansi
from sysmaster:sysdatabases;
Result:
-------
name stores_ansi
is_logging 1
is_buff_log 0
is_ansi 1
name stores_demo
is_logging 1
is_buff_log 0
is_ansi 0
你可以通過ontape或ondblog工具進行數據庫日志模式的切換。需要注意的是一旦數據庫設置為ANSI日志模式,它將不能被切換為其他
的日志模式。
隔離級:
在IDS中依賴于采用不同日志模式的數據庫,有4中不同的隔離級可供使用。后面將一一予以介紹。
IDS isolation levels and their ANSI counterparts
Informix SQL ANSI SQL
Dirty read Read uncommittted
Committed read Read committed
Cursor stability Not available
Repeatable read Serializable
臟讀(ANSI: Read uncommitted)
如果你在讀取數據的時候采取該隔離級,那么你將不會在涉及到的數據上設置鎖,也不會碰到任何鎖的問題。但是,你有可能得到的
是一個不準確的數據,因為該數據可能還沒有被提交。
臟讀是未帶日志模式的數據庫唯一可供選擇的隔離級別。
informix SQL
set isolation to dirty read [retain update locks]
ANSI SQL
set transaction isolation level read uncommitted
提交讀(ANSI: Read committed)
使用該隔離級可以確保所讀出的數據是已經被提交后的。
運行在提交讀隔離級下的會話會嘗試在需要讀取的數據上設置共享鎖,但不真正設置。這樣就能確保不會讀取到其他正在并發(fā)修改的
數據。然而,在讀取數據以后,由于沒有在該數據上設置任何鎖,因此其他的并發(fā)事物可以對該數據進行修改。
提交讀是使用緩沖日志模式和非緩沖日志模式數據庫鎖采用的缺省隔離級,但非使用ANSI日志模式的數據庫所采取的隔離級。
Informix SQL
set isolation to committed read [retain update locks]
ANSI SQL
set transaction isolation level read committed
游標穩(wěn)定讀(ANSI: --)
游標穩(wěn)定讀隔離級使用在更新游標中。
IDS在當前讀取的數據上放置一個更新鎖。如果該數據在此時被修改,則轉換為一個排他鎖并一直保持到事物結束,并不依賴于游標所
處的當前位置。如果讀取到下一條數據而當前的數據沒有被修改,那么當前數據上的更新鎖將被釋放,同時在下一條數據上放置更新鎖。如果
在set isolation命令中加上retain update locks子句則會改變這種情況。這樣在讀取到下一條數據的時候上一條數據上所放置的更新鎖將不
會被釋放。
更新鎖只能被放置在當前沒有被設置任何更新鎖或排他鎖的數據上。然而,一旦更新鎖被放置,其他會話仍然是可以在該數據上放置
共享鎖的。這種情況下如果你試圖去更新這些數據將會導致錯誤,因為由于存在共享鎖IDS將不能把一個更新鎖升級成為一個排他鎖。這是我們
希望得到的一種結果。如果你確定在后續(xù)的操作中你肯定需要更新數據,那么你可以使用dummy update將所有的更新鎖都升級為排他鎖。這將
避免其他的并發(fā)會話在相同的記錄上設置共享鎖。
Informix SQL
set isolation to cursor stability [retain update locks]
ANSI SQL
--
重復讀(ANSI: Serializable)
使用重復讀隔離級將會在所讀取的記錄上都設置共享鎖(如果使用頁級鎖將會在所涉及的數據頁上都設置共享鎖)。這將可以避免其
他用戶更改這些數據。
retain update locks子句在這種隔離級下將不能使用,因為這種隔離級本身就會自動保持共享鎖或更新鎖。
重復讀這種隔離級是采用ANSI日志模式的數據庫所使用的缺省隔離級別。
Informix SQL
set isolation to repeatable read
ANSI SQL
set transaction isolation level serializable
Informix SQL 與 ANSI SQL之間的區(qū)別:
在informix使用的SQL命令set isolation to <isol_level>和ANSI使用的SQL命令set transaction isolation level <isol_level>之
間具有2個重要的區(qū)別。
在informix中所執(zhí)行的命令是基于會話的。也就是說在整個會話中你一旦設置好隔離級就會立刻生效直到你下一次使用set isolation
to <isol_level>命令重新設置為止。
對應的在ANSI中所執(zhí)行的命令是基于事物的。隔離級設置好以后將會只對當前事物有效。一旦事物結束,則隔離級別將會自動切換為
當前打開數據庫所具有的缺省隔離級。更多的信息,請參照數據庫日志模式一節(jié)。
在數據庫事物中你將不能使用ANSI的SQL命令set transaction isolation level <isol_level>來設置新的隔離級別。這是不允許的。
但是informix允許在事物中進行隔離級別的切換。
會話的隔離級別:
你可以通過執(zhí)行onstat -g sql命令來得到當前正在執(zhí)行的會話所使用的隔離級別。
Listing 14. Listing isolation levels of individual database sessions
Command:
---------
onstat -g sql
Output:
-------
Sess SQL Current Iso Lock SQL ISAM F.E.
Id Stmt type Database Lvl Mode ERR ERR Vers Explain
50 - stores_demo RR Wait 10 0 0 9.03 Off
45 - stores_demo DR Not Wait 0 0 9.03 Off
...
...
當前數據庫所采用的隔離級別將顯示在Iso Lvl字段下,以下為其縮寫方式:
DR -- 臟讀
CR -- 提交讀
CS -- 游標穩(wěn)定讀
RR -- 重復讀
小結:
現在你對數據庫鎖的類型、鎖的粒度、數據庫的日志模式、隔離級別有了一個初步的認識。在后續(xù)部分我們將對以下內能進行介紹:
鎖的等待時間
鎖的動態(tài)分配
死鎖
鎖的等待情況
本文作者:Eric Herber |
評分
-
查看全部評分
|