达梦数据库集群-读写分离
<p>[TOC]</p>
<h2>1、准备环境:</h2>
<table>
<thead>
<tr>
<th>服务器ip</th>
<th>实例名</th>
<th>服务器作用</th>
<th>使用端口</th>
</tr>
</thead>
<tbody>
<tr>
<td>192.168.5.160</td>
<td>DM160</td>
<td>主库(读写)</td>
<td>61141、52141、33141、5236</td>
</tr>
<tr>
<td>192.168.5.162</td>
<td>DM162</td>
<td>备库(只读)</td>
<td>61141、52141、33141、5236</td>
</tr>
</tbody>
</table>
<h3>1.1配置前记得检查主备机的防火墙状态,使用root用户检查</h3>
<pre><code class="language-shell">systemctl status firewalld.service #查看防火墙状态
firewall-cmd --list-all #查看已开放端口列表</code></pre>
<h3>1.2数据库集群需开放以下端口:</h3>
<p><strong>主库、备份服务器</strong></p>
<pre><code class="language-shell">firewall-cmd --zone=public --add-port=61141/tcp --permanent
firewall-cmd --zone=public --add-port=52141/tcp --permanent
firewall-cmd --zone=public --add-port=5236/tcp --permanent
firewall-cmd --zone=public --add-port=33141/tcp --permanent
firewall-cmd --reload</code></pre>
<h3>1.3在主备机服务器上安装同版本的达梦数据库(此处省略安装过程)</h3>
<p>具体步骤参考:达梦数据库快速安装
数据库安装路径:/risen/soft/database/dmdba</p>
<h2>2、初始化数据库实例</h2>
<h3>2.1初始化</h3>
<p>主库、备份初始化一样</p>
<pre><code class="language-shell">su - dmdba
cd /risen/soft/database/dmdba/bin
./dminit path=/risen/soft/database/dmdba/data db_name=DMENG instance_name=DMSERVER CHARSET=1 CASE_SENSITIVE=0 LENGTH_IN_CHAR=1 EXTENT_SIZE=32 PAGE_SIZE=32 port_num=5236</code></pre>
<h3>2.2在两台服务器上分别前台启停库一次</h3>
<pre><code class="language-shell">./dmserver /risen/soft/database/dmdba/data/DMENG/dm.ini</code></pre>
<p>出现SYSTEM IS READY表示已经启动完成</p>
<h3>2.3优化数据库</h3>
<p>使用达梦工具连接数据库,执行达梦优化脚本对数据库进行调优
[达梦调优脚本.txt](<a href="http://60.191.64.5:16100/server/index.php?s=/api/attachment/visitFile/sign/cc73b4b02e71e61b5c9a14fe9a7f9193">http://60.191.64.5:16100/server/index.php?s=/api/attachment/visitFile/sign/cc73b4b02e71e61b5c9a14fe9a7f9193</a> "[达梦调优脚本.txt")
优化之后在./dmserver /risen/soft/database/dmdba/data/DMENG/dm.ini,窗口后输入exit退出。</p>
<h2>3、配置读写分离集群</h2>
<h3>3.1在主库上使用dmdba用户脱机备份</h3>
<p>如果备份过程中出现错误,通过ps -ef|grep dmap,查看dmap服务是否在运行中
如果不在运行中,用root用户启动</p>
<pre><code class="language-shell">systemctl start DmAPService.service</code></pre>
<p>使用达梦的脱机备份工具dmrman备份主库DM160</p>
<pre><code class="language-shell">su - dmdba
cd /risen/soft/database/dmdba/bin</code></pre>
<p>此处指定备份文件名为backup20210923</p>
<pre><code class="language-shell">./dmrman ctlstmt=&quot;backup database '/risen/soft/database/dmdba/data/DMENG/dm.ini' full to backup20210923 backupset '/risen/soft/database/dmdba/data/DMENG/bak/backup20210923/'&quot;</code></pre>
<p>备份成功以后,在/risen/soft/database/dmdba/data/DMENG/bak路径下会生成一个backup20210923备份文件夹
将整个备份文件夹发送到备库DM162</p>
<pre><code class="language-shell">su - dmdba
scp -r /risen/soft/database/dmdba/data/DMENG/bak/backup20210923 dmdba@192.168.5.162:/risen/soft/database/dmdba/data/DMENG/bak/</code></pre>
<h3>3.2在备库DM162上进行备份还原与更新</h3>
<p>在备库上使用dmrman工具,备份文件backup20210923还原、恢复与更新</p>
<pre><code class="language-shell">su - dmdba
cd /risen/soft/database/dmdba/bin</code></pre>
<p>还原:</p>
<pre><code class="language-shell">./dmrman ctlstmt=&quot;restore database '/risen/soft/database/dmdba/data/DMENG/dm.ini' from backupset '/risen/soft/database/dmdba/data/DMENG/bak/backup20210923/'&quot;</code></pre>
<p>恢复:</p>
<pre><code class="language-shell">./dmrman ctlstmt=&quot;recover database '/risen/soft/database/dmdba/data/DMENG/dm.ini' from backupset '/risen/soft/database/dmdba/data/DMENG/bak/backup20210923/'&quot;</code></pre>
<p>更新db_magic:</p>
<pre><code class="language-shell">./dmrman ctlstmt=&quot;recover database '/risen/soft/database/dmdba/data/DMENG/dm.ini' update db_magic&quot;</code></pre>
<h3>3.3配置集群参数</h3>
<h4>3.3.1修改dm.ini</h4>
<pre><code class="language-shell">su - dmdba
vim /risen/soft/database/dmdba/data/DMENG/dm.ini</code></pre>
<p>修改以下参数值
<strong>主库添加以下内容:</strong></p>
<pre><code class="language-shell">INSTANCE_NAME=DM160
ARCH_INI=1
MAL_INI=1
ALTER_MODE_STATUS= 0
ENABLE_OFFLINE_TS = 2</code></pre>
<p><strong>备库添加以下内容:</strong></p>
<pre><code class="language-shell">INSTANCE_NAME=DM162
ARCH_INI=1
MAL_INI=1
ALTER_MODE_STATUS= 0
ENABLE_OFFLINE_TS = 2</code></pre>
<h4>3.3.2配置dmmal.ini</h4>
<p>在/risen/soft/database/dmdba/data/DMENG文件夹下,创建dmmal.ini文件</p>
<pre><code class="language-shell">su - dmdba
vim /risen/soft/database/dmdba/data/DMENG/dmmal.ini</code></pre>
<p><strong>主、备库文件内容要相同</strong>
注意IP地址根据实际情况相应修改</p>
<pre><code class="language-shell">MAL_CHECK_INTERVAL = 5 #MAL 链路检测时间间隔
MAL_CONN_FAIL_INTERVAL = 5 #判定 MAL 链路断开的时间
[MAL_INST1]
MAL_INST_NAME = DM160 #与 dm.ini 中的 INSTANCE_NAME 一致
MAL_HOST = 192.168.5.160 #MAL 系统监听 TCP 内部网络 IP
MAL_PORT = 61141 #MAL 系统监听 TCP 连接的端口
MAL_INST_HOST = 192.168.5.160 #实例的对外服务 IP 地址
MAL_INST_PORT = 5236 #与 dm.ini 中的 PORT_NUM 一致
MAL_DW_PORT = 52141 #实例对应的守护进程监听 TCP 端口
MAL_INST_DW_PORT = 33141
[MAL_INST2]
MAL_INST_NAME = DM162 #与 dm.ini 中的 INSTANCE_NAME 一致
MAL_HOST = 192.168.5.162 # MAL 系统监听 TCP 内部网络 IP
MAL_PORT = 61141 #MAL 系统监听 TCP 连接的端口
MAL_INST_HOST = 192.168.5.162 #实例的对外服务 IP 地址
MAL_INST_PORT = 5236 #与 dm.ini 中的 PORT_NUM 一致
MAL_DW_PORT = 52141 #实例对应的守护进程监听 TCP 端口
MAL_INST_DW_PORT = 33141</code></pre>
<h4>3.3.3配置dmarch.ini</h4>
<p>在/risen/soft/database/dmdba/data/DMENG文件夹下,创建dmarch.ini文件</p>
<pre><code class="language-shell">su - dmdba
mkdir /risen/soft/database/dmdba/data/DMENG/arch
vim /risen/soft/database/dmdba/data/DMENG/dmarch.ini</code></pre>
<p><strong>主库添加以下内容:</strong></p>
<pre><code class="language-shell">[ARCHIVE_TYPE]
ARCH_TYPE = TIMELY #即时归档类型(适用于读写分离集群)
#ARCH_TYPE = REALTIME #实时归档类型(适用于主备集群)
ARCH_DEST = DM162 #即时归档目标实例名(备库侧填写主库实例名)
[ARCHIVE_LOCAL1]
ARCH_TYPE = LOCAL #本地归档类型
ARCH_DEST = /risen/soft/database/dmdba/data/DMENG/arch #本地归档文件存放路径
ARCH_FILE_SIZE = 128 #单位 MB,本地单个归档文件最大值
ARCH_SPACE_LIMIT = 10240 #单位 MB,0 表示无限制,范围 1024~4294967294 MB</code></pre>
<p><strong>备库添加以下内容:</strong></p>
<pre><code class="language-shell">[ARCHIVE_TYPE]
ARCH_TYPE = TIMELY #即时归档类型(适用于读写分离集群)
#ARCH_TYPE = REALTIME #实时归档类型(适用于主备集群)
ARCH_DEST = DM160 #即时归档目标实例名(备库侧填写主库实例名)
[ARCHIVE_LOCAL1]
ARCH_TYPE = LOCAL #本地归档类型
ARCH_DEST = /risen/soft/database/dmdba/data/DMENG/arch #本地归档文件存放路径
ARCH_FILE_SIZE = 128 #单位 MB,本地单个归档文件最大值
ARCH_SPACE_LIMIT = 10240 #单位 MB,0 表示无限制,范围 1024~4294967294 MB</code></pre>
<h4>3.3.4配置dmwatcher.ini</h4>
<p>在/risen/soft/database/dmdba/data/DMENG文件夹下,创建dmwatcher.ini文件</p>
<pre><code class="language-shell">su - dmdba
vim /risen/soft/database/dmdba/data/DMENG/dmwatcher.ini</code></pre>
<p><strong>主、备库文件内容要相同</strong></p>
<pre><code class="language-shell">[GRP_RW]
DW_TYPE = GLOBAL #全局守护类型
DW_MODE = AUTO #自动切换模式
DW_ERROR_TIME = 10 #远程守护进程故障认定时间
INST_RECOVER_TIME = 60 #主库守护进程启动恢复的间隔时间
INST_ERROR_TIME = 10 #本地实例故障认定时间
INST_OGUID = 453331 #守护系统唯一 OGUID 值
INST_INI = /risen/soft/database/dmdba/data/DMENG/dm.ini #dm.ini 配置文件路径
INST_AUTO_RESTART = 1 #打开实例的自动启动功能
INST_STARTUP_CMD = /risen/soft/database/dmdba/bin/dmserver #命令行方式启动</code></pre>
<h4>3.3.6配置数据库的OGUID</h4>
<p>到数据库安装目录下的bin目录</p>
<pre><code class="language-shell">su - dmdba
cd /risen/soft/database/dmdba/bin</code></pre>
<p>用dmdba用户以mount模式开启主、备库</p>
<pre><code class="language-shell">./dmserver /risen/soft/database/dmdba/data/DMENG/dm.ini mount</code></pre>
<p>一直到显示SYSTEM IS READY启动完成。此时不要关闭这个窗口,重新在主服务器上开启一个窗口
<img src="http://60.191.64.5:16100/server/index.php?s=/api/attachment/visitFile/sign/e51eb21c3ef1e50db4e00ff93402363c" alt="" />
使用达梦的系统管理员用户SYSDBA登录达梦的disql工具,默认密码为SYSDBA</p>
<pre><code class="language-shell">./disql SYSDBA/SYSDBA</code></pre>
<p>登录成功后,修改OGUID参数,OGUID号与dmwatcher.ini中的INST_OGUID 保持一致
<strong>主库修改内容:</strong></p>
<pre><code class="language-shell">设置修改OGUID
sp_set_oguid(453331);
修改数据库模式
alter database primary;</code></pre>
<p><strong>备库修改内容:</strong></p>
<pre><code class="language-shell">设置修改OGUID
sp_set_oguid(453331);
修改数据库模式
alter database standby;</code></pre>
<p>修改成功以后,在./dmserver /risen/soft/database/dmdba/data/DMENG/dm.ini mount 窗口后输入exit退出</p>
<h3>3.4在备库配置监视器</h3>
<p>在DM162服务器上使用dmdba用户创建dmmonitor.ini文件</p>
<pre><code class="language-shell">su - dmdba
vim /risen/soft/database/dmdba/data/DMENG/dmmonitor.ini</code></pre>
<p>dmmonitor.ini文件内容如下:
MON_INST_OGUID与dmwatcher.ini中的INST_OGUID 保持一致</p>
<pre><code class="language-shell">MON_DW_CONFIRM = 1 #为 0 是非确认监视器,为 1 是确认监视器
MON_LOG_PATH = /risen/soft/database/dmdba/log #监视器日志文件存放路径
MON_LOG_INTERVAL = 60 #每隔 60 s 定时记录系统信息到日志文件
MON_LOG_FILE_SIZE = 128 ##单位 MB,范围 1~2048 MB
MON_LOG_SPACE_LIMIT = 8192 #设置日志文件总占用空间MB
[GRP_RW]
MON_INST_OGUID = 453331 #组 GRP_RW 的唯一 OGUID 值
#以下配置为监视器到组 GRP_RW 的守护进程的连接信息,以“IP:PORT”的形式配置
#IP 对应 dmmal.ini 中的 MAL_HOST,PORT 对应 dmmal.ini 中的 MAL_DW_PORT
MON_DW_IP = 192.168.5.160:52141
MON_DW_IP = 192.168.5.162:52141</code></pre>
<h3>3.5注册服务</h3>
<p>使用root用户在数据库服务器上执行以下命令</p>
<pre><code class="language-shell">su - root
cd /risen/soft/database/dmdba/script/root/</code></pre>
<p>注册主、备库实例服务</p>
<pre><code class="language-shell">./dm_service_installer.sh -t dmserver -dm_ini /risen/soft/database/dmdba/data/DMENG/dm.ini -p DMSERVER</code></pre>
<p>注册主、备库守护进程服务</p>
<pre><code class="language-shell"> ./dm_service_installer.sh -t dmwatcher -watcher_ini /risen/soft/database/dmdba/data/DMENG/dmwatcher.ini -p DMSERVER</code></pre>
<p>注册备库监视器服务</p>
<pre><code class="language-shell">./dm_service_installer.sh -t dmmonitor -monitor_ini /risen/soft/database/dmdba/data/DMENG/dmmonitor.ini -p DMSERVER</code></pre>
<p>到目前为止,只是进行集群的搭建,为保障安全和性能,还需进行优化和设置备份</p>
<h2>4启动集群</h2>
<h3>4.1启动集群的顺序为</h3>
<p><strong>以服务方式启动</strong>
主库实例</p>
<pre><code class="language-shell">systemctl start DmServiceDMSERVER.service</code></pre>
<p>备库实例</p>
<pre><code class="language-shell">systemctl start DmServiceDMSERVER.service</code></pre>
<p>主库守护进程</p>
<pre><code class="language-shell">systemctl start DmWatcherServiceDMSERVER.service</code></pre>
<p>备库守护进程</p>
<pre><code class="language-shell">systemctl start DmWatcherServiceDMSERVER.service</code></pre>
<p>备库监视器服务</p>
<pre><code class="language-shell">systemctl start DmMonitorServiceDMSERVER.service</code></pre>
<h3>4.2关闭集群的顺序为</h3>
<p><strong>以服务方式关闭</strong>
备库监视器服务</p>
<pre><code class="language-shell">systemctl stop DmMonitorServiceDMSERVER.service</code></pre>
<p>备库守护进程</p>
<pre><code class="language-shell">systemctl stop DmWatcherServiceDMSERVER.service</code></pre>
<p>主库守护进程</p>
<pre><code class="language-shell">systemctl stop DmWatcherServiceDMSERVER.service</code></pre>
<p>主库实例</p>
<pre><code class="language-shell">systemctl stop DmServiceDMSERVER.service</code></pre>
<p>备库实例</p>
<pre><code class="language-shell">systemctl stop DmServiceDMSERVER.service</code></pre>
<h2>5.配置服务名(应用服务器上配置)</h2>
<p><strong>服务名的作用是可以在url串中替代IP地址连接整个集群</strong>
达梦安装时会生成一个配置文件dm_svc.conf,Linux服务器上该文件存在目录/etc下面;
该文件中包含 DM 各接口及客户端需要配置的一些参数</p>
<h3>5.1在应用服务器上修改dm_svc.conf文件。</h3>
<p>如果/etc下没有dm_svc.conf就手动创建一个</p>
<pre><code class="language-shell">su - root
vim /etc/dm_svc.conf</code></pre>
<p>dm_svc.conf配置内容如下:</p>
<pre><code class="language-shell">TIME_ZONE=(480)
LANGUAGE=(en)
DM_TEST=(192.168.5.160:5236,192.168.5.162:5236)
RW_SEPARATE=(1) #启用读写分离
RW_PERCENT=(30) #读事务分发到主库的比例
LOGIN_PRIMARY=(1) #表示是否仅登录到主机
SWITCH_TIME=6000
SWITCH_INTERVAL=500</code></pre>
<p><strong>参考:</strong>
DM_TEST=(192.168.5.160:5236,192.168.5.162:5236)表示把主机和备机都集成一个名字DM;
TIME_ZONE和LANGUAGE分别对应操作系统时区和语言;
RW_SEPARATE:取值0和1,默认0,表示不使用读写分离方式;
RW_PERCENT:取值0~100,默认值25。表示分发总事务的百分之多少到主机上执行;
LOGIN_PRIMARY表示是否仅登录到主机,支持的选项为1(只连接主机)和0(主机不存在的情况下可连接备机)。可以不指定,若不指定,默认值为1
(此处的服务名为DM_TEST(可以根据需要,设置成别的名字),即可以在url串中使用DM来替代IP地址)</p>
<h3>5.2jdbc连接达梦数据库</h3>
<p>在实际应用过程中,用户可以在JDBC连接串中增加对应的属性来启用和设置读写分离功能,主要属性有以下两个:
rwSeparate 是否使用读写分离系统,默认 0;取值(0 不使用,1 使用)
rwPercent 分发到主库的事务占主备库总事务的百分比,有效值 0~100,默认 值 25
驱动类名:dm.jdbc.driver.DmDriver
驱动包在安装目录下的driver/jdbc下
URL串格式:
单机: jdbc:dm://IP:端口
集群:jdbc:dm://服务名
例如此处:</p>
<pre><code class="language-shell">jdbc:dm://DM_TEST?rwSeparate=1&amp;rwPercent=30</code></pre>
<p><strong>参考:</strong>
<img src="http://60.191.64.5:16100/server/index.php?s=/api/attachment/visitFile/sign/79ab45ab13704a8b922ab644a6e57050" alt="" /></p>
<h2>6.达梦数据库读写分离的实现原理</h2>
<p>实现读写分离集群的基本思路是:利用备库提供只读服务、无法修改数据的特性,优先将所有操作发送到备库执行,一旦备库执行报错,则发送到主库重新执行。通过备库“试错”这么一个步骤,自然地将只读操作分流到备库执行。并且,备库“试错”由接口层自动完成,对应用透明</p>
<h3>6.1读写分离集群数据库连接创建流程</h3>
<p>1)用户发起数据库连接请求。
2)接口(JDBC、DPI 等)根据服务名配置(在 dm_svc.conf 中进行配置)登录主库
3)主库挑选一个有效即时备库的 IP/Port 返回给接口
4)接口根据返回的备库 IP 和 Port 信息,向备库发起一个连接请求
5)备库返回连接成功信息
6)接口响应用户数据库连接创建成功接口在备库上创建的连接是读写分离集群自动创建的;对用户而言,就是在主库上创建了一个数据库连接,下图以配置了两个备库的读写集群为例,说明了读写分离集群的连接创建流程
<img src="http://60.191.64.5:16100/server/index.php?s=/api/attachment/visitFile/sign/72388cdd26985b12e0949cbe060f0b39" alt="" /></p>
<h3>6.2读写分离集群语句分发流程</h3>
<p>1)接口收到用户的请求。
2)接口优先将 SQL 发送到备库执行
3)备库执行并返回执行结果。如果接口收到的是备库执行成功消息,则转到第 6 步,如果接口收到的是备库执行失败消息,则转到第 4 步
4)重新将执行失败的 SQL 发送到主库执行。只要第 3 步中的 SQL 在备库执行失败,则同一个事务后续的所有操作(包括只读操作)都会直接发送到主库执行
5)主库执行并返回执行结果给接口,一旦主库上执行的写事务提交,则下次继续从第1 步开始执行
6)接口响应用户并将执行结果返回给用户
<img src="http://60.191.64.5:16100/server/index.php?s=/api/attachment/visitFile/sign/df5e31679f0d3c47a0539f7e5552b55c" alt="" /></p>