การเพิ่มข้อมูลประจำตัวกำลังกระโดดในฐานข้อมูล SQL Server


126

ในตารางของฉันFeeในคอลัมน์ "ReceiptNo" ในการเพิ่มข้อมูลประจำตัวฐานข้อมูล SQL Server 2012 ก็เริ่มกระโดดไปที่ 100s แทนที่จะเป็น 1 ขึ้นอยู่กับสองสิ่งต่อไปนี้

  1. ถ้ามันคือ 1205446 มันกระโดดไปที่ 1206306 ถ้ามันคือ 1206321 มันจะข้ามไปที่ 1207306 และถ้ามันคือ 1207314 มันจะข้ามไปที่ 1208306 สิ่งที่ฉันอยากจะแจ้งให้คุณทราบก็คือตัวเลขสามตัวสุดท้ายจะคงที่นั่นคือ 306 ทุกครั้งที่กระโดด เกิดขึ้นดังแสดงในภาพต่อไปนี้

  2. ปัญหานี้เกิดขึ้นเมื่อฉันรีสตาร์ทคอมพิวเตอร์

ใส่คำอธิบายภาพที่นี่


ถ้าคุณเพิ่มorder by ReceiptNoในแบบสอบถามของคุณระเบียนเหล่านั้นไม่มีจริงหรือ คุณแน่ใจหรือไม่ว่าเมื่อแทรกระเบียนแล้วจะไม่มีข้อผิดพลาด หากบันทึกพยายามแทรกและล้มเหลวข้อมูลประจำตัวจะเพิ่มขึ้นเช่นเดียวกันหากบันทึกถูกลบ หากบันทึกถูกลบReceiptNoจะไม่รีเซ็ต คุณสามารถลงรายการบัญชีสร้างFeeตารางได้หรือไม่?
Taryn

4
คำถามแรกคือ - ทำไมถึงสำคัญ? ควรเป็น ID ที่ไม่ซ้ำกันโดยพลการ
Andrew

1
สิ่งนี้ทำงานบนเซิร์ฟเวอร์หรืออาจแสดงบนเดสก์ท็อป? สงสัยว่าทำไมดูเหมือนบริการเริ่มต้นใหม่บ่อยจัง?
Martin Smith

@bluefeet ฉันรู้เมื่อเกิดข้อผิดพลาดการเพิ่มตัวตนจะเกิดขึ้น มั่นใจ 100% ว่าไม่มีข้อผิดพลาด ฉันแก้ไขคำถามของฉันโดยเพิ่มตารางและขั้นตอนการจัดเก็บที่ฉันใช้เพื่อแทรกแถว
kashif

@kashif - แน่นอน 99% ว่าไม่จำเป็น กระโดดจากตรง 1,000 ( 1206306, 1207306, 1207806) หมายถึงคำอธิบายในรายการกระทู้ต่อเกือบจะแน่นอนมีผลบังคับใช้
Martin Smith

คำตอบ:


158

คุณกำลังพบกับลักษณะการทำงานนี้เนื่องจากการปรับปรุงประสิทธิภาพตั้งแต่ SQL Server 2012

โดยค่าเริ่มต้นจะใช้ขนาดแคช 1,000 เมื่อจัดสรรIDENTITYค่าสำหรับintคอลัมน์และการเริ่มบริการใหม่อาจ "สูญเสีย" ค่าที่ไม่ได้ใช้ (ขนาดแคชคือ 10,000 สำหรับbigint/ numeric)

สิ่งนี้ได้กล่าวไว้ในเอกสารประกอบ

