数据库-工具-oracle dataguard broker部署示例


前面已经手动一个个敲 Oracle参数部署了dg的测试环境,当然生产上的情况要复杂的多,我们尽可能多摸摸底。今天主要划分以下任务:

  1. dg管理工具broker的使用
  2. broker和手工dg的对比
  3. 性能压力测试

broker是oracle自带的dg管理工具,可以帮助dba快速部署dg,这里做以了解。

select sum(table_rows) from information_schema.tables where table_schema = 'test_schema' ;

select table_name,sum(table_rows)sr from information_schema.tables where table_schema = 'test_schema'  GROUP BY table_name order by sr desc

-- 想了下,测试不需要这样的铺底数据,而是实时同步的情况,所以不需要,我们写个小demo并发插数据就好了

-- 20200521 又想了一下
-- 数据量还是会影响性能的,排除其他因素不考虑(如存储等),delete,update这种还是会受索引数据量的影响,可能会使备库应用慢一下

方便起见,后面我们重新搭起来测试环境后,直接用Navicat同步一下(O(∩_∩)O哈哈~,Navicat也是逻辑同步嘛)

1. broker简介1

Data Guard broker是建立在Data Guard基础上的一个对Data Guard配置,集中管理操作的一个平台。Data Guard为我们提供了一套高可用的解决方案,但是在实际的使用方面确实显得有一些过于复杂,特别是在需要配置的standby机器多的时候更是如此,一个个机器去登陆配置显得特别的麻烦;在需要做switchover或者是failover的时候情况也是一样,需要操作一系列的命令才能完成一次switchover/failover的操作(同时也应该认识这种操作一般很少除非比如机房挂了这种比较严重的主库起不来的)。Data Guard broker的推出就是为了简化DG复杂的管理过程的,它最大的作用就是集中化的统一管理,下面列出来一些Data Guard broker优势所在:

使用broker不使用broker
一般将primary数据库与全部standby数据库看成一个整体进行管理必须对primary数据库和各个standby进行单独的操作。
创建standby通过使用OEM可以轻松的建立一个新的standby所有操作必须手工进行:拷贝数据/控制/日志文件,设置初始化参数等等
配置和管理可以在一个地方对所有的数据库进行统一的配置和管理,这些配置会被自动同步到各个数据库中必须手工的进行配置,然后对Primary和standby进行单独的管理
控制- 使用一个简单的命令进行failover和switchover的操作 - 通过配置可以自动的进行failover操作- 必须使用多个SQLPLUS才能完成对数据库的管理
监控- 自动持续对数据库配置,数据库状态以及其他参数进行自动管理 - 提供详细的数据库状态报告 - 和OEM Events集成- 没有统一的视图进行管理,只能通过Fixed View一个个进行查看 – 自定义OEM Events管理

1.1 broker组成

broker的组成主要分成两大部分,分别是:

1.1.1 客户端组件

客户端组件是一个管理员与broker服务器端组件的接口,用户通过客户端来发出命令对服务器端的行为进行控制。客户端组件由OEMDGMGRL两个组成

  • OEM2Oracle Enterprise Manager):图形化的Oracle管理工具,提供了多个向导功能方便DG的管理工作。

  • DGMGRLData Guard command-line interface): 命令行管理界面,可以通过命令很方便的操作以及监控数据库,命令列表见文档

    1.1.2 服务器端组件

在每个配置了broker的数据库上面都存在一个服务器进程进行broker的管理操作,这个服务器进程就是Data Guard broker monitor(DMON),而这个DMON所用到的所有配置信息都会保留在一个配置文件中。这个DMON进程和配置文件就构成了每个数据库上broker的服务器端组件

  • Data guard broker monitor process(DMON):DMON是一个用来管理broker的后台进程,这个进程负责与本地数据库以及远程数据库的DMON进程进行通讯(与远端数据库的DMON进程进行通讯的时候使用的是一个 动态注册 的service name “db_unique_name_DGB.db_domain”)。这个进程负责维护配置文件的正确性以及不同数据库之间配置文件的一致性。在第一次创建一个broker配置文件或者是将一个数据库加入一个现存的broker配置中的时候DMON会先收集现有数据库的DG配置信息并保存到配置文件中。
  • 配置文件:配置文件有DMON进行操作,它保存了broker管理的所有的数据库的状态信息,以及数据库相关属性(即数据库的初始化参数信息)的信息。同一个broker配置管理下的每个数据库上面都有一份相同的配置文件。

