XA işlemlerini anlama

JDBC sürücüsünü indirin

SQL Server için Microsoft JDBC Sürücüsü Java Platformu, Enterprise Edition/JDBC 2.0 isteğe bağlı dağıtılmış işlemler için destek sağlar. SQLServerXADataSource sınıfından alınan JDBC bağlantıları Java Platformu, Enterprise Edition (Java EE) uygulama sunucuları gibi standart dağıtılmış işlem ortamlarına katılabilir.

Bu makalede XA, genişletilmiş mimari anlamına gelir.

Uyarı

SQL için Microsoft JDBC Driver 4.2 (ve üzeri), hazır olmayan işlemlerin otomatik geri alınması için mevcut özellik için yeni zaman aşımı seçenekleri içerir. Daha fazla ayrıntı için bu konunun devamındaki Hazırlanmamış işlemlerin otomatik geri alınması için sunucu tarafı zaman aşımı ayarlarını yapılandırma konusuna bakın.

Açıklamalar

Dağıtılmış işlem uygulamasının sınıfları aşağıdaki gibidir:

Class Uygulamalar Description
com.microsoft.sqlserver.jdbc.SQLServerXADataSource javax.sql.XADataSource Dağıtılmış bağlantılar için sınıf fabrikası.
com.microsoft.sqlserver.jdbc.SQLServerXAResource javax.transaction.xa.XAResource İşlem yöneticisi için kaynak bağdaştırıcı.

Uyarı

XA dağıtılmış işlem bağlantıları varsayılan olarak Read Committed yalıtım düzeyine ayarlanır.

XA işlemlerini kullanırken yönergeler ve sınırlamalar

Sıkı bir şekilde bağlanmış işlemler için aşağıdaki ek yönergeler geçerlidir:

  • XA işlemlerini Microsoft Dağıtılmış İşlem Düzenleyicisi (MS DTC) ile birlikte kullandığınızda, MS DTC'nin geçerli sürümünün sıkı bir şekilde bağlanmış XA dal davranışını desteklemediğini fark edebilirsiniz. Örneğin, MS DTC'nin XA dal işlem kimliği (XID) ile MS DTC işlem kimliği arasında bire bir eşlemesi vardır ve gevşek bir şekilde bağlanmış XA dallarının gerçekleştirdiği çalışmalar birbirinden yalıtılır.

  • MS DTC, aynı genel işlem kimliğine (GTRID) sahip birden çok XA dallarının tek bir MS DTC işlem kimliğine eşlendiği sıkı bir şekilde bağlanmış XA dallarını da destekler. Bu destek, SQL Server gibi kaynak yöneticilerinde birbirlerinin değişikliklerini görmek için birbirine sıkı bir şekilde bağlı birden fazla XA dalını etkinleştirir.

  • SSTRANSTIGHTLYCPLD bayrağı, uygulamaların farklı XA dal işlem kimliklerine (BQUAL) sahip ancak aynı genel işlem kimliğine (GTRID) ve biçim kimliğine (FormatID) sahip sıkı bir şekilde bağlanmış XA işlemleri kullanmasına olanak tanır. Bu özelliği kullanmak için XAResource.start yönteminin flags parametresinde SSTRANSTIGHTLYCPLD'yi ayarlamanız gerekir:

    xaRes.start(xid, SQLServerXAResource.SSTRANSTIGHTLYCPLD);
    

Yapılandırma yönergeleri

Dağıtılmış işlemleri işlemek için XA veri kaynaklarını Microsoft Dağıtılmış İşlem Düzenleyicisi (MS DTC) ile birlikte kullanmak istiyorsanız aşağıdaki adımlar gereklidir. Üst düzey adımlar şunlardır:

  1. MS DTC hizmetinin çalıştığından ve otomatik olarak başlatıldığından emin olun.
  2. Sunucu tarafı bileşenlerini yapılandırın.
  3. Sunucu tarafı zaman aşımını yapılandırın (isteğe bağlı).
  4. Kullanıcılara erişim izni verme.

MS DTC hizmetini çalıştırma

