Gia tăng danh tính đang nhảy trong cơ sở dữ liệu SQL Server


126

Trong một trong các bảng của tôi Feetrong cột "Biên lai" trong phần tăng nhận dạng cơ sở dữ liệu SQL Server 2012 đột nhiên bắt đầu nhảy lên 100 thay vì 1 tùy thuộc vào hai điều sau đây.

  1. nếu là 1205446 thì nó nhảy lên 1206306, nếu là 1206321, nó nhảy lên 1207306 và nếu là 1207314, nó nhảy lên 1208306. Điều tôi muốn làm bạn lưu ý là ba chữ số cuối cùng không đổi, tức là ba chữ số cuối cùng không đổi xảy ra như trong hình dưới đây.

  2. sự cố này xảy ra khi tôi khởi động lại máy tính

nhập mô tả hình ảnh ở đây


Nếu bạn thêm order by ReceiptNovào truy vấn của bạn là những hồ sơ thực sự không có? Bạn có chắc chắn khi hồ sơ được chèn không có lỗi? Nếu một bản ghi cố gắng để được chèn và không nhận dạng sẽ tăng lên, điều tương tự nếu các bản ghi bị xóa. Nếu hồ sơ bị xóa, ReceiptNokhông thiết lập lại. Bạn có thể đăng bảng tạo cho Feebảng?
Taryn

4
Câu hỏi đầu tiên là - tại sao nó quan trọng? nó phải là một ID duy nhất tùy ý
Andrew

1
Đây có phải đang chạy trên một máy chủ hoặc có lẽ nó thể hiện trên máy tính để bàn? Tự hỏi tại sao có vẻ như dịch vụ được khởi động lại thường xuyên như vậy?
Martin Smith

@bluefeet Tôi biết khi xảy ra lỗi, việc tăng danh tính diễn ra. Tôi chắc chắn 100% không có lỗi. Tôi đang chỉnh sửa câu hỏi của mình bằng cách thêm bảng và quy trình được lưu trữ mà tôi sử dụng để chèn các hàng.
kashif

@kashif - Chắc chắn 99% là không cần thiết. Các nhảy bằng chính xác 1.000 ( 1206306, 1207306, 1207806) có nghĩa là lời giải thích trong Connect mục Chủ đề gần như chắc chắn được áp dụng.
Martin Smith

Câu trả lời:


158

Bạn đang gặp phải hành vi này do cải thiện hiệu suất kể từ SQL Server 2012.

Hiện tại theo mặc định, nó sử dụng kích thước bộ đệm là 1.000 khi phân bổ IDENTITYgiá trị cho một intcột và khởi động lại dịch vụ có thể "mất" các giá trị không sử dụng (Kích thước bộ đệm là 10.000 cho bigint/ numeric).

Điều này được đề cập trong tài liệu

SQL Server có thể lưu trữ các giá trị nhận dạng vì lý do hiệu suất và một số giá trị được gán có thể bị mất trong khi cơ sở dữ liệu bị lỗi hoặc khởi động lại máy chủ. Điều này có thể dẫn đến khoảng trống trong giá trị nhận dạng khi chèn. Nếu các khoảng trống không được chấp nhận thì ứng dụng nên sử dụng cơ chế riêng để tạo các giá trị chính. Sử dụng trình tạo trình tự với NOCACHEtùy chọn có thể giới hạn các khoảng trống cho các giao dịch không bao giờ được cam kết.

Từ dữ liệu bạn đã hiển thị, có vẻ như điều này đã xảy ra sau khi nhập dữ liệu vào ngày 22 tháng 12 sau đó khi nó khởi động lại SQL Server dành các giá trị 1206306 - 1207305. Sau khi nhập dữ liệu trong 24 - 25 tháng 12, một lần khởi động lại khác và SQL Server dành phạm vi tiếp theo 1207306 - 1208305hiển thị trong các mục nhập cho ngày 28.

Trừ khi bạn đang khởi động lại dịch vụ với tần suất bất thường, bất kỳ giá trị "bị mất" nào cũng không thể tạo ra bất kỳ vết lõm đáng kể nào trong phạm vi giá trị được cho phép bởi kiểu dữ liệu, vì vậy chính sách tốt nhất là không phải lo lắng về điều đó.

Nếu đây là vì một lý do nào đó, một vấn đề thực sự cho bạn một số cách giải quyết có thể là ...

  1. Bạn có thể sử dụng SEQUENCEthay vì một cột danh tính và xác định kích thước bộ đệm nhỏ hơn chẳng hạn và sử dụng NEXT VALUE FORtheo mặc định của cột.
  2. Hoặc áp dụng cờ theo dõi 272 để IDENTITYphân bổ được ghi lại như trong các phiên bản cho đến 2008 R2. Điều này áp dụng trên toàn cầu cho tất cả các cơ sở dữ liệu.
  3. Hoặc, đối với các phiên bản gần đây, hãy thực thi ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE = OFFđể tắt bộ đệm ẩn danh tính cho cơ sở dữ liệu cụ thể.