1.2 配置管理

Broker通过将DG环境划分成数据库配置和数据库这两种对象来简化DG的管理工作。

  • 数据库配置对象:数据库配置是一个包含多个数据库信息的集合,这些数据库信息包括数据库对象当前的形态、状态、及属性设置。同时这个集合可以同时混合了物理standby和逻辑standby。
  • 数据库对象:数据库对象指的是Primary和standby数据库。一般情况下一个数据库对象只包含一个实例,但是在RAC系统中一个数据库对象会包含多个实例。

broker通过将一个DG中的Primary数据库及所有的standby数据库逻辑的组成一个逻辑组来进行集中管理,因此每个broker配置就是一个数据库的逻辑集合,它包含了组成数据库的日志传输服务、日志应用服务等逻辑的对象。DBA可以broker来控制这个逻辑集合的配置,改变它的配置,同时还能监控这个组的整体健康状态。

DMON进程负责设置和维护broker配置,有了DMON的维护之后在实际管理中我们只需要把一个broker配置当初单个的单元管理就行了,剩下的工作由DMON去做,因此当执行一个影响到多个数据库的命令的时候,DMON实际上会进行下面的操作:

  1. 在Primary数据库上处理请求。
  2. 协调其他相关数据库上的DMON进程处理相应的请求。
  3. 更新本地系统中的配置文件。
  4. 与其他数据库上的DMON进程通讯以更新各自的配置文件。

每组配置文件中可以包含多个broker配置,但是每个数据库只会维护一组配置文件,因此在RAC环境中,配置文件是由组成这个RAC的各个instance共享的。DMON进程负责各个数据库之间配置文件的同步工作。

DMON进程通过配置文件中设定的数据库的参数来控制数据库的行为,这些属性通常都和数据库的某个DG相关的初始化参数相关联,在通过OEM或DGMGRL修改这些属性的时候,这些属性记录会先保存在配置文件中,然后DMON进程同时也会对相关数据库的参数进行修改,这就要求我们在配置数据库的时候必须使用SPFILE,保证DMON修改之后的参数能保留下来。

2. 使用broker搭建dg环境

环境:

hostnameipdb_namedb_unique_namenet service name
db3172.17.0.4orclprimaryorcl
db4172.17.0.5orclstandbyorcl

2.0 启动数据库

# primary
    docker run -d --name db3 -p 15213:1521 -p 223:22 -p 11583:1158 -v /mnt/hgfs/E/Workspace/Database/Oracle/data/db3:/data/oracle_data -e oracle_allow_remote=true registry.cn-hangzhou.aliyuncs.com/jydsun/oracle11g_ssg:1.04
# standby
docker run -d --name db4 -p 15214:1521 -p 224:22 -p 11584:1158 -v /mnt/hgfs/E/Workspace/Database/Oracle/data/db4:/data/oracle_data -e oracle_allow_remote=true registry.cn-hangzhou.aliyuncs.com/jydsun/oracle11g_ssg:1.04

因为镜像的原因,我这里挂载映射了本地路径,来存放数据文件,减少虚拟机空间。后面dg搭建好后,测试数据要新建个表空间存储。

2.1 broker配置

2.1.0 预先设置

后面发现有些东西还是要先设置好的…

2.1.0.1 主库开启日志
-- 开启日志
SQL> shutdown immediate
SQL> startup mount
SQL> alter database archivelog;
SQL> alter database force logging;
SQL> alter database open;

--查看归档
SQL> archive log list;
SQL> select force_logging from v$database;
-- 设置standby log,日志文件可以设置大点500m
--查看Redo和Standby Redo
SQL> select * from v$logfile;		
SQL> select * from v$log;	

SQL> alter database add standby logfile group 21 '/opt/oracle/app/oradata/orcl/standby21.log' size 50M;
SQL> alter database add standby logfile group 22 '/opt/oracle/app/oradata/orcl/standby22.log' size 50M;
SQL> alter database add standby logfile group 23 '/opt/oracle/app/oradata/orcl/standby23.log' size 50M;
SQL> alter database add standby logfile group 24 '/opt/oracle/app/oradata/orcl/standby24.log' size 50M;
2.1.0.2 生成standby controlfile

