เนื่องจากคุณกำลังใช้ Sequence คุณสามารถใช้ฟังก์ชันNEXT VALUE FORเดียวกันกับที่คุณมีอยู่แล้วในข้อ จำกัด เริ่มต้นบนId
ฟิลด์คีย์หลัก - เพื่อสร้างId
ค่าใหม่ล่วงหน้า การสร้างคุณค่าก่อนหมายความว่าคุณไม่จำเป็นต้องกังวลเกี่ยวกับการไม่มีSCOPE_IDENTITY
ซึ่งหมายความว่าคุณไม่จำเป็นต้องใช้OUTPUT
ประโยคหรือทำเพิ่มเติมSELECT
เพื่อรับค่าใหม่ คุณจะมีค่าก่อนที่จะทำINSERT
และคุณไม่จำเป็นต้องยุ่งกับSET IDENTITY INSERT ON / OFF
:-)
เพื่อดูแลส่วนหนึ่งของสถานการณ์โดยรวม INSERT
อีกส่วนหนึ่งคือการจัดการกับปัญหาการทำงานพร้อมกันของทั้งสองกระบวนการในเวลาเดียวกันที่แน่นอนไม่ได้หาแถวที่มีอยู่สำหรับสายเดียวกันที่แน่นอนและการดำเนินการกับ ข้อกังวลคือเกี่ยวกับการหลีกเลี่ยงการละเมิดข้อ จำกัด ที่จะเกิดขึ้น
วิธีหนึ่งในการจัดการปัญหาการเกิดพร้อมกันประเภทนี้คือการบังคับให้การดำเนินการเฉพาะนี้เป็นเธรดเดี่ยว วิธีการทำคือการใช้แอปพลิเคชันล็อค (ซึ่งทำงานในช่วง) ในขณะที่มีประสิทธิภาพพวกเขาอาจเป็นคนที่ค่อนข้างหนักสำหรับสถานการณ์เช่นนี้ซึ่งความถี่ของการชนอาจจะค่อนข้างต่ำ
อีกวิธีหนึ่งในการจัดการกับการชนคือการยอมรับว่าบางครั้งพวกเขาจะเกิดขึ้นและจัดการกับพวกเขาแทนที่จะพยายามหลีกเลี่ยงพวกเขา ใช้TRY...CATCH
สร้างคุณสามารถได้อย่างมีประสิทธิภาพกับดักข้อผิดพลาด (ในกรณีนี้ "ละเมิดข้อ จำกัด ที่ไม่ซ้ำกัน" ข่าวสารเกี่ยวกับ 2601) และอีกครั้งดำเนินการSELECT
ที่จะได้รับId
ความคุ้มค่าเนื่องจากเรารู้ว่ามันตอนนี้มีอยู่เนื่องจากอยู่ในCATCH
บล็อกที่มีเฉพาะที่ ความผิดพลาด ข้อผิดพลาดอื่น ๆ สามารถจัดการได้ตามปกติRAISERROR
/ RETURN
หรือTHROW
ลักษณะ
การตั้งค่าการทดสอบ: ลำดับตารางและดัชนีที่ไม่ซ้ำกัน
USE [tempdb];
CREATE SEQUENCE dbo.MagicNumber
AS INT
START WITH 1
INCREMENT BY 1;
CREATE TABLE dbo.NameLookup
(
[Id] INT NOT NULL
CONSTRAINT [PK_NameLookup] PRIMARY KEY CLUSTERED
CONSTRAINT [DF_NameLookup_Id] DEFAULT (NEXT VALUE FOR dbo.MagicNumber),
[ItemName] NVARCHAR(50) NOT NULL
);
CREATE UNIQUE NONCLUSTERED INDEX [UIX_NameLookup_ItemName]
ON dbo.NameLookup ([ItemName]);
GO
ทดสอบการตั้งค่า: กระบวนงานที่เก็บไว้
CREATE PROCEDURE dbo.GetOrInsertName
(
@SomeName NVARCHAR(50),
@ID INT OUTPUT,
@TestRaceCondition BIT = 0
)
AS
SET NOCOUNT ON;
BEGIN TRY
SELECT @ID = nl.[Id]
FROM dbo.NameLookup nl
WHERE nl.[ItemName] = @SomeName
AND @TestRaceCondition = 0;
IF (@ID IS NULL)
BEGIN
SET @ID = NEXT VALUE FOR dbo.MagicNumber;
INSERT INTO dbo.NameLookup ([Id], [ItemName])
VALUES (@ID, @SomeName);
END;
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() = 2601) -- "Cannot insert duplicate key row in object"
BEGIN
SELECT @ID = nl.[Id]
FROM dbo.NameLookup nl
WHERE nl.[ItemName] = @SomeName;
END;
ELSE
BEGIN
;THROW; -- SQL Server 2012 or newer
/*
DECLARE @ErrorNumber INT = ERROR_NUMBER(),
@ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
RAISERROR(N'Msg %d: %s', 16, 1, @ErrorNumber, @ErrorMessage);
RETURN;
*/
END;
END CATCH;
GO
บททดสอบ
DECLARE @ItemID INT;
EXEC dbo.GetOrInsertName
@SomeName = N'test1',
@ID = @ItemID OUTPUT;
SELECT @ItemID AS [ItemID];
GO
DECLARE @ItemID INT;
EXEC dbo.GetOrInsertName
@SomeName = N'test1',
@ID = @ItemID OUTPUT,
@TestRaceCondition = 1;
SELECT @ItemID AS [ItemID];
GO
คำถามจาก OP
นี่คือเหตุผลที่ดีกว่าMERGE
? ฉันจะไม่ได้รับฟังก์ชั่นเดียวกันโดยไม่ต้องTRY
ใช้WHERE NOT EXISTS
ประโยคหรือไม่?
MERGE
มี "ปัญหา" ต่างๆ (การอ้างอิงหลายรายการเชื่อมโยงอยู่ในคำตอบของ @ SqlZim ดังนั้นไม่จำเป็นต้องทำซ้ำข้อมูลนั้นที่นี่) และไม่มีการล็อคเพิ่มเติมในวิธีนี้ (การช่วงชิงน้อยกว่า) ดังนั้นจึงควรดีกว่าในการใช้งานพร้อมกัน ในวิธีการนี้คุณจะไม่ได้รับการฝ่าฝืนข้อ จำกัด ที่ไม่เหมือนใครทั้งหมดโดยไม่มีข้อ จำกัดHOLDLOCK
ฯลฯ รับประกันได้เลยว่าทำงานได้จริง
เหตุผลเบื้องหลังแนวทางนี้คือ:
- หากคุณมีการประมวลผลขั้นตอนนี้มากพอจนคุณต้องกังวลเกี่ยวกับการชนคุณไม่ต้องการ:
- ทำตามขั้นตอนมากกว่าที่จำเป็น
- พักล็อคทรัพยากรใด ๆ นานกว่าที่จำเป็น
- เนื่องจากการชนสามารถเกิดขึ้นได้กับรายการใหม่เท่านั้น (รายการใหม่ที่ส่งในเวลาเดียวกันแน่นอน ) ความถี่ของการตกลงไปใน
CATCH
บล็อกในสถานที่แรกจะค่อนข้างต่ำ มันเหมาะสมกว่าที่จะปรับโค้ดให้เหมาะสมซึ่งจะรัน 99% ของเวลาแทนที่จะเป็นโค้ดที่จะรัน 1% ของเวลา (นอกเสียจากว่าจะไม่มีค่าใช้จ่ายในการปรับให้เหมาะสมทั้งสอง แต่ไม่ใช่กรณีที่นี่)
ความคิดเห็นจากคำตอบของ @ SqlZim (เน้นการเพิ่ม)
ผมเองชอบที่จะลองและตัดการแก้ปัญหาที่จะหลีกเลี่ยงการทำว่าเมื่อเป็นไปได้ ในกรณีนี้ฉันไม่รู้สึกว่าการใช้ระบบล็อคserializable
เป็นวิธีที่ถนัดและฉันมั่นใจว่ามันจะรองรับการทำงานพร้อมกันได้เป็นอย่างดี
ฉันจะเห็นด้วยกับประโยคแรกนี้หากมีการแก้ไขให้รัฐ "และ _ เมื่อระมัดระวัง" เพียงเพราะสิ่งที่เป็นไปได้ทางเทคนิคไม่ได้หมายความว่าสถานการณ์ (เช่นการใช้งานที่ตั้งใจ) จะได้รับประโยชน์จากมัน
ปัญหาที่ฉันเห็นด้วยวิธีนี้คือมันล็อคมากกว่าสิ่งที่ถูกแนะนำ มันเป็นสิ่งสำคัญที่จะอ่านเอกสารที่ยกมาอีกครั้งใน "serializable" โดยเฉพาะต่อไปนี้ (เน้นเพิ่ม):
- ธุรกรรมอื่น ๆ ไม่สามารถแทรกแถวใหม่ด้วยค่าคีย์ที่จะอยู่ในช่วงของคีย์ที่อ่านโดยคำสั่งใด ๆ ในธุรกรรมปัจจุบันจนกว่าธุรกรรมปัจจุบันจะเสร็จสมบูรณ์
ตอนนี้นี่คือความคิดเห็นในรหัสตัวอย่าง:
SELECT [Id]
FROM dbo.NameLookup WITH (SERIALIZABLE) /* hold that key range for @vName */
คำผ่าตัดมี "ช่วง" การล็อคที่ใช้ไม่ได้เป็นเพียงแค่ค่า@vName
แต่ยังมีช่วงเริ่มต้นที่แม่นยำยิ่งขึ้นตำแหน่งที่ควรค่าใหม่นี้ (เช่นระหว่างค่าคีย์ที่มีอยู่ในทั้งสองด้านของค่าใหม่ที่เหมาะสม) แต่ไม่ใช่ค่าตัวเอง ความหมายกระบวนการอื่น ๆ จะถูกบล็อกไม่ให้แทรกค่าใหม่ขึ้นอยู่กับค่าที่ค้นหาอยู่ในปัจจุบัน หากการค้นหากำลังทำที่ด้านบนของช่วงจากนั้นให้แทรกสิ่งที่อาจครอบครองตำแหน่งเดียวกันนั้นจะถูกบล็อก ตัวอย่างเช่นหากมีค่า "a", "b" และ "d" อยู่หากกระบวนการหนึ่งกำลังเลือก SELECT บน "f" ดังนั้นจะไม่สามารถแทรกค่า "g" หรือแม้แต่ "e" ( ตั้งแต่หนึ่งในนั้นจะมาทันทีหลังจาก "d") แต่การแทรกค่า "c" จะเป็นไปได้เนื่องจากจะไม่อยู่ในช่วง "สงวน"
ตัวอย่างต่อไปนี้ควรแสดงให้เห็นถึงพฤติกรรมนี้:
(ในแท็บแบบสอบถาม (เช่นเซสชัน) # 1)
INSERT INTO dbo.NameLookup ([ItemName]) VALUES (N'test5');
BEGIN TRAN;
SELECT [Id]
FROM dbo.NameLookup WITH (SERIALIZABLE) /* hold that key range for @vName */
WHERE ItemName = N'test8';
--ROLLBACK;
(ในแท็บแบบสอบถาม (เช่นเซสชัน) # 2)
EXEC dbo.NameLookup_getset_byName @vName = N'test4';
-- works just fine
EXEC dbo.NameLookup_getset_byName @vName = N'test9';
-- hangs until you either hit "cancel" in this query tab,
-- OR issue a COMMIT or ROLLBACK in query tab #1
EXEC dbo.NameLookup_getset_byName @vName = N'test7';
-- hangs until you either hit "cancel" in this query tab,
-- OR issue a COMMIT or ROLLBACK in query tab #1
EXEC dbo.NameLookup_getset_byName @vName = N's';
-- works just fine
EXEC dbo.NameLookup_getset_byName @vName = N'u';
-- hangs until you either hit "cancel" in this query tab,
-- OR issue a COMMIT or ROLLBACK in query tab #1
ในทำนองเดียวกันหากมีค่า "C" อยู่และมีการเลือกค่า "A" (และถูกล็อกไว้) คุณสามารถแทรกค่าเป็น "D" แต่ไม่ใช่ค่า "B":
(ในแท็บแบบสอบถาม (เช่นเซสชัน) # 1)
INSERT INTO dbo.NameLookup ([ItemName]) VALUES (N'testC');
BEGIN TRAN
SELECT [Id]
FROM dbo.NameLookup WITH (SERIALIZABLE) /* hold that key range for @vName */
WHERE ItemName = N'testA';
--ROLLBACK;
(ในแท็บแบบสอบถาม (เช่นเซสชัน) # 2)
EXEC dbo.NameLookup_getset_byName @vName = N'testD';
-- works just fine
EXEC dbo.NameLookup_getset_byName @vName = N'testB';
-- hangs until you either hit "cancel" in this query tab,
-- OR issue a COMMIT or ROLLBACK in query tab #1
เพื่อความเป็นธรรมในแนวทางที่แนะนำของฉันเมื่อมีข้อยกเว้นจะมี 4 รายการในบันทึกการทำธุรกรรมที่จะไม่เกิดขึ้นในแนวทาง "การทำธุรกรรมต่อเนื่อง" นี้ แต่อย่างที่ฉันได้กล่าวไว้ข้างต้นหากมีข้อยกเว้นเกิดขึ้น 1% (หรือ 5%) ของเวลานั่นส่งผลกระทบน้อยกว่ากรณีที่เป็นไปได้มากกว่าของ SELECT เริ่มต้นที่บล็อกการดำเนินการ INSERT ชั่วคราว
อีกอย่างหนึ่งถึงแม้ว่ารองลงมาปัญหาด้วยวิธีการ "การทำธุรกรรมแบบอนุกรม + ข้อออก OUTPUT" วิธีนี้ก็คือOUTPUT
ข้อ (ในการใช้งานในปัจจุบัน) ส่งข้อมูลกลับเป็นชุดผลลัพธ์ ชุดผลลัพธ์ต้องการค่าใช้จ่ายเพิ่มเติม (อาจเป็นทั้งสองด้าน: ใน SQL Server เพื่อจัดการเคอร์เซอร์ภายในและในเลเยอร์แอปเพื่อจัดการวัตถุ DataReader) กว่าOUTPUT
พารามิเตอร์แบบง่าย เนื่องจากเรากำลังเผชิญกับค่าสเกลาร์เดียวและการสันนิษฐานนั้นเป็นความถี่สูงของการประหารชีวิตซึ่งค่าใช้จ่ายเพิ่มเติมของชุดผลลัพธ์อาจเพิ่มขึ้น
ในขณะที่OUTPUT
ข้อสามารถใช้ในลักษณะที่จะกลับOUTPUT
พารามิเตอร์ที่จะต้องมีขั้นตอนเพิ่มเติมในการสร้างตารางชั่วคราวหรือตัวแปรตารางแล้วเพื่อเลือกค่าออกจากตัวแปรตาราง / ตาราง temp นั้นลงในOUTPUT
พารามิเตอร์
ชี้แจงเพิ่มเติม: การตอบสนองต่อการตอบสนองของ @ SqlZim (คำตอบที่อัปเดต) ต่อการตอบสนองของฉันต่อการตอบสนองของ @ SqlZim (ในคำตอบเดิม) ต่อแถลงการณ์ของฉันเกี่ยวกับการเกิดพร้อมกันและประสิทธิภาพ ;-)
ขออภัยถ้าส่วนนี้ยาวนิดหน่อย แต่ ณ จุดนี้เราเพิ่งไปถึงความแตกต่างของสองวิธี
ฉันเชื่อว่าวิธีการนำเสนอข้อมูลอาจนำไปสู่ข้อสันนิษฐานที่ผิดพลาดเกี่ยวกับจำนวนการล็อกที่คาดว่าจะพบเมื่อใช้serializable
ในสถานการณ์จำลองตามที่แสดงในคำถามเดิม
ใช่ฉันจะยอมรับว่าฉันลำเอียงแม้ว่าจะยุติธรรม:
- เป็นไปไม่ได้ที่มนุษย์จะไม่ลำเอียงอย่างน้อยก็ระดับเล็กและฉันพยายามทำให้เป็นอย่างน้อย
- ตัวอย่างที่ให้มานั้นง่าย แต่นั่นก็เพื่อจุดประสงค์ในการอธิบายพฤติกรรมโดยไม่ทำให้มันซับซ้อน การใช้คลื่นความถี่ที่มากเกินไปนั้นไม่ได้มีจุดมุ่งหมายถึงแม้ว่าฉันจะเข้าใจว่าฉันไม่ได้ระบุไว้เป็นอย่างอื่นอย่างชัดเจนและสามารถอ่านได้ว่าเป็นการบ่งบอกถึงปัญหาที่ใหญ่กว่าที่มีอยู่จริง ฉันจะพยายามชี้แจงว่าด้านล่าง
- ฉันได้รวมตัวอย่างของการล็อคช่วงระหว่างสองคีย์ที่มีอยู่แล้ว (ชุดที่สองของ "Query tab 1" และ "Query tab 2" block)
- ฉันพบ (และอาสาสมัคร) "ค่าใช้จ่ายแอบแฝง" ของวิธีการของฉันนั่นคือการบันทึก Tran Log สี่ครั้งในแต่ละครั้งที่
INSERT
ล้มเหลวเนื่องจากการละเมิดข้อ จำกัด ที่ไม่ซ้ำกัน ฉันไม่ได้เห็นที่กล่าวถึงในคำตอบ / โพสต์อื่น ๆ
เกี่ยวกับวิธีการ "JFDI" ของ @ gbn, โพสต์ของ "Michael Pragmatism For The Win" ของ Michael J. Swart และความคิดเห็นของ Aaron Bertrand ในโพสต์ของ Michael (เกี่ยวกับการทดสอบของเขาแสดงให้เห็นว่าสถานการณ์มีประสิทธิภาพลดลง) และความคิดเห็นของคุณ การปรับตัวของสจ๊วตของขั้นตอนการลองจับ JFDI ของ @ gbn "ระบุ:
หากคุณกำลังแทรกค่าใหม่บ่อยกว่าการเลือกค่าที่มีอยู่สิ่งนี้อาจมีประสิทธิภาพมากกว่าเวอร์ชั่นของ @ srutzky มิฉะนั้นฉันจะชอบเวอร์ชันของ @ srutzky มากกว่าอันนี้
ด้วยความเคารพต่อการอภิปราย gbn / Michael / Aaron ที่เกี่ยวข้องกับวิธีการ "JFDI" มันจะไม่ถูกต้องที่จะเทียบคำแนะนำของฉันกับแนวทาง "JFDI" ของ gbn เนื่องจากลักษณะของการดำเนินการ "รับหรือแทรก" มีความจำเป็นอย่างชัดเจนที่จะต้องทำSELECT
เพื่อรับID
ค่าสำหรับระเบียนที่มีอยู่ SELECT นี้ทำหน้าที่เป็นการIF EXISTS
ตรวจสอบซึ่งทำให้วิธีการนี้มากขึ้นดังนั้นจึงถือเอาการเปลี่ยนแปลง "CheckTryCatch" ของการทดสอบของแอรอน โค้ดที่เขียนขึ้นใหม่ของไมเคิล (และการปรับตัวครั้งสุดท้ายของคุณเกี่ยวกับการปรับตัวของไมเคิล) ยังรวมถึงการWHERE NOT EXISTS
ตรวจสอบสิ่งเดียวกันก่อน ดังนั้นข้อเสนอแนะของฉัน (พร้อมด้วยรหัสสุดท้ายของไมเคิลและการดัดแปลงรหัสสุดท้ายของคุณ) จะไม่กระทบกับCATCH
บล็อกทั้งหมดที่มักเกิดขึ้นจริง อาจเป็นสถานการณ์ที่มีสองเซสชันเท่านั้นItemName
INSERT...SELECT
ในช่วงเวลาเดียวกันนั้นทั้งสองเซสชันจะได้รับ "ความจริง" WHERE NOT EXISTS
ในช่วงเวลาเดียวกันและทำให้ทั้งคู่พยายามทำINSERT
ในช่วงเวลาเดียวกัน ว่าสถานการณ์ที่เฉพาะเจาะจงมากที่เกิดขึ้นมากน้อยบ่อยกว่าการเลือกตัวเลือกที่มีอยู่ItemName
หรือการใส่ใหม่ItemName
เมื่อไม่มีกระบวนการอื่น ๆ ที่เป็นความพยายามที่จะทำเช่นนั้นในขณะเดียวกันแน่นอน
กับทุกคนในใจ: ทำไมฉันถึงชอบแนวทางของฉัน?
อันดับแรกให้ดูที่การล็อคเกิดขึ้นในวิธี "เรียงลำดับได้" ดังกล่าวข้างต้น "ช่วง" ที่ถูกล็อคขึ้นอยู่กับค่าคีย์ที่มีอยู่ในทั้งสองด้านของที่ค่าคีย์ใหม่จะพอดี จุดเริ่มต้นหรือจุดสิ้นสุดของช่วงอาจเป็นจุดเริ่มต้นหรือจุดสิ้นสุดของดัชนีตามลำดับหากไม่มีค่าคีย์ที่มีอยู่ในทิศทางนั้น สมมติว่าเรามีดัชนีและคีย์ต่อไปนี้ ( ^
หมายถึงจุดเริ่มต้นของดัชนีในขณะที่$
แสดงถึงจุดสิ้นสุดของมัน):
Range #: |--- 1 ---|--- 2 ---|--- 3 ---|--- 4 ---|
Key Value: ^ C F J $
หากเซสชัน 55 พยายามแทรกค่าคีย์ของ:
A
ดังนั้นช่วง # 1 (จาก^
ถึงC
) ถูกล็อค: เซสชัน 56 ไม่สามารถแทรกค่าB
แม้ว่าจะไม่ซ้ำกันและถูกต้อง (ยัง) แต่เซสชั่น 56 สามารถแทรกค่านิยมของD
, และG
M
D
จากนั้นช่วง # 2 (จากC
ถึงF
) ถูกล็อค: เซสชัน 56 ไม่สามารถแทรกค่าE
(ยัง) ได้ แต่เซสชั่น 56 สามารถแทรกค่านิยมของA
, และG
M
M
จากนั้นช่วง # 4 (จากJ
ถึง$
) ถูกล็อค: เซสชัน 56 ไม่สามารถแทรกค่าX
(ยัง) ได้ แต่เซสชั่น 56 สามารถแทรกค่านิยมของA
, และD
G
เมื่อมีการเพิ่มค่าคีย์มากขึ้นช่วงระหว่างค่าคีย์จะแคบลงดังนั้นจึงลดความน่าจะเป็น / ความถี่ของค่าหลายค่าที่ถูกแทรกในเวลาเดียวกันเพื่อต่อสู้กับช่วงเดียวกัน เป็นที่ยอมรับว่านี่ไม่ใช่ปัญหาใหญ่และโชคดีที่มันดูเหมือนจะเป็นปัญหาที่ลดลงตามกาลเวลา
ปัญหากับวิธีการของฉันถูกอธิบายไว้ข้างต้นมันจะเกิดขึ้นเมื่อสองช่วงพยายามที่จะแทรกเดียวกันค่าคีย์ในเวลาเดียวกัน ในแง่นี้มันเกิดขึ้นกับสิ่งที่มีความเป็นไปได้สูงกว่าที่จะเกิดขึ้น: มีการพยายามสองค่าที่แตกต่างกัน แต่ปิดในเวลาเดียวกันหรือพยายามใช้ค่าคีย์เดียวกันพร้อมกันหรือไม่ ฉันคิดว่าคำตอบนั้นอยู่ในโครงสร้างของแอปที่ทำส่วนแทรก แต่โดยทั่วไปแล้วฉันคิดว่ามันน่าจะเป็นไปได้มากกว่าที่จะมีค่าที่ต่างกันสองค่าที่เพิ่งเกิดขึ้นเพื่อแชร์ช่วงเดียวกัน แต่วิธีเดียวที่จะรู้ได้อย่างแท้จริงคือการทดสอบทั้งสองอย่างบนระบบ OPs
ถัดไปให้เราพิจารณาสองสถานการณ์และวิธีที่แต่ละวิธีจัดการกับสถานการณ์:
คำขอทั้งหมดเป็นค่าคีย์ที่ไม่ซ้ำกัน:
ในกรณีนี้CATCH
บล็อกในคำแนะนำของฉันจะไม่ถูกป้อนดังนั้นจึงไม่มี "ปัญหา" (เช่นรายการบันทึก 4 tran และเวลาที่ใช้ในการทำเช่นนั้น) แต่ในแนวทาง "ต่อเนื่องได้" แม้จะมีเม็ดมีดทั้งหมดที่ไม่ซ้ำกัน แต่ก็มีความเป็นไปได้ที่จะบล็อกเม็ดมีดอื่น ๆ ในช่วงเดียวกันเสมอ (แม้ว่าจะไม่ได้ยาวมากก็ตาม)
ความถี่สูงของการร้องขอค่าคีย์เดียวกันในเวลาเดียวกัน:
ในกรณีนี้ - ระดับต่ำมากของความเป็นเอกลักษณ์ในแง่ของการร้องขอที่เข้ามาสำหรับค่าคีย์ที่ไม่มีอยู่ - CATCH
บล็อกในคำแนะนำของฉันจะถูกป้อนเป็นประจำ ผลของสิ่งนี้จะเป็นไปได้ว่าการแทรกแต่ละครั้งที่ล้มเหลวจะต้องย้อนกลับอัตโนมัติและเขียน 4 รายการลงในบันทึกการทำธุรกรรมซึ่งเป็นผลการปฏิบัติงานเล็กน้อยในแต่ละครั้ง แต่การดำเนินการโดยรวมไม่ควรล้มเหลว (อย่างน้อยก็ไม่ใช่เพราะสิ่งนี้)
(มีปัญหากับวิธี "อัปเดต" รุ่นก่อนหน้าที่อนุญาตให้ประสบปัญหาการหยุดชะงักได้updlock
คำแนะนำเพิ่มไปยังที่อยู่นี้และไม่ได้รับการหยุดชะงักอีกต่อไป)แต่ในแนวทาง "ต่อเนื่องได้" (แม้จะเป็นเวอร์ชั่นที่ได้รับการปรับปรุงและปรับให้เหมาะสมที่สุด) การดำเนินการจะหยุดชะงัก ทำไม? เนื่องจากserializable
พฤติกรรมป้องกันINSERT
การดำเนินการในช่วงที่อ่านและถูกล็อก มันไม่ได้ป้องกันSELECT
การดำเนินการในช่วงนั้น
serializable
วิธีการในกรณีนี้ก็ดูเหมือนจะไม่มีค่าใช้จ่ายเพิ่มเติมและอาจดำเนินการเล็กน้อยดีกว่าสิ่งที่ผมแนะนำ
เช่นเดียวกับการสนทนาจำนวนมาก / ส่วนใหญ่เกี่ยวกับประสิทธิภาพเนื่องจากมีปัจจัยมากมายที่สามารถส่งผลกระทบต่อผลลัพธ์วิธีเดียวที่จะมีความรู้สึกว่าสิ่งที่จะทำคือการลองในสภาพแวดล้อมเป้าหมายที่มันจะทำงาน ณ จุดนี้มันจะไม่เป็นเรื่องของความเห็น :)