Bạn nên lưu ý rằng không có cách giải quyết nào đảm bảo không có khoảng trống. Điều này chưa bao giờ được đảm bảo bởi IDENTITYvì nó chỉ có thể được thực hiện bằng cách nối tiếp các phần chèn vào bảng. Nếu bạn cần một cột không khe hở, bạn sẽ cần sử dụng một giải pháp khác với IDENTITYhoặcSEQUENCE


1
để xác minh những gì bạn nói tôi đã chèn một số giá trị và nhận 1208309, 1208310 và sau đó tôi khởi động lại máy chủ và sau đó khi tôi thêm hàng tôi nhận được 1209309, điều đó có nghĩa là những gì bạn nói là hoàn toàn đúng. cảm ơn rất nhiều. Bây giờ bạn có thể cho tôi biết làm thế nào tôi có thể giải quyết vấn đề này. bạn có thể đề nghị tôi sử dụng máy chủ sql 2008 thay vì năm 2012 mà trước đây tôi đã sử dụng hoặc thậm chí sử dụng 2012 vấn đề này có thể được giải quyết không ??
kashif

1
@kashif - Nó thực sự là một vấn đề cho bạn? Ngay cả khi bạn sử dụng tối đa 1.000 giá trị danh tính mỗi ngày, vẫn sẽ mất 2 triệu ngày trước khi bạn hết giá trị. Nếu bạn muốn hành vi cũ, bạn có thể đặt SQL Server khởi động với cờ theo dõi 272 hoặc bạn có thể sử dụng SEQUENCEthay vì IDENTITYvà đặt Chuỗi để có kích thước bộ đệm 0.
Martin Smith

Tôi đã nhận được câu trả lời khá ấn tượng và thỏa đáng từ bạn cho giải pháp của tôi cảm ơn rất nhiều.
kashif

Thực tế từ bạn CREATE TABLEtôi thấy bạn đang sử dụng numeric(7)và đã bắt đầu đánh số ở 1200001đó có nghĩa là bạn sẽ hết sau 8,799vài ngày (24 năm) nếu bạn sử dụng 1.000 mỗi ngày.
Martin Smith

Giá trị thực tế "nhảy" được cho là phụ thuộc vào loại cột được sử dụng. Chẳng hạn, một big intcột thường sẽ "nhảy" 10.000 mỗi lần khởi động lại.
StarPilot

60

Sự cố này xảy ra sau khi khởi động lại SQL Server.

Giải pháp là:

  • Chạy Trình quản lý cấu hình máy chủ SQL .

  • Chọn Dịch vụ máy chủ SQL .

    Trình quản lý cấu hình máy chủ SQL

  • Bấm chuột phải vào Máy chủ SQL và chọn Thuộc tính .

  • Trong cửa sổ mở bên dưới Thông số khởi động , nhập -T272và nhấp vào Thêm , sau đó nhấn nút Áp dụng và khởi động lại.

    Thông số khởi động máy chủ SQL


1
Phương pháp này thực sự hiệu quả, cảm ơn rất nhiều! và như đã nói ở đây , vấn đề này sẽ không được khắc phục trong SQL Server 2012 và trong gói dịch vụ của nó, - chỉ trong phiên bản tiếp theo.
Đoạn

2
Có cách nào để áp dụng cờ theo dõi cho cơ sở dữ liệu cá nhân không? Tôi không muốn thực hiện thay đổi này trên toàn bộ máy chủ vì tôi có cơ sở dữ liệu của bên thứ ba và tôi không chắc điều này sẽ ảnh hưởng đến họ như thế nào.
Ege Ersoz 7/07/2015

1
Tôi đã không làm theo các lý do, nhưng rõ ràng một số người dùng đã phải sử dụng chữ "t" chữ thường để làm cho nó hoạt động. Xem liên kết được đăng bởi Fragment trong bình luận ở trên.
Savage

33

Từ SQL Server 2017+bạn có thể sử dụng CONFIGURATION ALTER DATABASE SCOPED :

IDENTITY_CACHE = {ON | TẮT }

Cho phép hoặc vô hiệu hóa bộ đệm nhận dạng ở cấp cơ sở dữ liệu. Mặc định là BẬT. Bộ nhớ đệm danh tính được sử dụng để cải thiện hiệu suất INSERT trên các bảng có cột Danh tính. Để tránh các lỗ hổng trong các giá trị của cột Danh tính trong trường hợp máy chủ khởi động lại bất ngờ hoặc không thành công với máy chủ thứ cấp, hãy tắt tùy chọn IDENTITY_CACHE. Tùy chọn này tương tự như Cờ theo dõi máy chủ SQL hiện tại, ngoại trừ việc nó có thể được đặt ở cấp cơ sở dữ liệu thay vì chỉ ở cấp máy chủ.

(...)

G. Đặt IDENTITY_CACHE

