常にトランザクションを作成することは悪い習慣ですか?


88

常にトランザクションを作成することは悪い習慣ですか?

たとえば、1つだけのトランザクションを作成することをお勧めしSELECTますか?

トランザクションが本当に必要でない場合、トランザクションを作成するコストはいくらですか?

のような分離レベルを使用している場合でも、READ UNCOMMITTEDそれは悪い習慣ですか?


1
BEGIN TRAN SELECT ... COMMITvsの影響を見るSELECTと、パフォーマンスにごくわずかな違いがあるように見えます。
マーティンスミス

回答:


100

トランザクションを常に作成するのは悪い習慣ですか?

ここで話しているコンテキストによって異なります。更新の場合、TRANSACTIONSを明示的に使用することを強くお勧めします。SELECTの場合、NO(明示的に)。

しかし、最初に理解する必要があることを待ってください。SQLサーバーのすべてがトランザクションに含まれています。

セッションオプションIMPLICIT_TRANSACTIONSOFFあり、明示的に指定begin tranしたcommit/rollback場合、これは一般にExplicit Transactionとして知られています。それ以外の場合は、自動コミットトランザクションを取得します。

ときに IMPLICIT_TRANSACTIONSされた暗黙のトランザクションは、オンラインブックの記事(例えばに記載の種類の文を実行したときに自動的に起動される/ / )、それがコミットまたは明示的にロールバックする必要があります。このモードでを実行すると、別の「ネストされた」トランザクションが増加して開始されます)ONSELECTUPDATECREATEBEGIN TRAN@@TRANCOUNT

現在のモードを切り替えるには、次を使用します

SET IMPLICIT_TRANSACTIONS ON

または

SET IMPLICIT_TRANSACTIONS OFF

select @@OPTIONS & 2

上記が2を返す場合、暗黙のトランザクションモードになっています。0が返された場合、自動コミットされています。

トランザクションが本当に必要でない場合、トランザクションを作成するコストはいくらですか?

データベースをある一貫性のある状態から別の一貫性のある状態にするには、トランザクションが必要です。トランザクションに代わるものがないため、トランザクションにはコストがかかりません。参照:行バージョン管理ベースの分離レベルの使用

分離レベルread_uncomittedを使用している場合でも。悪い習慣ですか?ロックに問題はないはずだからです。

READ_UNCOMMITED分離レベルは、定義によりダーティリードを許可します。つまり、1つのトランザクションは、他のトランザクションによって行われたコミットされていない変更を見ることができます。この分離レベルが行うことは、ロックのオーバーヘッドを緩和することです。ロックを取得してデータベースの同時実行性を保護する方法です。

これを接続/クエリレベルで使用して、他のクエリに影響を与えないようにすることができます。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

食事哲学者パズルによるデッドロックと読み取りコミットスナップショット分離レベルについて説明するジェフアトウッドによる 興味深い記事を見つけました。

編集:

好奇心から、以下のグラフのように、Log Bytes Flushed / Sec、Log Flush Waits / Sec(ログフラッシュの発生を待機している1秒あたりのコミット数)などのPerfmonカウンターでTログへの影響を測定するテストを行いました。

ここに画像の説明を入力してください

サンプルコード:

create table testTran (id int, Name varchar(8))
go

-- 19 sec
-- Autocommit transaction
declare @i int
set @i = 0
while @i < 100000
begin 
insert into testTran values (1,'Kin Shah')
set @i = @i+1
end
---------------------------------------------------
-- 2 sec
-- Implicit transaction
SET IMPLICIT_TRANSACTIONS ON
declare @i int
set @i = 0
while @i < 100000
begin 
insert into testTran values (1,'Kin Shah')
set @i = @i+1
end
COMMIT;
SET IMPLICIT_TRANSACTIONS OFF


----------------------------------------------------
-- 2 sec
-- Explicit transaction
declare @i int
set @i = 0
BEGIN TRAN
WHILE @i < 100000
Begin
INSERT INTO testTran values (1,'Kin Shah')
set @i = @i+1
End
COMMIT TRAN

トランザクションの自動コミット @ TravisGanで強調表示されているように編集)

  • 挿入には19秒かかりました。
  • すべてのAutocommitは、自動コミットによりTログバッファをディスクにフラッシュします(@TravisGanが強調表示された後、言及するのを忘れました)。
  • CHECKPOINTプロセスは、頻繁に静かに実行されるため、フラッシュに必要なダーティログバッファの量が少なくなるため、高速で完了します。

暗黙的および明示的なトランザクション:

  • 挿入には2秒かかりました。
  • EXPLICITトランザクションの場合、ログバッファーは、いっぱいになったときにのみフラッシュされます。
  • 自動コミットトランザクションとは異なり、EXPLICITトランザクションでは、CHECKPOINTプロセスはより多くのログバッファーをフラッシュするため、より長い時間がかかります(ログバッファーはいっぱいになったときにのみフラッシュされることに注意してください)。

データベースレベルでトランザクションに関する情報を返すDMV sys.dm_tran_database_transactionsがあります。

明らかに、これは影響を示すためのより単純なテストです。ディスクサブシステム、データベースの自動拡張設定、データベースの初期サイズ、同じサーバー\データベースで実行されている他のプロセスなど、他の要因も影響します。

上記のテストから、暗黙的トランザクションと明示的トランザクションの違いはほとんどありません。

@TravisGanが回答をさらに追加してくれたことに感謝します。


35

SQLステートメントは常にトランザクションで実行されます。明示的に開始しない場合、すべてのSQLステートメントはそれ自体のトランザクションで実行されます。

唯一の選択肢は、1つのトランザクションに複数のステートメントをバンドルするかどうかです。複数のステートメントにまたがるトランザクションは、並行性を損なうロックを残します。したがって、トランザクションを「常に」作成することはお勧めできません。費用と利益のバランスを取る必要があります。


1

問題は、操作のグループを単一のアクションとして扱う必要があるかどうかです。つまり、すべての操作を正常に完了してコミットする必要があります。そうでない場合、操作をコミットできません。予備データを読み取り、そのデータに基づいて更新を実行する必要があるシナリオがある場合、最初の読み取りはおそらくトランザクションの一部である必要があります。注:意図的に選択/挿入/更新を避けています。トランザクションスコープは、実際にはアプリケーションレベルであり、複数のデータベース操作が含まれる場合があります。飛行機の座席予約や銀行残高照会/引き出しなどの古典的なパターンを考えてください。アプリケーション全体から信頼性の高い一貫性のあるデータが得られるようにするには、問題をより広く理解する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.