Поделиться через


Основные сведения о транзакциях XA

Скачать драйвер JDBC

Microsoft JDBC Driver для SQL Server обеспечивает поддержку дополнительных распределенных транзакций на платформе Java, Enterprise Edition/JDBC 2.0. Соединения JDBC, получаемые с помощью класса SQLServerXADataSource, можно использовать в стандартных средах обработки распределенных транзакций, например на серверах приложений платформы Java Enterprise Edition (Java EE).

В этой статье XA обозначает расширенную архитектуру.

Предупреждение

Драйвер Microsoft JDBC Driver 4.2 для SQL (и более поздних версий) включает новые параметры времени ожидания для автоматического отката неподготовленных транзакций существующих компонентов. Дополнительные сведения см. в разделе Настройка параметров времени ожидания сервера для автоматического отката неподготовленных транзакций в этой статье.

Замечания

Далее представлены классы для реализации распределенных транзакций.

Класс Реализации Description
com.microsoft.sqlserver.jdbc.SQLServerXADataSource javax.sql.XADataSource Фабрика класса для распределенных соединений.
com.microsoft.sqlserver.jdbc.SQLServerXAResource javax.transaction.xa.XAResource Адаптер ресурсов для диспетчера транзакций.

Примечание.

Для соединений распределенных транзакций XA по умолчанию устанавливается уровень изоляции Read Committed.

Правила и ограничения, связанные с использованием транзакций XA

Следующие дополнительные рекомендации применяются к тесно связанных транзакциях:

  • При использовании транзакций XA вместе с координатором распределенных транзакций Майкрософт (MS DTC) можно заметить, что текущая версия MS DTC не поддерживает тесно связанное поведение ветви XA. Например, MS DTC имеет сопоставление между идентификатором транзакции XA (XID) и идентификатором транзакции MS DTC и работой, которая слабо связана с ветвями XA, изолирована друг от друга.

  • MSDTC также обеспечивает поддержку сильно связанных ветвей XA, когда несколько ветвей XA с одним глобальным идентификатором транзакции (GTRID) сопоставляются с одним идентификатором транзакции MS DTC. Эта поддержка позволяет нескольким тесно связанных ветвей XA видеть изменения в диспетчере ресурсов, например SQL Server.

  • Флаг SSTRANSTIGHTLYCPLD разрешает приложениям использовать тесно связанные транзакции XA, имеющие различные идентификаторы ветвей транзакции (BQUAL), но одинаковые глобальные идентификаторы транзакции (GTRID) и идентификатор формата (FormatID). Для использования этой функции необходимо задать значение SSTRANSTIGHTLYCPLD в параметре флагов метода XAResource.start:

    xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);
    

Инструкции по настройке

Если источники данных XA нужно использовать вместе с координатором распределенных транзакций (Майкрософт) (MS DTC) для обработки распределенных транзакций, необходимо выполнить следующие действия. Для этого выполните следующие обобщенные действия.

  1. Убедитесь, что служба MS DTC запущена и запускается автоматически.
  2. Настройте серверные компоненты.
  3. Настройка времени ожидания на стороне сервера (необязательно).
  4. Предоставление доступа пользователям.

Запуск службы MS DTC

Служба MS DTC должна быть помечена как автоматическая в Service Manager, чтобы убедиться, что она выполняется при запуске службы SQL Server. Чтобы включить MS DTC для транзакций XA, необходимо выполнить следующие действия.

В Windows Vista и более поздних версиях:

  1. Нажмите кнопку "Пуск", введите dcomcnfg в поле "Пуск поиска", а затем нажмите клавишу ВВОД, чтобы открыть службы компонентов. Вы также можете ввести %windir%\system32\comexp.msc в поле StartSearch , чтобы открыть службы компонентов.

  2. Разверните узлы "Службы компонентов", "Компьютеры", "Мой компьютер" и "Координатор распределенных транзакций".

  3. Щелкните правой кнопкой мыши узел Локальная DTC и выберите пункт Свойства.

  4. Откройте вкладку "Безопасность" в диалоговом окне "Свойства локального DTC".

  5. Установите флажок "Включить транзакции XA" и нажмите кнопку "ОК". Это действие приводит к перезапуску службы MS DTC.

  6. Нажмите кнопку "ОК ", чтобы закрыть диалоговое окно "Свойства ", а затем закройте службы компонентов.

  7. Остановите и перезапустите SQL Server, чтобы убедиться, что он синхронизируется с изменениями MS DTC. (Этот шаг необязателен для SQL Server 2019 и SQL Server 2017 CU16 и более поздних версий.)

Настройка компонентов распределенных транзакций JDBC