简直要哭了呀…

还是要用主库生成的standby controlfile来启动备库才行,好多人都没说明这些,他们是真的在已经部署好的dg上搞的嘛

用rman先同步一次备库…

# 在备库执行,连接目标库和辅助数据库,看上去在那个库都能做这里用的是远程连接?
rman target sys/oracle@primary auxiliary sys/oracle@standby

# 恢复数据文件,standby控制文件,standby日志组
RMAN> duplicate target database for standby from active database nofilenamecheck;

# 注意如果没有用rman,控制文件要手动建的,在主库创建standby controlfile,并复制为备库启动的controlfile
# alter database create standby controlfile as '/data/oracle_data/dataguard/standby.ctl';
# scp /data/oracle_data/dataguard/standby.ctl db2:/data/oracle_data/dataguard/standby.ctl
# cp /data/oracle_data/dataguard/standby.ctl /opt/oracle/app/oradata/orcl/control01.ctl
# cp /data/oracle_data/dataguard/standby.ctl /opt/oracle/app/flash_recovery_areduplicate target database for standby from active database nofilenamecheck;a/orcl/control02.ctl
-- 查下备库状态,已经是physical standby了
SQL> select database_role,protection_mode,protection_level,open_mode from v$database; 

2.1.1 主库启用broker3

-- 查看dg_broker_start参数设置
SQL>  show parameter dg_broker_start;

-- 启用dg_broker_start,启用后oracle会自动启动一个dmon进程
SQL> alter system set dg_broker_start = true;

-- 默认broker配置文件位置
SQL> show parameter dg_broker_config_file;

2.1.2 主库配置listener.ora和tnsnames.ora

broker里面的连接的service_name默认是<db_unique_name>_DGMGRL,所以需要增加一个监听给broker用。

不配置也可以用命令指定

DGMGRL>edit database db1 set property StaticConnectIdentifier= '(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=beijing-fuli-hadoop-01)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=db1)(INSTANCE_NAME=db1)(SERVER=DEDICATED)))'; 

太坑了,broker不能帮助我们自动调整db_unique_name,还是要自己先设置每个库的db_unique_name,保证区分。

-- 主库db3:
SQL> alter system set db_unique_name='primary' scope=spfile;

-- 备库db4:
SQL> alter system set db_unique_name='standby' scope=spfile;

-- 而且这玩意需要重启库才能生效
# 切换监听文件目录
cd /opt/oracle/app/product/11.2.0/dbhome_1/network/admin/ 
# 配置listener.ora
vi listener.ora

# listener.ora Network Configuration File: C:\app\Administrator\product\11.2.0\dbhome_1\network\admin\listener.ora
# Generated by Oracle configuration tools.

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC = 
    	(GLOBAL_DBNAME = orcl)
    	(SID_NAME = orcl) 
    	(ORACLE_HOME = /opt/oracle/app/product/11.2.0/dbhome_1)
    )
    (SID_DESC = 
    	(GLOBAL_DBNAME = primary_DGMGRL)
    	(SID_NAME = orcl) 
    	(ORACLE_HOME = /opt/oracle/app/product/11.2.0/dbhome_1)
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.2)(PORT = 1521))
    )
  )

ADR_BASE_LISTENER = /opt/oracle/app/admin
# 配置tnsnames.ora(和手工一样)
vi tnsnames.ora

# tnsnames.ora Network Configuration File: C:\app\Administrator\product\11.2.0\dbhome_1\network\admin\tnsnames.ora
# Generated by Oracle configuration tools.

ORACLR_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
    (CONNECT_DATA =
      (SID = CLRExtProc)
      (PRESENTATION = RO)
    )
  )

orcl =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.2)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )
  
#primary
primary =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.2)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )
#standby
standby =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 172.17.0.3)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )
# 重加载监听
lsnrctl reload

2.1.3 备库启用broker和配置listener.ora&tnsnames.ora

同主库操作

哇,这不一样好麻烦,痛苦死了

2.1.4 主库创建broker配置文件

# 登陆dgmgrl控制台,primary是在tns配置的
[oracle@f77ebb7cf0da admin]$ dgmgrl sys/oracle@primary
# 创建配置文件,第一个primary是db_unique_name,第二个primary是tnsname
DGMGRL> CREATE CONFIGURATION dgconf01 AS PRIMARY DATABASE IS primary CONNECT IDENTIFIER IS primary; 

