1. 程式人生 > 實用技巧 >資料庫效能優化4

資料庫效能優化4

概述

  隔離級別

  鎖

  事務

  總結

  In-Memoery Database(記憶體資料庫)

  分庫分表

1,鎖和事務

  隔離級別:是事務實現的前提,隔離級別影響鎖的行為,從而影響事務的併發性;

    ),隔離級別只對當前連結有效;在會話中,是可以修改隔離級別的

    ),隔離級別分成四個等級;由低到高分別為Read uncommitted 、Read committed 、Repeatable read 、Serializable

    1),Read uncommitted(讀未提交):一個事務可以讀取其他未提交事務的資料;

      它的優勢是執行效率最快,避免大量阻塞,它的劣勢

會產生髒讀問題

      適用的場景:在一些不重要的查詢顯示中使用它可以提高併發,比如未讀資訊;待辦資訊記錄等

      示例:事務A讀取a欄位,事務B修改a欄位;事務B已修改a欄位的資料,

        但是事務B未提交(事務B隨時有可能會回滾的可能性);事務A讀取了事務B修改的a欄位資料;

        如果事務B正常提交,事務A讀取到的資料也不是預期的;如果事務B回滾,那麼事務A讀取的資料就是不存在的;

    髒讀:事務A讀到了事務B還沒有提交的資料

      示例事務A先啟動,事務A設定隔離級別為Read uncommitted;第一次讀取[Description]欄位後,等待4秒;再去讀[Description]欄位

        事務B後啟動,在事務A結束之前啟動;負責修改記錄的欄位值,延遲9秒,最後選擇回滾;並查詢回滾後的記錄值

------髒讀事務A,先啟動它
begin transaction
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
select [Description] from  Company4 where Id='C13192'
waitfor delay '00:00:04:000'
select [Description] from  Company4 where Id='C13192'
commit

  

---髒讀事務B,在事務A啟動之後,在事務A結束之前啟動
begin transaction
update Company4 set [Description]='貝蒂哈使'
where Id='C13192'
waitfor delay '00:00:09:000'
rollback
select [Description] from Company4 where Id='C13192'

  

    2),Read Committed(讀提交):讀已提交,一個事務要等另一個事務提交後才能讀取資料;

      它的優勢是可以解決髒讀問題,它的劣勢是仍然會產生重複讀和幻讀的問題

      示例:事務B在對a進行更新(UPDATE)操作時,事務A要等待事務B這個更新操作事務提交後才能讀取a

    重複讀:在一個事務裡面讀取了兩次某個資料,讀出來的資料不一致;不可重複讀對應的是修改,即UPDATE操作      

      示例:company4表,在sql server中開啟兩個事務,事務A先啟動,事務A會在第一個讀取後等待9秒;事務B後啟動;事務B等待4秒,第二次事務A讀到的同資料和第一次讀到同一的資料已經發生了變化;這是一個【重複讀】  

------重複讀演示事務A,先啟動它
begin transaction
select [Description] from  Company4 where Id='C13192'
waitfor delay '00:00:09:000'
select [Description] from  Company4 where Id='C13192'
commit

------重複讀事務B,在事務A啟動後再啟動它 ,但要在事務A等待9秒之前啟動它
begin transaction
update Company4 set [Description]='你好'
where Id='C13192'
waitfor delay '00:00:04:000'
commit

  

    3),Repeatable read(可重複讀):在資料讀出來之後加鎖,類似"select * from XXX for update",明確資料讀取出來就是為了更新用的,所以要加一把鎖,防止別人修改它。

      在這個隔離級別上;讀取了一條資料,這個事務不結束,別的事務就不可以改這條記錄;事務提交之後才會釋放共享鎖

      它的優勢是可以解決髒讀,重複讀問題,它的劣勢是仍然會產生幻讀的問題

      適用場景:不要在更新時間長的場景下使用此隔離級別

    幻讀:在一個事務裡面的操作中發現了未被操作的資料;幻讀出現的前提是併發的事務中有事務發生了插入、刪除操作;實際上更新操作也可以造成幻讀

    示例:storeusers表,在sql server中開啟兩個事務,事務A先啟動,事務A會在第一個讀取後等待9秒;事務B後啟動;事務B等待4秒,相同過濾條件下事務A前後兩次讀取的資料量發生了變化;這是一個【幻讀】

