[sql server] 鎖知識

來源:互聯網
上載者:User

未整理。

 

 

--1
雖然不能完全避免死結,但可以使死結的數量減至最少。將死結減至最少可以增加事務的輸送量並減少系統開銷,因為只有很少的事務:
復原,而復原會取消事務執行的所有工作。
由於死結時復原而由應用程式重新提交。
下列方法有助於最大限度地降低死結:
按同一順序訪問對象。
避免事務中的使用者互動。
保持事務簡短並在一個批處理中。
使用低隔離等級。
使用綁定串連。
按同一順序訪問對象
如果所有並發事務按同一順序訪問對象,則發生死結的可能性會降低。例如,如果兩個並發事務獲得 Supplier 表上的鎖,然後獲得 Part 表上的鎖,則在其中一個事務完成之前,另一個事務被阻塞在 Supplier 表上。第一個事務提交或復原後,第二個事務繼續進行。不發生死結。將預存程序用於所有的資料修改可以標準化訪問對象的順序。
避免事務中的使用者互動
避免編寫包含使用者互動的事務,因為運行沒有使用者互動的批處理的速度要遠遠快於使用者手動響應查詢的速度,例如回覆應用程式請求參數的提示。例如,如果事務正在等待使用者輸入,而使用者去吃午餐了或者甚至回家過周末了,則使用者將此事務掛起使之不能完成。這樣將降低系統的輸送量,因為事務持有的任何鎖只有在事務提交或復原時才會釋放。即使不出現死結的情況,訪問同一資源的其它事務也會被阻塞,等待該事務完成。
保持事務簡短並在一個批處理中
在同一資料庫中並發執行多個需要長時間啟動並執行事務時通常發生死結。事務已耗用時間越長,其持有排它鎖或更新鎖定的時間也就越長,從而堵塞了其它活動並可能導致死結。
保持事務在一個批處理中,可以最小化事務的網路通訊往返量,減少完成事務可能的延遲並釋放鎖。
使用低隔離等級
確定事務是否能在更低的隔離等級上運行。執行提交讀允許事務讀取另一個事務已讀取(未修改)的資料,而不必等待第一個事務完成。使用較低的隔離等級(例如提交讀)而不使用較高的隔離等級(例如可串列讀)可以縮短持有共用鎖定的時間,從而降低了鎖定爭奪。
使用綁定串連
使用綁定串連使同一應用程式所開啟的兩個或多個串連可以相互合作。次級串連所獲得的任何鎖可以象由主串連獲得的鎖那樣持有,反之亦然,因此不會相互阻塞
檢測死結
如果發生死結了,我們怎麼去檢測具體發生死結的是哪條SQL語句或預存程序?
這時我們可以使用以下預存程序來檢測,就可以查出引起死結的進程和SQL語句。SQL Server內建的系統預存程序sp_who和sp_lock也可以用來尋找阻塞和死結, 但沒有這裡介紹的方法好用。
 
use master
go
create procedure sp_who_lock
as
begin
declare @spid int,@bl int,
 @intTransactionCountOnEntry  int,
        @intRowcount    int,
        @intCountProperties   int,
        @intCounter    int
 create table #tmp_lock_who (
 id int identity(1,1),
 spid smallint,
 bl smallint)
 
 IF @@ERROR<>0 RETURN @@ERROR
 
 insert into #tmp_lock_who(spid,bl) select  0 ,blocked
   from (select * from sysprocesses where  blocked>0 ) a
   where not exists(select * from (select * from sysprocesses where  blocked>0 ) b
   where a.blocked=spid)
   union select spid,blocked from sysprocesses where  blocked>0
 IF @@ERROR<>0 RETURN @@ERROR
 
-- 找到暫存資料表的記錄數
 select  @intCountProperties = Count(*),@intCounter = 1
 from #tmp_lock_who
 
 IF @@ERROR<>0 RETURN @@ERROR
 
 if @intCountProperties=0
  select '現在沒有阻塞和死結資訊' as message
