SQL Server实例间同步登录用户
SQL Server实例间同步登录用户
问题痛点:由于AlwaysOn和数据库镜像无法同步数据库外实例对象,例如 登录用户、作业、链接服务器等,导致主库切换之后,应用连接不上数据库或者作业不存在导致每晚跑批任务漏跑等
目前来看,作业等其他实例对象的同步还比较难实现,比如作业分为很多步骤,而且作业包含的命令也比较复杂,作业也支持调用其他子系统,比如 PowerShell ,ActiveX,CmdExec等数据库外部程序和命令,用动态SQL方式很难处理
本文主要介绍的是登录用户的同步,毕竟登录用户的重要性还是比较高的,应用需要先通过登录用户登录DB实例才能执行后续的操作
要在SQLServer实例间同步登录用户,主要有几种方法
1、创建操作系统域用户,然后创建基于这个域用户的登录用户,因为域用户在域里面是同步的,但是这种方法前提是需要有域环境,而且普通开发人员一般也没有域控机器权限创建域用户
2、使用外部第三方工具,比如 sqlcmd,PowerShell
3、使用链接服务器 和 动态拼接SQL方法
本文主要使用第三种方法,因为第三种方法本人认为有下面几种优势
1、保证最低维护成本,纯SQL实现,不需要借助第三方工具
2、通用性,几乎所有SQL Server版本都能用,也不需要像第三方工具例如 PowerShell那样有时候需要升级版本
3、兼容性,跨操作系统平台Linux、Windows
4、高可靠性,使用SQLServer自带原生工具,足够简单高效
这个工具脚本的主要流程如下

