การเข้าสู่ระบบไม่ได้ซิงค์กับกลุ่มความพร้อมใช้งาน


13

เรามี 2 เซิร์ฟเวอร์ในกลุ่ม AlwaysOn

ในขณะที่บัญชีผู้ใช้ภายในแต่ละฐานข้อมูลที่ซิงโครไนซ์มีอยู่บนทั้งสองเซิร์ฟเวอร์ แต่อินสแตนซ์ระดับฐานข้อมูลจะมีอยู่ในหนึ่งในเซิร์ฟเวอร์เท่านั้น เช่น DBINSTANCE-> Security-> การเข้าสู่ระบบจะหายไปในเซิร์ฟเวอร์เดียว

ดังนั้นเมื่อมีความล้มเหลวฉันได้รับความล้มเหลวในการเข้าสู่ระบบบนเซิร์ฟเวอร์ที่สอง (ซึ่งไม่มีการเข้าสู่ระบบในระดับอินสแตนซ์ที่เกี่ยวข้อง)

ฉันจะเอาชนะปัญหานี้ได้อย่างไร ฉันควรจะตั้งค่าบัญชีผู้ใช้ด้วยวิธีพิเศษหรือไม่?


ผมได้เริ่มต้นมองหาที่หนึ่งเมื่อเร็ว ๆ นี้เกินไปผมรีดของตัวเองและกำลังอธิบายไว้ในบล็อกใหม่ของฉัน (ส่วนใหญ่เพื่อประโยชน์ของตัวเองเป็นที่ที่ฉันไปข้างหน้าและลืมสิ่ง แต่ถ้ามันช่วยให้คนอื่น ๆ ที่ดีกว่า) ชีวิต -and-dev.blogspot.co.uk/2015/04/…สิ่งนี้และคู่ต่อไปนี้จะแสดง AlwaysOn การติดตามการเฟลโอเวอร์และงาน / การซิงค์ผู้ใช้

คำตอบ:


15

ความเข้าใจของฉันคือถ้าคุณไม่ได้ใช้ฐานข้อมูลที่มีอยู่คุณจะต้องตรวจสอบให้แน่ใจว่ามีการสร้างการเข้าสู่ระบบในอินสแตนซ์อื่นด้วยตนเอง

สิ่งที่ต้องการสคริปต์นี้จากSQLSoldierซึ่งโพสต์ครั้งแรกเป็นการถ่ายโอนการเข้าสู่ระบบไปยังฐานข้อมูลกระจกควรทำเคล็ดลับ


ฉันรอคอยที่จะแปลงฐานข้อมูลการผลิต '05 หลายรายการของเราให้เป็นรูปแบบที่บรรจุใหม่เมื่อเราเปลี่ยนเป็น '12 ในสองหรือสามสัปดาห์ถัดไป ในระหว่างนี้ฉันใช้เครื่องมือของ Idera เพื่อคัดลอกข้อมูลล็อกอินงาน ฯลฯ ไปยังอินสแตนซ์สำรอง
Max Vernon

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

โปรดทราบว่าฉันกำลังถามเกี่ยวกับ AlwaysOn ไม่ใช่การสะท้อน ฉันลองใช้สคริปต์อย่างไรก็ตามตาราง sys.servers มีเฉพาะเซิร์ฟเวอร์ในตัวเท่านั้นดังนั้นฉันจึงได้รับข้อผิดพลาดต่อไปนี้เมื่อฉันเรียกใช้การดำเนินการที่เก็บไว้: Msg 7202, Level 11, State 2, Line 1 Could not find server 'otherserver' in sys.servers. Verify that the correct server name was specified. If necessary, execute the stored procedure sp_addlinkedserver to add the server to sys.servers.
John

ฉันคิดออกแล้วโพสต์คำตอบใหม่ที่เหมาะสมสำหรับ AlwaysOn
จอห์น

@JohnHughes สคริปต์นี้เหมาะสำหรับ AlwaysOn อย่างไรก็ตามคุณจะต้องสร้างเซิร์ฟเวอร์ที่เชื่อมโยงระหว่างอินสแตนซ์ตามที่ระบุโดยข้อความแสดงข้อผิดพลาด
Mark Storey-Smith

7

คุณต้องใช้ฐานข้อมูลที่มีอยู่หรือคุณต้องสร้างผู้ใช้บนเซิร์ฟเวอร์อื่นด้วยแฮชรหัสผ่านเดียวกันและ SID

