DROP USER mất quá nhiều thời gian khi có nhiều người dùng


11

Trên phiên bản SQL Server 2014 có đủ RAM và đĩa nhanh, có hơn 160 người dùng có quyền truy cập vào cơ sở dữ liệu. Vì một số lý do mà tôi không biết, việc chạy lệnh DROP USER [username]trong cơ sở dữ liệu này mất tối đa 5 giây cho mỗi người dùng.

Việc ánh xạ lại người dùng để đăng nhập và khôi phục quyền của họ rất nhanh.

Trong bối cảnh làm mới cơ sở dữ liệu DEV từ sản xuất, tôi phải loại bỏ và tạo lại tất cả người dùng cơ sở dữ liệu. Vì vậy, có thả người dùng cơ sở dữ liệu và tạo lại chúng là cần thiết.

Làm thế nào để tôi tăng tốc DROP USERlệnh?

Hãy nhớ rằng, tôi phải chạy nó hơn 160 lần cho ví dụ tôi đang viết.

Đây là SQL tôi đang sử dụng:

DECLARE drop_user_cur CURSOR FOR 
SELECT name FROM #drop_users

OPEN drop_user_cur
FETCH NEXT FROM drop_user_cur INTO @user

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @sql = 'use [' + @db_name + '] DROP USER [' + @user + ']'
    BEGIN TRY
        print @sql
        EXECUTE(@sql)
    END TRY
    BEGIN CATCH
        print 'ERREUR : ' + @sql
    END CATCH
    FETCH NEXT FROM drop_user_cur INTO @user
END

CLOSE drop_user_cur
DEALLOCATE drop_user_cur

Vấn đề không đến từ con trỏ; đó là thực tế DROP USERmất tới 5 giây.

Sử dụng sp_whoisactive, Wait_type là NULL.

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

Đừng chú ý đến thời lượng, DROPCREATE USERđã được chạy trong một WHILEvòng lặp, đó là lý do tại sao nó nói hơn một phút.

Profiler hiển thị hơn 125.000 lượt đọc để thực thi DROP USER.

Môi giới dịch vụ không được kích hoạt.


1
@CraigEfrein Bạn có thể phục hồi câu trả lời của bạn. Có vẻ tốt và cảm ơn vì bao gồm nhận xét của tôi trong câu trả lời của bạn. Tôi rất vui khi bạn tìm thấy giải pháp làm việc!
Kin Shah

Câu trả lời:


6

Việc giải quyết vấn đề này là cho phép nhà môi giới dịch vụ trên cơ sở dữ liệu.

ALTER DATABASE [Database_name] SET NEW_BROKER WITH ROLLBACK IMMEDIATE;

Sau khi kích hoạt môi giới dịch vụ cho cơ sở dữ liệu, người dùng thả thực tế là ngay lập tức.

Kin hỏi nếu người môi giới dịch vụ đã được kích hoạt trong một nhận xét trước đó đã gửi cho tôi tìm kiếm đúng hướng.


2

đây không phải là một câu trả lời cho câu hỏi mà là một lý lẽ để loại bỏ nó hoàn toàn.

Nếu tôi hiểu chính xác bình luận của bạn:

Trong bối cảnh làm mới cơ sở dữ liệu DEV từ sản xuất, tôi phải loại bỏ và tạo lại tất cả người dùng cơ sở dữ liệu. Vì vậy, có thả người dùng cơ sở dữ liệu và tạo lại chúng là cần thiết.

Mục tiêu của bạn là sao chép dữ liệu sản xuất vào hệ thống nhà phát triển và để nó hoạt động với quyền tương tự như sản xuất, sử dụng cùng tên người dùng .

Cách nhanh nhất là sao chép thông tin đăng nhập sql từ hệ thống sản xuất lên hệ thống dev bằng cách sử dụng quy trình được mô tả bởi ms trong bài viết kb này .

với thủ tục được xác định bởi ms, kết quả là bạn có thể khôi phục cơ sở dữ liệu trên máy chủ dev của mình và quyền đang hoạt động mà không thay đổi.