-- 迴圈開始
while @intCounter <= @intCountProperties
begin
-- 取第一條記錄
  select  @spid = spid,@bl = bl
  from #tmp_lock_who where Id = @intCounter
 begin
  if @spid =0
            select '引起資料庫死結的是: '+ CAST(@bl AS VARCHAR(10)) + '進程號,其執行的SQL文法如下'
 else
            select '進程號SPID:'+ CAST(@spid AS VARCHAR(10))+ '被' + '進程號SPID:'+ CAST(@bl AS VARCHAR(10)) +'阻塞,其當前進程執行的SQL文法如下'
 DBCC INPUTBUFFER (@bl )
 end
-- 迴圈指標下移
 set @intCounter = @intCounter + 1
end
drop table #tmp_lock_who
return 0
end
 
殺死結和進程
如何去手動的殺死進程和鎖?最簡單的辦法,重新啟動服務。但是這裡要介紹一個預存程序,通過顯式的調用,可以殺死進程和鎖。
use master
go
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_killspid]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_killspid]
GO
create proc p_killspid
@dbname varchar(200)    --要關閉進程的資料庫名
as 
    declare @sql  nvarchar(500) 
    declare @spid nvarchar(20)
    declare #tb cursor for
        select spid=cast(spid as varchar(20)) from master..sysprocesses where dbid=db_id(@dbname)
    open #tb
    fetch next from #tb into @spid
    while @@fetch_status=0
    begin 
        exec('kill '+@spid)
        fetch next from #tb into @spid
    end 
    close #tb
    deallocate #tb
go
--用法 
exec p_killspid  'newdbpy'
 
查看鎖資訊
如何查看系統中所有鎖的詳細資料?在企業管理管理器中,我們可以看到一些進程和鎖的資訊,這裡介紹另外一種方法。
--查看鎖資訊
create table #t(req_spid int,obj_name sysname)
declare @s nvarchar(4000)
    ,@rid int,@dbname sysname,@id int,@objname sysname
declare tb cursor for
    select distinct req_spid,dbname=db_name(rsc_dbid),rsc_objid
    from master..syslockinfo where rsc_type in(4,5)
open tb
fetch next from tb into @rid,@dbname,@id
while @@fetch_status=0
begin
    set @s='select @objname=name from ['+@dbname+']..sysobjects where id=@id'
    exec sp_executesql @s,N'@objname sysname out,@id int',@objname out,@id
    insert into #t values(@rid,@objname)
    fetch next from tb into @rid,@dbname,@id
end
close tb
deallocate tb
select 進程id=a.req_spid
    ,資料庫=db_name(rsc_dbid)
    ,類型=case rsc_type when 1 then 'NULL 資源(未使用)'
        when 2 then '資料庫'
        when 3 then '檔案'
        when 4 then '索引'
        when 5 then '表'
        when 6 then '頁'
        when 7 then '鍵'
        when 8 then '擴充盤區'
        when 9 then 'RID(行 ID)'
        when 10 then '應用程式'
    end
    ,對象id=rsc_objid
    ,對象名=b.obj_name
    ,rsc_indid
 from master..syslockinfo a left join #t b on a.req_spid=b.req_spid
go
drop table #t

--2
--try

set nocount on ;
if object_id('T1') is not null
    drop table T1
go
create table T1(ID int primary key,Col1 int,Col2 nvarchar(20))
insert T1 select 1,101,'A'
insert T1 select 2,102,'B'
insert T1 select 3,103,'C'
go

if object_id('T2') is not null
    drop table T2
go
create table T2(ID int primary key,Col1 int,Col2 nvarchar(20))
insert T2 select 1,201,'X'
insert T2 select 2,202,'Y'
insert T2 select 3,203,'Z'

go
產生表數據:
/*
T1:
ID          Col1        Col2
----------- ----------- --------------------
1           101         A
2           101         B
3           101         C

T2:
ID          Col1        Col2
----------- ----------- --------------------
1           201         X
2           201         Y
3           201         Z
*/

防止死鎖:
1、    最少化阻塞。阻塞越少,發生死鎖機會越少
2、    在事務中按順序訪問表(以上例子:死鎖2)
3、    在錯誤處理程式中檢查錯誤1205並在錯誤發生時重新提交事務
4、    在錯誤處理程式中加一個過程將錯誤的詳細寫入日誌
5、    索引的合理使用(以上例子:死鎖1、死鎖3)
當發生死鎖時,事務自動提交,可通過日誌來監視死鎖

