Cách truy cập SQL Server trong bối cảnh giao dịch XA bằng trình điều khiển ODBC Easysoft SQL Server và Oracle Tuxedo.
Giới thiệu
Tại sao cần giao dịch phân tán
Giao dịch là một chuỗi các hành động được thực hiện như một thao tác đơn lẻ, trong đó tất cả các hành động được thực hiện hoặc không có hành động nào trong số chúng. Một giao dịch kết thúc bằng một hành động cam kết làm cho các thay đổi vĩnh viễn. Nếu bất kỳ thay đổi nào không thể được cam kết, giao dịch sẽ quay trở lại, đảo ngược tất cả các thay đổi.
Một giao dịch phân tán là một giao dịch có thể kéo dài nhiều tài nguyên. Ví dụ, một hoặc nhiều cơ sở dữ liệu hoặc một cơ sở dữ liệu và một hàng đợi tin nhắn. Để giao dịch cam kết thành công, tất cả các tài nguyên riêng lẻ phải cam kết thành công; nếu bất kỳ trong số chúng không thành công, giao dịch phải khôi phục lại tất cả các tài nguyên. Ví dụ:một giao dịch phân tán có thể bao gồm chuyển tiền giữa hai tài khoản ngân hàng, được lưu trữ bởi các ngân hàng khác nhau và cũng như vậy trên các cơ sở dữ liệu khác nhau. Bạn sẽ không muốn một trong hai giao dịch được cam kết mà không có sự đảm bảo rằng cả hai sẽ hoàn tất thành công. Nếu không, dữ liệu có thể bị trùng lặp (nếu quá trình chèn hoàn tất và quá trình xóa không thành công) hoặc bị mất (nếu quá trình xóa hoàn thành và lần chèn không thành công).
Do đó, bất cứ khi nào một ứng dụng cần truy cập hoặc cập nhật dữ liệu trong nhiều tài nguyên giao dịch, nó nên sử dụng một giao dịch phân tán. Có thể sử dụng một giao dịch riêng biệt trên mỗi tài nguyên, nhưng cách tiếp cận này dễ xảy ra lỗi. Nếu giao dịch trong một tài nguyên cam kết thành công nhưng một tài nguyên khác không thành công và phải quay trở lại, giao dịch đầu tiên không thể được khôi phục nữa, do đó trạng thái của ứng dụng trở nên không nhất quán. Nếu một tài nguyên cam kết thành công nhưng hệ thống bị treo trước khi tài nguyên khác có thể cam kết thành công, ứng dụng lại không nhất quán.
XA
Mô hình xử lý giao dịch phân tán (DTP) X / Open xác định một kiến trúc để xử lý giao dịch phân tán. Trong kiến trúc DTP, một người quản lý giao dịch điều phối cho mỗi tài nguyên biết cách xử lý một giao dịch, dựa trên kiến thức của nó về tất cả các tài nguyên tham gia vào giao dịch. Các tài nguyên thường quản lý cam kết và khôi phục giao dịch của riêng họ ủy quyền nhiệm vụ này cho người quản lý giao dịch.
Đặc điểm kỹ thuật XA của kiến trúc cung cấp một tiêu chuẩn mở đảm bảo khả năng tương tác giữa các sản phẩm cơ sở dữ liệu và phần mềm trung gian giao dịch phù hợp. Do đó, các tài nguyên khác nhau này có thể tham gia cùng nhau trong một giao dịch phân tán.
Mô hình DTP bao gồm ba thành phần có liên quan với nhau:
- Chương trình ứng dụng xác định ranh giới giao dịch và chỉ định các hành động cấu thành giao dịch.
- Người quản lý tài nguyên chẳng hạn như cơ sở dữ liệu hoặc hệ thống tệp cung cấp quyền truy cập vào tài nguyên được chia sẻ.
- Người quản lý giao dịch chỉ định số nhận dạng cho các giao dịch, theo dõi tiến trình của chúng và chịu trách nhiệm về việc hoàn thành giao dịch cũng như khắc phục lỗi.
Tiêu chuẩn XA xác định giao thức cam kết hai pha và giao diện được sử dụng để giao tiếp giữa Trình quản lý giao dịch và Trình quản lý tài nguyên. Giao thức cam kết hai giai đoạn cung cấp một đảm bảo tất cả hoặc không có gì rằng tất cả những người tham gia vào giao dịch có thể cam kết hoặc quay trở lại cùng nhau. Do đó, toàn bộ giao dịch cam kết hoặc toàn bộ giao dịch sẽ quay trở lại.
Cam kết hai giai đoạn bao gồm một giai đoạn chuẩn bị và một giai đoạn cam kết. Trong giai đoạn chuẩn bị, tất cả những người tham gia giao dịch phải đồng ý hoàn thành các thay đổi theo yêu cầu của giao dịch. Nếu bất kỳ người tham gia nào báo cáo sự cố, giai đoạn chuẩn bị sẽ không thành công và giao dịch sẽ quay trở lại. Nếu giai đoạn chuẩn bị thành công, giai đoạn hai, giai đoạn cam kết bắt đầu. Trong giai đoạn cam kết, Người quản lý giao dịch hướng dẫn tất cả những người tham gia thực hiện giao dịch.
SQL Server và XA
Để bật hỗ trợ XA trong SQL Server 2019, hãy làm theo hướng dẫn trong phần "Chạy dịch vụ MS DTC" có trong tài liệu này:
Hiểu các giao dịch XA
Để bật hỗ trợ XA trong các phiên bản SQL Server cũ hơn, hãy làm theo hướng dẫn trong tài liệu này:
Định cấu hình các giao dịch XA trong Microsoft SQL Server for IBM Business Process Manager (BPM)
Trình điều khiển SQL Server ODBC đã được kiểm tra với các phiên bản SQL Server 2016 và 2019 hỗ trợ XA.
Trình điều khiển ODBC Easysoft SQL Server
Hỗ trợ XA đã được thêm vào trình điều khiển ODBC SQL Server trong phiên bản 1.11.3. Hỗ trợ XA của trình điều khiển đã được thử nghiệm với Oracle Tuxedo và SQL Server 2016 và 2019.
Để sử dụng trình điều khiển ODBC SQL Server trong giao dịch XA, bạn cần sử dụng cấu trúc có tên es_xa_context
trong ứng dụng của bạn. es_xa_context
kết nối với nguồn dữ liệu ODBC mà bạn đã chỉ định trong cấu hình trình quản lý tài nguyên XA của mình và trả về một tay cầm kết nối. Ví dụ:
int ret; SQLHANDLE hEnv, hConn; ret = es_xa_context( NULL, &hEnv, &hConn );
Trong Tuxedo, nguồn dữ liệu ODBC es_xa_context
kết nối với được chỉ định trong Trình quản lý tài nguyên OPENINFO
chuỗi trong tệp cấu hình Tuxedo. Trong ví dụ này, đó là "SQLSERVER_SAMPLE":
OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE"
Tên Trình quản lý tài nguyên XA do trình điều khiển xác định và công tắc XA là EASYSOFT_SQLSERVER_ODBC
và essql_xaosw
.
Trong Tuxedo, bạn chỉ định những điều này trong tệp định nghĩa Trình quản lý tài nguyên Tuxedo, ${TUXDIR}/udataobj/RM
. Ví dụ:
EASYSOFT_SQLSERVER_ODBC:essql_xaosw:-L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbcinst
Mẫu ứng dụng Easysoft / Tuxedo / SQL Server XA
Trước tiên, hãy thiết lập nguồn dữ liệu trình điều khiển ODBC SQL Server kết nối với phiên bản SQL Server hỗ trợ XA:
- Trên máy Tuxedo của bạn, hãy cài đặt trình điều khiển SQL Server ODBC.
- Tạo nguồn dữ liệu trình điều khiển ODBC SQL Server trong odbc.ini. Ví dụ:
[SQLSERVER_SAMPLE] Driver=Easysoft ODBC-SQL Server Description=Easysoft SQL Server ODBC driver Server=mymachine\myxaenabledinstance User=mydomain\myuser Password=mypassword Database=XA1
- Tạo bảng mẫu cho ứng dụng Tuxedo:
$ /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE SQL> CREATE TABLE [dbo].[tx_test1]([i] [int] NULL,[c] [varchar](100) NULL)
Tạo và chạy Ứng dụng Tuxedo XA mẫu.
-
$ cd ~ $ mkdir simpdir $ cd simpdir $ touch simpcl.c simpserv.c ubbsimple
- Thêm các dòng này vào simpcl.c:
#include <stdio.h> #include "atmi.h" /* TUXEDO Header File */ #if defined(__STDC__) || defined(__cplusplus) main(int argc, char *argv[]) #else main(argc, argv) int argc; char *argv[]; #endif { char *sendbuf, *rcvbuf; long sendlen, rcvlen; int ret; if(argc != 2) { (void) fprintf(stderr, "Usage: simpcl <SQL>\n"); exit(1); } /* Attach to System/T as a Client Process */ if (tpinit((TPINIT *) NULL) == -1) { (void) fprintf(stderr, "Tpinit failed\n"); exit(1); } sendlen = strlen(argv[1]); /* Allocate STRING buffers for the request and the reply */ if((sendbuf = (char *) tpalloc("STRING", NULL, sendlen+1)) == NULL) { (void) fprintf(stderr,"Error allocating send buffer\n"); tpterm(); exit(1); } if((rcvbuf = (char *) tpalloc("STRING", NULL, sendlen+1)) == NULL) { (void) fprintf(stderr,"Error allocating receive buffer\n"); tpfree(sendbuf); tpterm(); exit(1); } (void) strcpy(sendbuf, argv[1]); /* Request the service EXECUTE, waiting for a reply */ ret = tpcall("EXECUTE", (char *)sendbuf, 0, (char **)&rcvbuf, &rcvlen, (long)0); if(ret == -1) { (void) fprintf(stderr, "Can't send request to service EXECUTE\n"); (void) fprintf(stderr, "Tperrno = %d\n", tperrno); tpfree(sendbuf); tpfree(rcvbuf); tpterm(); exit(1); } (void) fprintf(stdout, "Returned string is: %s\n", rcvbuf); /* Free Buffers & Detach from System/T */ tpfree(sendbuf); tpfree(rcvbuf); tpterm(); return(0); }
- Thêm các dòng này vào simpserv.c:
#include <stdio.h> #include <ctype.h> #include <atmi.h> /* TUXEDO Header File */ #include <userlog.h> /* TUXEDO Header File */ #include <xa.h> #include <sql.h> #include <sqlext.h> #include <string.h> /* tpsvrinit is executed when a server is booted, before it begins processing requests. It is not necessary to have this function. Also available is tpsvrdone (not used in this example), which is called at server shutdown time. */ int tpsvrinit(int argc, char *argv[]) { int ret; /* Some compilers warn if argc and argv aren't used. */ argc = argc; argv = argv; /* simpapp is non-transactional, so there is no need for tpsvrinit() to call tx_open() or tpopen(). However, if this code is modified to run in a Tuxedo group associated with a Resource Manager then either a call to tx_open() or a call to tpopen() must be inserted here. */ /* userlog writes to the central TUXEDO message log */ userlog("Welcome to the simple server"); ret = tpopen(); userlog("tpopen returned %d, error=%x", ret, tperrno ); return(0); } void tpsvrdone( void ) { int ret; ret = tpclose(); userlog("tpclose returned %d", ret); } /* This function performs the actual service requested by the client. Its argument is a structure containing among other things a pointer to the data buffer, and the length of the data buffer. */ xa_open_entry() call. int es_xa_context( int* rmid, SQLHANDLE* henv, SQLHANDLE* hdbc ); void EXECUTE(TPSVCINFO *rqst) { int ret; char *result; SQLHANDLE hStmt; char str[ 256 ]; SQLHANDLE hEnv, hConn; SQLSMALLINT slen; ret = es_xa_context( NULL, &hEnv, &hConn ); userlog("es_xa_context returns %d, hEnv = %p, hConn = %p", ret, hEnv, hConn ); if ( ret != 0 ) { result = tpalloc( "STRING", "*", 128 ); sprintf( result, "es_xa_context returned %d", ret ); /* Return the transformed buffer to the requestor. */ tpreturn(TPSUCCESS, 0, result, strlen( result ), 0); } else { ret = tpbegin( 0, 0 ); ret = SQLAllocHandle( SQL_HANDLE_STMT, hConn, &hStmt ); ret = SQLExecDirect( hStmt, rqst -> data, rqst -> len ); ret = SQLFreeHandle( SQL_HANDLE_STMT, hStmt ); ret = tpcommit( 0 ); result = tpalloc( "STRING", "*", 128 ); sprintf( result, "tpcommit returns %d", ret ); /* Return the transformed buffer to the requestor. */ tpreturn(TPSUCCESS, 0, result, strlen( result ), 0); } }
- Thêm các dòng này vào ubbsimple:
*RESOURCES IPCKEY 123456 DOMAINID simpapp MASTER simple MAXACCESSERS 20 MAXSERVERS 10 MAXSERVICES 10 MODEL SHM LDBAL N *MACHINES DEFAULT: APPDIR="/home/myuser/simpdir" TUXCONFIG="/home/myuser/simpdir/tuxconfig" TUXDIR="/home/myuser/OraHome/tuxedo12.2.2.0.0" mymachine LMID=simple TLOGNAME=TLOG TLOGDEVICE="/home/myuser/simpdir/tuxlog" *GROUPS GROUP1 LMID=simple GRPNO=1 OPENINFO=NONE TMSNAME=mySQLSERVER_TMS OPENINFO="EASYSOFT_SQLSERVER_ODBC:DSN=SQLSERVER_SAMPLE" *SERVERS DEFAULT: CLOPT="-A" simpserv SRVGRP=GROUP1 SRVID=1 *SERVICES EXECUTE
- Đặt môi trường của bạn:
export TUXDIR=/home/myuser/OraHome/tuxedo12.2.2.0.0 export TUXCONFIG=/home/myuser/simpdir/tuxconfig export PATH=$PATH:$TUXDIR/bin export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TUXDIR/lib:/usr/local/easysoft/unixODBC/lib: \ /usr/local/easysoft/sqlserver/lib:/usr/local/easysoft/lib
- Xây dựng ứng dụng khách mẫu:
buildclient -o simpcl -f simpcl.c
Nếu bạn gặp lỗi "tham chiếu không xác định tới dlopen" khi tạo ứng dụng khách, hãy thử lệnh này thay thế:
buildclient -o simpcl -f "-Xlinker --no-as-needed simpcl.c"
- Xây dựng máy chủ mẫu:
buildserver -r EASYSOFT_SQLSERVER_ODBC -s EXECUTE -o simpserv -f "simpserv.c \ -L/usr/local/easysoft/sqlserver/lib -lessqlsrv -lodbc"
- Tạo tệp TUXCONFIG cho ứng dụng mẫu:
tmloadcf ubbsimple
- Tạo thiết bị ghi Tuxedo cho ứng dụng mẫu:
$ tmadmin -c > crdl -z /home/myuser/simpdir/tuxlog -b 512
- Xây dựng trình quản lý giao dịch Tuxedo giao diện với trình điều khiển ODBC SQL Server:
$ buildtms -o mySQLSERVER_TMS -r EASYSOFT_SQLSERVER_ODBC
- Khởi động máy chủ mẫu:
$ tmboot
- Kiểm tra ứng dụng mẫu:
./simpcl "insert into tx_test1 values( 1, 'hello world' )" /usr/local/easysoft/unixODBC/bin/isql.sh -v SQLSERVER_SAMPLE SQL> select * from tx_test1 +------------+--------------+ | i | c | +------------+--------------+ | 1 | hello world | +------------+--------------+
- Nếu bạn thấy dữ liệu trong bảng SQL Server, hãy tắt máy chủ mẫu:
tmshutdown
Nếu không, hãy tham khảo ULOG.nnn trong thư mục ứng dụng mẫu.