# 配置有问题就
# DGMGRL> remove configuration;

这里我备份了一下spfile

SQL> create pfile='/data/oracle_data/pfile_after_create_config.ora' from spfile;

看到这时候因为前面设了db_unique_name,就多了这么一个参数

# 添加备库
DGMGRL> add database standby as connect identifier is standby maintained as physical; 

这里报了个错,

Error: ORA-16831: operation disallowed on this standby database type

Failed.

目测可能是standby日志什么的没开,哎,前面补一下。

然后发现有的参考是这么写的

在配置DG broker之前需要确保Dataguard配置正常且主库和备库均使用spfile.

我都蒙了,都配好了还要broker干啥,方便拓展操作么

哭了呀,弄好了日志也不是,仔细瞅了下,应该是备库不能创建为standby databse,我是直接startup的所以不行,然后停掉尝试mount standby database,然后还不行,controlfile不对,不是standby controlfile…

555~

standby controlfile可以在主库创建复制过来,也可以使用rman同步。索性我们先假装有主库,得先rman同步一次吧

rman完备库是physical standby状态

好了成功了继续吧…555~

# 启用配置
DGMGRL> ENABLE CONFIGURATION; 

# 查看下
DGMGRL> show configuration;

再备份下spfile看看

SQL> create pfile='/data/oracle_data/pfile_after_enable_config.ora' from spfile;

对比下多了以下的参数配置:

# 主库增加以下配置
*.archive_lag_target=0
*.log_archive_config='dg_config=(primary,standby)'
*.log_archive_dest_1='location=USE_DB_RECOVERY_FILE_DEST','valid_for=(ALL_LOGFILES, ALL_ROLES)'
*.log_archive_dest_2='service="standby"','LGWR ASYNC NOAFFIRM delay=0 optional compression=disable max_failure=0 max_connections=1 reopen=300 db_unique_name="standby" net_timeout=30','valid_for=(all_logfiles,primary_role)'
orcl.log_archive_format='%t_%s_%r.dbf'
*.log_archive_max_processes=4
*.log_archive_min_succeed_dest=1
orcl.log_archive_trace=0
*.standby_file_management='MANUAL'

# 备库增加以下配置
*.archive_lag_target=0
*.fal_server='primary'
*.log_archive_config='dg_config=(standby,primary)'
*.log_archive_dest_1='location=USE_DB_RECOVERY_FILE_DEST','valid_for=(ALL_LOGFILES, ALL_ROLES)'
orcl.log_archive_format='%t_%s_%r.dbf'
*.log_archive_max_processes=4
*.log_archive_min_succeed_dest=1
orcl.log_archive_trace=0
*.standby_file_management='MANUAL'

基本还是比较优秀的?对比手工的参数

*.db_unique_name='primary'
*.archive_lag_target=1800
*.fal_client='standby'
*.fal_server='primary'
*.log_archive_config='DG_CONFIG=(primary,standby)'
*.log_archive_dest_1='location=/data/oracle_data/dataguard/log valid_for=(all_logfiles,all_roles) db_unique_name=primary'
*.log_archive_dest_2='service=standby lgwr async noaffirm valid_for=(online_logfiles,primary_role) compression=enable db_unique_name=standby' 
*.log_archive_dest_state_1='enable'
*.log_archive_dest_state_2='enable'
*.log_archive_format='%t_%s_%r.dbf'
*.DB_FILE_NAME_CONVERT='/opt/oracle/app/oradata/orcl','/opt/oracle/app/oradata/orcl'
*.LOG_FILE_NAME_CONVERT='/data/oracle_data/dataguard/log','/data/oracle_data/dataguard/log'
*.standby_file_management='auto'

可以看出来

  1. 这个单纯的是主备库设置,我们之前手工设置还存在主备切换后的配置

  2. *.standby_file_managementMANUAL手动的,一般还是设置AUTO,可以自动备份表空间等变化

  3. 归档日志使用了默认的USE_DB_RECOVERY_FILE_DEST,要注意日志不清理就会爆满了,之前是手工设置了日志保留位置

    SQL> show parameter db_recovery_file_dest
    SQL> show parameter db_recovery_file_dest
    
    NAME				     TYPE	 VALUE
    ------------------------------------ ----------- ------------------------------
    db_recovery_file_dest		     string	 /opt/oracle/app/flash_recovery
    						 _area
    db_recovery_file_dest_size	     big integer 3882M
    
  4. log_archive_dest_state_1这个参数没有自动生成,目测默认是enable

  5. *.DB_FILE_NAME_CONVERT*.LOG_FILE_NAME_CONVERT这个也不会自动生成,这是肯定的,但是我们自己要清楚,这是用来设置路径转换的,主备库路径不一样时需要转换的

