1. 测试原理
????????读写分离功能组件开启状况下,可在dm_svc.conf中配置守护组及其数据访问流向,通过以下配置:
????????LOGIN_MODE=(1)
????????DW_SEPARATE=(1)
????????DW_PERCENT=(0)
????????将业务系统访问数据库的所有读操作转移到备库执行。业务系统执行读操作,分别在主备库查看sql’日志可定位语句执行库。
2. 环境准备
? ? 2.1 测试虚拟机划分:
虚拟机外网IP | 虚拟机内网IP(MAL) | 达梦版本 | 数据库模式 | 备注 | 192..168.111.133 | 192.168.196.133 | DM8 | STANDBY | 读写分离备库 | 192.168.111.134 | 192.168.196.134 | DM8 | PRIMARY | 读写分离主库 |

? ? ?2.2 SQL日志功能开启
? ? ? ? SVR_LOG=1
? ? ? ? 使用安装数据库时默认的sql配置。

3. 查看数据守护组
? ? 通过确认监视器执行show命令查看数据守护组状态

4. 程序端dm_svc.conf配置

参数说明:
DATA_WATCH:数据守护组对外提供的服务名格式为IP:PORT
LOGIN_MODE:指定优先登录的服务器模式,=1为只连接主库
DW_SEPARATE:是否启用读写分离。(0不启用,1启用)
DW_PERCENT:读写分离分发百分比,0表示读操作全部转交到备库。
注:dm_svc.conf为程序端配置,路径为
WINDOWS::C:\Windows\System32\dm_svc.conf
LINUX(centos):/etc/dm_svc.conf
5. ODBC数据源配置

注:ODBC数据源详细配置请参考
VS2022下通过ODBC访问达梦数据库(C++)_cn_lyt的博客-CSDN博客
?6. 数据库测试数据查看

7. 读操作测试
? ? 代码如下?
#include<iostream>
#include<Windows.h>
#include<sql.h>
#include<sqltypes.h>
#include<sqlext.h>
using namespace std;
#define RC_SUCCESSFUL(rc) ((rc) == SQL_SUCCESS || (rc) == SQL_SUCCESS_WITH_INFO)// #define SQL_SUCCEEDED(rc) (((rc)&(~1))==0)
#define RC_NOTSUCCESSFUL(rc) (!(RC_SUCCESSFUL(rc)))
SQLHENV henv;/* 环境句柄 */
SQLHDBC hdbc;/* 连接句柄 */
SQLHDBC hstmt;/* 语句句柄 */
SQLRETURN sret; /* 返回代码 */
void getError(SQLHDBC hhandle, SQLSMALLINT TYPE);
#define LEN 50
SQLCHAR name[LEN], code[LEN];
SQLINTEGER people_max;
SQLLEN pName, pCode, pPeople_Max;
int main()
{
/* 申请一个环境句柄 */
sret = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
/* 设置环境句柄的 ODBC 版本 */
sret = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,
SQL_IS_INTEGER);
/* 申请一个连接句柄 */
sret = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
sret = SQLConnectW(hdbc, (SQLWCHAR*)L"DM8-2", SQL_NTS, (SQLWCHAR*)L"SYSDBA", SQL_NTS, (SQLWCHAR*)L"SYSDBA", SQL_NTS);
if (RC_NOTSUCCESSFUL(sret)) {
getError(hdbc, SQL_HANDLE_DBC);
return 0;
}
SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
wchar_t sql[1024];
swprintf(sql, 1024, L"%hs", "SELECT NAME, CODE, PEOPLE_MAX FROM LYT.SCHOOL");
sret = SQLExecDirect(hstmt, sql, SQL_NTS);
//SQLPrepare(hstmt, sql, SQL_NTS);
if (RC_NOTSUCCESSFUL(sret)) {
getError(hstmt, SQL_HANDLE_STMT);
return 0;
}
while (true)
{
sret = SQLFetch(hstmt);
if (RC_NOTSUCCESSFUL(sret)) {
getError(hstmt, SQL_HANDLE_STMT);
break;
}
SQLGetData(hstmt, 1, SQL_CHAR, &name, LEN, &pName);
SQLGetData(hstmt, 2, SQL_CHAR, &code, LEN, &pCode);
SQLGetData(hstmt, 3, SQL_C_LONG, &people_max, LEN, &pPeople_Max);
printf_s("%s, %s, %d\n", name, code, people_max);
}
/* 释放语句句柄 */
SQLFreeHandle(SQL_HANDLE_DBC, hstmt);
/* 释放连接句柄 */
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
/* 释放环境句柄 */
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return 0;
}
void getError(SQLHDBC hhandle, SQLSMALLINT TYPE)
{
/* 连接数据源失败! */
SQLINTEGER NumRecords = 0;
SQLGetDiagField(TYPE,
hhandle,
0,
SQL_DIAG_NUMBER,
&NumRecords,
SQL_IS_INTEGER,
NULL);
printf("Total Number of diagnostic records: %d\n", NumRecords);
SQLSMALLINT Counter = 0;
SQLINTEGER NativeErr = 0;
SQLWCHAR SQLState[6];
SQLWCHAR ErrMsg[512];
SQLSMALLINT ErrMsgLen = 0;
for (Counter = 1; Counter <= NumRecords; Counter++)
{
SQLGetDiagRec(TYPE,
hhandle,
Counter,
SQLState,
&NativeErr,
ErrMsg,
sizeof(ErrMsg) / 2,
&ErrMsgLen);
printf("SQLSTATE : %ls\n", SQLState);
printf("%ls\n", ErrMsg);
}
}
?执行结果如下:

8. 主备库日志查看
? ? ? 主库SQL日志 :

? ? ? ? 备库SQL日志:

????????可以看出业务系统的数据访问经过了主库,但是最后的语句执行及结果是由备库处理。读写分离测试成功。?
注:详细的开发细节请参考《DM8程序员手册.pdf》及微软官方API文档
相关链接:
产品下载-达梦数据库-国产数据库-掌握全部源代码-拥有完全自主知识产权-大数据-云平台-DCA认证培训-数据库培训-柔性替代方案
ODBC |Microsoft Docs - ODBC API Reference | Microsoft Docs
更多达梦相关技术文档可访问???????
达梦数据库 - 新一代大型通用关系型数据库 | 达梦云适配中心
|