Microsoft มีการจัดทำสคริปต์ให้: วิธีการถ่ายโอนการเข้าสู่ระบบและรหัสผ่านระหว่างอินสแตนซ์ของ SQL Server

โซลูชันของ Mark ถูกต้องเพียงบางส่วน แต่โซลูชันที่แนะนำของเขานั้นมีไว้สำหรับฐานข้อมูลมิเรอร์ซึ่งตรงข้ามกับ AlwaysOn ซึ่งเป็นคำถามที่ถาม


2
โซลูชันสำหรับฐานข้อมูลที่มิร์เรอร์แตกต่างจากโซลูชันสำหรับกลุ่มความพร้อมใช้งานอย่างไร (หมายเหตุ: AlwaysOn เป็นป้ายกำกับทางการตลาดสำหรับเทคโนโลยีจำนวนมากไม่ใช่คุณสมบัติ) ชื่อของโพสต์บล็อกไม่ได้เปลี่ยนความจริงที่ว่ามันเป็นการกู้คืนการล็อกอินที่ขาดหายไปซึ่งในตัวมันเองและไม่มีอะไรเกี่ยวข้องกับว่าคุณกำลังใช้การมิเรอร์กลุ่มความพร้อมใช้งานการจัดส่งบันทึกสำรอง / คืนด้วยตนเอง ฯลฯ
Aaron Bertrand

น่าเสียดายที่ฉันไม่สามารถใช้บัญชี AD ได้เสมอ เรากำลังดิ้นรนกับวิธีการเปรียบเทียบรหัสผ่าน เราไม่ถูกต้องโดยสมมติว่าหากรหัสผ่านไม่ตรงกันความล้มเหลวระหว่างเซิร์ฟเวอร์เรพลิกาจะล้มเหลว

1

ฉันตอบกลับโพสต์เป็นเวลานาน แต่อาจช่วยคนอื่นในเรื่องที่คล้ายกัน PowerShell สามารถใช้เพื่อคัดลอกข้อมูลเข้าสู่ระบบจากแบบจำลองหลักไปยังแบบจำลองรอง รายละเอียดสามารถพบได้ที่นี่https://maq.guru/synchronizing-sql-server-logins-in-an-always-on-availability-group/

การเปิดเผยแบบเต็ม: ฉันเป็นเจ้าของเว็บไซต์ดังกล่าว

สคริปต์ PowerShell:

$Conn=New-Object System.Data.SqlClient.SQLConnection
$QueryTimeout = 120
$ConnectionTimeout = 30

###########################################################
# Execute Query function 
###########################################################
Function executequery($Query, $QueryTimeout, $ServerName)
{
    $Datatable = New-Object System.Data.DataTable
    $ConnectionString = "Server={0};Database={1};Integrated Security=True;Connect Timeout={2}" -f $ServerName,$Database,$ConnectionTimeout
    $Conn.ConnectionString=$ConnectionString
    $Cmd=New-Object system.Data.SqlClient.SqlCommand($Query,$Conn)
    $Cmd.CommandTimeout=$QueryTimeout

            do
                {   
                    $Conn.Open()
                    Start-Sleep -Seconds 2
                }while ($Conn.State -ne 'Open')

            $Reader = $cmd.ExecuteReader()
            $Datatable.Load($Reader)
            $Conn.Close()
            return $Datatable    
}


###########################################################
# Create spHexaDecimal Stored Procedure
###########################################################

Function CreatespHexaDecimal ($ServerName)
    {
    $Query='USE [master];
                GO
                SET ANSI_NULLS ON;
                GO
                SET QUOTED_IDENTIFIER ON;
                GO
                CREATE PROCEDURE [dbo].[spHexaDecimal]
                (
                    @BinValue VARBINARY(256)
                    , @HexValue VARCHAR(514) OUTPUT
                )
                AS

                DECLARE @CharValue VARCHAR(514)
                DECLARE @i INT
                DECLARE @Length INT
                DECLARE @HexString CHAR(16)

                SET @CharValue = ''0x''
                SET @i = 1
                SET @Length = DATALENGTH(@BinValue)
                SET @HexString = ''0123456789ABCDEF''

                WHILE (@i <= @Length)
                BEGIN

                    DECLARE @TempInt INT
                    DECLARE @FirstInt INT
                    DECLARE @SecondInt INT

                    SET @TempInt = CONVERT(INT, SUBSTRING(@BinValue, @i, 1))
                    SET @FirstInt = FLOOR(@TempInt/16)
                    SET @SecondInt = @TempInt - (@FirstInt * 16)
                    SET @CharValue = @CharValue 
                                        + SUBSTRING(@HexString, @FirstInt + 1, 1)
                                        + SUBSTRING(@HexString, @SecondInt + 1, 1)

                    SET @i = @i + 1

                END --WHILE (@i <= @Length)

                SET @HexValue = @CharValue'

                Invoke-Sqlcmd -Query $Query -ServerInstance $ServerName
    }