Tôi đã sử dụng thành công quy trình được đề cập ở trên để sao chép một env sản xuất sql 2005 vào cả hai môi trường test & dev sql 2012.
Sau khi nhân bản người dùng, một khôi phục cơ sở dữ liệu đơn giản đã đưa tôi đến một cặp hệ thống mới hoạt động hoàn toàn với dữ liệu cập nhật.

Điểm cộng rất lớn của giải pháp đó là để làm mới dữ liệu dev, bạn chỉ cần khôi phục cơ sở dữ liệu sản xuất và đó là nó; bạn sẽ phải điều chỉnh các tham chiếu, từ đồng nghĩa và như vậy nhưng quyền sẽ không bị phá vỡ.

Đây là mã từ bài viết, chỉ để tham khảo, nhưng vui lòng tham khảo bài viết có nhận xét và chi tiết về khả năng tương thích với các phiên bản sql cổ, chi tiết mã hóa mật khẩu và thông tin khác có thể giúp bạn đỡ đau đầu khi chuyển thông tin đăng nhập qua máy chủ:

USE master
GO
IF OBJECT_ID ('sp_hexadecimal') IS NOT NULL
  DROP PROCEDURE sp_hexadecimal
GO
CREATE PROCEDURE sp_hexadecimal
    @binvalue varbinary(256),
    @hexvalue varchar (514) OUTPUT
AS
DECLARE @charvalue varchar (514)
DECLARE @i int
DECLARE @length int
DECLARE @hexstring char(16)
SELECT @charvalue = '0x'
SELECT @i = 1
SELECT @length = DATALENGTH (@binvalue)
SELECT @hexstring = '0123456789ABCDEF'
WHILE (@i <= @length)
BEGIN
  DECLARE @tempint int
  DECLARE @firstint int
  DECLARE @secondint int
  SELECT @tempint = CONVERT(int, SUBSTRING(@binvalue,@i,1))
  SELECT @firstint = FLOOR(@tempint/16)
  SELECT @secondint = @tempint - (@firstint*16)
  SELECT @charvalue = @charvalue +
    SUBSTRING(@hexstring, @firstint+1, 1) +
    SUBSTRING(@hexstring, @secondint+1, 1)
  SELECT @i = @i + 1
END

SELECT @hexvalue = @charvalue
GO

IF OBJECT_ID ('sp_help_revlogin') IS NOT NULL
  DROP PROCEDURE sp_help_revlogin
GO
CREATE PROCEDURE sp_help_revlogin @login_name sysname = NULL AS
DECLARE @name sysname
DECLARE @type varchar (1)
DECLARE @hasaccess int
DECLARE @denylogin int
DECLARE @is_disabled int
DECLARE @PWD_varbinary  varbinary (256)
DECLARE @PWD_string  varchar (514)
DECLARE @SID_varbinary varbinary (85)
DECLARE @SID_string varchar (514)
DECLARE @tmpstr  varchar (1024)
DECLARE @is_policy_checked varchar (3)
DECLARE @is_expiration_checked varchar (3)

DECLARE @defaultdb sysname

IF (@login_name IS NULL)
  DECLARE login_curs CURSOR FOR

      SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM 
sys.server_principals p LEFT JOIN sys.syslogins l
      ON ( l.name = p.name ) WHERE p.type IN ( 'S', 'G', 'U' ) AND p.name <> 'sa'
ELSE
  DECLARE login_curs CURSOR FOR


      SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM 
sys.server_principals p LEFT JOIN sys.syslogins l
      ON ( l.name = p.name ) WHERE p.type IN ( 'S', 'G', 'U' ) AND p.name = @login_name
OPEN login_curs

FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
IF (@@fetch_status = -1)
BEGIN
  PRINT 'No login(s) found.'
  CLOSE login_curs
  DEALLOCATE login_curs
  RETURN -1
