Eventos
31 mar, 23 - 2 abr, 23
Evento de aprendizaje de SQL, Fabric y Power BI más grande. 31 de marzo – 2 de abril. Use el código FABINSIDER para ahorrar $400.
Regístrate hoyEste explorador ya no se admite.
Actualice a Microsoft Edge para aprovechar las características y actualizaciones de seguridad más recientes, y disponer de soporte técnico.
Se aplica a: SQL Server: solo Windows
Este artículo le guía a través de una configuración completa de un recurso DTC agrupado para un grupo de disponibilidad Always On de SQL Server. La configuración completa puede tardar una hora en completarse.
En el tutorial se crea un recurso DTC agrupado y los grupos de disponibilidad de SQL Server para adaptarlos a los requisitos de Cómo agrupar en clústeres el servicio DTC para un grupo de disponibilidad Always On.
El tutorial usa scripts de PowerShell y Transact-SQL (T-SQL). Muchos de los scripts de T-SQL requieren que la opción Modo SQLCMD esté habilitada. Para obtener más información sobre el modo sqlcmd, vea Editar scripts SQLCMD con el Editor de Power Query. El módulo de PowerShell FailoverClusters
debe importarse. Para obtener más información sobre la importación de un módulo de PowerShell, vea Importación de un módulo de PowerShell. Este tutorial se basa en las siguientes opciones de configuración:
contoso.lab
.sqlbackups
para las copias de seguridad.SQLNODE1
y SQLNODE2
.presume commit
.5022
.Cluster
Cluster Network 1
SQLNODE1, SQLNODE2
Cluster Disk 3
(propiedad de SQLNODE1
)DTCnet1
DTC1
DTCDisk1
192.168.2.54
, 255.255.255.0
DTCIP1
Para transacciones distribuidas compatibles, Grupos de disponibilidad Always On se debe ejecutar en Windows Server 2012 R2 o posterior. Para Windows Server 2012 R2, debe instalar la actualización de KB3090973. Este script comprueba la versión del sistema operativo y si es necesario instalar la revisión 3090973. Ejecute el siguiente script de PowerShell en SQLNODE1
.
# A few OS checks
<#
Script:
1) is re-runnable
2) will work on any node in the cluster, and
3) will execute against every node in the cluster
#>
$nodes = (Get-ClusterNode).Name;
foreach ($node in $nodes) {
# At least 2012 R2
$os = (Get-WmiObject -class Win32_OperatingSystem -ComputerName $node).caption;
if ($os -like "*2012 R2*" -or $os -like "*2016*" -or $os -like "*2019*" -or $os -like "*2022*") {
Write-Host "$os is supported on $node.";
}
else {
Write-Host "STOP! $os is not supported on $node.";
}
# KB 3090973
if ($os -like "*2012 R2*") {
$kb = Get-Hotfix -ComputerName $node | Where { $_.HotFixID -eq 'KB3090973' };
if ($kb) {
Write-Host "KB3090973 is installed on $node."
}
else {
Write-Host "HotFixID KB3090973 must be applied on $node. See https://support.microsoft.com/help/3090973 for additional information and to download the hotfix.";
}
}
else {
Write-Host "KB3090973 does not apply to $os on $node."
}
}
Este script configura el firewall para permitir el tráfico DTC en cada servidor SQL Server que hospeda una réplica del grupo de disponibilidad y en cualquier otro servidor implicado en la transacción distribuida. El script también configura el servidor de seguridad de modo que permita las conexiones del punto de conexión de creación de reflejos de la base de datos. Ejecute el siguiente script de PowerShell en SQLNODE1
.
# Configure Firewall
<#
Script:
1) is re-runnable
2) will work on any node in the cluster, and
3) will execute against every node in the cluster
#>
$nodes = (Get-ClusterNode).Name;
foreach ($node in $nodes) {
Get-NetFirewallRule -CimSession $node -DisplayGroup 'Distributed Transaction Coordinator' -Enabled False -ErrorAction SilentlyContinue | Enable-NetFirewallRule;
New-NetFirewallRule -CimSession $node -DisplayName 'SQL Server Mirroring' -Description 'Port 5022 for SQL Server Mirroring' -Action Allow -Direction Inbound -Protocol TCP -LocalPort 5022 -RemotePort Any -LocalAddress Any -RemoteAddress Any;
};
Este script establece la opción de configuración de servidor in-doubt xact resolution para "suponer la confirmación" para las transacciones dudosas.
Ejecute el siguiente script T-SQL en SQL Server Management Studio (SSMS) contra SQLNODE1
en el modo SQLCMD.
/*******************************************************************
Execute script in its entirety on SQLNODE1 in SQLCMD mode
*******************************************************************/
-- Configure in-doubt xact resolution on all SQL Server instances to presume commit
IF (
SELECT CAST(value_in_use AS BIT)
FROM sys.configurations WITH (NOLOCK)
WHERE [name] = N'show advanced options'
) = 0
BEGIN
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
END
-- Configure the server to presume commit for in-doubt transactions.
IF (
SELECT CAST(value AS BIT)
FROM sys.configurations WITH (NOLOCK)
WHERE [name] = N'in-doubt xact resolution'
) <> 1
BEGIN
EXEC sp_configure 'in-doubt xact resolution', 1;
RECONFIGURE;
END
GO
-----------------------------------------------------------------------------
:connect SQLNODE2
IF (
SELECT CAST(value_in_use AS BIT)
FROM sys.configurations WITH (NOLOCK)
WHERE [name] = N'show advanced options'
) = 0
BEGIN
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
END
-- Configure the server to presume commit for in-doubt transactions.
IF (
SELECT CAST(value AS BIT)
FROM sys.configurations WITH (NOLOCK)
WHERE [name] = N'in-doubt xact resolution'
) <> 1
BEGIN
EXEC sp_configure 'in-doubt xact resolution', 1;
RECONFIGURE;
END
GO
El script crea una base de datos denominada AG1
en SQLNODE1
y una base de datos denominada dtcDemoAG1
en SQLNODE2
. Ejecute el siguiente script T-SQL en SSMS contra SQLNODE1
en el modo SQLCMD.
/*******************************************************************
Execute script in its entirety on SQLNODE1 in SQLCMD mode
*******************************************************************/
-- On SQLNODE1
USE master;
GO
SET NOCOUNT ON;
IF EXISTS (
SELECT *
FROM sys.databases
WHERE name = N'AG1'
)
BEGIN
DROP DATABASE AG1;
END
GO
CREATE DATABASE AG1;
GO
ALTER DATABASE AG1
SET RECOVERY FULL WITH NO_WAIT;
ALTER AUTHORIZATION ON DATABASE::AG1 TO sa;
GO
USE AG1;
CREATE TABLE [dbo].[Names] (
[Name] [varchar](64) NULL,
[EditDate] DATETIME
);
INSERT [dbo].[Names]
VALUES ('AG1', GETDATE());
GO
-- Against SQNODE2
:connect SQLNODE2
USE master;
GO
SET NOCOUNT ON;
IF EXISTS (
SELECT *
FROM sys.databases
WHERE name = N'dtcDemoAG1'
)
BEGIN
DROP DATABASE dtcDemoAG1;
END
GO
CREATE DATABASE dtcDemoAG1;
GO
ALTER DATABASE dtcDemoAG1
SET RECOVERY SIMPLE WITH NO_WAIT;
ALTER AUTHORIZATION ON DATABASE::dtcDemoAG1 TO sa;
GO
USE dtcDemoAG1;
GO
CREATE TABLE [dbo].[Names] (
[Name] [varchar](64) NULL,
[EditDate] DATETIME
);
GO
Este script crea un punto de conexión denominado AG1_endpoint
que escucha en el puerto TCP 5022
. Ejecute el siguiente script T-SQL en SSMS contra SQLNODE1
en el modo SQLCMD.
/**********************************************
Execute on SQLNODE1 in SQLCMD mode
**********************************************/
-- Create endpoint on the server instance that hosts the primary replica:
IF NOT EXISTS (SELECT * FROM sys.database_mirroring_endpoints)
BEGIN
CREATE ENDPOINT AG1_endpoint AUTHORIZATION [sa]
STATE = STARTED
AS TCP (LISTENER_PORT = 5022)
FOR DATABASE_MIRRORING(ROLE = ALL);
END
GO
-----------------------------------------------------------------------------
:connect SQLNODE2
IF NOT EXISTS (SELECT *FROM sys.database_mirroring_endpoints)
BEGIN
CREATE ENDPOINT AG1_endpoint AUTHORIZATION [sa]
STATE = STARTED
AS TCP (LISTENER_PORT = 5022)
FOR DATABASE_MIRRORING(ROLE = ALL);
END
GO
El script realiza una copia de seguridad de AG1
en SQLNODE1
y la restaura en SQLNODE2
. Ejecute el siguiente script T-SQL en SSMS contra SQLNODE1
en el modo SQLCMD.
/*******************************************************************
Execute script in its entirety on SQLNODE1 in SQLCMD mode
*******************************************************************/
-- Backup database
BACKUP DATABASE AG1 TO DISK = N'\\sqlnode1\sqlbackups\AG1.bak'
WITH FORMAT, STATS = 10;
-- Backup transaction log
BACKUP LOG AG1 TO DISK = N'\\sqlnode1\sqlbackups\AG1_Log.bak'
WITH FORMAT, STATS = 10;
GO
-- Restore database and logs on secondary WITH NORECOVERY
:connect SQLNODE2
USE [master];
GO
RESTORE DATABASE AG1
FROM DISK = N'\\sqlnode1\sqlbackups\AG1.bak'
WITH NORECOVERY, STATS = 10;
RESTORE LOG AG1
FROM DISK = N'\\sqlnode1\sqlbackups\AG1_Log.bak'
WITH NORECOVERY, STATS = 10;
GO
Los grupos de disponibilidad Always On se deben crear con el comando CREATE AVAILABILITY GROUP
y la cláusula WITH DTC_SUPPORT = PER_DB
. Actualmente no se puede modificar un grupo de disponibilidad existente. El asistente Nuevo grupo de disponibilidad no permite habilitar la compatibilidad con DTC para un nuevo grupo de disponibilidad. El siguiente script crea el nuevo grupo de disponibilidad y une el secundario. Ejecute el siguiente script T-SQL en SSMS contra SQLNODE1
en el modo SQLCMD.
/*******************************************************************
Execute the script in its entirety on SQLNODE1 in SQLCMD mode
*******************************************************************/
-- Create availability group on SQLNODE1
USE master;
GO
CREATE AVAILABILITY GROUP DTCAG1
WITH (DTC_SUPPORT = PER_DB)
FOR DATABASE AG1 REPLICA ON
'SQLNODE1' WITH (
ENDPOINT_URL = 'TCP://SQLNODE1.contoso.lab:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = MANUAL
),
'SQLNODE2' WITH (
ENDPOINT_URL = 'TCP://SQLNODE2.contoso.lab:5022',
AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
FAILOVER_MODE = MANUAL
);
GO
-- SQLNODE2
-- Join a secondary replica to the availability group
:connect SQLNODE2
ALTER AVAILABILITY GROUP DTCag1 JOIN;
-- Join the database to the availability group
ALTER DATABASE AG1
SET HADR AVAILABILITY GROUP = DTCAG1;
GO
A partir de SQL Server 2016 (13.x) Service Pack 2, puede modificar un grupo de disponibilidad para transacciones distribuidas. Para las versiones de SQL Server 2016 (13.x) anteriores a Service Pack 2, no se puede habilitar DTC en Grupos de disponibilidad de Always On existentes. SQL Server acepta la siguiente sintaxis para un grupo de disponibilidad existente:
USE master;
GO
ALTER AVAILABILITY GROUP <availability_group>
SET (DTC_Support = Per_DB);
Sin embargo, no se realiza ningún cambio de configuración. Puede confirmar la configuración dtc_support con la siguiente consulta T-SQL:
SELECT name, dtc_support FROM sys.availability_groups;
La única manera de habilitar la compatibilidad con DTC en un grupo de disponibilidad para las versiones anteriores a SQL Server 2016 (13.x) antes de Service Pack 2 es mediante la creación de un grupo de disponibilidad mediante Transact-SQL.
Este script prepara los recursos dependientes de DTC: disco y dirección IP. El almacenamiento compartido se agrega a Windows Cluster. Se crean los recursos de red y, luego, DTC se crea y convierte en un recurso en el grupo de disponibilidad. Ejecute el siguiente script de PowerShell en SQLNODE1
. Este script se basa en la muestra que se encuentra en How to Properly Configurar DTC para instancias agrupadas de SQL Server (revisado).
# Create a clustered Microsoft Distributed Transaction Coordinator properly in the resource group with SQL Server
<#----------------------------------- Begin User Input -----------------------------------#>
$AGgrp = "DTCag1"; # Name of the WSFC resource group that will contain the DTC resource
$WSFC = (Get-Cluster).Name; # Windows Failover Cluster name
$DTCnetwk = "Cluster Network 1" # WSFC Network to use for the DTC IP address
$ClusterAvailableDisk = "Cluster Disk 3"; # Designated disk that can support failover clustering and is visible to all nodes but not yet part of the set of clustered disks
$DTCdisk = "DTCDisk1"; # Name of the disk to be used with DTC
$DTCipresnm = "DTCIP1"; # WSFC Friendly Name of the DTC's IP resource
$DTCipaddr = "192.168.2.54"; # IP address of the DTC resource
$DTCsubnet = "255.255.255.0"; # Subnet for the DTC IP address
$DTCnetnm = "DTCNet1"; # WSFC Friendly Name of the Network Name resource
$DTCresnm = "DTC1"; # Name of the WSFC DTC Network Name resource; Name must be unique in AD
<#------------------------------------ End User Input ------------------------------------#>
# Make a new disk available for use in a failover cluster.
Get-ClusterAvailableDisk | Where-Object { $_.Name -eq $ClusterAvailableDisk } | Add-ClusterDisk;
# Rename disk
$resource = Get-ClusterResource $ClusterAvailableDisk; $resource.Name = $DTCdisk;
# Create the IP resource
Add-ClusterResource -Name $DTCipresnm -ResourceType "IP Address" -Group $AGgrp;
# Set the network to use, IP address, and subnet
# All three have to be configured at the same time
$DTCIPres = Get-ClusterResource $DTCipresnm;
$ntwk = New-Object Microsoft.FailoverClusters.PowerShell.ClusterParameter $DTCipres, Network, $DTCnetwk;
$ipaddr = New-Object Microsoft.FailoverClusters.PowerShell.ClusterParameter $DTCipres, Address, $DTCipaddr;
$subnet = New-Object Microsoft.FailoverClusters.PowerShell.ClusterParameter $DTCipres, SubnetMask, $dtcsubnet;
$setdtcipparams = $ntwk, $ipaddr, $subnet;
$setdtcipparams | Set-ClusterParameter;
# Create the Network Name resource
Add-ClusterResource $DTCnetnm -ResourceType "Network Name" -Group $AGgrp;
# Set the value for the Network Name resource
Get-ClusterResource $DTCnetnm | Set-ClusterParameter DnsName $DTCresnm;
# Add the IP address as a dependency of the Network Name resource
Add-ClusterResourceDependency $DTCnetnm $DTCipresnm;
# Create the Distributed Transaction Coordinator resource
Add-ClusterResource $DTCresnm -ResourceType "Distributed Transaction Coordinator" -Group $AGgrp;
# Add the Network Name as a dependency of the DTC resource
Add-ClusterResourceDependency $DTCresnm $DTCnetnm;
# Move the disk into the resource group with SQL Server
Move-ClusterResource -Name $DTCdisk -Group $AGgrp;
# Add the disk as a dependency of the DTC resource
Add-ClusterResourceDependency $DTCresnm $DTCdisk;
# Bring the IP resource online
Start-ClusterResource $DTCipresnm;
# Bring the Network Name resource online
Start-ClusterResource $DTCnetnm;
# Bring the DTC resource online
Start-ClusterResource $DTCresnm;
El siguiente script permite el acceso DTC de red para el servicio DTC agrupado para permitir que los equipos remotos participen en transacciones distribuidas a través de la red. Ejecute el siguiente script de PowerShell en SQLNODE1
.
# Enable Network DTC access for the clustered DTC service
<#
Script:
1) is re-runnable
2) will work on any node in the cluster, and
3) will execute against every node in the cluster
#>
# Enter the Name of DTC resource
$DtcName = "DTC1";
<# ------- End of User Input ------- #>
[bool]$restart = 0;
$node = (Get-ClusterResource -Name $DtcName).OwnerNode.Name;
$DtcSettings = Get-DtcNetworkSetting -DtcName $DtcName;
if ($DtcSettings.InboundTransactionsEnabled -eq $false) {
Set-DtcNetworkSetting -CimSession $node -DtcName $DtcName -AuthenticationLevel "Mutual" -InboundTransactionsEnabled $true -Confirm:$false;
$restart = 1;
}
if ($DtcSettings.OutboundTransactionsEnabled -eq $false) {
Set-DtcNetworkSetting -CimSession $node -DtcName $DtcName -AuthenticationLevel "Mutual" -OutboundTransactionsEnabled $true -Confirm:$false;
$restart = 1;
}
if ($restart -eq 1) {
Stop-Dtc -CimSession $node -DtcName $DTCname -Confirm:$false;
Start-Dtc -CimSession $node -DtcName $DTCname;
}
Deshabilite el DTC local en ambos nodos para garantizar que las transacciones distribuidas usen el recurso DTC agrupado. El siguiente script deshabilita y detiene el servicio DTC local en cada nodo. Ejecute el siguiente script de PowerShell en SQLNODE1
.
# Disable local DTC service
<#
Script:
1) is re-runnable
2) will work on any node in the cluster, and
3) will execute against every node in the cluster
#>
$DTCname = 'Local';
$nodes = (Get-ClusterNode).Name;
foreach ($node in $nodes) {
$service = Get-WmiObject -class Win32_Service -computername $node -Filter "Name='MSDTC'";
if ($service.StartMode -ne 'Disabled') {
$service.ChangeStartMode('Disabled');
}
if ($service.State -ne 'Stopped') {
$service.StopService();
}
}
Con el servicio DTC agrupado completamente configurado, debe detener y reiniciar cada instancia de SQL Server del grupo de disponibilidad para asegurarse de que SQL Server esté inscrito para usar este servicio DTC.
La primera vez que el servicio SQL Server necesita una transacción distribuida. Se inscribe con un servicio DTC. El servicio SQL Server sigue usando ese servicio DTC hasta que se reinicie. Si un servicio DTC agrupado está disponible, SQL Server se inscribe con él. Si hay un servicio DTC agrupado no está disponible, SQL Server se inscribe con él. Para comprobar que SQL Server se inscribe con el servicio DTC agrupado, detenga y reinicie cada instancia de SQL Server.
Siga los pasos contenidos en el siguiente script T-SQL:
/*
Gracefully cycle the SQL Server service and failover the availability group
a. On SQLNODE2, cycle the SQL Server service from SQL Server Configuration Manager
b. On SQLNODE2 failover, the availability group to SQLNODE2
Execute the T-SQL script below on SQLNODE2 (Use Results to Text)
c. On SQLNODE1, cycle the SQL Server service from SQL Server Configuration Manager
d. On SQLNODE1, failover the availability group to SQLNODE1 once the databases are back in sync.
Execute the T-SQL script below on SQLNODE1 (Use Results to Text)
*/
SET NOCOUNT ON;
-- Ensure replica is secondary
IF (
SELECT rs.is_primary_replica
FROM sys.availability_groups ag
INNER JOIN sys.dm_hadr_database_replica_states rs
ON ag.group_id = rs.group_id
WHERE ag.name = N'DTCag1' AND rs.is_local = 1
) = 0
BEGIN
-- Wait for SYNCHRONIZED state
DECLARE @ctr TINYINT = 0;
DECLARE @msg VARCHAR(128);
WHILE (
SELECT synchronization_state
FROM sys.availability_groups ag
INNER JOIN sys.dm_hadr_database_replica_states rs
ON ag.group_id = rs.group_id
WHERE ag.name = N'DTCag1'
AND rs.is_primary_replica = 0
AND rs.is_local = 1
) <> 2
BEGIN
WAITFOR DELAY '00:00:01';
SET @ctr += 1;
SET @msg = 'Waiting for databases to become synchronized. Duration in seconds: ' + cast(@ctr AS VARCHAR(3));
RAISERROR (@msg, 0, 1) WITH NOWAIT;
END;
ALTER AVAILABILITY GROUP DTCAG1 FAILOVER;
SELECT 'Failover complete' AS [Sucess];
END;
ELSE
BEGIN
SELECT 'This instance is the primary replica. Connect to the secondary replica and try again.' AS [Error];
END;
Esta prueba usa un servidor vinculado desde SQLNODE1
a SQLNODE2
para crear una transacción distribuida. Asegúrese de que la réplica principal del grupo de disponibilidad está en SQLNODE1
. Para probar la configuración tendrá que:
El siguiente script crea dos servidores vinculados en SQLNODE1
. Ejecute el siguiente script T-SQL en SSMS contra SQLNODE1
.
-- SQLNODE1
IF NOT EXISTS (SELECT * FROM sys.servers WHERE name = N'SQLNODE1')
BEGIN
EXEC master.dbo.sp_addlinkedserver @server = N'SQLNODE1';
END
IF NOT EXISTS (SELECT * FROM sys.servers WHERE name = N'SQLNODE2')
BEGIN
EXEC master.dbo.sp_addlinkedserver @server = N'SQLNODE2';
END
Este script devuelve primero las estadísticas de transacción DTC actuales. Después, el script ejecuta una transacción distribuida utilizando bases de datos en SQLNODE1
y SQLNODE2
. Luego, el script devuelve las estadísticas de transacción DTC, que ahora deberían aumentar el recuento. Conéctese físicamente a SQLNODE1
y ejecute el siguiente script T-SQL en SSMS contra SQLNODE1
en el modo SQLCMD.
/*******************************************************************
Execute the script in its entirety on SQLNODE1 in SQLCMD mode
Must be physically connected to SQLNODE1
*******************************************************************/
USE AG1;
GO
SET NOCOUNT ON;
-- Get Baseline
!! Powershell; $DtcNameC = Get-DtcClusterDefault; Get-DtcTransactionsStatistics -DtcName $DtcNameC;
SET XACT_ABORT ON
BEGIN DISTRIBUTED TRANSACTION
INSERT INTO SQLNODE1.[AG1].[dbo].[Names] VALUES ('TestValue1', GETDATE());
INSERT INTO SQLNODE2.[dtcDemoAG1].[dbo].[Names] VALUES ('TestValue2', GETDATE());
COMMIT TRAN
GO
-- Review DTC Transaction Statistics
!! Powershell; $DtcNameC = Get-DtcClusterDefault; Get-DtcTransactionsStatistics -DtcName $DtcNameC;
Importante
El instrucción USE AG1
se debe ejecutar para garantizar que el contexto de base de datos se establece en AG1
. De lo contrario, es probable que reciba el mensaje de error siguiente: "Contexto de transacción en uso por otra sesión".
Eventos
31 mar, 23 - 2 abr, 23
Evento de aprendizaje de SQL, Fabric y Power BI más grande. 31 de marzo – 2 de abril. Use el código FABINSIDER para ahorrar $400.
Regístrate hoy