많은 사용자가있을 때 DROP USER가 너무 오래 걸립니다


11

충분한 RAM과 빠른 디스크가있는 SQL Server 2014 인스턴스에는 데이터베이스에 액세스 할 수있는 160 명 이상의 사용자가 있습니다. 나에게 알려지지 않은 이유로이 DROP USER [username]데이터베이스 에서 명령 을 실행하는 데 사용자 당 최대 5 초가 걸립니다.

사용자를 다시 로그인하여 권한을 복원하는 것은 매우 빠릅니다.

프로덕션에서 DEV 데이터베이스를 새로 고치려면 모든 데이터베이스 사용자를 삭제하고 다시 작성해야합니다. 따라서 데이터베이스 사용자를 삭제하고 다시 작성해야합니다.

DROP USER명령 속도를 높이려면 어떻게합니까 ?

내가 쓰고있는 인스턴스에 대해 160 번 이상 실행해야합니다.

이것은 내가 사용하는 SQL입니다.

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

커서에서 문제가 발생하지 않습니다. 실제 DROP USER최대 5 초가 소요됩니다.

을 사용 sp_whoisactive하면 wait_type은 NULL입니다.

여기에 이미지 설명을 입력하십시오

기간에주의를 지불하지 마십시오 DROPCREATE USERA의 실행되고 있었다 WHILE더 분도 말하는 이유는 루프.

프로파일 러는 125,000 개가 넘는 읽기를 실행으로 표시 DROP USER합니다.

Service Broker가 활성화되어 있지 않습니다.


1
@CraigEfrein 답변을 삭제 취소 할 수 있습니다. 답변에 내 의견을 포함시켜 주셔서 감사합니다. 솔루션이 작동하는 것을 알게되어 기쁩니다!
Kin Shah

답변:


6

이 문제의 해결은 데이터베이스에서 Service Broker를 활성화하는 것이 었습니다.

ALTER DATABASE [Database_name] SET NEW_BROKER WITH ROLLBACK IMMEDIATE;

데이터베이스에 서비스 브로커를 활성화 한 후 드롭 사용자는 실제로 즉각적이었습니다.

Kin은 서비스 브로커가 이전 의견에서 사용 가능한지 물었고 올바른 방향으로 검색했습니다.


2

이것은 질문에 대한 답변이 아니라 그것을 완전히 무시한다는 주장입니다.

귀하의 의견을 올바르게 이해하면

프로덕션에서 DEV 데이터베이스를 새로 고치려면 모든 데이터베이스 사용자를 삭제하고 다시 작성해야합니다. 따라서 데이터베이스 사용자를 삭제하고 다시 작성해야합니다.

목표는 프로덕션 데이터를 개발 시스템에 복사 하고 동일한 사용자 이름을 사용하여 프로덕션 시스템과 동일한 권한으로 작업하는 것 입니다.

가장 빠른 경로는 이 KB 기사의 ms에 설명 된 절차를 사용하여 프로덕션 시스템에서 dev 시스템으로 SQL 로그인을 복제하는 것 입니다.

ms로 정의 된 절차를 사용하면 개발자 서버에서 데이터베이스를 복원 할 수 있으며 권한이 변경없이 작동합니다.

위의 절차를 성공적으로 사용하여 SQL 2005 프로덕션 환경을 테스트 및 개발자 SQL 2012 환경에 복사했습니다.
사용자를 복제 한 후 간단한 데이터베이스 복원으로 최신 데이터가 포함 된 완전히 작동하는 새로운 시스템 쌍이 생겼습니다.

이 솔루션의 가장 큰 장점은 프로덕션 데이터베이스를 복원하기 만하면 개발 데이터를 새로 고칠 수 있다는 것입니다. 참조, 동의어 등을 조정해야하지만 권한은 손상되지 않습니다.

다음은 참조 용으로 기사의 코드이지만, 고대 SQL 버전과의 호환성에 대한 설명과 세부 사항, 암호 암호화 세부 사항 및 서버간에 로그인을 전송하는 동안 두통을 덜 수있는 기타 정보가있는 기사를 참조하십시오.

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
안녕하십니까. 프로덕션에서 사용자의 정확한 사본을 만들려고하지 않습니다. DEV는 실제로 정확한 프로덕션 사본이 아닙니다. 로그인은 동일한 비밀번호를 사용하지 않으며 dev는 동일한 도메인 내에 있지 않습니다. 내가 당신에게 지루하지 않을 데이터베이스 사용자를 삭제 해야하는 다른 제약이 있습니다.
Craig Efrein 2016 년

보안 측면에서 프로덕션 환경의 비밀번호가 개발 환경에 절대로 영향을 미치지 않아야합니다. 방금 개발자에게 프로덕션 환경에 대한 액세스 권한을 제공했으며 보안 관리자 역할을 갖지 않기를 바랍니다.

사용자가 암호를 가지고 있다면 SQL 사용자를 다루므로 도메인이 관련이 없습니다. 또한 비밀번호가 잠기지 않으므로 사용자가 작성되는 즉시 비밀번호를 변경할 수 있습니다. 스크립트는 실행하기 전에 변경 될 수 있습니다. 원치 않는 / 필요하지 않은 모든 사용자를 제거하십시오. 내 제안은 문제의 해결책이 아니지만 복제 후에는 더 이상 권한을 처리하지 않기 때문에 더 적은 사용자를 삭제해야합니다. 최적은 아니지만 개선 일 것입니다 ^^
Paolo

0

이와 비슷한 커서가 작동합니다

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.