2.3 broker管理dg

回想了下,漏了用broker设置保护模式,日志发送方式这些。额有兴趣自己了解下…

2.3.1 管理数据库

  • 操作数据库
# 在primary登陆dgmgrl控制台
dgmgrl sys/oracle

# 启动数据库
DGMGRL> enable database primary;
DGMGRL> enable database standby;

# 瞅下数据库状态,都是success
DGMGRL> show database verbose primary;
DGMGRL> show database verbose standby; 

# 设置standby database 只读
DGMGRL> edit database standby set state='read-only'; 

# 将主库离线: 
DGMGRL> edit database orcl set state='offline'; 
DGMGRL> edit database orcl set state='online'; 
  • 设置数据库参数

broker相当于提供了自己的语法,而且一旦用了broker,就不能用sqlplus来管理。(即用比如用broker修改参数,通过sqlplus查看可以看到参数被修改;但是用sqlplus修改参数,broker却没有同步修改。可以类似认为broker缓存了一份配置,它的管理是在它的缓存操作并通知数据库同步,但是数据库变了它反而不知道)

设置*.standby_file_management

DGMGRL> show database verbose primary StandbyFileManagement;

DGMGRL> edit database primary set property StandbyFileManagement='auto';

2.3.2 管理dg

  • 管理primary database发送日志

    DGMGRL> edit database primary set state='transport-off';
    
    DGMGRL> edit database primary set state='transport-on'; 
    
  • 管理standby database应用日志

    # 停止应用
    DGMGRL> edit database standby set state="APPLY-OFF"; 
    
    # 启用应用
    DGMGRL> edit database standby set state="APPLY-ON"; 
    
  • 管理配置和备库

    # 禁用配置
    disable configuration
    # 删除配置
    remove configuration
    
    # 禁用某个备用库
    disable database 'standby';
    
    # 从配置中删除备用库
    remove database 'standby'
    

2.3.3 主备切换

这里单独从管理dg里拿出来了

2.3.3.1 Switchover

Swithchover通常都是人为的有计划的进行角色互换,比如升级等。它通常都是无损的,即不会有数据丢失。其执行主要分为两个阶段:

  1. Primary转为Standby

  2. Standby之一转为Primary

# 切换备库为主库
DGMGRL> switchover to standby;

# 启动下主备库
DGMGRL> enable database primary;
DGMGRL> enable database standby;

# 查看配置
DGMGRL> show configuration;

到这里忽然就有点香了,手工切换主备库…虽然也就2行命令,总怕哪里搞的不对(其实也不会了,搞清楚就很从容,手工更稳定点没错的),好了这里我们切换成功了,再看一下spfile是不是参数也互换了?

SQL> create pfile='/data/oracle_data/pfile_after_switchover_config.ora' from spfile;

配置文件有所变化:

# 主库变化
*.fal_server='standby'
*.log_archive_dest_2=''
*.log_archive_dest_state_2='ENABLE'

# 备库变化
*.log_archive_dest_2='service="primary"','LGWR ASYNC NOAFFIRM delay=0 optional compression=disable max_failure=0 max_connections=1 reopen=300 db_unique_name="primary" net_timeout=30','valid_for=(all_logfiles,primary_role)'
*.log_archive_dest_state_2='ENABLE'

可以看到

  1. 主库切换为备库后,增加了备库的参数
  2. 备库切换为主库后,增加了主库的参数
  3. log_archive_dest_state这个是用来控制主库日志是否输出的,看上去还是需要的,第一次主库没有自动生成估计是哪里出了小问题吧…

完美,我们先切换回来做些测试。

2.3.3.2 failover

Failover是指由于Primary故障无法短时间恢复,Standby不得不充当Primay的角色,如果处于最高性能模式,这种切换很有可能导致数据丢失。