Ví dụ này vô hiệu hóa bộ đệm nhận dạng.

ALTER DATABASE SCOPED CONFIGURATION SET IDENTITY_CACHE=OFF ;

25

Tôi biết câu trả lời của tôi có thể bị trễ bữa tiệc. Nhưng tôi đã giải quyết theo một cách khác bằng cách thêm một thủ tục lưu trữ khởi động trong SQL Server 2012.

Tạo một thủ tục được lưu trữ sau đây trong DB chính.

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

Sau đó thêm nó vào Start up bằng cách sử dụng cú pháp sau.

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

Đây là một ý tưởng tốt nếu bạn có một vài bảng. nhưng nếu bạn phải làm cho nhiều bảng, phương pháp này vẫn hoạt động nhưng không phải là một ý tưởng tốt.


Ý kiến ​​hay. Nhưng nó không hoạt động cho các bảng phụ thuộc, phải không? Ý tôi là, doest nó sửa các giá trị khóa ngoài?
rom5jp

@ rom5jp Sửa FK không phải là điểm của câu trả lời này. Đó là tất cả về việc sửa giá trị PK tiếp theo có thể của một bảng. Miễn là MAX (id) không có trong bất kỳ FK nào, nó sẽ hoạt động.
Jeyara

14

Đây vẫn là một vấn đề rất phổ biến giữa nhiều nhà phát triển và ứng dụng bất kể kích thước.

Thật không may, các đề xuất ở trên không khắc phục được tất cả các tình huống, ví dụ như Lưu trữ được chia sẻ, bạn không thể dựa vào máy chủ của mình để đặt tham số khởi động -t272.

Ngoài ra, nếu bạn có các bảng hiện có sử dụng các cột nhận dạng này cho các khóa chính, thì việc bỏ các cột đó và tạo lại các cột mới để sử dụng giải pháp trình tự BS là một nỗ lực rất lớn. Cách giải quyết Sequence chỉ tốt nếu bạn đang thiết kế các bảng mới từ đầu trong SQL 2012+

Điểm mấu chốt là, nếu bạn đang sử dụng Sql Server 2008R2, thì hãy TRÊN NÓ. Nghiêm túc, ở lại trên nó. Cho đến khi Microsoft thừa nhận rằng họ đã giới thiệu một lỗi HUGE, vẫn còn tồn tại ngay cả trong Sql Server 2016, thì chúng ta không nên nâng cấp cho đến khi họ sở hữu nó và FIX IT.

Microsoft đã giới thiệu một thay đổi đột phá, tức là họ đã phá vỡ một API hoạt động không còn hoạt động như thiết kế, do thực tế là hệ thống của họ quên danh tính hiện tại của họ khi khởi động lại. Bộ nhớ cache hoặc không có bộ đệm, điều này là không thể chấp nhận được và nhà phát triển Microsoft có tên Bryan cần sở hữu nó, thay vì nói với thế giới rằng đó là "theo thiết kế" và "tính năng". Chắc chắn, bộ nhớ đệm là một tính năng, nhưng mất theo dõi danh tính tiếp theo sẽ là gì, KHÔNG PHẢI LÀ ĐẶC ĐIỂM. Đó là một BUG bực bội !!!

Tôi sẽ chia sẻ cách giải quyết mà tôi đã sử dụng, bởi vì DB của tôi đang ở trên các máy chủ Shared Hosting, đồng thời, tôi sẽ không bỏ và tạo lại các cột Chính của mình, đó sẽ là một PITA khổng lồ.

Thay vào đó, đây là bản hack đáng xấu hổ của tôi (nhưng không đáng xấu hổ như lỗi POS này mà microsoft đã giới thiệu).

Hack / Sửa lỗi:

Trước các lệnh chèn của bạn, chỉ cần kiểm tra lại danh tính của bạn trước mỗi lần chèn. Khắc phục sự cố này chỉ được khuyến nghị nếu bạn không có quyền kiểm soát của quản trị viên đối với phiên bản Sql Server của bạn, nếu không tôi khuyên bạn nên khởi động lại khi khởi động lại máy chủ.

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)

Chỉ cần 3 dòng ngay trước khi chèn của bạn, và bạn nên đi. Nó thực sự sẽ không ảnh hưởng đến hiệu suất nhiều, tức là nó sẽ không được chú ý.

Chúc may mắn.


7

Có nhiều lý do có thể để nhảy giá trị danh tính. Chúng bao gồm từ chèn trở lại để quản lý danh tính để nhân rộng. Điều gì gây ra điều này trong trường hợp của bạn tôi không thể nói mà không dành thời gian trong hệ thống của bạn.

Tuy nhiên, bạn nên biết rằng trong mọi trường hợp, bạn không thể giả sử một cột danh tính là contiguos. Có quá nhiều thứ có thể gây ra những khoảng trống.

Bạn có thể tìm thêm một chút thông tin về điều này tại đây: http://sqlity.net/en/792/the-gap-in-the-identity-value- resultence/

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.