SQL Server อาจแคชค่าข้อมูลประจำตัวด้วยเหตุผลด้านประสิทธิภาพและค่าที่กำหนดบางค่าอาจสูญหายไประหว่างฐานข้อมูลล้มเหลวหรือเซิร์ฟเวอร์เริ่มทำงานใหม่ ซึ่งอาจส่งผลให้เกิดช่องว่างในค่าเอกลักษณ์เมื่อใส่ หากไม่สามารถยอมรับช่องว่างได้แอปพลิเคชันควรใช้กลไกของตัวเองเพื่อสร้างค่าคีย์ การใช้ตัวสร้างลำดับที่มีNOCACHEตัวเลือกสามารถ จำกัด ช่องว่างในการทำธุรกรรมที่ไม่เคยกระทำ

จากข้อมูลที่คุณแสดงดูเหมือนว่าสิ่งนี้เกิดขึ้นหลังจากการป้อนข้อมูลในวันที่ 22 ธันวาคมจากนั้นเมื่อเริ่มต้น SQL Server ใหม่สงวนค่า1206306 - 1207305ไว้ หลังจากการป้อนข้อมูลสำหรับวันที่ 24-25 ธันวาคมได้ทำการรีสตาร์ทอีกครั้งและ SQL Server สงวนช่วงถัดไปที่1207306 - 1208305มองเห็นได้ในรายการสำหรับวันที่ 28

เว้นแต่คุณจะเริ่มบริการใหม่ด้วยความถี่ที่ผิดปกติค่าที่ "สูญหาย" ใด ๆ ก็ไม่น่าจะทำให้เกิดความเสียหายอย่างมีนัยสำคัญใด ๆ ในช่วงของค่าที่อนุญาตโดยประเภทข้อมูลดังนั้นนโยบายที่ดีที่สุดคือไม่ต้องกังวล

หากนี่เป็นเหตุผลบางประการปัญหาที่แท้จริงสำหรับคุณวิธีแก้ปัญหาที่เป็นไปได้บางประการคือ ...

  1. คุณสามารถใช้SEQUENCEแทนคอลัมน์ข้อมูลประจำตัวและกำหนดขนาดแคชที่เล็กลงเช่นและใช้NEXT VALUE FORในค่าเริ่มต้นของคอลัมน์
  2. หรือใช้แฟล็กการติดตาม 272 ซึ่งทำให้การIDENTITYจัดสรรถูกบันทึกเป็นในเวอร์ชันถึง 2008 R2 สิ่งนี้ใช้ได้ทั่วโลกกับฐานข้อมูลทั้งหมด
  3. หรือสำหรับเวอร์ชันล่าสุดให้ดำเนินการALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFเพื่อปิดใช้งานการแคชข้อมูลประจำตัวสำหรับฐานข้อมูลเฉพาะ

คุณควรทราบวิธีแก้ปัญหาเหล่านี้ไม่รับประกันว่าจะไม่มีช่องว่าง สิ่งนี้ไม่เคยได้รับการรับประกันIDENTITYเนื่องจากจะทำได้โดยการจัดลำดับเม็ดมีดเข้ากับโต๊ะเท่านั้น หากคุณต้องการคอลัมน์ที่ไม่มีช่องว่างคุณจะต้องใช้โซลูชันอื่นที่ไม่ใช่IDENTITYหรือSEQUENCE


1
เพื่อตรวจสอบสิ่งที่คุณบอกว่าฉันใส่ค่าบางอย่างและได้ 1208309, 1208310 จากนั้นฉันรีสตาร์ทเซิร์ฟเวอร์จากนั้นเมื่อฉันเพิ่มแถวฉันได้ 1209309 นั่นหมายความว่าสิ่งที่คุณพูดนั้นถูกต้อง ขอบคุณมาก. ตอนนี้คุณช่วยบอกฉันหน่อยได้ไหมว่าฉันจะแก้ปัญหานี้ได้อย่างไร คุณช่วยแนะนำให้ฉันใช้ sql server 2008 แทน 2012 ที่ฉันใช้ก่อนหน้านี้หรือแม้กระทั่งใช้ 2012 ปัญหานี้สามารถแก้ไขได้หรือไม่?
kashif