此时,主库不可用,登陆备库failover。一般这时候需要重建dg,极其痛苦。(要么rman增量备份?)

# 登陆备库failover
DGMGRL> failover to standby;

# 查看dg配置 备库已切换为主库,主库disable
DGMGRL> show configuration;

# 假装主库又起来了,先让它变备库同步这段时间的数据

# 一般这时候需要重建dg,极其痛苦

# 另外了解到如果主备库开启了闪回flashback,只要主库闪回到failover之前,就能用broker自动重建
# flashback还是开着的好,它的大小也会决定能会退的数据,如果短时间大量dml语句,基本就崩了很难回去,但是可以参考手工修复用scn,rman啥的修复?往下不继续测试了,了解下...
DGMGRL> reinstate database primary;

# 再切换主库回来
# 切换备库为主库
DGMGRL> switchover to primary;

# 启动下主备库
DGMGRL> enable database standby;
DGMGRL> enable database primary;

# 查看配置
DGMGRL> show configuration;
2.3.3.3 备库宕机

问题不大,和手工方式类似,手工方式需要重启并手工再开启应用。使用broker不用,备库启动后会自动应用日志。

2.4 扩展总结

无论手工还是broker,我们假设已有dg环境下扩展备库(以broker说明):

  • 主库修改tnsname.ora,增加新备库tns
  • 新备库启用broker
  • 新备库同步主库为standby database(controlfile等)
  • broker configuration增加新备库
  • 在线调整参数

看上去扩展的时候主库不需要离线

3. 压力测试

因为上面测failover把主库搞挂了,用rman增量备份又麻烦,还好现在没啥数据,直接重新用rman全备到db3/primary,然后再切换主备库。

# db3同步db4
rman target sys/oracle@standby auxiliary sys/oracle@primary

# 恢复数据文件,standby控制文件,standby日志组
RMAN> duplicate target database for standby from active database nofilenamecheck;
# 登陆db4,启用primary
dgmgrl sys/oracle@standby

# 启用primary
DGMGRL> enable database primary;

# 查看状态,已经正常了
DGMGRL> show configuration;

# switchover切换
DGMGRL> switchover to primary;

好了,我们的环境又回来了

3.1 新增表空间

一方面新增表空间,datafile映射到主机上,另一方面,这不也可以再次验证dg吗。

首先得调下参数

DGMGRL> edit database primary set property StandbyFileManagement='auto';
DGMGRL> edit database standby set property StandbyFileManagement='auto';

# 还要注意,归档日志在DB_RECOVERY_FILE_DEST,这个大小不够,我们测试调大点不清理

开启闪回

# 备库关闭实时应用日志
SQL> sALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;

# 关闭dg
DGMGRL> disable configuration;

# 主备库开启闪回
SQL> select flashback_on from V$database;

SQL> ALTER SYSTEM SET UNDO_RETENTION=3600;
SQL> ALTER SYSTEM SET DB_FLASHBACK_RETENTION_TARGET=4320;
SQL> alter database flashback on;
SQL> ALTER SYSTEM SET db_recovery_file_dest_size=100G;

# 开启dg
DGMGRL> enable configuration

# 备库开启实时应用日志
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE using current logfile disconnect from session ;

主库表空间操作

SQL> create tablespace test datafile '/data/oracle_data/test_01.dbf' size 30m autoextend on next 128m maxsize 30G ;
SQL> create user test identified by 1 default tablespace test;
SQL> grant connect,resource to test;

备库开启只读

SQL> alter database open;

# 查一下
SQL> select default_tablespace from dba_users where username='TEST';

好的都没啥问题

3.2 并发写入

-- 表
SQL> create table test.test ( id number(30), name varchar2(20) );

写了一个小demo,并发1000线程往数据库写10000条数据,写完去查备库

其实跟1条我觉得差不多,并发是增加了数据库的压力,会反映慢一点。但是一个事务日志完成,就会去写日志到备库,更多的还是在网络延迟上

这里还是没有比较好的效果,1是数据造的简单,本身不大(但是又能有多大呢是吧?),2是在本机网络没啥延迟。其实我觉得还是看应用吧,写不多的情况,等等能看到效果问题不大。(话说现在好多app就连支付宝都会有问题,从理财转到银行卡,理财的金额还在显示,短暂的显示问题不大,而且这边也是做读库,看错了再刷一下说不定就好了)