MS DTC hizmeti, SQL Server hizmeti başlatıldığında çalıştığından emin olmak için Service Manager'da Otomatik olarak işaretlenmelidir. XA işlemleri için MS DTC'yi etkinleştirmek için şu adımları izlemeniz gerekir:

Windows Vista ve sonraki sürümlerde:

  1. Başlangıç düğmesini seçin, Aramayı Başlat kutusuna dcomcnfg yazın ve ardından Bileşen Hizmetleri'ni açmak için ENTER tuşuna basın. %windir%\system32\comexp.msc açmak için StartSearch kutusuna da yazabilirsiniz.

  2. Bileşen Hizmetleri, Bilgisayarlar, Bilgisayarım ve ardından Dağıtılmış İşlem Düzenleyicisi'ni genişletin.

  3. Yerel DTC'ye sağ tıklayın ve Özellikler'i seçin.

  4. Yerel DTC Özellikleri iletişim kutusunda Güvenlik sekmesini seçin.

  5. XA İşlemlerini Etkinleştir onay kutusunu ve ardından Tamam'ı seçin. Bu eylem MS DTC hizmetinin yeniden başlatılmasına neden olur.

  6. Özellikler iletişim kutusunu kapatmak için tekrar Tamam'ı seçin ve ardından Bileşen Hizmetleri'ni kapatın.

  7. SQL Server'ın MS DTC değişiklikleriyle eşitlenmiş olduğundan emin olmak için sql server'ı durdurun ve yeniden başlatın. (Bu adım SQL Server 2019 ve SQL Server 2017 CU 16 ve üzeri için isteğe bağlıdır.)

JDBC dağıtılmış işlem bileşenlerini yapılandırma

Sunucu tarafı bileşenlerini yapılandırma adımları, hedef sunucu sürümünüze bağlı olarak farklılık gösterir. Sunucu sürümünüzü denetlemek için sorguyu SELECT @@VERSION sunucuda yürüterek çıktıyı görüntüleyin. SQL Server 2017 Toplu Güncelleştirmesi (CU) 16 ve üzeri için SQL Server 2017 CU16 ve üzeri yönergeleri izleyin. Eski SQL Server sürümleri için SQL Server 2017 CU15 ve daha düşük yönergeleri izleyin.

SQL Server 2017 CU16 ve üzeri

Gerekli bileşenlerin JDBC sürücüsünü kullanarak XA dağıtılmış işlemleri gerçekleştirmesini sağlamak için aşağıdaki saklı yordamı yürütebilirsiniz.

EXEC sp_sqljdbc_xa_install

Bileşenleri devre dışı bırakmak için aşağıdaki saklı yordamı çalıştırın.

EXEC sp_sqljdbc_xa_uninstall

Hazır olmayan işlemlerin otomatik geri alınması için sunucu tarafı zaman aşımı ayarlarını yapılandırma bölümüne atlayın.

SQL Server 2017 CU15 ve daha düşük

Uyarı

Bu yalnızca SQL Server 2017 CU15 ve daha düşük sürümler için geçerlidir. sqljdbc_xa.dll tarafından sağlanan işlevler SQL Server 2017 CU16 ve üzeri sürümlerde zaten bulunur.

JDBC dağıtılmış işlem bileşenleri, JDBC sürücü yüklemesinin xa dizinine eklenir. Bu bileşenler xa_install.sql ve sqljdbc_xa.dll dosyalarını içerir. Farklı istemcilerde JDBC sürücüsünün farklı sürümleri varsa, sunucudaki en yeni sqljdbc_xa.dll kullanmanız önerilir.