死鎖1(索引):
--連接視窗1
--1步:
begin tran
    update t1 set col2=col2+'A' where col1=101

--3步:
    select * from t2 where col1=201
commit tran

--連接視窗2

--2步:
begin tran
    update t2 set col2=col2+'B' where col1=203

--4步:
    select * from t1 where col1=103
commit tran

 

--連接視窗1:收到死鎖錯誤,連接視窗2得到結果:

/*
訊息 1205,層級 13,狀態 51,行 3
交易 (處理序識別碼 53) 在 鎖定 資源上被另一個處理序鎖死並已被選擇作為死結的犧牲者。請重新執行該交易。
*/

--連接視窗2:得到結果

/*
----------- ----------- --------------------
3           103         C
*/

處理方法:
--在t1、t2表的col1條件列建索引
create index IX_t1_col1 on t1(col1)
create index IX_t2_col1 on t2(col1)
go

--連接視窗1
--1步:
begin tran
    update t1 set col2=col2+'A' where col1=101

--3步:
select * from t2 with(index=IX_t2_col1)where col1=201    --因表數據少,只能指定索引提示才能確保SQL Server使用索引
commit tran

 

--連接視窗2

--2步:
begin tran
    update t2 set col2=col2+'B' where col1=203

--4步:
select * from t1 with(index=IX_t1_col1) where col1=103    --因表數據少,只能指定索引提示才能確保SQL Server使用索引
commit tran

 

--連接視窗1:
/*
ID          Col1        Col2
----------- ----------- --------------------
1           201         X

(1 個資料列受到影響)

*/
--連接視窗2
/*
ID          Col1        Col2
----------- ----------- --------------------
3           103         C

(1 個資料列受到影響)
*/

死鎖2(訪問表順序):

--連接視窗1:
--1步:
begin tran
    update t1 set col1=col1+1 where ID=1

--3步:
select col1 from t2 where ID=1
commit tran

 

--連接視窗2:
--2步:
begin tran
    update t2 set col1=col1+1 where ID=1

--4步
select col1 from t1 where ID=1
commit tran

--連接視窗1:

/*
col1
-----------
201

(1 個資料列受到影響)
*/

--連接視窗2:

/*
col1
-----------
訊息 1205,層級 13,狀態 51,行 1
交易 (處理序識別碼 54) 在 鎖定 資源上被另一個處理序鎖死並已被選擇作為死結的犧牲者。請重新執行該交易。
*/

處理方法:

--改變訪問表的順序

--連接視窗1:
--1步:
begin tran
    update t1 set col1=col1+1 where ID=1

--3步:
    select col1 from t2 where ID=1
commit tran

--連接視窗2:
--2步:
begin tran
    select col1 from t1 where ID=1--會等待連接視窗1提交
--4步
    update t2 set col1=col1+1 where ID=1
commit tran

死鎖3(單表):

--連接視窗1:

while 1=1
    update T1 set col1=203-col1 where ID=2

--連接視窗2:
declare @i  nvarchar(20)
while 1=1
    set @i=(select col2 from T1 with(index=IX_t1_col1)where Col1=102);--因表數據少,只能指定索引提示才能確保SQL Server使用索引

--連接視窗1
/*
訊息 1205,層級 13,狀態 51,行 4
交易 (處理序識別碼 53) 在 鎖定 資源上被另一個處理序鎖死並已被選擇作為死結的犧牲者。請重新執行該交易。
*/

處理方法:
1、刪除col1上的非叢集索引,這樣影響SELECT速度,不可取.
    drop index IX_t1_col1 on t1
2、建一個覆蓋索引
    A、drop index IX_t1_col1 on t1
    B、create index IX_t1_col1_col2 on t1(col1,col2)

通過SQL Server Profiler查死鎖資訊:

啟動SQL Server Profiler——連接實例——事件選取範圍——顯示所有事件
選擇項:
TSQL——SQL:StmtStarting
Locks——Deadlock graph(這是SQL2005新增事件,產生包含死鎖資訊的xml值)
     ——Lock:DeadlockChain 死鎖鏈中的進程產生該事件,可標識死鎖進程的ID並跟蹤操作
     ——Lock:Deadlock 該事件發生了死鎖