--------幻讀演示事務A
begin transaction
select age from SortUsers where age>8998;
waitfor delay '00:00:09:000'
select age from SortUsers where age>8998;
commit

  

----幻讀演示事務B
begin transaction
select age from SortUsers where age>8998;
INSERT INTO [dbo].[SortUsers]([Id],[Name],[Code],[Desciption],[Age],[UserId])
     VALUES('C19000','C19000','C19000','C19000',9000,'C19000')
waitfor delay '00:00:04:000'
commit
select age from SortUsers where age>8998;

  

  4),Serializabale(序列化):將資料庫中所有事務以串聯的方式連線起來執行,防止一個事務影響其他事務

    這個隔離級別使用的是[區間鎖]的原理來實現的;對某個區域的資料進行加鎖

    它的優勢是可以解決髒讀,重複讀,幻讀問題;其保證了一個事務不會讀到另一個並行事務已修改但未提交的資料,它的劣勢是沒有併發事務;效能差,併發差

    適用場景:罕見

  5),預設隔離級別:SQLServer預設使用Read Committed;Mysql預設使用Repeatable read

  6),SnapShot:使用快照,快照使用的是樂觀鎖機制,使用TempDB來實現,將要操作的資料建立副本,從而資料隔離開來

    從而避免髒讀,幻讀,重複讀;

    它的優勢是有很好的隔離性,仍然有併發性,它的劣勢是因為使用TempDB,IO效能將會下降;對併發性的支援也是有瓶頸

  

  事務:可以認為是一組操作的集合,事務的具有ACID四個特性

  永續性(durability);事務一旦提交,它對資料庫中資料的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響

  一致性(consistency);事務必須是使資料庫從一個一致性狀態變到另一個一致性狀態,並且不違反任何一致性約束

  原子性(atomicity):一個事務是一個不可分割的工作單位,事務中的操作要麼都做,要麼都不做;

  隔離性(isolation):一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的資料對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾

鎖:事務使用鎖和隔離級別來實現事務的四個特性;但是鎖的特性也影響到事務的效能和併發性;事務執行中的死鎖,併發問題是鎖常見的問題

會話:一個數據庫連結就是一個會話;每一個會話都有一個唯一的SessionID;事務是和會話相關聯的;會話在成功接入sqlserver之後,事務就會在某個隔離級別上執行

阻塞:阻塞指的是至少兩個事務中,一個事務被另一個事務阻塞的現象

鎖:

  行鎖:粒度最小的鎖級別,行鎖是針對指定的行進行加鎖;根據行是否有聚集索引,行鎖可以分為鍵值鎖和行標識鎖

  頁鎖:針對資料頁的加鎖,查詢使用了行鎖時,不會對資料使用頁鎖;使用頁鎖時,也不會使用行鎖

  表鎖:對整個表加鎖,表鎖包含的資料頁和資料行上此時都無法加鎖

  鎖的模式:

    排他鎖,也稱X鎖;寫操作,增刪改會對受影響的行加X鎖,同一時刻只有一個事務能持有對統一物件的X鎖;其他事務需要等待

      X鎖的釋放才能操作;事務的隔離級別不影響X鎖的行為

    共享鎖,也稱S鎖;讀操作,多個事務可以同時訪問S鎖上的資源,共享鎖是受事務隔離級別的影響

    意向鎖,也稱I鎖;放置在資源層次結構的一個級別上的鎖,以保護較低級別資源上的共享鎖或排他鎖

    更新鎖,也稱U鎖,在資料修改中,可能需要先查詢再更新,使用U鎖對查詢出來的資料進行加鎖,告訴其他事務這些資料即將被更新;

      加鎖成功之後,再將U鎖升級到X鎖,然後再執行更新

  鎖的相容性:鎖是否相容,會影響事務之間的阻塞;相容的鎖,事務之間的操作不會阻塞,典型的S鎖;不相容的鎖,則會造成事務之間的阻塞

    典型的X鎖,X鎖和任何鎖都不相容

    鎖的相容性圖

    

  

  執行時檢視當前鎖狀況:sqlserver中,使用系統函式sp_lock檢視鎖的狀況

  

    自定義檢視某個事務的鎖:

