資料庫效能優化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功能
分庫分表屬於架構級的,在此不討論