--3
1 如何鎖一個表的某一行

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

SELECT * FROM table ROWLOCK WHERE id = 1

2 鎖定資料庫的一個表

SELECT * FROM table WITH (HOLDLOCK)

加鎖語句:
sybase:
update 表 set col1=col1 where 1=0 ;
MSSQL:
select col1 from 表 (tablockx) where 1=0 ;
oracle:
LOCK TABLE 表 IN EXCLUSIVE MODE ;
加鎖後其它人不可操作,直到加鎖使用者解鎖,用commit或rollback解鎖

幾個例子協助大家加深印象
設table1(A,B,C)
A B C
a1 b1 c1
a2 b2 c2
a3 b3 c3

1)排它鎖
建立兩個串連
在第一個串連中執行以下語句
begin tran
update table1
set A='aa'
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran
在第二個串連中執行以下語句
begin tran
select * from table1
where B='b2'
commit tran

若同時執行上述兩個語句,則select查詢必須等待update執行完畢才能執行即要等待30秒

2)共用鎖定
在第一個串連中執行以下語句
begin tran
select * from table1 holdlock -holdlock人為加鎖
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran

在第二個串連中執行以下語句
begin tran
select A,C from table1
where B='b2'
update table1
set A='aa'
where B='b2'
commit tran

若同時執行上述兩個語句,則第二個串連中的select查詢可以執行
而update必須等待第一個事務釋放共用鎖定轉為排它鎖後才能執行 即要等待30秒

3)死結
增設table2(D,E)
D E
d1 e1
d2 e2
在第一個串連中執行以下語句
begin tran
update table1
set A='aa'
where B='b2'
waitfor delay '00:00:30'
update table2
set D='d5'
where E='e1'
commit tran

在第二個串連中執行以下語句
begin tran
update table2
set D='d5'
where E='e1'
waitfor delay '00:00:10'
update table1
set A='aa'
where B='b2'
commit tran

同時執行,系統會檢測出死結,並中止進程

補充一點:
Sql Server2000支援的表級鎖定提示

HOLDLOCK 持有共用鎖定,直到整個事務完成,應該在被鎖對象不需要時立即釋放,等於SERIALIZABLE交易隔離等級

NOLOCK 語句執行時不發出共用鎖定,允許髒讀 ,等於 READ UNCOMMITTED交易隔離等級

PAGLOCK 在使用一個表鎖的地方用多個頁鎖

READPAST 讓sql server跳過任何鎖定行,執行事務,適用於READ UNCOMMITTED交易隔離等級只跳過RID鎖,不跳過頁,地區和表鎖

ROWLOCK 強制使用行鎖

TABLOCKX 強制使用獨佔表級鎖,這個鎖在事務期間阻止任何其他事務使用這個表

UPLOCK 強制在讀表時使用更新而不用共用鎖定

應用程式鎖:
應用程式鎖就是產生用戶端程式碼的鎖,而不是sql server本身產生的鎖

處理應用程式鎖的兩個過程

sp_getapplock 鎖定應用程式資源

sp_releaseapplock 為應用程式資源解鎖

注意: 鎖定資料庫的一個表的區別

SELECT * FROM table WITH (HOLDLOCK) 其他事務可以讀取表,但不能更新刪除

SELECT * FROM table WITH (TABLOCKX) 其他事務不能讀取表,更新和刪除

SQL code
1 如何鎖一個表的某一行

A 串連中執行

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

begin tran

select * from tablename with (rowlock) where id=3

waitfor delay '00:00:05'

commit tran

B串連中如果執行

update tablename set colname='10' where id=3 --則要等待5秒

update tablename set colname='10' where id<>3 --可立即執行

2 鎖定資料庫的一個表

SELECT * FROM table WITH (HOLDLOCK)

注意: 鎖定資料庫的一個表的區別

SELECT * FROM table WITH (HOLDLOCK)
其他事務可以讀取表,但不能更新刪除

SELECT * FROM table WITH (TABLOCKX)
其他事務不能讀取表,更新和刪除

--4
--處理死結

 查看當前進程,或死結進程,並能自動殺掉死進程

 因為是針對死的,所以如果有死結進程,只能查看死結進程
 當然,你可以通過參數控制,不管有沒有死結,都只查看死結進程