-----在目標資料庫建立一個名叫DBlocks的檢視用於檢視引擎中的鎖
Use Foundation---你的資料庫名字
go
IF EXISTS ( SELECT  1 FROM  sys.views WHERE   name = 'DBlocks' )
DROP VIEW DBlocks ;
GO
CREATE VIEW DBlocks 
AS
SELECT  request_session_id AS spid ,
DB_NAME(resource_database_id) AS dbname ,
CASE WHEN resource_type = 'OBJECT'
THEN OBJECT_NAME(resource_associated_entity_id)
WHEN resource_associated_entity_id = 0 THEN 'N/A'
ELSE OBJECT_NAME(p.object_id)
END AS entity_name , index_id , resource_type AS resource ,
        resource_description AS description ,
        request_mode AS mode , request_status AS status
FROM    sys.dm_tran_locks t LEFT JOIN sys.partitions p
ON p.partition_id = t.resource_associated_entity_id
WHERE   resource_database_id = DB_ID()
AND resource_type <> 'DATABASE' ;

    在建立DBBlocks檢視後,就可以在DBBlocks檢視引擎中的鎖狀況;如下是查詢當前執行的事務的鎖

    

    

    阻塞:當一個事務的執行需要等待另一個事務執行釋放資源時,便是阻塞

      示例:Company4表,一個事務執行對指定行進行更新,但是這個事務就是不提交也不回滾;此時另外一個查詢視窗去查詢這行資料時,就會一直在等待

    

    定位阻塞事務的鎖

      示例:

        1,先啟動引起阻塞的事務A      

        2,啟動查詢B查詢被A阻塞的資源,啟動後,查詢B一直處於執行狀態

        3,啟動指令碼[前面所建立的DBBlocks檢視]查詢當前查詢B的會話Id(查詢B是一個S鎖);再查詢查詢B的阻塞資訊

          下面這個圖演示了會話53被會話52給阻塞了,X鎖不相容S鎖(X鎖不相容任何鎖)

------第一步:事務A,啟動後不提交也不回滾
begin transaction
update Company4 set [Description]='貝蒂哈使'
where Id='C13192'
-----第二步,啟動查詢B查詢被A阻塞的資源,啟動後,查詢B一直處於執行狀態
select [Description] from Company4 where Id='C13192'

 

----查詢實時的阻塞情況
-- 實時偵測阻塞
select * from DBlocks;
-- 鎖申請及其狀態
select session_id,wait_time,wait_type,blocking_session_id 
from sys.dm_exec_requests  where session_id=53;----53是當前被阻塞會話ID
select session_id,wait_duration_ms,wait_type,
blocking_session_id,resource_description 
from sys.dm_os_waiting_tasks where session_id=53;----53是當前被阻塞會話ID

  

  你也可以使用以下語句來檢視當前系統所有的更多的阻塞資訊(包括執行指令碼和執行計劃等);而且也無需知道會話的Id  

select 
    tl1.resource_type as [Resource Type] 
    ,db_name(tl1.resource_database_id) as [DB Name] 
    ,case tl1.resource_type 
when  'OBJECT' then object_name(tl1.resource_associated_entity_id
,tl1.resource_database_id) 
        when 'DATABASE' then 'DB' 
        else 
            case when tl1.resource_database_id = db_id() 
                then 
                     (  select object_name(object_id, tl1.resource_database_id) 
                        from sys.partitions 
                        where hobt_id = tl1.resource_associated_entity_id ) 
                else '(Run under DB context)' 
            end 
    end as [Object] 
    ,tl1.resource_description as [Resource] 
    ,tl1.request_session_id as [Session] 
    ,tl1.request_mode as [Mode] 
    ,tl1.request_status as [Status] 
    ,wt.wait_duration_ms as [Wait (ms)] 
	   ,qi.sql 
    ,qi.query_plan 