Действия по настройке компонентов на стороне сервера отличаются в зависимости от версии целевого сервера. Чтобы проверить версию сервера, выполните запрос SELECT @@VERSION к серверу и просмотрите выходные данные. Для SQL Server 2017 Накопительный пакет обновления (CU) 16 и выше следуйте инструкциям SQL Server 2017 CU16 и выше . Для более старых версий SQL Server следуйте инструкциям в SQL Server 2017 CU15 и более поздних версиях.

SQL Server 2017 CU16 и более поздних версий

Чтобы необходимые компоненты могли выполнять распределенные транзакции XA с помощью драйвера JDBC, выполните следующую сохраненную процедуру.

EXEC sp_sqljdbc_xa_install

Чтобы отключить компоненты, выполните следующую хранимую процедуру.

EXEC sp_sqljdbc_xa_uninstall

Перейдите к параметрам времени ожидания на стороне сервера для автоматического отката неподдержаемых транзакций .

SQL Server 2017 CU15 и более низкие

Примечание.

Это относится только к SQL Server 2017 CU15 и более низкому. Функции, предоставляемые sqljdbc_xa.dll, уже включены в SQL Server 2017 CU16 и более поздних версий.

Компоненты распределенных транзакций JDBC находятся в каталоге XA в каталоге установки драйвера JDBC. К этим компонентам относятся файлы xa_install.sql и sqljdbc_xa.dll. Если на разных клиентах установлены разные версии драйвера JDBC, рекомендуется использовать последнюю версию sqljdbc_xa.dll на сервере.

Чтобы настроить компоненты распределенных транзакций драйвера JDBC, можно выполнить следующие действия

  1. Скопируйте новый sqljdbc_xa.dll из каталога установки драйвера JDBC в каталог Binn каждого компьютера SQL Server, который участвует в распределенных транзакциях.

    Примечание.

    Если вы используете транзакции XA с 32-разрядной версией SQL Server (применимо только к SQL Server 2014 или более ранней версии), используйте файл sqljdbc_xa.dll в папке x86, даже если SQL Server установлен на процессоре x64. Если вы используете транзакции XA с 64-разрядным SQL Server на процессоре x64, используйте файл sqljdbc_xa.dll в папке x64.

  2. Выполните скрипт базы данных xa_install.sql на каждом экземпляре SQL Server, который участвует в распределенных транзакциях. Этот скрипт устанавливает расширенные хранимые процедуры, которые вызываются из sqljdbc_xa.dll. Эти расширенные хранимые процедуры реализуют распределенную транзакцию и XA-поддержку драйвера Microsoft JDBC для SQL Server. Этот скрипт необходимо запустить в качестве администратора экземпляра SQL Server.

  3. Чтобы предоставить определенному пользователю разрешения для участия в распределенных транзакциях через драйвер JDBC, его необходимо включить в роль SqlJDBCXAUser.

Вы можете настроить только одну версию сборки sqljdbc_xa.dll на каждом экземпляре SQL Server одновременно. Приложениям может потребоваться использовать разные версии драйвера JDBC для подключения к одному экземпляру SQL Server с помощью подключения XA. В этом случае sqljdbc_xa.dll, который поставляется с новейшим драйвером JDBC, должен быть установлен на экземпляре SQL Server.

Существует три способа проверить версию sqljdbc_xa.dll, установленной в настоящее время в экземпляре SQL Server:

  1. Откройте каталог LOG компьютера SQL Server, который участвует в распределенных транзакциях. Выберите и откройте файл SQL Server ERRORLOG. Найдите в файле ERRORLOG фразу "Используется SQLJDBC_XA.dll версии...".

  2. Откройте каталог Binn компьютера SQL Server, который участвует в распределенных транзакциях. Выберите сборку sqljdbc_xa.dll.

    • В Windows Vista и более поздних версиях: щелкните правой кнопкой мыши файл sqljdbc_xa.dll и выберите пункт меню «Свойства». Затем выберите вкладку "Сведения ". В поле "Версия файла" отображается версия sqljdbc_xa.dll, которая в настоящее время установлена в экземпляре SQL Server.
  3. Настройте ведение журнала, как показано в примере кода в следующем разделе. Найдите в выходном файле журнала фразу «Версия XA DLL на сервере:...».

Обновление файла sqljdbc_xa.dll

Примечание.

Это относится только к SQL Server 2017 CU15 и более низкому. Функции, предоставляемые sqljdbc_xa.dll, уже включены в SQL Server 2017 CU16 и более поздних версий.

При установке новой версии драйвера JDBC также следует обновить файл sqljdbc_xa.dll, расположенный на сервере, с помощью файла sqljdbc_xa.dll из этой новой версии.

Внимание