1
@kashif - มันเป็นปัญหาสำหรับคุณจริงหรือ? แม้ว่าคุณจะใช้ค่าประจำตัวถึง 1,000 ค่าต่อวัน แต่ก็ยังต้องใช้เวลา 2 ล้านวันก่อนที่ค่าจะหมด ถ้าคุณไม่ต้องการพฤติกรรมเก่าคุณสามารถตั้งค่า SQL Server เพื่อเริ่มต้นด้วยค่าสถานะการติดตาม 272 หรือคุณสามารถใช้SEQUENCEแทนIDENTITYและตั้งค่าลำดับให้มีขนาดแคชเป็น0.
Martin Smith

ฉันได้รับคำตอบที่น่าประทับใจและน่าพอใจจากคุณสำหรับการแก้ปัญหาของฉันขอบคุณมาก
kashif

อันที่จริงแล้วจากของคุณCREATE TABLEฉันเห็นว่าคุณใช้งานอยู่numeric(7)และได้เริ่มใช้เลขที่1200001นั่นหมายความว่าคุณจะหมดลงหลังจากนั้นหลาย8,799วัน (24 ปี) ถ้าคุณใช้ 1,000 ต่อวัน
Martin Smith

ค่าที่แท้จริงควรจะขึ้นอยู่กับประเภทคอลัมน์ที่ใช้ ตัวอย่างเช่นbig intคอลัมน์มักจะ "กระโดด" 10,000 ครั้งต่อการรีสตาร์ท
StarPilot

60

ปัญหานี้เกิดขึ้นหลังจากรีสตาร์ท SQL Server

วิธีแก้ปัญหาคือ:

  • เรียกใช้จัดการการกำหนดค่าของ SQL Server

  • เลือกบริการของ SQL Server

    ตัวจัดการการตั้งค่าคอนฟิกเซิร์ฟเวอร์ SQL

  • คลิกขวาSQL ServerและเลือกProperties

  • ในหน้าต่างเปิดภายใต้พารามิเตอร์การเริ่มต้นพิมพ์-T272และคลิกเพิ่มจากนั้นกดปุ่มใช้และเริ่มต้นใหม่

    พารามิเตอร์การเริ่มต้นเซิร์ฟเวอร์ SQL


1
วิธีนี้ใช้ได้ผลจริงขอบคุณมาก! และตามที่บอกไว้ที่นี่ปัญหานี้จะไม่ได้รับการแก้ไขใน SQL Server 2012 และในเซอร์วิสแพ็ค - เฉพาะในรุ่นถัดไป
Fragment

2
มีวิธีใช้แฟล็กการติดตามกับแต่ละฐานข้อมูลหรือไม่ ฉันไม่ต้องการทำการเปลี่ยนแปลงนี้กับเซิร์ฟเวอร์ทั้งหมดเนื่องจากฉันมีฐานข้อมูลของบุคคลที่สามและฉันไม่แน่ใจว่าสิ่งนี้จะส่งผลอย่างไร
Ege Ersoz

1
ฉันไม่ได้ทำตามเหตุผล แต่เห็นได้ชัดว่าผู้ใช้บางคนต้องใช้ตัวพิมพ์เล็ก "t" เพื่อให้ใช้งานได้ ดูลิงค์ที่โพสต์โดย Fragment ในความคิดเห็นด้านบน
Savage

33

จากSQL Server 2017+คุณสามารถใช้ALTER DATABASE SCOPED CONFIGURATION :

IDENTITY_CACHE = {บน | ปิด}