Aşağıdaki adımları izleyerek JDBC sürücüsü dağıtılmış işlem bileşenlerini yapılandırabilirsiniz:

  1. Yeni sqljdbc_xa.dll JDBC sürücüsü yükleme dizininden dağıtılmış işlemlere katılan her SQL Server bilgisayarının Binn dizinine kopyalayın.

    Uyarı

    XA işlemlerini 32 bit SQL Server ile kullanıyorsanız (yalnızca SQL Server 2014 veya üzeri için geçerlidir), SQL Server bir x64 işlemciye yüklenmiş olsa bile x86 klasöründeki sqljdbc_xa.dll dosyasını kullanın. x64 işlemcisinde 64 bit SQL Server ile XA işlemleri kullanıyorsanız, x64 klasöründeki sqljdbc_xa.dll dosyasını kullanın.

  2. Dağıtılmış işlemlere katılan her SQL Server örneğinde veritabanı betiğini xa_install.sql yürütür. Bu script, sqljdbc_xa.dll tarafından çağrılan genişletilmiş saklı yordamları yükler. Bu genişletilmiş saklı yordamlar, SQL Server için Microsoft JDBC Sürücüsü için dağıtılmış işlem ve XA desteği uygular. Bu betiği SQL Server örneğinin yöneticisi olarak çalıştırmanız gerekir.

  3. Belirli bir kullanıcıya JDBC sürücüsüyle dağıtılmış işlemlere katılma izni vermek için kullanıcıyı SqlJDBCXAUser rolüne ekleyin.

Aynı anda her SQL Server örneğinde sqljdbc_xa.dll derlemesinin yalnızca bir sürümünü yapılandırabilirsiniz. Uygulamaların, XA bağlantısını kullanarak aynı SQL Server örneğine bağlanmak için JDBC sürücüsünün farklı sürümlerini kullanması gerekebilir. Bu durumda, en yeni JDBC sürücüsüyle birlikte gelen sqljdbc_xa.dll, SQL Server örneğine yüklenmelidir.

SQL Server örneğinde yüklü olan sqljdbc_xa.dll sürümünü doğrulamanın üç yolu vardır:

  1. Dağıtılmış işlemlere katılan SQL Server bilgisayarının LOG dizinini açın. SQL Server "ERRORLOG" dosyasını seçin ve açın. "ERRORLOG" dosyasında "'SQLJDBC_XA.dll' sürümünün kullanıldığı ..." ifadesini arayın.

  2. Dağıtılmış işlemlere katılan SQL Server bilgisayarının Binn dizinini açın. sqljdbc_xa.dll derlemesini seçin.

    • Windows Vista veya sonraki bir sürümde: sqljdbc_xa.dll sağ tıklayın ve özellikler'i seçin. Ardından Ayrıntılar sekmesini seçin. Dosya Sürümü alanında, SQL Server örneğinde yüklü olan sqljdbc_xa.dll sürümü gösterilir.
  3. Sonraki bölümdeki kod örneğinde gösterildiği gibi günlüğe kaydetme işlevini ayarlayın. Çıkış günlüğü dosyasında "Sunucu XA DLL sürümü:..." ifadesini arayın.

sqljdbc_xa.dll yükseltme

Uyarı

Bu yalnızca SQL Server 2017 CU15 ve daha düşük sürümler için geçerlidir. sqljdbc_xa.dll tarafından sağlanan işlevler SQL Server 2017 CU16 ve üzeri sürümlerde zaten bulunur.

JDBC sürücüsünün yeni bir sürümünü yüklediğinizde, sunucudaki sqljdbc_xa.dll yükseltmek için yeni sürümden sqljdbc_xa.dll de kullanmanız gerekir.

Önemli

Bakım penceresi sırasında veya devam eden MS DTC işlemi olmadığında sqljdbc_xa.dll yükseltmeniz gerekir.

  1. Transact-SQL komutunu kullanarak sqljdbc_xa.dll kaldırın:

    DBCC sqljdbc_xa (FREE)
    
  2. Yeni sqljdbc_xa.dll JDBC sürücüsü yükleme dizininden dağıtılmış işlemlere katılan her SQL Server bilgisayarının Binn dizinine kopyalayın.

    sqljdbc_xa.dll'da genişletilmiş bir yordam çağrıldığında yeni DLL yüklenir. Yeni tanımları yüklemek için SQL Server'ı yeniden başlatmanız gerekmez.

Hazırlanmamış işlemlerin otomatik geri alınması için sunucu tarafı zaman aşımı ayarlarını yapılandırma