package com.example;

import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.*;
import java.time.Instant;
import java.util.Properties;
import java.util.concurrent.*;

/**
 * @author theskyzero
 * @create 2020/5/15 下午4:43
 */
public class Test {
    public static void main(String[] args) throws IOException, InterruptedException {
        DruidDataSource source = dataSource();

        TimeUnit unit;
        BlockingQueue workQueue;
        ThreadPoolExecutor threadPoolExecutor = threadPoolExecutor();

        String sql = new String("insert into test(id,name) values(?,?)");
        final int count = 10000;
        CountDownLatch latch = new CountDownLatch(count);
        Instant begin = Instant.now();
        for (int i = 0; i < count; i++) {
            threadPoolExecutor.execute(new T1EveryConn(latch, source));
        }

        latch.await();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:15213:orcl", "test", "1");
                    PreparedStatement pst = connection.prepareStatement("select count(1) ct from test");
                    ResultSet resultSet = pst.executeQuery();
                    if (resultSet.next()){
                        System.out.println(resultSet.getInt("ct"));
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }

            }
        });
        thread.join();
        thread.start();

        Instant end = Instant.now();
        System.out.println(end.toEpochMilli()-begin.toEpochMilli());
        System.out.println(begin);
        System.out.println(end);

    }

    public static ThreadPoolExecutor threadPoolExecutor() {
        final int corePoolSize = 10;
        final int maximumPoolSize = 100;
        long keepAliveTime = 0;
        BlockingQueue workQueue = new LinkedBlockingQueue<>(10);
        return new ThreadPoolExecutor(
                corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }

    public static DruidDataSource dataSource() throws IOException {
        DruidDataSource source = new DruidDataSource();
        source.configFromPropety(dataSourceProperties());
        return source;
    }

    public static Properties dataSourceProperties() throws IOException {
        final String fileName = "druid.properties";
        Properties properties = new Properties();
        properties.load(Test.class.getResourceAsStream(fileName));
        return properties;
    }

    static  class T1EveryConn implements Runnable{
        CountDownLatch latch;
        DataSource source;
        private  static String sql = new String("insert into test(id,name) values(?,?)");

        T1EveryConn(CountDownLatch latch, DataSource source){
            this.latch = latch;
            this.source = source;
        }

        @Override
        public void run() {
            Connection connection = null;
            PreparedStatement pst = null;
            try {
                connection = source.getConnection();
                pst = connection.prepareStatement(sql);
                int finalI = 1;
                pst.setInt(1, finalI);
                pst.setString(2, finalI + "");
                pst.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                if (null != pst) {
                    try {
                        pst.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                latch.countDown();
            }
        }
    }

    static  class T2OneConn implements Runnable{
        CountDownLatch latch;
        DataSource source;
        private  static String sql = new String("insert into test(id,name) values(?,?)");

        T2OneConn(CountDownLatch latch, DataSource source){
            this.latch = latch;
            this.source = source;
        }

        @Override
        public void run() {
            Connection connection = null;
            PreparedStatement pst = null;
            try {
                connection = source.getConnection();
                pst = connection.prepareStatement(sql);
                int finalI = 1;
                pst.setInt(1, finalI);
                pst.setString(2, finalI + "");
                pst.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                if (null != pst) {
                    try {
                        pst.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                latch.countDown();
            }
        }
    }
}

# druid.properties
druid.db-type=com.alibaba.druid.pool.DruidDataSource
druid.driverClassName=oracle.jdbc.OracleDriver
druid.url=jdbc:oracle:thin:@localhost:15213:orcl
druid.username=test
druid.password=1

druid.initialSize=3
druid.minIdle=3
druid.maxActive=10000
druid.maxWait=5000


  1. 总体上dg可能麻烦在部署过程,运维过程中基本是用不着的。broker相当于半自动生成手工部署的参数步骤等,多了解一点也好。 ↩︎

  2. 这玩意也是要选择部署的,瞅了一下我的镜像没有开,哎,还是用命令行DGMGRL吧 ↩︎

  3. 为什么用主库开启呢?从上面的介绍可以看到这个也会占用一部分资源的,而且感觉更像是个单独的工具,不需要和主库绑在一起吧,在主库上是为了更快完成主库发送日志吗?实际会部署在附近的备库吗? ↩︎

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theskyzero

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值