เปิดหรือปิดใช้งานแคชข้อมูลประจำตัวที่ระดับฐานข้อมูล ค่าเริ่มต้นคือเปิด การแคชข้อมูลประจำตัวใช้เพื่อปรับปรุงประสิทธิภาพของ INSERT บนตารางที่มีคอลัมน์ Identity เพื่อหลีกเลี่ยงช่องว่างในค่าของคอลัมน์ Identity ในกรณีที่เซิร์ฟเวอร์รีสตาร์ทโดยไม่คาดคิดหรือล้มเหลวในเซิร์ฟเวอร์สำรองให้ปิดใช้งานตัวเลือก IDENTITY_CACHE อ็อพชันนี้คล้ายกับ SQL Server Trace Flag 272 ที่มีอยู่ยกเว้นว่าสามารถตั้งค่าได้ที่ระดับฐานข้อมูลแทนที่จะเป็นเฉพาะในระดับเซิร์ฟเวอร์

( ... )

G. ตั้งค่า IDENTITY_CACHE

ตัวอย่างนี้ปิดใช้งานแคชข้อมูลประจำตัว

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;

25

ฉันรู้ว่าคำตอบของฉันอาจจะไปงานปาร์ตี้ช้า แต่ฉันได้แก้ไขด้วยวิธีอื่นโดยการเพิ่มการเริ่มต้นกระบวนงานที่เก็บไว้ใน SQL Server 2012

สร้างกระบวนงานที่เก็บไว้ต่อไปนี้ในฐานข้อมูลหลัก

USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[ResetTableNameIdentityAfterRestart]
AS
BEGIN

begin TRAN
    declare @id int = 0
    SELECT @id =  MAX(id) FROM [DatabaseName].dbo.[TableName]
    --print @id
    DBCC CHECKIDENT ('[DatabaseName].dbo.[TableName]', reseed, @id)
Commit

END

จากนั้นเพิ่มใน Start up โดยใช้ไวยากรณ์ต่อไปนี้

EXEC sp_procoption 'ResetOrderIdentityAfterRestart', 'startup', 'on';

นี่เป็นความคิดที่ดีหากคุณมีโต๊ะไม่กี่โต๊ะ แต่ถ้าคุณต้องทำหลาย ๆ ตารางวิธีนี้ก็ยังใช้ได้ แต่ไม่ใช่ความคิดที่ดี


ความคิดที่ดี แต่มันใช้ไม่ได้กับตารางที่อ้างอิงใช่หรือไม่? ฉันหมายความว่าจะแก้ไขค่าคีย์ต่างประเทศหรือไม่
rom5jp

@ rom5jp การแก้ไข FK ไม่ใช่ประเด็นของคำตอบนี้ ทั้งหมดนี้เกี่ยวกับการแก้ไขค่า PK ถัดไปที่เป็นไปได้ของตาราง ตราบใดที่ MAX (id) ไม่อยู่ใน FK ใด ๆ ก็ควรใช้งานได้
Jeyara

14

ปัญหานี้ยังคงเป็นปัญหาที่พบบ่อยในหมู่นักพัฒนาและแอปพลิเคชันจำนวนมากโดยไม่คำนึงถึงขนาด

น่าเสียดายที่คำแนะนำข้างต้นไม่สามารถแก้ไขสถานการณ์ทั้งหมดได้เช่นโฮสติ้งที่ใช้ร่วมกันคุณไม่สามารถพึ่งพาโฮสต์ของคุณในการตั้งค่าพารามิเตอร์เริ่มต้น -t272 ได้

นอกจากนี้หากคุณมีตารางที่ใช้คอลัมน์ข้อมูลประจำตัวเหล่านี้สำหรับคีย์หลักเป็นความพยายามอย่างมากในการวางคอลัมน์เหล่านั้นและสร้างใหม่เพื่อใช้วิธีแก้ปัญหาลำดับ BS วิธีแก้ปัญหาลำดับจะดีก็ต่อเมื่อคุณออกแบบตารางใหม่ตั้งแต่ต้นใน SQL 2012+