###########################################################
# CheckStroedProc 
###########################################################

Function CheckStoredProc ($Server)
{
    $Query= 'SELECT 1 AS ExistCheck
             FROM   sysobjects 
             WHERE  id = object_id(N''[dbo].[spHexaDecimal]'') 
                 AND OBJECTPROPERTY(id, N''IsProcedure'') = 1 '

    $Result=executequery $Query $QueryTimeout $Server
    $Exist=$Result | SELECT -ExpandProperty ExistCheck
    IF ($Exist -ne 1)
        {
            CreatespHexaDecimal -ServerName $Server
        }
}

###########################################################
# Get Login Script
###########################################################

Function Get-Script ($Server)
{

$Query='DECLARE @TempTable TABLE
(Script NVARCHAR(MAX))
DECLARE @Login NVARCHAR (MAX)
DECLARE CURLOGIN CURSOR FOR
SELECT name 
FROM sys.server_principals
WHERE CONVERT(VARCHAR(24),create_date,103) = CONVERT(VARCHAR(24),GETDATE(),103)
    OR CONVERT(VARCHAR(24),modify_date,103) = CONVERT(VARCHAR(24),GETDATE(),103)

OPEN CURLOGIN
    FETCH NEXT FROM CURLOGIN INTO @Login

WHILE @@FETCH_STATUS = 0
BEGIN
    SET NOCOUNT ON
    DECLARE @Script NVARCHAR (MAX)
    DECLARE @LoginName VARCHAR(500)= @Login
    DECLARE @LoginSID VARBINARY(85)
    DECLARE @SID_String VARCHAR(514)
    DECLARE @LoginPWD VARBINARY(256)
    DECLARE @PWD_String VARCHAR(514)
    DECLARE @LoginType CHAR(1)
    DECLARE @is_disabled BIT
    DECLARE @default_database_name SYSNAME
    DECLARE @default_language_name SYSNAME
    DECLARE @is_policy_checked BIT
    DECLARE @is_expiration_checked BIT
    DECLARE @createdDateTime DATETIME



    SELECT @LoginSID = P.[sid]
        , @LoginType = P.[type]
        , @is_disabled = P.is_disabled 
        , @default_database_name = P.default_database_name 
        , @default_language_name = P.default_language_name 
        , @createdDateTime = P.create_date 
    FROM sys.server_principals P
    WHERE P.name = @LoginName

    /** Some Output **/
    SET @Script = ''''




    --If the login is a SQL Login, then do a lot of stuff...
    IF @LoginType = ''S''
    BEGIN

        SET @LoginPWD = CAST(LOGINPROPERTY(@LoginName, ''PasswordHash'') AS VARBINARY(256))

        EXEC spHexaDecimal @LoginPWD, @PWD_String OUT   
        EXEC spHexaDecimal @LoginSID, @SID_String OUT

        SELECT @is_policy_checked = S.is_policy_checked
            , @is_expiration_checked = S.is_expiration_checked
        FROM sys.sql_logins S

        /** Create Script **/
        SET @Script = @Script + CHAR(13) + CHAR(13)
                        + ''IF EXISTS (SELECT name FROM sys.server_principals WHERE name= ''''''+ @LoginName + '''''') '' 
                        + CHAR(13) + '' BEGIN ''
                        + CHAR(13) + CHAR(9) + '' ALTER LOGIN '' + QUOTENAME(@LoginName)
                        + CHAR(13) + CHAR(9) + ''WITH PASSWORD = '' + @PWD_String + '' HASHED''
                        + CHAR(13) + CHAR(9) + '', DEFAULT_DATABASE = ['' + @default_database_name + '']''
                        + CHAR(13) + CHAR(9) + '', DEFAULT_LANGUAGE = ['' + @default_language_name + '']''
                        + CHAR(13) + CHAR(9) + '', CHECK_POLICY '' + CASE WHEN @is_policy_checked = 0 THEN ''=OFF'' ELSE ''=ON'' END
                        + CHAR(13) + CHAR(9) + '', CHECK_EXPIRATION '' + CASE WHEN @is_expiration_checked = 0 THEN ''=OFF'' ELSE ''=ON'' END
                        + CHAR(13) + '' END ''
                        + CHAR(13) + ''ELSE''
                        + CHAR(13) + '' BEGIN ''
                        + CHAR(13) + CHAR(9) + '' CREATE LOGIN '' + QUOTENAME(@LoginName)
                        + CHAR(13) + CHAR(9) + ''WITH PASSWORD = '' + @PWD_String + '' HASHED''
                        + CHAR(13) + CHAR(9) + '', SID = '' + @SID_String
                        + CHAR(13) + CHAR(9) + '', DEFAULT_DATABASE = ['' + @default_database_name + '']''
                        + CHAR(13) + CHAR(9) + '', DEFAULT_LANGUAGE = ['' + @default_language_name + '']''
                        + CHAR(13) + CHAR(9) + '', CHECK_POLICY '' + CASE WHEN @is_policy_checked = 0 THEN ''=OFF'' ELSE ''=ON'' END
                        + CHAR(13) + CHAR(9) + '', CHECK_EXPIRATION '' + CASE WHEN @is_expiration_checked = 0 THEN ''=OFF'' ELSE ''=ON'' END
                        + CHAR(13) + '' END ''

        SET @Script = @Script + CHAR(13) + CHAR(13)
                        + '' ALTER LOGIN ['' + @LoginName + '']''
                        + CHAR(13) + CHAR(9) + ''WITH DEFAULT_DATABASE = ['' + @default_database_name + '']''
                        + CHAR(13) + CHAR(9) + '', DEFAULT_LANGUAGE = ['' + @default_language_name + '']''

    END
    ELSE
    BEGIN

        --The login is a NT login (or group).
        SET @Script = @Script + CHAR(13) + CHAR(13)
                        + ''IF NOT EXISTS (SELECT name FROM sys.server_principals WHERE name= ''''''+ @LoginName + '''''') '' 
                        + CHAR(13) + '' BEGIN ''
                        + CHAR(13) + CHAR(9) + '' CREATE LOGIN '' + QUOTENAME(@LoginName) + '' FROM WINDOWS''
                        + CHAR(13) + CHAR(9) + ''WITH DEFAULT_DATABASE = ['' + @default_database_name + '']''
                        + CHAR(13) + '' END ''
    END

    /******************************************************************************************/
    --This section deals with the Server Roles that belong to that login...
    /******************************************************************************************/

    DECLARE @ServerRoles TABLE
        (
        ServerRole SYSNAME
        , MemberName SYSNAME
        , MemberSID VARBINARY(85)
        )

    INSERT INTO @ServerRoles EXEC sp_helpsrvrolemember

    --Remove all Roles
    SET @Script = @Script + CHAR(13)
    SET @Script = @Script 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''sysadmin''''''
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''securityadmin''''''
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''serveradmin'''''' 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''setupadmin'''''' 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''processadmin''''''
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''diskadmin'''''' 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''dbcreator'''''' 
                            + CHAR(13) + ''EXEC sp_dropsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + ''''''bulkadmin'''''' 

    /** Output to script... **/
    --SET @Script = @Script + CHAR(13) + CHAR(13)

    --Test if there are any server roles for this login...
    IF EXISTS(SELECT 1 FROM @ServerRoles WHERE MemberName = @LoginName)
    BEGIN

        SET @Script = @Script + CHAR(13)

        DECLARE @ServerRole SYSNAME
        DECLARE curRoles CURSOR LOCAL STATIC FORWARD_ONLY

        FOR SELECT ServerRole 
            FROM @ServerRoles
            WHERE MemberName = @LoginName

        OPEN curRoles

        FETCH NEXT FROM curRoles
        INTO @ServerRole

        WHILE @@FETCH_STATUS = 0
        BEGIN

            /** Output to Script **/
            SET @Script = @Script 
                            + CHAR(13) + ''EXEC sp_addsrvrolemember '' + QUOTENAME(@LoginName, '''''''') + '', '' + QUOTENAME(@ServerRole, '''''''')

            FETCH NEXT FROM curRoles
            INTO @ServerRole

        END

        --Cleanup.
        CLOSE curRoles
        DEALLOCATE curRoles

    END
    INSERT INTO @TempTable
    VALUES(@Script)

    FETCH NEXT FROM CURLOGIN INTO @Login
END
CLOSE CURLOGIN;
DEALLOCATE CURLOGIN;
SELECT Script FROM @TempTable'

$Result=executequery $Query $QueryTimeout $Server

If($Result -eq $null)
    {
        break
    }
Else
    {
        [Void][System.IO.Directory]::CreateDirectory("C:\temp")
        $Path = "C:\temp"
        $Acl = (Get-Item $Path).GetAccessControl('Access')
        $Username = Get-WmiObject win32_service | Where name -EQ 'SQLSERVERAGENT' | Select -ExpandProperty StartName
        $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($Username, 'Full', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
        $Acl.SetAccessRule($Ar)
        Set-Acl -path $Path -AclObject $Acl
        $Result | select -ExpandProperty Script | Out-File C:\temp\Script.txt
    }
}


###########################################################
# SCRIPT BODY 
###########################################################

$Query= "SELECT ISNULL(SERVERPROPERTY ('InstanceName'), 'DEFAULT') InstanceName 
            , name AGName
            , replica_server_name Replica
            , role_desc 
            FROM sys.dm_hadr_availability_replica_states hars 
            INNER JOIN sys.availability_groups ag ON ag.group_id = hars.group_id 
            INNER JOIN sys.availability_replicas ar ON ar.replica_id = hars.replica_id
            WHERE role_desc = 'PRIMARY'
            ORDER BY role_desc asc"
Write-Host "Is this Primary Replica?"
$Result=executequery $Query $QueryTimeout $PrimaryReplica
If ($Result -eq $null)
    {
        Write-Host "No, it's not."
        break
    }
Else
    {
        Write-Host "Yes, it is."
        $PrimaryReplica= $Result | select -ExpandProperty Replica
        Write-Host "Check for prerequisite, if not present deploy it."
        CheckStoredProc -Server $PrimaryReplica
        Write-Host "Get script for new/modifies login(s)."
        Get-Script -Server $PrimaryReplica

        $Query= "SELECT ISNULL(SERVERPROPERTY ('InstanceName'), 'DEFAULT') InstanceName 
                    , name AGName
                    , replica_server_name Replica
                    , role_desc 
                    FROM sys.dm_hadr_availability_replica_states hars 
                    INNER JOIN sys.availability_groups ag ON ag.group_id = hars.group_id 
                    INNER JOIN sys.availability_replicas ar ON ar.replica_id = hars.replica_id
                    WHERE role_desc = 'SECONDARY'
                    ORDER BY role_desc asc"

        $Result=executequery $Query $QueryTimeout $PrimaryReplica
        $SecondaryReplicas= $Result | select -ExpandProperty Replica
        $Query= Get-Content -Path 'C:\temp\Script.txt' | Out-String
        ForEach($SecondaryReplica in $SecondaryReplicas)
            {
                Invoke-Sqlcmd -Query $Query -ServerInstance $SecondaryReplica
                Write-Host "Successfully copied login(s) to $SecondaryReplica"
            }
        Remove-Item C:\temp\Script.txt
    }

2
ขอบคุณมูฮัมหมัดคุณสามารถรวมเนื้อหาบางส่วนของลิงก์ได้หรือไม่? หากลิงค์เสียชีวิตคำถามของคุณจะมีประโยชน์น้อยลงอย่างมาก!
LowlyDBA

@ LowlyDBA ฉันพยายามใส่สคริปต์ที่นี่ แต่น่าเสียดายที่มันยาวเกินไป :( ลิงก์จะไม่ตายเหมือนที่ฉันเป็นเจ้าของเว็บไซต์นี้
Muhammad

สคริปต์ไม่นานมาก ฉันจะเพิ่มให้คุณ และในกรณีนี้คุณต้องเปิดเผยความร่วมมือเมื่อโพสต์ลิงก์ของคุณเอง
LowlyDBA

ขอโทษ @LowlyDBA ฉันไม่ทราบเกี่ยวกับการเปิดเผย ฉันพยายามใส่สคริปต์ที่นี่ในส่วนความคิดเห็น แต่ จำกัด ไว้เฉพาะบางตัวอักษร ฉันได้เพิ่มสคริปต์ที่สมบูรณ์ ขอบคุณ
มูฮัมหมัด

น่าเสียดายที่การซิงค์ระดับเซิร์ฟเวอร์ไม่ใช่ส่วนหนึ่งของ AG คำตอบของคุณจะได้รับการโหวตให้บทความเรียก MS ออกมา
youcantryreachingme

-1

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

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