Dağıtılmış işlemlerin zaman aşımı davranışını denetlemek için iki kayıt defteri ayarı (DWORD değerleri) vardır:

  • XADefaultTimeout (saniye): Kullanıcı herhangi bir zaman aşımı belirtmediğinde kullanılacak varsayılan zaman aşımı değeridir. Varsayılan değer 0'dır.

  • XAMaxTimeout (saniye): Bir kullanıcının ayarlayabileceğiniz en yüksek zaman aşımı değeridir. Varsayılan değer 0'dır.

Bu ayarlar SQL Server örneğine özeldir ve aşağıdaki kayıt defteri anahtarı altında oluşturulmalıdır:

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

Uyarı

64 bit makinelerde çalışan 32 bit SQL Server için (yalnızca SQL Server 2014 ve üzeri için geçerlidir), kayıt defteri ayarları aşağıdaki anahtar altında oluşturulmalıdır: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server\MSSQL<version>.<instance_name>\XATimeout

Başlatıldığında her işlem için bir zaman aşımı değeri ayarlanır ve zaman aşımı süresi dolarsa SQL Server işlemi geri alır. Zaman aşımı, bu kayıt defteri ayarlarına ve kullanıcının XAResource.setTransactionTimeout() aracılığıyla ne belirttiğine bağlı olarak belirlenir. Bu zaman aşımı değerlerinin nasıl yorumlandığına ilişkin birkaç örnek:

  • XADefaultTimeout = 0, XAMaxTimeout = 0

    Varsayılan bir zaman aşımı kullanılmadığını ve istemciler için herhangi bir maksimum zaman aşımı sınırının uygulanmadığını belirtir. Bu durumda, işlemlerin zaman aşımı olması için istemcinin XAResource.setTransactionTimeout kullanarak zaman aşımı ayarlaması gerekir.

  • XADefaultTimeout = 60, XAMaxTimeout = 0

    İstemci herhangi bir zaman aşımı belirtmezse tüm işlemlerin 60 saniyelik zaman aşımına sahip olduğu anlamına gelir. İstemci bir zaman aşımı belirtirse, bu zaman aşımı değeri kullanılır. Zaman aşımı için maksimum değer uygulanmaz.

  • XADefaultTimeout = 30, XAMaxTimeout = 60

    İstemci herhangi bir zaman aşımı belirtmezse tüm işlemlerin 30 saniyelik zaman aşımına sahip olduğu anlamına gelir. İstemci herhangi bir zaman aşımı belirtirse, 60 saniyeden kısa olduğu sürece (maksimum değer) istemcinin zaman aşımı kullanılır.

  • XADefaultTimeout = 0, XAMaxTimeout = 30

    İstemci herhangi bir zaman aşımı belirtmezse tüm işlemlerin 30 saniyelik zaman aşımı (maksimum değer) olduğu anlamına gelir. İstemci herhangi bir zaman aşımı belirtirse, 30 saniyeden kısa (maksimum değer) sürece istemcinin zaman aşımı kullanılır.

Kullanıcı tanımlı rolleri yapılandırma

Belirli bir kullanıcıya JDBC sürücüsüyle dağıtılmış işlemlere katılma izni vermek için kullanıcıyı SqlJDBCXAUser rolüne ekleyin. Örneğin, SqlJDBCXAUser rolüne 'shelly' adlı bir kullanıcı ('shelly' adlı SQL standart oturum açma kullanıcısı) eklemek için aşağıdaki Transact-SQL kodunu kullanın:

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

SQL kullanıcı tanımlı roller veritabanı başına tanımlanır. Güvenlik amacıyla kendi rolünüzü oluşturmak için, rolü her veritabanında tanımlamanız ve veritabanı başına kullanıcı eklemeniz gerekir. Ana veritabanında bulunan SQL JDBC genişletilmiş saklı yordamlarına erişim vermek için kullanıldığından, SqlJDBCXAUser rolü kesinlikle ana veritabanında tanımlanır. Önce tek tek kullanıcılara ana veritabanına erişim vermeli ve ardından ana veritabanında oturum açmış durumdayken onlara SqlJDBCXAUser rolüne erişim vermeliydiniz.

Example

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 = "<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);
    }
}

Ayrıca bakınız

JDBC sürücüsüyle işlem gerçekleştirme