บรรทัดล่างคือถ้าคุณอยู่บน Sql Server 2008R2 ให้อยู่กับมัน อย่างจริงจังอยู่กับมัน จนกว่า Microsoft จะยอมรับว่าพวกเขานำเสนอบั๊กขนาดใหญ่ซึ่งยังคงมีอยู่แม้ใน Sql Server 2016 เราก็ไม่ควรอัปเกรดจนกว่าพวกเขาจะเป็นเจ้าของและแก้ไขมัน

Microsoft เปิดตัวการเปลี่ยนแปลงที่รุนแรงกล่าวคือพวกเขาทำลาย API ที่ใช้งานได้ซึ่งไม่ทำงานตามที่ออกแบบไว้อีกต่อไปเนื่องจากระบบของพวกเขาลืมข้อมูลประจำตัวปัจจุบันของพวกเขาในการรีสตาร์ท แคชหรือไม่มีแคชนี่เป็นสิ่งที่ยอมรับไม่ได้และนักพัฒนาของ Microsoft ที่ใช้ชื่อว่า Bryan จำเป็นต้องเป็นเจ้าของมันแทนที่จะบอกให้โลกรู้ว่ามันเป็น "โดยการออกแบบ" และ "คุณลักษณะ" แน่นอนว่าการแคชเป็นคุณสมบัติ แต่การสูญเสียการติดตามว่าข้อมูลประจำตัวต่อไปควรเป็นอย่างไรไม่ใช่คุณสมบัติ มันเป็นบั๊กที่ไร้สาระ !!!

ฉันจะแบ่งปันวิธีแก้ปัญหาที่ฉันใช้เนื่องจากฐานข้อมูลของฉันอยู่บนเซิร์ฟเวอร์โฮสติ้งที่ใช้ร่วมกันฉันไม่ได้ทิ้งและสร้างคอลัมน์คีย์หลักของฉันใหม่ซึ่งจะเป็น PITA ขนาดใหญ่

แต่นี่เป็นการแฮ็คที่น่าอับอายของฉัน (แต่ไม่น่าอับอายเท่ากับข้อบกพร่อง POS ที่ไมโครซอฟท์แนะนำ)

สับ / แก้ไข:

ก่อนคำสั่งแทรกของคุณเพียงแค่ระบุตัวตนของคุณใหม่ก่อนการแทรกแต่ละครั้ง ขอแนะนำให้แก้ไขนี้เฉพาะในกรณีที่คุณไม่มีผู้ดูแลระบบควบคุมอินสแตนซ์ Sql Server ของคุณมิฉะนั้นฉันขอแนะนำให้ทำการรีสตาร์ทเซิร์ฟเวอร์ใหม่

declare @newId int -- where int is the datatype of your PKey or Id column
select @newId = max(YourBuggedIdColumn) from YOUR_TABLE_NAME
DBCC CheckIdent('YOUR_TABLE_NAME', RESEED, @newId)

เพียงแค่ 3 บรรทัดนั้นก่อนแทรกคุณก็ควรจะไป มันจะไม่ส่งผลกระทบต่อประสิทธิภาพมากขนาดนั้นกล่าวคือจะไม่สามารถสังเกตเห็นได้

โชคดี.


7

มีหลายสาเหตุที่เป็นไปได้ในการกระโดดค่าตัวตน มีตั้งแต่การแทรกย้อนกลับไปจนถึงการจัดการข้อมูลประจำตัวสำหรับการจำลองแบบ สิ่งที่ทำให้เกิดสิ่งนี้ในกรณีของคุณฉันไม่สามารถบอกได้โดยไม่ใช้เวลาในระบบของคุณ

อย่างไรก็ตามคุณควรทราบว่าไม่ว่าในกรณีใดคุณสามารถถือว่าคอลัมน์ข้อมูลประจำตัวเป็น contiguos ได้ มีเพียงหลายสิ่งที่อาจทำให้เกิดช่องว่าง

คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่นี่: http://sqlity.net/en/792/the-gap-in-the-identity-value-sequence/

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.