记一次SQL Server删除SQL调优

今天测试反应问题,性能测试环境一个脚本执行了大约3个小时没有出结果,期间其他dba已经建立了一些索引但是没有效果。

语句:

DELETE T  from License T  WHERE exists (select 1 from  #i_RollbackTrans r where r.LicenseID= T.LicenseID)

表 License数据量4千万

表 #i_RollbackTrans数据量5万

最终删除数据量5万

部分执行计划:

可以发现表上有很多索引, 临时表经过distinct之后和索引进行nested loop,问题不大。

通过以下sql查询会话的read write和cpu 变化:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263;WITH task_space_usage AS (-- SUM alloc/delloc pagesSELECT session_id,request_id,SUM(internal_objects_alloc_page_count) AS internal_alloc_pages,SUM(user_objects_alloc_page_count) AS user_alloc_pagesFROM sys.dm_db_task_space_usage WITH (NOLOCK)WHERE session_id <> @@SPIDGROUP BY session_id, request_id)SELECT r.session_id,r.percent_complete --,r.query_hash--,r.sql_handle--,r.plan_handle,r.start_time,getdate(),datediff(ss,r.start_time,getdate()) duration, DB_NAME(r.database_id) AS dbname,[Parent Query] = st.text--  ,[Individual Query] = SUBSTRING (st.text, (r.statement_start_offset/2) + 1,  --((CASE WHEN r.statement_end_offset = -1--THEN LEN(CONVERT(NVARCHAR(MAX), st.text)) * 2-- ELSE r.statement_end_offset -- END - r.statement_start_offset)/2) + 1,[Individual Query] = SUBSTRING (st.text, (r.statement_start_offset/2),  ((CASE WHEN r.statement_end_offset = -1THEN DATALENGTH(st.text)ELSE r.statement_end_offset END - r.statement_start_offset)/2))                                       ,r.wait_type                                               ,r.cpu_time, r.reads, r.writes,r.blocking_session_id,r.session_id--,dbo.udf_GetHeadBlocker(r.session_id) AS 'blockChain' ,r.percent_complete ,r.wait_resource,r.status, r.command, r.transaction_isolation_level,s.host_name, s.login_name ,case when s.program_name like 'SQLAgent - TSQL JobStep%' then --substring(s.program_name,30,34)(select name from msdb.dbo.sysjobs where cast(job_id as binary(16)) = convert(varbinary(16),substring(s.program_name,30,34),1))else s.program_nameend as program_name, s.host_process_id, c.client_net_address--,deqp.query_plan,blocking_cache.text AS blocking_text    ,   waitstats.wait_duration_ms ,TSU.internal_alloc_pages * 1.0 / 128 AS [internal object MB space],TSU.user_alloc_pages * 1.0 / 128 AS [user object MB space]FROM sys.dm_exec_requests AS rleft JOIN task_space_usage AS TSU ON TSU.session_id = r.session_id AND TSU.request_id = r.request_idleft JOIN sys.dm_exec_sessions AS ON s.session_id=r.session_idleft JOIN sys.dm_exec_connections AS ON s.session_id=c.session_idLEFT JOIN sys.dm_exec_connections AS blocking ON blocking.session_id = r.blocking_session_idCROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS stOUTER APPLY sys.dm_exec_sql_text(blocking.most_recent_sql_handle) blocking_cache--OUTER APPLY sys.dm_exec_query_plan(r.plan_handle) deqpLEFT OUTER JOIN sys.dm_os_waiting_tasks waitstats ON waitstats.session_id = r.session_idWHERE r.session_id>50AND r.session_Id NOT IN (@@SPID) ORDER BY r.cpu_time DESC,r.reads DESC, r.blocking_session_id DESC GO

观察结果发现,read很少只有300,write到30万,但是执行期间一直没有变化,cpu_time持续增加,会话状态是running并且没有等待事件。

通过分析得出,执行计划查询部分和上面分析一样不存在问题,虽然表上有很多索引,但是删除数据占比表整体数据较少0.1%, 而且writes始终没变化,证明不是阻塞再索引的删除上面。那么是什么消耗cpu_time但是没有任何IO变化?

下面看被表引用的外键关系:

可以看到相当多的外键引用该表,并且是强制检查状态,其中一部分还是自引用。

最终通过临时禁用外键检测,删除,再启用的方式将3个小时没有执行完的sql优化再20分钟完成。

ASYNC_NETWORK_IO网络等待和优化过程

测试反应测试数据库整体出现hang的情况,检查对应的等待事件,发现大量的resource_semaphore等待事件,

查看内存占用情况:

SELECT  *  FROM    sys.dm_exec_query_memory_grants 

发现会话189占用大量的granted_memory, 检查189对应的session,执行了超过24小时未结束,但是reads仅仅是77286,状态为suspended,说明有等待,检查等待事件ASYNC_NETWORK_IO,并且结果集大小是207466行。

以上分析说明应用程序调用该sql使用了RBAR (Row-By-Agonizing-Row)方式一行一行调用,每次调用都经过network roundtrip,和开发沟通,修改应用程序为批量接收结果集。

SQL Server DACPAC数据库部署错误

DACPAC使用sqlpackage.exe进行部署,部署时候报错:

EXEC : error SQL72035: [dbo].[table] is under change data capture control and cannot be modified 

该错误由于cdc的启用,在部署过程中添加以下参数解决

/p:DoNotAlterChangeDataCaptureObjects=False

添加之后部署出现新的错误:

EXEC : error SQL72035: [dbo].[table] is treated as replicated due to change tracking and cannot be modified. 

这个错误是由于cdc内部对于实际上和replication的实现是一致的,所以在内部认为是replicated,继续添加以下参数解决:

/p: DoNotAlterReplicatedObjects false

总结,在cdc或者replication等高级特性启动的情况下通过DACPAC部署需要添加两个额外参数:

/p:DoNotAlterChangeDataCaptureObjects=False

/p: DoNotAlterReplicatedObjects false

SQL Server统计数据库中表大小

use testdb
go
if object_id(‘tempdb.dbo.#tablespaceinfo’,’U’) is not null
  drop table #tablespaceinfo
create table #tablespaceinfo (  
    nameinfo varchar(555),  
    rowsinfo bigint,  
    reserved varchar(255),  
    datainfo varchar(255),  
    index_size varchar(255),  
    unused varchar(255)  
)  

DECLARE @tablename varchar(255);  

DECLARE Info_cursor CURSOR FOR
    SELECT [name] FROM sys.tables WHERE type=’U’;  

OPEN Info_cursor  
FETCH NEXT FROM Info_cursor INTO @tablename  

WHILE @@FETCH_STATUS = 0  
BEGIN
    insert into #tablespaceinfo exec sp_spaceused @tablename  
    FETCH NEXT FROM Info_cursor  
    INTO @tablename  
END

CLOSE Info_cursor  
DEALLOCATE Info_cursor  

if object_id(‘tempdb.dbo.#tab’,’U’) is not null
  drop table #tab
SELECT
 nameinfo
 ,rowsinfo
 ,cast(replace(reserved,’ KB’,”) as bigint)/1024 “reserved(MB)”
 ,cast(replace(datainfo,’ KB’,”) as bigint)/1024 “datainfo(MB)”
 ,cast(replace(index_size,’ KB’,”) as bigint)/1024 “index_size(MB)”
 ,cast(replace(unused,’ KB’,”) as bigint)/1024 “unused(MB)”
into #tab
FROM #tablespaceinfo  
ORDER BY Cast(Replace(reserved,’KB’,”) as INT) DESC

SQL Server如何导出db所有用户权限创建语句

use db

go

DECLARE 

@Database varchar(255),

@loginName varchar(255),

@roleName varchar(255),

@sql nvarchar(max);

SET @sql=N”;

DECLARE curLogin CURSOR LOCAL for

select db_name() as dbname,dp.name as username,dpr.name as rolename 

from sys.database_principals dp 

join sys.database_role_members drm on drm.member_principal_id=dp.principal_id

join sys.database_principals dpr on drm.role_principal_id=dpr.principal_id 

join sys.server_principals sp on sp.name=dp.name

where 1=1

–and dpr.is_fixed_role=1 

and dp.type<>’R’

and dp.type in(‘S’,’U’,’G’) –SQL USER,WINDOWS USER AND windows group

order by username,rolename

OPEN curLogin;

FETCH NEXT FROM curLogin INTO @Database,@loginName,@roleName;

WHILE @@FETCH_STATUS = 0

BEGIN

SET @sql=@sql+N’

use ‘+@Database+’;

if not exists(select * from ‘+@Database+’.sys.database_principals where name=”’+@LoginName+”’) 

begin

CREATE USER ‘+QUOTENAME(@LoginName)+’;

end

else 

begin

ALTER USER ‘+QUOTENAME(@LoginName)+’ with login = ‘+QUOTENAME(@LoginName)+’

end

;

–print @sql

–exec sp_executesql @sql

select @sql=@sql+N’

use ‘+@Database+’;

exec sp_addrolemember ”’+@roleName+”’, ”’ + @LoginName + ””

–exec sp_executesql @sql

FETCH NEXT FROM curLogin INTO @Database,@loginName,@roleName;

END

CLOSE curLogin

DEALLOCATE curLogin

;

–select len(@sql)

–print @sql –this will be truncated 

exec sysadmin.dbo.printmax @sql

go

SQL Server一次SQL调优案例

环境:Microsoft SQL Server 2016 (SP2-CU3)企业版

问题SQL:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111select RowNumber = ROW_NUMBER() OVER -- This ordering is from the various Fulfillment Map sort orders to match the fulfillment app's row order.ORDER BY htly.LicenseYear, mht.Nameh.HuntFirstOpenDate, h.DisplayOrder,h.HuntCode,ci_orderby.LastName,ci_orderby.FirstName,fmu.FulfillmentMailingUnitID),ShippingName = ISNULL(fism_aot.ShippingName,  dbo.udf_GetCustomerName(c.CustomerID)),FulfillmentMailingUnitID = fmu.FulfillmentMailingUnitID,GoID = goid.IdentityValue,MailingZip = ISNULL(fism_zc.ZipCode, zc.ZipCode),TransactionID = fism_th.TransactionID,TransactionHeaderID = fism_th.TransactionHeaderID,HuntDate = h.HuntFirstOpenDate,HuntCode = h.HuntCode,-- Header infoBatchNumber = fmulg.FulfillmentMailingUnitLockGroupID,PrintedByUserName = au.UserName,LockedDate = fmulg.LockedDatefromdbo.FulfillmentMailingUnitLockGroup fmulgcross join dbo.Enum_IdentityType eitcross join dbo.Enum_LicenseActionType elatinner join dbo.FulfillmentMailingUnitLock fmulon fmulg.FulfillmentMailingUnitLockGroupID = fmul.FulfillmentMailingUnitLockGroupIDinner join dbo.FulfillmentMailingUnit fmuon fmul.LockedFulfillmentMailingUnitID = fmu.FulfillmentMailingUnitIDinner join dbo.ApplicationUser auon fmulg.LockedByApplicationUserID = au.ApplicationUserID-- Getting to the Transaction Header by FulfillmentInternetSalesMap OR FulfillmentDrawIssuanceMapleft join dbo.FulfillmentInternetSalesMap fismon fmu.FulfillmentMailingUnitID = fism.FulfillmentMailingUnitIDleft join dbo.FulfillmentDrawIssuanceMap fdimon fmu.FulfillmentMailingUnitID = fdim.FulfillmentMailingUnitIDleft join dbo.TransactionHeader th  on fism.TransactionHeaderID = th.TransactionHeaderIDor fdim.TransactionHeaderID = th.TransactionHeaderIDleft join dbo.TransactionHeader fdim_thon fdim.TransactionHeaderID = fdim_th.TransactionHeaderID-- Getting to License from FulfillmentDrawNotificationMapleft join dbo.FulfillmentDrawNotificationMap fdnmon fmu.FulfillmentMailingUnitID = fdnm.FulfillmentMailingUnitIDleft join dbo.DrawTicketLicense fdnm_dtlon fdnm.DrawTicketLicenseID = fdnm_dtl.DrawTicketLicenseID left join dbo.License fdnm_lon fdnm_dtl.LicenseID = fdnm_l.LicenseIDleft join dbo.DrawTicket fdnm_dton fdnm_dtl.DrawTicketID = fdnm_dt.DrawTicketIDleft join dbo.DrawTicketHuntChoice fdnm_dthcon fdnm_dt.DrawTicketID = fdnm_dthc.DrawTicketIDand(-- If the draw ticket is a winner, link to the hunt choice that won.(fdnm_dt.WasDrawn = 1 and fdnm_dthc.WasDrawn = 1)-- Else if the draw ticket was not a winner, link to the first hunt choice since-- Losing and Alternate notifications are not valid for multi-choice huntsor (fdnm_dt.WasDrawn = 0 and fdnm_dthc.OrderIndex = 1))left join dbo.TransactionDetail fdim_tdon fdim.TransactionHeaderID = fdim_td.TransactionHeaderIDleft join dbo.LicenseAction fdim_laon fdim_td.TransactionDetailID = fdim_la.TransactionDetailID-- This might be silly since it should only be Issued for issuance... (currently it's sold in the stored proc that issues tags)and (fdim_la.LicenseActionTypeID = elat.Sold or fdim_la.LicenseActionTypeID = elat.Issued or fdim_la.LicenseActionTypeID = elat.Duplicated)left join dbo.License fdim_lon fdim_la.LicenseID = fdim_l.LicenseID left join dbo.Hunt hon fdnm_dthc.HuntID = h.HuntIDor fdim_l.HuntID = h.HuntIDleft join dbo.HuntTypeLicenseYear htlyon h.HuntTypeLicenseYearID = htly.HuntTypeLicenseYearIDleft join dbo.MasterHuntType mhton htly.MasterHuntTypeID = mht.MasterHuntTypeIDleft join dbo.Customer con fdnm_l.CustomerID = c.CustomerIDor th.CustomerID = c.CustomerIDleft join dbo.CustomerIndividual cion c.CustomerID = ci.CustomerIDleft join dbo.CustomerIdentity goidon c.CustomerID = goid.CustomerIDand goid.IdentityTypeID = eit.GOIDand goid.[Status] = 1left join dbo.AddressDetail adon c.MailingAddressID = ad.AddressIDand ad.IsActive = 1left join dbo.ZipCode zcon ad.ZipCodeID = zc.ZipCodeIDleft join dbo.CustomerIndividual ci_orderbyon fdnm_l.CustomerID = ci_orderby.CustomerIDor fdim_th.CustomerID = ci_orderby.CustomerID left join dbo.TransactionHeader fism_th on fism.TransactionHeaderID = fism_th.TransactionHeaderIDleft join dbo.ActiveOutdoorsTransaction fism_aoton fism_aot.TransactionID = fism_th.TransactionIDleft join dbo.AddressDetail fism_ad on fism_aot.ShippingAddressID = fism_ad.AddressIDand fism_ad.IsActive = 1left join dbo.ZipCode fism_zcon fism_ad.ZipCodeID = fism_zc.ZipCodeIDwherefmulg.FulfillmentMailingUnitLockGroupID = @FulfillmentMailingUnitLockGroupID

该SQL执行192s后出记录,分析一下sql的执行计划:

分析一:

最终的排序消耗了大量的cost:

分析二:

该SQL存在大量多表连接,MSSQL引擎由于统计信息的算法单一,在处理大量级联连接时,实际数据可能严重偏离统计信息

连接中存在Actual Rows和Estimated Rows严重不一致的情况,随着连接表数目增加,该不一致更加严重:

经过分析,优化的目标是减少多表连接的统计信息不一致导致的执行计划错误并且对最终的排序操作进行外推。

优化的手法主要是利用临时表固化统计信息,外推排序:

最终优化SQL:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189select fmu.FulfillmentMailingUnitID,elat.Sold,elat.Issued,elat.Duplicated,fmulg.FulfillmentMailingUnitLockGroupID,au.UserName,fmulg.LockedDate,eit.GOIDinto #tempfrom dbo.FulfillmentMailingUnitLockGroup fmulgcross join dbo.Enum_IdentityType eitcross join dbo.Enum_LicenseActionType elatinner join dbo.FulfillmentMailingUnitLock fmulon fmulg.FulfillmentMailingUnitLockGroupID = fmul.FulfillmentMailingUnitLockGroupIDinner join dbo.FulfillmentMailingUnit fmuon fmul.LockedFulfillmentMailingUnitID = fmu.FulfillmentMailingUnitIDinner join dbo.ApplicationUser auon fmulg.LockedByApplicationUserID = au.ApplicationUserIDwherefmulg.FulfillmentMailingUnitLockGroupID = @FulfillmentMailingUnitLockGroupIDselect fdnm_l.CustomerID fdnm_l_CustomerID,th.CustomerID th_CustomerID,fdim_th.CustomerID fdim_th_CustomerID,t.FulfillmentMailingUnitID,h.HuntFirstOpenDate,h.HuntCode,t.FulfillmentMailingUnitLockGroupID,t.UserName,LockedDate,t.GOID,htly.LicenseYear, mht.Nameh.DisplayOrder,--ci_orderby.LastName,--ci_orderby.FirstName,fism.TransactionHeaderIDinto #temp1from #temp t -- Getting to the Transaction Header by FulfillmentInternetSalesMap OR FulfillmentDrawIssuanceMapleft join dbo.FulfillmentInternetSalesMap fismon t.FulfillmentMailingUnitID = fism.FulfillmentMailingUnitIDleft join dbo.FulfillmentDrawIssuanceMap fdimon t.FulfillmentMailingUnitID = fdim.FulfillmentMailingUnitIDleft join dbo.TransactionHeader th  on fism.TransactionHeaderID = th.TransactionHeaderIDor fdim.TransactionHeaderID = th.TransactionHeaderIDleft join dbo.TransactionHeader fdim_thon fdim.TransactionHeaderID = fdim_th.TransactionHeaderID-- Getting to License from FulfillmentDrawNotificationMapleft join dbo.FulfillmentDrawNotificationMap fdnmon t.FulfillmentMailingUnitID = fdnm.FulfillmentMailingUnitIDleft join dbo.DrawTicketLicense fdnm_dtlon fdnm.DrawTicketLicenseID = fdnm_dtl.DrawTicketLicenseID left join dbo.License fdnm_lon fdnm_dtl.LicenseID = fdnm_l.LicenseIDleft join dbo.DrawTicket fdnm_dton fdnm_dtl.DrawTicketID = fdnm_dt.DrawTicketIDleft join dbo.DrawTicketHuntChoice fdnm_dthcon fdnm_dt.DrawTicketID = fdnm_dthc.DrawTicketIDand(-- If the draw ticket is a winner, link to the hunt choice that won.(fdnm_dt.WasDrawn = 1 and fdnm_dthc.WasDrawn = 1)-- Else if the draw ticket was not a winner, link to the first hunt choice since-- Losing and Alternate notifications are not valid for multi-choice huntsor (fdnm_dt.WasDrawn = 0 and fdnm_dthc.OrderIndex = 1))left join dbo.TransactionDetail fdim_tdon fdim.TransactionHeaderID = fdim_td.TransactionHeaderIDleft join dbo.LicenseAction fdim_laon fdim_td.TransactionDetailID = fdim_la.TransactionDetailID-- This might be silly since it should only be Issued for issuance... (currently it's sold in the stored proc that issues tags)and (fdim_la.LicenseActionTypeID = t.Sold or fdim_la.LicenseActionTypeID = t.Issued or fdim_la.LicenseActionTypeID = t.Duplicated)left join dbo.License fdim_lon fdim_la.LicenseID = fdim_l.LicenseID left join dbo.Hunt hon fdnm_dthc.HuntID = h.HuntIDor fdim_l.HuntID = h.HuntIDleft join dbo.HuntTypeLicenseYear htlyon h.HuntTypeLicenseYearID = htly.HuntTypeLicenseYearIDleft join dbo.MasterHuntType mhton htly.MasterHuntTypeID = mht.MasterHuntTypeID--set statistics io on--set statistics time onselect t1.LicenseYear,t1.Namet1.DisplayOrder,c.CustomerID,t1.FulfillmentMailingUnitID,t1.GOID,zc.ZipCode,t1.HuntFirstOpenDate,t1.HuntCode,t1.FulfillmentMailingUnitLockGroupID,t1.UserName,t1.LockedDate,t1.fdnm_l_CustomerID,t1.fdim_th_CustomerID,t1.TransactionHeaderIDinto #temp2from #temp1 t1 -- Getting to Cusotmer from the joined transaction header or the license from the DrawTicketLicenseleft join dbo.Customer con t1.fdnm_l_CustomerID = c.CustomerIDor t1.th_CustomerID = c.CustomerIDleft join dbo.CustomerIndividual cion c.CustomerID = ci.CustomerID left join dbo.AddressDetail adon c.MailingAddressID = ad.AddressIDand ad.IsActive = 1left join dbo.ZipCode zcon ad.ZipCodeID = zc.ZipCodeID select t2.LicenseYear,t2.Namet2.DisplayOrder,ci_orderby.LastName,ci_orderby.FirstName,ShippingName = ISNULL(fism_aot.ShippingName,  dbo.udf_GetCustomerName(t2.CustomerID)),FulfillmentMailingUnitID = t2.FulfillmentMailingUnitID,GoID = goid.IdentityValue,MailingZip = ISNULL(fism_zc.ZipCode, t2.ZipCode),TransactionID = fism_th.TransactionID,TransactionHeaderID = fism_th.TransactionHeaderID,HuntDate = t2.HuntFirstOpenDate,HuntCode = t2.HuntCode,-- Header infoBatchNumber = t2.FulfillmentMailingUnitLockGroupID,PrintedByUserName = t2.UserName,LockedDate = t2.LockedDate into #temp3from #temp2 t2left join dbo.CustomerIdentity goidon t2.CustomerID = goid.CustomerIDand goid.IdentityTypeID = t2.GOID and goid.[Status] = 1left join dbo.CustomerIndividual ci_orderbyon t2.fdnm_l_CustomerID = ci_orderby.CustomerIDor t2.fdim_th_CustomerID = ci_orderby.CustomerID left join dbo.TransactionHeader fism_th on t2.TransactionHeaderID = fism_th.TransactionHeaderIDleft join dbo.ActiveOutdoorsTransaction fism_aoton fism_aot.TransactionID = fism_th.TransactionIDleft join dbo.AddressDetail fism_ad on fism_aot.ShippingAddressID = fism_ad.AddressIDand fism_ad.IsActive = 1left join dbo.ZipCode fism_zcon fism_ad.ZipCodeID = fism_zc.ZipCodeIDselect  RowNumber = ROW_NUMBER() OVER -- This ordering is from the various Fulfillment Map sort orders to match the fulfillment app's row order.ORDER BY t3.LicenseYear, t3.Namet3.HuntDate, t3.DisplayOrder,t3.HuntCode,t3.LastName,t3.FirstName,t3.FulfillmentMailingUnitID),ShippingName,FulfillmentMailingUnitID,GoID,MailingZip,TransactionID,TransactionHeaderID,HuntDate,HuntCode,-- Header infoBatchNumber,PrintedByUserName,LockedDatefrom #temp3 t3drop table #tempdrop table #temp1drop table #temp2drop table #temp3

经过测试,执行时间由192秒降低到2秒。

主流关系数据库锁实现的区别

SQL Server实现的锁主要是由通过锁表来实现,在内存中开辟专门区域对于不同级别的对象(行、键-块-对象、索引-库)相应的锁记录,事务层和存储层完全分离,并且锁占用开销较大,当锁占用较多资源的时候,会进行锁升级降低并发性。

MySQL对与锁的实现主要是行键的实现,采用在页面头部记录位图的方式,这样做在检索行锁记录的时候需要到页面头部获取数据,页面头部因为用位图信息记录行锁,所以相比SQL Server省去了较多的资源,无需进行锁升级。另外gap lock的实现可以很好的支持高级别的隔离级别和并发性的提升,例如RR隔离级别。

Oracle对锁的实现是将事务层和存储层结合的方式进行处理,锁的信息在块头的ITL事务槽和行信息中,这样做极大的节省了内存资源和锁资源,锁几乎不占用任何资源,锁定信息完全结合事务槽和行标志进行判断,不像MySQL有单独的内存锁表分离来处理,在rac模式下,可以很方便的对于除了数据之外的事务和锁信息进行同步,存储及事务,使得rac的设计单一有效,但缺点也很明显,就是过于结合紧密的事务层和存储层使得基于事务的扩展变得很困难。

PostgreSQL的锁设计内存中不记录行锁信息,行锁信息由行上的transactionid信息得到,节省内存资源, 在索引回表时采用mvcc技术避免普通的snapshot now模式或者其他非mvcc需要使用锁定的劣势。