END
SET @tmpstr = '/* sp_help_revlogin script '
PRINT @tmpstr
SET @tmpstr = '** Generated ' + CONVERT (varchar, GETDATE()) + ' on ' + @@SERVERNAME + ' */'
PRINT @tmpstr
PRINT ''
WHILE (@@fetch_status <> -1)
BEGIN
  IF (@@fetch_status <> -2)
  BEGIN
    PRINT ''
    SET @tmpstr = '-- Login: ' + @name
    PRINT @tmpstr
    IF (@type IN ( 'G', 'U'))
    BEGIN -- NT authenticated account/group

      SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + @defaultdb + ']'
    END
    ELSE BEGIN -- SQL Server authentication
        -- obtain password and sid
            SET @PWD_varbinary = CAST( LOGINPROPERTY( @name, 'PasswordHash' ) AS varbinary (256) )
        EXEC sp_hexadecimal @PWD_varbinary, @PWD_string OUT
        EXEC sp_hexadecimal @SID_varbinary,@SID_string OUT

        -- obtain password policy state
        SELECT @is_policy_checked = CASE is_policy_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = @name
        SELECT @is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN 'ON' WHEN 0 THEN 'OFF' ELSE NULL END FROM sys.sql_logins WHERE name = @name

            SET @tmpstr = 'CREATE LOGIN ' + QUOTENAME( @name ) + ' WITH PASSWORD = ' + @PWD_string + ' HASHED, SID = ' + @SID_string + ', DEFAULT_DATABASE = [' + @defaultdb + ']'

        IF ( @is_policy_checked IS NOT NULL )
        BEGIN
          SET @tmpstr = @tmpstr + ', CHECK_POLICY = ' + @is_policy_checked
        END
        IF ( @is_expiration_checked IS NOT NULL )
        BEGIN
          SET @tmpstr = @tmpstr + ', CHECK_EXPIRATION = ' + @is_expiration_checked
        END
    END
    IF (@denylogin = 1)
    BEGIN -- login is denied access
      SET @tmpstr = @tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME( @name )
    END
    ELSE IF (@hasaccess = 0)
    BEGIN -- login exists but does not have access
      SET @tmpstr = @tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME( @name )
    END
    IF (@is_disabled = 1)
    BEGIN -- login is disabled
      SET @tmpstr = @tmpstr + '; ALTER LOGIN ' + QUOTENAME( @name ) + ' DISABLE'
    END
    PRINT @tmpstr
  END

  FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
   END
CLOSE login_curs
DEALLOCATE login_curs
RETURN 0
GO

1
Xin chào. Tôi không cố gắng tạo một bản sao chính xác của người dùng từ sản xuất. DEV không thực sự là một bản sao chính xác của sản xuất. Thông tin đăng nhập không sử dụng cùng một mật khẩu và dev không nằm trong cùng một tên miền. Có những ràng buộc khác khiến việc xóa người dùng cơ sở dữ liệu trở nên cần thiết mà tôi sẽ không làm bạn chán.
Craig Efrein

Về mặt bảo mật, bạn KHÔNG BAO GIỜ có mật khẩu từ sản xuất chạm vào môi trường phát triển của bạn. Bạn chỉ cung cấp cho nhà phát triển của mình quyền truy cập vào hoạt động sản xuất đó và hy vọng không ai trong số họ có vai trò quản trị viên bảo mật.

nếu người dùng có mật khẩu thì bạn đang giao dịch với người dùng sql nên tên miền không liên quan. Ngoài ra, mật khẩu không bị khóa để bạn có thể thay đổi chúng ngay khi người dùng được tạo. tập lệnh có thể được thay đổi trước khi chạy nó: loại bỏ tất cả người dùng không mong muốn / không cần thiết và bạn sẽ ổn thôi. đề nghị của tôi không phải là giải pháp cho vấn đề của bạn nhưng có thể sau khi nhân bản, bạn sẽ cần giảm ít người dùng hơn vì không được phép xử lý nữa. không tối ưu nhưng có thể là một sự cải tiến ^^
Paolo

0

Một con trỏ tương tự như vậy sẽ hoạt động

Use DB_name

declare @username varchar(50)
declare user_cursor cursor for select '['+name+']' from sys.database_principals where type in ('u', 's') and principal_id>4
open user_cursor
fetch next from user_cursor into @username
while (@@fetch_status=0)
begin
EXEC sp_dropuser @username
fetch next from user_cursor into @username
end
close user_cursor
deallocate user_cursor
go
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.