--鄒建 2004.4--

--調用樣本

 exec p_lockinfo
--
create proc p_lockinfo
@kill_lock_spid bit=1,  --是否殺掉死結的進程,1 殺掉, 0 僅顯示
@show_spid_if_nolock bit=1 --如果沒有死結的進程,是否顯示正常進程資訊,1 顯示,0 不顯示
as
declare @count int,@s nvarchar(1000),@i int
select id=identity(int,1,1),標誌,
 進程ID=spid,線程ID=kpid,塊進程ID=blocked,資料庫ID=dbid,
 資料庫名=db_name(dbid),使用者ID=uid,使用者名稱=loginame,累計CPU時間=cpu,
 登陸時間=login_time,開啟事務數=open_tran, 進程狀態=status,
 工作站名=hostname,應用程式名稱=program_name,工作站進程ID=hostprocess,
 網域名稱=nt_domain,網卡地址=net_address
into #t from(
 select 標誌='死結的進程',
  spid,kpid,a.blocked,dbid,uid,loginame,cpu,login_time,open_tran,
  status,hostname,program_name,hostprocess,nt_domain,net_address,
  s1=a.spid,s2=0
 from master..sysprocesses a join (
  select blocked from master..sysprocesses group by blocked
  )b on a.spid=b.blocked where a.blocked=0
 union all
 select '_犧牲品_',
  spid,kpid,blocked,dbid,uid,loginame,cpu,login_time,open_tran,
  status,hostname,program_name,hostprocess,nt_domain,net_address,
  s1=blocked,s2=1
 from master..sysprocesses a where blocked0
)a order by s1,s2

select @count=@@rowcount,@i=1

if @count=0 and @show_spid_if_nolock=1
begin
 insert #t
 select 標誌='正常的進程',
  spid,kpid,blocked,dbid,db_name(dbid),uid,loginame,cpu,login_time,
  open_tran,status,hostname,program_name,hostprocess,nt_domain,net_address
 from master..sysprocesses
 set @count=@@rowcount
end

if @count0
begin
 create table #t1(id int identity(1,1),a nvarchar(30),b Int,EventInfo nvarchar(255))
 if @kill_lock_spid=1
 begin
  declare @spid varchar(10),@標誌 varchar(10)
  while @i=@count
  begin
   select @spid=進程ID,@標誌=標誌 from #t where id=@i
   insert #t1 exec('dbcc inputbuffer('+@spid+')')
   if @標誌='死結的進程' exec('kill '+@spid)
   set @i=@i+1
  end
 end
 else
  while @i=@count
  begin
   select @s='dbcc inputbuffer('+cast(進程ID as varchar)+')' from #t where id=@i
   insert #t1 exec(@s)
   set @i=@i+1
  end
 select a.,進程的SQL語句=b.EventInfo
 from #t a join #t1 b on a.id=b.id
end
go

--5

 SQL Server鎖類型(SQL) 收藏
1. HOLDLOCK 在該表上保持共用鎖定,直到整個事務結束,而不是在語句執行完立即釋放所添加的鎖。  
  2. NOLOCK:不添加共用鎖定和排它鎖,當這個選項生效後,可能讀到未提交讀的資料或“髒資料”,這個選項僅僅應用於SELECT語句。  
  3. PAGLOCK:指定添加頁鎖(否則通常可能添加表鎖)。 
  4. READCOMMITTED用與運行在提交讀隔離等級的事務相同的鎖語義執行掃描。預設情況下,SQL Server 2000 在此隔離等級上操作。。 
  5. READPAST 跳過已經加鎖的資料行,這個選項將使事務讀取資料時跳過那些已經被其他事務鎖定的資料行,而不是阻塞直到其他事務釋放鎖,READPAST僅僅應用於READ COMMITTED隔離性層級下事務操作中的SELECT語句操作。  
  6. READUNCOMMITTED:等同於NOLOCK。  
  7. REPEATABLEREAD:設定事務為可重複讀隔離性層級。 
  8. ROWLOCK:使用行級鎖,而不使用粒度更粗的頁級鎖和表級鎖。  
  9. SERIALIZABLE:用與運行在可串列讀隔離等級的事務相同的鎖語義執行掃描。等同於 HOLDLOCK。 
  10. TABLOCK:指定使用表級鎖,而不是使用行級或頁面級的鎖,SQL Server在該語句執行完後釋放這個鎖,而如果同時指定了HOLDLOCK,該鎖一直保持到這個事務結束。  
  11. TABLOCKX:指定在表上使用排它鎖,這個鎖可以阻止其他事務讀或更新這個表的資料,直到這個語句或整個事務結束。 
  12. UPDLOCK :指定在讀表中資料時設定更新 鎖(update lock)而不是設定共用鎖定,該鎖一直保持到這個語句或整個事務結束,使用UPDLOCK的作用是允許使用者先讀取資料(而且不阻塞其他使用者讀資料),並且保證在後來再更新資料時,這一段時間內這些資料沒有被其他使用者修改。