具体使用步骤
假如有三个AlwaysOn节点,分别是
node1 ip:192.168.10.10
node2 ip:192.168.10.11
node3 ip:192.168.10.12
step1: 创建链接服务器,在所有AlwaysOn节点上创建其他节点的链接服务器,比如在192.168.10.10上创建其他节点链接服务器,下面脚本在192.168.10.10服务器上执行,其他节点以此类推
--create linkedserver
USE [master]
GO DECLARE @IP NVARCHAR(MAX)
DECLARE @Login NVARCHAR(MAX)
DECLARE @PWD NVARCHAR(MAX) SET @Login = N'sa' --★Do
SET @PWD = N'xxxxxx' --★Do
SET @IP ='192.168.10.11,1433' --★Do EXEC master.dbo.sp_addlinkedserver @server = @IP,@srvproduct = N'SQL Server' EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'collation compatible', @optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'data access', @optvalue = N'true'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'dist',@optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'pub',@optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'rpc',@optvalue = N'true'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'rpc out',@optvalue = N'true'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'sub',@optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'connect timeout', @optvalue = N'0'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'collation name', @optvalue = NULL
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'lazy schema validation', @optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'query timeout', @optvalue = N'0'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'use remote collation', @optvalue = N'true'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'connect timeout', @optvalue = N'120'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'query timeout', @optvalue = N'120'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'remote proc transaction promotion',@optvalue = N'true' USE [master]
EXEC master.dbo.sp_addlinkedsrvlogin
@rmtsrvname = @IP,
@locallogin = NULL,
@useself = N'False',
@rmtuser = @Login,
@rmtpassword = @PWD --------------------------------------------------------------------------------------------------------------------------- --create linkedserver
USE [master]
GO DECLARE @IP NVARCHAR(MAX)
DECLARE @Login NVARCHAR(MAX)
DECLARE @PWD NVARCHAR(MAX) SET @Login = N'sa' --★Do
SET @PWD = N'xxxxxx' --★Do
SET @IP ='192.168.10.12,1433' --★Do EXEC master.dbo.sp_addlinkedserver @server = @IP,@srvproduct = N'SQL Server' EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'collation compatible', @optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'data access', @optvalue = N'true'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'dist',@optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'pub',@optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'rpc',@optvalue = N'true'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'rpc out',@optvalue = N'true'
EXEC master.dbo.sp_serveroption @server = @IP, @optname = N'sub',@optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'connect timeout', @optvalue = N'0'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'collation name', @optvalue = NULL
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'lazy schema validation', @optvalue = N'false'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'query timeout', @optvalue = N'0'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'use remote collation', @optvalue = N'true'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'connect timeout', @optvalue = N'120'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'query timeout', @optvalue = N'120'
EXEC master.dbo.sp_serveroption @server = @IP,@optname = N'remote proc transaction promotion',@optvalue = N'true' USE [master]
EXEC master.dbo.sp_addlinkedsrvlogin
@rmtsrvname = @IP,
@locallogin = NULL,
@useself = N'False',
@rmtuser = @Login,
@rmtpassword = @PWD
step2: 创建存储过程,在所有AlwaysOn节点上创建存储过程,记住是所有AlwaysOn节点都要执行
USE [master]
GO
-- =================================================================
-- Author: <steven>
-- Create date: <2021-12-26>
-- Description: <Synchronize login users between multiple SQLServer Instances>
-- =================================================================
create PROCEDURE [dbo].[usp_SyncLoginUserRegularBetweenInstances]
AS
BEGIN IF EXISTS(SELECT 1 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 [hars].[is_local] = 1 AND [hars].[role_desc] = 'PRIMARY'AND [hars].[operational_state_desc] = 'ONLINE'
AND [hars].[synchronization_health_desc] = 'HEALTHY')
BEGIN
----Check for prerequisite, if not present deploy it.
IF NOT EXISTS (SELECT id FROM [master].[dbo].[sysobjects] where name='sp_hexadecimal' and xtype='P')
BEGIN
DECLARE @sp_hexadecimalcreatescript NVARCHAR(3000)
SET @sp_hexadecimalcreatescript = N'
CREATE PROCEDURE [dbo].[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;' EXEC [master].[dbo].sp_executesql @sp_hexadecimalcreatescript
END DECLARE @TempTable TABLE
(id INT IDENTITY ,Script NVARCHAR(MAX))
DECLARE @Login NVARCHAR(MAX)
DECLARE CURLOGIN CURSOR FOR
SELECT name
FROM sys.server_principals
WHERE [type] = 'S' AND [is_disabled] =0 AND [name] <> 'sa'
--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(1500) = @Login
DECLARE @LoginSID VARBINARY(400)
DECLARE @SID_String VARCHAR(1514)
DECLARE @LoginPWD VARBINARY(1256)
DECLARE @PWD_String VARCHAR(1514)
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 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 [master].[dbo].[sp_hexadecimal] @LoginPWD, @PWD_String OUT
EXEC [master].[dbo].[sp_hexadecimal] @LoginSID, @SID_String OUT
SELECT @is_policy_checked = S.is_policy_checked
, @is_expiration_checked = S.is_expiration_checked
FROM sys.sql_logins S
WHERE S.[type] = 'S' AND S.[is_disabled] =0 -- Create Script
SET @Script = @Script + CHAR(13)
+ CHAR(13) + ''''
+ CHAR(13) + 'USE [master];' + CHAR(13)
+ 'IF EXISTS (SELECT name FROM sys.server_principals WHERE name= ''''' + @LoginName + ''''') '
+ CHAR(13) + 'BEGIN '
+ CHAR(13) + CHAR(9) + ' DECLARE @CurrentLoginPWD VARBINARY(512)'
+ CHAR(13) + CHAR(9) + ' DECLARE @CurrentPWD_String VARCHAR(1514)'
+ CHAR(13) + CHAR(9) + ' DECLARE @CurrentLoginSID VARBINARY(400)'
+ CHAR(13) + CHAR(9) + ' DECLARE @CurrentSID_String VARCHAR(1514)'
+ CHAR(13) + CHAR(9) + ' SELECT @CurrentLoginSID = [sid] FROM sys.server_principals WHERE name = '''''+ @LoginName +''''''
+ CHAR(13) + CHAR(9) + ' SET @CurrentLoginPWD =CAST(LOGINPROPERTY(''''' + @LoginName + ''''', ' + '''''PasswordHash''''' +') AS VARBINARY(512))'
+ CHAR(13) + CHAR(9) + ' EXEC [master].[dbo].[sp_hexadecimal] @CurrentLoginPWD , @CurrentPWD_String OUT '
+ CHAR(13) + CHAR(9) + ' EXEC [master].[dbo].[sp_hexadecimal] @CurrentLoginSID, @CurrentSID_String OUT '
+ CHAR(13) + CHAR(9) + ' --Compare two SID if the same '
+ CHAR(13) + CHAR(9) + ' IF ''''' + @SID_String + ''''' = @CurrentSID_String '
+ CHAR(13) + CHAR(9) + ' BEGIN'
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + ' --Compare two password if the same '
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + ' IF ''''' + @PWD_String + ''''' <> @CurrentPWD_String '
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + ' BEGIN'
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + CHAR(9) + '--Just update login user password'
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + CHAR(9) + ' ALTER LOGIN ' + QUOTENAME(@LoginName)
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + CHAR(9) + ' WITH PASSWORD = ' + @PWD_String + ' HASHED'
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + CHAR(9) + ', DEFAULT_DATABASE = [' + @default_database_name + ']'
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + CHAR(9) + ', DEFAULT_LANGUAGE = [' + @default_language_name + ']'
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + CHAR(9) + ', CHECK_POLICY ' + CASE WHEN @is_policy_checked = 0 THEN '=OFF' ELSE '=ON' END
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + CHAR(9) + ', CHECK_EXPIRATION ' + CASE WHEN @is_expiration_checked = 0 THEN '=OFF' ELSE '=ON' END
+ CHAR(13) + CHAR(9) + CHAR(9) + CHAR(9) + ' END'
+ CHAR(13) + CHAR(9) + ' END'
+ CHAR(13) + 'END '
+ CHAR(13) + 'ELSE'
+ CHAR(13) + 'BEGIN '
+ CHAR(13) + CHAR(9) + ' --Create new login user '
+ 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(185)
) ----Prevent multiple records from being inserted into the @ServerRoles table
IF NOT EXISTS (SELECT 1 FROM @ServerRoles )
BEGIN
INSERT INTO @ServerRoles EXEC sp_helpsrvrolemember
END ----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) + ', ' + '''''' + @ServerRole + '''''' FETCH NEXT FROM curRoles
INTO @ServerRole
END --Cleanup.
CLOSE curRoles
DEALLOCATE curRoles
END
SET @Script = @Script + CHAR(13) + ''''
INSERT INTO @TempTable
VALUES(@Script) FETCH NEXT FROM CURLOGIN INTO @Login
END
CLOSE CURLOGIN;
DEALLOCATE CURLOGIN;
SELECT id, Script FROM @TempTable ORDER BY id ------------------------------------------------------------------------------------
--Use linked servers to send scripts to remote machines for execution
--------------------------------------------------------------------------------
DECLARE @LinkedServerName NVARCHAR(512);
DECLARE @DynamicSQL NVARCHAR(MAX);
DECLARE @EXISTSSQL NVARCHAR(2000); DECLARE cursor_linked_servers CURSOR FOR
SELECT name
FROM sys.servers
WHERE is_linked = 1
AND [product]='SQL Server'
AND [provider]='SQLNCLI'
AND [connect_timeout]>0 AND [query_timeout] >0; OPEN cursor_linked_servers;
FETCH NEXT FROM cursor_linked_servers INTO @LinkedServerName;
WHILE @@FETCH_STATUS = 0
BEGIN
--Determine whether the remote machine has the stored procedure call "sp_hexadecimal"
--if not have do not execute the scripts
CREATE TABLE #EXISTSTB(id BIGINT)
SET @EXISTSSQL='SELECT * FROM OPENQUERY('+ QUOTENAME(@LinkedServerName) + ', ''SELECT id FROM [master].[dbo].[sysobjects] WHERE name = ''''sp_hexadecimal'''' AND xtype=''''P'''' '')'
INSERT INTO #EXISTSTB EXEC(@EXISTSSQL)
IF EXISTS(SELECT * FROM #EXISTSTB)
BEGIN
DECLARE @RunSQL NVARCHAR(MAX)
DECLARE CURSYNC CURSOR FOR
SELECT Script FROM @TempTable ORDER BY id OPEN CURSYNC
FETCH NEXT FROM CURSYNC INTO @RunSQL WHILE @@FETCH_STATUS = 0
BEGIN
SET @DynamicSQL = 'EXEC('+ @RunSQL + ') AT ['+ @LinkedServerName +']'
EXEC sp_executesql @DynamicSQL; FETCH NEXT FROM CURSYNC INTO @RunSQL
END; CLOSE CURSYNC
DEALLOCATE CURSYNC
END
DROP TABLE #EXISTSTB FETCH NEXT FROM cursor_linked_servers INTO @LinkedServerName;
END; -- close cursor
CLOSE cursor_linked_servers;
DEALLOCATE cursor_linked_servers; END END
step3: 创建作业定时执行上面的存储过程,在所有AlwaysOn节点上创建作业,记住是所有AlwaysOn节点都要执行,下面脚本默认是60分钟执行一次
USE [msdb]
GO /****** Object: Job [synchronize_loginusers] Script Date: 2023/9/6 15:46:26 ******/
BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT @ReturnCode = 0
/****** Object: JobCategory [Database Maintenance] Script Date: 2023/9/6 15:46:26 ******/
IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'Database Maintenance' AND category_class=1)
BEGIN
EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'Database Maintenance'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback END DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'synchronize_loginusers',
@enabled=1,
@notify_level_eventlog=0,
@notify_level_email=0,
@notify_level_netsend=0,
@notify_level_page=0,
@delete_level=0,
@description=N'Synchronize login users between SQL Server Instances',
@category_name=N'Database Maintenance',
@owner_login_name=N'sa', @job_id = @jobId OUTPUT
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
/****** Object: Step [execute SyncLoginUsers script] Script Date: 2023/9/6 15:46:26 ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'execute SyncLoginUsers script',
@step_id=1,
@cmdexec_success_code=0,
@on_success_action=1,
@on_success_step_id=0,
@on_fail_action=2,
@on_fail_step_id=0,
@retry_attempts=0,
@retry_interval=0,
@os_run_priority=0, @subsystem=N'TSQL',
@command=N'exec [master].[dbo].[usp_SyncLoginUserRegularBetweenInstances] ',
@database_name=N'master',
@flags=0
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobschedule @job_id=@jobId, @name=N'Schedule SyncLoginUsers',
@enabled=1,
@freq_type=4,
@freq_interval=1,
@freq_subday_type=4,
@freq_subday_interval=60,
@freq_relative_interval=0,
@freq_recurrence_factor=0,
@active_start_date=20110224,
@active_end_date=99991231,
@active_start_time=200,
@active_end_time=235959,
@schedule_uid=N'563258f6-0b3f-47bf-b9b3-2f597038cc38'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)'
IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION
EndSave: GO
所有步骤完毕
对于这个自动定时同步登录用户脚本有以下说明
本工具脚本遵循的思想是,不做减法,只做加法,尽可能减少对线上生产环境影响,主要有下面几点:
(1)新增用户:如果从库不存在同步过来的用户,就新建用户
(2)更新用户:如果从库存在同步过来的用户,就会判断用户SID和用户密码是否一样,如果用户SID一样,密码不一样,就更新用户密码
(3)删除用户:如果从库存在同名用户,就不新建用户,否则新建用户,为了尽可能减少对线上生产环境影响,不做删除用户操作,所以如果从库存在同名用户并且用户SID不同,建议手动删除用户由脚本自动同步主库用户过来
无论是新增用户还是更新用户,都会执行添加服务器角色权限的步骤,如果同名用户已经存在当前服务器角色权限,那么再次执行添加服务器角色权限并不会有任何影响,而且本工具脚本并不会删除同名用户的服务器角色权限
最终目的:不做减法,只做加法,尽可能减少对线上生产环境影响
对于这个自动定时同步登录用户脚本有下面几个注意点
1、本工具脚本使用sa用户来创建链接服务器,所以不会同步sa用户,注意如果更改了sa用户密码,也要同步更改链接服务器密码,当然您也可以使用其他有足够权限的用户来创建链接服务器
2、新建登录用户的时候,一定要确保在主库上新建登录用户,否则可能会无法实施数据库角色权限或者无法同步该登录用户
3、新建登录用户时候,登录用户名不要带有特殊字符,例如单引号,否则差异脚本有可能不work
4、这个脚本是针对AlwaysOn集群,当然如果你是数据库镜像环境,你可以改一下代码,把判断AlwaysOn主库的部分改为判断镜像主库就可以了
参考文章
https://maq.guru/synchronizing-sql-server-logins-in-an-always-on-availability-group/
本文版权归作者所有,未经作者同意不得转载。
SQL Server实例间同步登录用户的更多相关文章
- 单用户模式启动SQL Server实例总结
在SQL Server的数据库维护过程中,有时候在一些特殊情况下需要在单用户模式下启动SQL Server实例. 下面总结一下单用户模式启动SQL Server的几种方式: 1:命令模式(sqls ...
- 登录到 SQL Server 实例
登录到 SQL Server 实例(命令提示符) 登录到 SQL Server 的默认实例 从命令提示符输入以下命令,使用 Windows 身份验证进行连接: sqlcmd [ /E ] ...
- SQL SERVER 数据库表同步复制 笔记
SQL SERVER 数据库表同步复制 笔记 同步复制可运行在不同版本的SQL Server服务之间 环境模拟需要两台数据库192.168.1.1(发布),192.168.1.10(订阅) 1.在发布 ...
- (4.22)Microsoft 管理控制台启用 SSL 加密的 SQL Server 实例
如何通过使用 Microsoft 管理控制台启用 SSL 加密的 SQL Server 实例 关键词:MSSQL加密,sql server加密,sql server客户端与服务器传输内容加密 转自:h ...
- SQL server数据库 账户SA登录失败,提示错误:18456
在我们使用数据库的时候,偶尔会遇到一些登录上的错误提示.比如,在数据库配置上没有正确开启用户的登录策略以及服务器身份验证模式时,就会提示"用户'sa'登录失败.(Microsoft SQL ...
- SQL SERVER实例解析
什么是SQL SERVER实例 ------------ SQL SERVER实例的概念和“类与对象”的概念很相似.可以把SQL SERVER的安装程序看做是一个类,安装过程则是创建对象的过程,创建出 ...
- 你所不知道的SQL Server数据库启动过程(用户数据库加载过程的疑难杂症)
前言 本篇主要是上一篇文章的补充篇,上一篇我们介绍了SQL Server服务启动过程所遇到的一些问题和解决方法,可点击查看,我们此篇主要介绍的是SQL Server启动过程中关于用户数据库加载的流程, ...
- 利用PowerUpSQL攻击SQL Server实例
这篇博客简述如何快速识别被第三方应用使用的SQL Server实例,该第三方软件用PowerUpSQL配置默认用户/密码配置.虽然我曾经多次提到过这一话题,但是我认为值得为这一主题写一篇简短的博客,帮 ...
- (4.21)SQL Server数据库启动过程(用户数据库加载过程的疑难杂症)
转自:指尖流淌 http://www.cnblogs.com/zhijianliutang/p/4100103.html SQL Server数据库启动过程(用户数据库加载过程的疑难杂症) 前言 本篇 ...
- 修改sql server实例、数据库、表、字段的排序规则
转自:http://blog.51cto.com/jimshu/1095780 概念与详情请参考:字符编码与排序规则:https://www.cnblogs.com/gered/p/9145123.h ...
随机推荐
- 前端自动识别CAD图纸提取信息方法总结
前言 CAD图纸自动识别和提取信息具有许多意义,包括以下几个方面: 提高工作效率:传统上,对于大量的CAD图纸,人工识别和提取信息是一项耗时且繁琐的任务.通过自动化这一过程,可以大大提高工作效率,节省 ...
- Java关键字break、continue 、return的区别,嵌套循环,数组的概念以及数组案例
一.关键字 break.continue .return的区别 1.break : 用于在switch..case中放置语句块穿透, 用于跳出循环 // 从1-100 遇到7的倍数 break f ...
- Jackson前后端开发模式必备json利器
前言 json是我们现代互联网程序最常用的交互格式,是否你在工作中会遇到前端说字段不一致需要改的需求,是否遇到过数据库字段名与javaBean的规范不同,是否遇到过json与javaBean相互转换时 ...
- 《数据结构》之栈和堆结构及JVM简析
导言: 在数据结构中,我们第一了解到了栈或堆栈,它的结构特点是什么呢?先进后出,它的特点有什么用呢?我们在哪里可以使用到栈结构,栈结构那么简单,使用这么久了为什么不用其它结构替代? 一.程序在内存中的 ...
- 慢 SQL 优化之索引的作用是什么?
前言 本文针对 MySQL 数据库的 InnoDB 存储引擎,介绍其中索引的实现以及索引在慢 SQL 优化中的作用. 本文主要讨论不同场景下索引生效与失效的原因. 慢SQL与索引的关系 慢SQL优化原 ...
- 【WebServer】项目总体流程
1. 项目总体流程: 事件处理模式:采用Epoll边沿触发的IO多路复用技术,模拟Proactor模式: 主线程使用epoll监听与客户端连接的socket,并在主线程中对这些socket执行数据读写 ...
- P8903 [USACO22DEC] Bribing Friends G 看电影
P8903 [USACO22DEC] Bribing Friends G 看电影 目录 P8903 [USACO22DEC] Bribing Friends G 看电影 题目描述 输入格式 输出格式 ...
- Blazor前后端框架Known功能介绍:系统安装激活及自定义
本章介绍系统安装与激活及其自定义功能. 概述 框架内置简单的系统安装功能. 录入企业编码.名称.系统名称.产品密钥.管理员密码信息完成安装. 可自定义高级安装功能,如安装数据库等您产品所需的安装信息. ...
- AI 时代的视频云转码移动端化——更快、更好、更低、更广
编者按: AI技术的落地是渐渐地从服务器端.云端落地,逐步到移动端及边缘设备上.这些年随着AI技术的进步,轻量级算法模型开始在移动端实时跑起来,并且移动端算法也在不断进行迭代和完善,而对于实时直播场景 ...
- 如何使用CCXT交易数字货币现货
更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. 数字货币现货标准化接口 数字货币市场与股票.期货市场最大的不同点在于数字货币主流交易所数量很多.举个例子 ...