Файл sqljdbc_xa.dll следует обновлять во время планового обслуживания либо в случае, если не выполняется ни одной транзакции MS DTC.

  1. Выгрузите sqljdbc_xa.dll с помощью команды Transact-SQL:

    DBCC sqljdbc_xa (FREE)
    
  2. Скопируйте новый sqljdbc_xa.dll из каталога установки драйвера JDBC в каталог Binn каждого компьютера SQL Server, который участвует в распределенных транзакциях.

    Новая библиотека DLL загружается при вызове расширенной процедуры в sqljdbc_xa.dll. Для загрузки новых определений не нужно перезапустить SQL Server.

Настройка параметров времени ожидания на стороне сервера для автоматического отката неподготовленных транзакций

Существует два параметра реестра (типа DWORD) для управления временем ожидания распределенных транзакций.

  • XADefaultTimeout (в секундах): значение времени ожидания по умолчанию, используемое, если пользователь не указывает время ожидания. По умолчанию установлено значение 0.

  • XAMaxTimeout (в секундах): максимальное значение времени ожидания, которое может задать пользователь. По умолчанию установлено значение 0.

Эти параметры относятся к экземпляру SQL Server и должны быть созданы в следующем разделе реестра:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL<version>.<instance_name>\XATimeout

Примечание.

Для 32-разрядного SQL Server, работающего на 64-разрядных компьютерах (применимо только к SQL Server 2014 и более старым версиям), параметры реестра должны быть созданы в следующем разделе: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server\MSSQL<version>.<instance_name>\XATimeout

Значение времени ожидания устанавливается для каждой транзакции при запуске, и SQL Server откатывает транзакцию, если истекает время ожидания. Время ожидания зависит от параметров реестра и указанных пользователем параметров в XAResource.setTransactionTimeout(). Вот несколько примеров интерпретации таких значений времени ожидания.

  • XADefaultTimeout = 0, XAMaxTimeout = 0

    Означает, что время ожидания по умолчанию не используется, а максимальное время ожидания не применяется для клиентов. В этом случае транзакции имеют время ожидания, только если клиент задает время ожидания с помощью XAResource.setTransactionTimeout.

  • XADefaultTimeout = 60, XAMaxTimeout = 0

    Означает, что все транзакции имеют 60-секундное время ожидания, если клиент не указывает время ожидания. Если клиент задает время ожидания, используется это значение времени ожидания. Максимальное значение времени ожидания не применяется.

  • XADefaultTimeout = 30, XAMaxTimeout = 60

    Означает, что все транзакции имеют 30-секундное время ожидания, если клиент не указывает время ожидания. Если клиент указывает время ожидания, время ожидания клиента используется до тех пор, пока оно меньше 60 секунд (максимальное значение).

  • XADefaultTimeout = 0, XAMaxTimeout = 30

    Означает, что все транзакции имеют 30-секундное время ожидания (максимальное значение), если клиент не указывает время ожидания. Если клиент указывает время ожидания, время ожидания клиента используется до тех пор, пока оно меньше 30 секунд (максимальное значение).

Настройка определяемых пользователем ролей

Чтобы предоставить определенному пользователю разрешения для участия в распределенных транзакциях через драйвер JDBC, его необходимо включить в роль SqlJDBCXAUser. Например, следующий код Transact-SQL позволяет добавить пользователя с именем shelly (стандартное имя пользователя для входа в SQL) в роль SqlJDBCXAUser:

USE master
GO
EXEC sp_grantdbaccess 'shelly', 'shelly'
GO
EXEC sp_addrolemember [SqlJDBCXAUser], 'shelly'

Определяемые пользователем роли SQL определяются в рамках базы данных. Чтобы создать собственную роль для целей безопасности, необходимо определить роль в каждой базе данных и добавить пользователей в каждую базу данных. Роль SqlJDBCXAUser строго определена в базе данных master, так как она используется для предоставления доступа к расширенным хранимым процедурам SQL JDBC, находящимся в базе данных master. Сначала необходимо предоставить отдельным пользователям доступ к главной базе данных, а затем предоставить им доступ к роли SqlJDBCXAUser при входе в базу данных master.

Пример

import java.net.Inet4Address;
import java.sql.*;
import java.util.Random;
import javax.sql.XAConnection;
import javax.transaction.xa.*;
import com.microsoft.sqlserver.jdbc.*;


public class testXA {