from 
    sys.dm_tran_locks tl1 with (nolock) join sys.dm_tran_locks tl2 with (nolock) on 
        tl1.resource_associated_entity_id = tl2.resource_associated_entity_id 
    left outer join sys.dm_os_waiting_tasks wt with (nolock) on 
        tl1.lock_owner_address = wt.resource_address and tl1.request_status = 'WAIT' 
    outer apply 
    ( 
        select 
            substring(s.text, (er.statement_start_offset / 2) + 1, 
                 ((  case er.statement_end_offset 
                            when -1 
                            then datalength(s.text) 
                            else er.statement_end_offset 
                      end - er.statement_start_offset) / 2) + 1) as sql 
            , qp.query_plan 
        from 
            sys.dm_exec_requests er with (nolock) 
                cross apply sys.dm_exec_sql_text(er.sql_handle) s 
                outer apply sys.dm_exec_query_plan(er.plan_handle) qp 
        where 
            tl1.request_session_id = er.session_id 
    ) qi 
where 
    tl1.request_status <> tl2.request_status and 
    ( 
        tl1.resource_description = tl2.resource_description or 
        ( tl1.resource_description is null and tl2.resource_description is null ) 
    ) 
option (recompile)

    

   總結:

      1,阻塞發生再多個會話/事務的資源爭奪

      2,如果沒有索引;即使是更新一行資料,可能會導致鎖升級,也可能導致鎖定大量的資料

      3,確保事務及時提交,X鎖總是在事務結束後才釋放,縮短事務執行時間

      4,更新操作儘可能靠近事務的結尾

      5,避免在一個事務中多次更新相同的資料,如多個Update更新相同資料的不同欄位,這會增加鎖的數量和開銷

      6,監控鎖升級

      7,同一個事務中,不要混用DML和DDL;如DDL修改表結構,會導致部分sql重新編譯,統計資訊無效,執行計劃重新編譯,執行快取清楚

      8,不要瘋狂使用with nolock;with nolock不一定能夠解決寫操作導致的阻塞;同時可能髒讀髒到使用者無法接受的程度

      9,事務和鎖並沒有大大的提升資料庫效能

      10,資料庫本身不是為了效能而存在,資料庫的一致性才是資料庫的事務的前提

  In-Memory(IMDB)簡介:sql server2014+版本

    在計算機中,

網路的IO速度慢於磁碟的IO速度,

磁碟的IO速度慢於記憶體的訪問速度,

記憶體的訪問速度慢於cpu三級快取的訪問速度

cpu三級快取訪問速度慢於cpu二級快取的訪問速度

cpu二級快取訪問速度慢於cpu一級快取的訪問速度

cpu一級快取訪問速度慢於cpu計算速度

    傳統的關係資料庫和現在的redis,mecached等快取技術相比;

      傳統的關係型資料庫大部分是磁碟IO;它們通過將訪問的資料載入到記憶體中進行查詢匹配等操作,當記憶體不足時,則將大量的記憶體中所需的資料轉移到磁碟上的TempDB(這是典型的記憶體磁碟資料交換)

      而磁碟的IO速度是遠遠不如記憶體;這就是磁碟表和記憶體表

    redis,mecached等快取技術則更多是執行在記憶體中,記憶體IO的速度是遠遠高於磁碟IO的

In-Mempory Database;即記憶體資料庫,是一種依賴於主存作為資料儲存介質的一種資料庫管理系統;

記憶體優化表,儲存在記憶體中的表,它也有索引,包括雜湊索引和範圍索引,而且至少要有一個索引;最多8個索引;除主鍵外沒有唯一索引;

  建立之後不可以修改;建表的時候就要定義所有的索引

  預設情況下,記憶體優化表具有完全永續性。與(傳統)基於磁碟的表上的事務一樣,記憶體優化表上的事務具有完全原子性、一致性、隔離性和永續性 (ACID)。

  記憶體優化表和本機編譯的儲存過程僅支援一部分Transact-SQL功能

  分庫分表屬於架構級的,在此不討論