本文來自CSDN部落格,轉載請標明出處:httpblog.csdn.netarrow_gxarchive200806012501368.aspx

--SQL Server 2005中解決死結問題

資料庫操作的死結是不可避免的,本文並不打算討論死結如何產生,重點在於解決死結,通過SQL Server 2005, 現在似乎有了一種新的解決辦法。 

將下面的SQL語句放在兩個不同的串連裡面,並且在5秒內同時執行,將會發生死結。 

use Northwind
begin tran
  insert into Orders(CustomerId) values(@#ALFKI@#)
  waitfor delay @#000005@#
  select  from Orders where CustomerId = @#ALFKI@#
commit
print @#end tran@# 

   
SQL Server對付死結的辦法是犧牲掉其中的一個,拋出異常,並且復原事務。在SQL Server 2000,語句一旦發生異常,T-SQL將不會繼續運行,上面被犧牲的串連中, print @#end tran@#語句將不會被運行,所以我們很難在SQL Server 2000的T-SQL中對死結進行進一步的處理。 

現在不同了,SQL Server 2005可以在T-SQL中對異常進行捕獲,這樣就給我們提供了一條處理死結的途徑: 

下面利用的try ... catch來解決死結。 

SET XACT_ABORT ON
declare @r int
set @r = 1
while @r  = 3
begin
  begin tran
  
  begin try   
    insert into Orders(CustomerId) values(@#ALFKI@#)
    waitfor delay @#000005@#
    select  from Orders where CustomerId = @#ALFKI@#
    
    commit
    break
  end try
    
  begin catch
    rollback
    waitfor delay @#000003@#
    set @r = @r + 1
    continue
  end catch
end 

   
解決方案當然就是重試,但捕獲錯誤是前提。rollback後面的waitfor不

解決方案當然就是重試,但捕獲錯誤是前提。rollback後面的waitfor不可少,發生衝突後需要等待一段時間,@retry數目可以調整以應付不同的要求。 

但是現在又面臨一個新的問題 錯誤被掩蓋了,一但問題發生並且超過3次,異常卻不會被拋出。SQL Server 2005 有一個RaiseError語句,可以拋出異常,但卻不能直接拋出原來的異常,所以需要重新定義發生的錯誤,現在,解決方案變成了這樣 

declare @r int
set @r = 1
while @r  = 3
begin
  begin tran
  
  begin try   
    insert into Orders(CustomerId) values(@#ALFKI@#)
    waitfor delay @#000005@#
    select  from Orders where CustomerId = @#ALFKI@#
    
    commit
    break
  end try
    
  begin catch
    rollback
    waitfor delay @#000003@#
    set @r = @r + 1
    continue
  end catch
end
if ERROR_NUMBER()   0
begin
  declare @ErrorMessage nvarchar(4000);
  declare @ErrorSeverity int;
  declare @ErrorState int;
  select
    @ErrorMessage = ERROR_MESSAGE(),
    @ErrorSeverity = ERROR_SEVERITY(),
    @ErrorState = ERROR_STATE();
  raiserror (@ErrorMessage,
        @ErrorSeverity,
        @ErrorState
        );
end 

   
我希望將來SQL Server 2005能夠直接拋出原有異常,比如提供一個無參數的RaiseError。 

因此方案有點臃腫,但將死結問題封裝到T-SQL中有助於明確職責,提高高層系統的清晰度。現在,對於DataAccess的代碼,或許再也不需要考慮死結問題了。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.