    public static void main(String[] args) throws Exception {

        // Create variables for the connection string.
        String prefix = "jdbc:sqlserver://";
        String serverName = "localhost";
        int portNumber = 1433;
        String databaseName = "AdventureWorks";
        String user = "UserName";
        String password = "*****";

        String connectionUrl = prefix + serverName + ":" + portNumber + ";encrypt=true;databaseName=" + databaseName + ";user="
                + user + ";password=" + password;

        Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");

        try (Connection con = DriverManager.getConnection(connectionUrl); Statement stmt = con.createStatement()) {
            stmt.executeUpdate("CREATE TABLE XAMin (f1 int, f2 varchar(max))");

        }
        // Create the XA data source and XA ready connection.
        SQLServerXADataSource ds = new SQLServerXADataSource();
        ds.setUser(user);
        ds.setPassword(password);
        ds.setServerName(serverName);
        ds.setPortNumber(portNumber);
        ds.setDatabaseName(databaseName);

        XAConnection xaCon = ds.getXAConnection();
        try (Connection con = xaCon.getConnection()) {

            // Get a unique Xid object for testing.
            XAResource xaRes = null;
            Xid xid = null;
            xid = XidImpl.getUniqueXid(1);

            // Get the XAResource object and set the timeout value.
            xaRes = xaCon.getXAResource();
            xaRes.setTransactionTimeout(0);

            // Perform the XA transaction.
            System.out.println("Write -> xid = " + xid.toString());
            xaRes.start(xid, XAResource.TMNOFLAGS);
            PreparedStatement pstmt = con.prepareStatement("INSERT INTO XAMin (f1,f2) VALUES (?, ?)");
            pstmt.setInt(1, 1);
            pstmt.setString(2, xid.toString());
            pstmt.executeUpdate();

            // Commit the transaction.
            xaRes.end(xid, XAResource.TMSUCCESS);
            xaRes.commit(xid, true);
        }
        xaCon.close();

        // Open a new connection and read back the record to verify that it worked.
        try (Connection con = DriverManager.getConnection(connectionUrl); Statement stmt = con.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT * FROM XAMin")) {
            rs.next();
            System.out.println("Read -> xid = " + rs.getString(2));
            stmt.executeUpdate("DROP TABLE XAMin");
        }
    }
}


class XidImpl implements Xid {

    public int formatId;
    public byte[] gtrid;
    public byte[] bqual;

    public byte[] getGlobalTransactionId() {
        return gtrid;
    }

    public byte[] getBranchQualifier() {
        return bqual;
    }

    public int getFormatId() {
        return formatId;
    }

    XidImpl(int formatId, byte[] gtrid, byte[] bqual) {
        this.formatId = formatId;
        this.gtrid = gtrid;
        this.bqual = bqual;
    }

    public String toString() {
        int hexVal;
        StringBuffer sb = new StringBuffer(512);
        sb.append("formatId=" + formatId);
        sb.append(" gtrid(" + gtrid.length + ")={0x");
        for (int i = 0; i < gtrid.length; i++) {
            hexVal = gtrid[i] & 0xFF;
            if (hexVal < 0x10)
                sb.append("0" + Integer.toHexString(gtrid[i] & 0xFF));
            else
                sb.append(Integer.toHexString(gtrid[i] & 0xFF));
        }
        sb.append("} bqual(" + bqual.length + ")={0x");
        for (int i = 0; i < bqual.length; i++) {
            hexVal = bqual[i] & 0xFF;
            if (hexVal < 0x10)
                sb.append("0" + Integer.toHexString(bqual[i] & 0xFF));
            else
                sb.append(Integer.toHexString(bqual[i] & 0xFF));
        }
        sb.append("}");
        return sb.toString();
    }

    // Returns a globally unique transaction id.
    static byte[] localIP = null;
    static int txnUniqueID = 0;

    static Xid getUniqueXid(int tid) {

        Random rnd = new Random(System.currentTimeMillis());
        txnUniqueID++;
        int txnUID = txnUniqueID;
        int tidID = tid;
        int randID = rnd.nextInt();
        byte[] gtrid = new byte[64];
        byte[] bqual = new byte[64];
        if (null == localIP) {
            try {
                localIP = Inet4Address.getLocalHost().getAddress();
            } catch (Exception ex) {
                localIP = new byte[] {0x01, 0x02, 0x03, 0x04};
            }
        }
        System.arraycopy(localIP, 0, gtrid, 0, 4);
        System.arraycopy(localIP, 0, bqual, 0, 4);

        // Bytes 4 -> 7 - unique transaction id.
        // Bytes 8 ->11 - thread id.
        // Bytes 12->15 - random number generated by using seed from current time in milliseconds.
        for (int i = 0; i <= 3; i++) {
            gtrid[i + 4] = (byte) (txnUID % 0x100);
            bqual[i + 4] = (byte) (txnUID % 0x100);
            txnUID >>= 8;
            gtrid[i + 8] = (byte) (tidID % 0x100);
            bqual[i + 8] = (byte) (tidID % 0x100);
            tidID >>= 8;
            gtrid[i + 12] = (byte) (randID % 0x100);
            bqual[i + 12] = (byte) (randID % 0x100);
            randID >>= 8;
        }
        return new XidImpl(0x1234, gtrid, bqual);
    }
}

См. также

Выполнение транзакций с помощью JDBC Driver