How to modify timezone settings in Docker Container

我们之前从Oracle官方docker仓库中生成的Oracle 19.3镜像,默认的时区并非是中国时区,这会让在查看alertlog等日志文件时比较困惑。

那么如何修改容器中的时区呢?

确认容器中已经包含了tzdata包

$ docker exec -it oracle19c_new rpm -qa|grep tz
tzdata-2019a-1.el7.noarch

通过-e参数设置容器运行的时区参数

$ docker run --name oracle19c_new \
-p 11521:1521 -p 15500:5500 \
-v /Users/Kamus/oracle/oradata/oracle19c:/opt/oracle/oradata \
-e TZ=Asia/Chongqing \
-e ORACLE_SID=ORCLNEW \
oracle/database:19.3.0-ee

检查新生成的容器的时区

$ docker exec -it oracle19c_new date +"%Z %z"
CST +0800

CST+0800表示东8时区,正是设置的中国时区。

如何在MarsEdit中使用Markdown编写文章

一直在寻找一个适合编写完毕以后直接发布到Wordpress中,同时又能完美支持Markdown语法的工具,尝试过Ulysess还有Typora,Ulysess本身对于Markdown的支持很奇怪,当粘贴的代码中有一些Markdown语法的关键字时,会出现很难编辑的情况;而Typora本身确实是很好的Markdown编辑器,但是无法直接将本地编写的文章一键发布到Wordpress中是致命弱点。

Setapp中近期引入了MarsEdit,这是一个较为著名的Blog编写工具,一键发布到Wordpress中是这类工具的标准功能,而稍作配置之后,又可以完美支持Markdown语法。

设置MarsEdit

首先需要设置MarsEdit
NewImage

NewImage

设置Wordpress

还需要设置Wordpress。在Wordpress中首先先安装Jetpack插件,实际上Jetpack已经基本上变为Wordpress的标准插件了。
在Jetpack插件的设置界面,将“使用Markdown语法编写纯文本文章”的选项勾上。
NewImage

用MarsEdit编写文章的优点

在MarsEdit中可以混用HTML和Markdown标志来编写文章,因此一些容易记忆的Markdown语法,比如标题,这是我最常用的Markdown语法,没有之一。比如代码块,对于写技术文章的人来说,代码块是非常方便的。

Markdown示例

以下是代码块的例子。

package main

import (
    "fmt"
    "database/sql"
    _ "gopkg.in/goracle.v2"

How to build and run Oracle Database 19c on Docker

Oracle官方已经正式发布了Oracle 19.3的dockerfile,在自己的笔记本上安装Oracle数据库,docker方式已然成为最简单的方式之一。另外的一种方式是rpm安装,但是要求操作系统是Linux。对于使用macOS的用户来说,Oracle on docker是非常优秀的体验和测试Oracle数据库的方法。

下载Oracle 19.3.0 for Linux安装盘

在OTN网站上下载最新的Oracle Database 19c for Linux x86-64的安装盘。

下载官方dockerfile

在任意目录下通过git方式将dockerfile下载到本地。这里我们创建了~/oracle目录。

mkdir ~/oracle cd ~/oracle git clone https://github.com/oracle/docker-images.git

将下载的安装盘拷贝到dockerfile相同目录下

cp LINUX.X64_193000_db_home.zip ~/oracle/docker-images/OracleDatabase/SingleInstance/dockerfiles/19.3.0/

构建docker镜像


$ cd ~/oracle/docker-images/OracleDatabase/SingleInstance/dockerfiles 
$ ./buildDockerImage.sh -v 19.3.0 -e 

完成以后可以看到已经有成功构建的Oracle 19c docker image了,同时构建了Oracle Linux 7的基础镜像。


$ docker image ls 
REPOSITORY TAG IMAGE ID CREATED SIZE 
oracle/database 19.3.0-ee 04c75bcbb886 4 minutes ago 6.64GB
oraclelinux 7-slim f7512ac13c1b 3 weeks ago 118MB 

运行容器

完整的运行指南可以在git下来的官方dockerfile目录中找到。

docker-images/OracleDatabase/SingleInstance/README.md 

也可以从github页面中直接阅读 – Oracle Database on Docker

我们创建一个目录,以存储Oracle数据文件。

mkdir -p ~/oracle/oradata/oracle19c 

在第一次运行容器的时候,会自动创建新的数据库,使用-v参数,将之前新创建的目录映射到容器内的/opt/oracle/oradata目录中,这样就完成了将数据文件存储在本机文件系统中,而非docker容器内。

docker run --name oracle19c \
-p 1521:1521 \
-p 5500:5500 \
-v /Users/Kamus/oracle/oradata/oracle19c:/opt/oracle/oradata \
oracle/database:19.3.0-ee 

执行改命令之后,可以注意到第一行的回显列出了自动生成的SYS等用户的密码,本文中是sSNc5GFeSAg=1。

ORACLE PASSWORD FOR SYS, SYSTEM AND PDBADMIN: sSNc5GFeSAg=1 

如果需要修改数据库用户密码,可以在容器运行之后,通过以下命令修改。

docker exec <container name> ./setPassword.sh <your password> 

比如下面的例子,将数据库sys用户密码设置为简单的oracle(当然这不推荐)。

$ docker exec oracle19c ./setPassword.sh oracle
The Oracle base remains unchanged with value /opt/oracle

SQL*Plus: Release 19.0.0.0.0 - Production on Tue May 21 15:30:50 2019
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.


Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL>
User altered.

SQL>
User altered.

SQL>
Session altered.

SQL>
User altered.

SQL> Disconnected from Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

一直到出现以下字样,表示数据库已经正常创建并且可以使用了。

######################### DATABASE IS READY TO USE! ######################### 

默认创建的数据库SID是ORCLCDB,创建的PDB是ORCLPDB1,也可以在第一次docker run的时候,用-e参数来指定SID和PDB的名字。比如:

docker run --name new-oracle19c \ 
-p 1521:1521 -p 5500:5500 \ 
-e ORACLE_SID=ORCL \ 
-e ORACLE_PDB=MYPDB1 \ 
-v /Users/Kamus/oracle/oradata/oracle19c:/opt/oracle/oradata \
oracle/database:19.3.0-ee 

测试容器中的Oracle 19c数据库

在成功运行完docker run命令以后,可以看到容器已经正常运行。

$ docker ps | grep oracle 
b03dae342bf5 oracle/database:19.3.0-ee "/bin/sh -c 'exec $O…" About an hour ago Up About an hour (healthy) 0.0.0.0:1521->1521/tcp, 0.0.0.0:5500->5500/tcp oracle19c 

直接登录容器使用sqlplus做简单的验证。注意使用之前docker run时候回显的用户密码。


$ docker exec -it oracle19c /bin/bash

SQL*Plus: Release 19.0.0.0.0 - Production on Mon May 6 04:42:04 2019
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.


Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL> show con_id

CON_ID
------------------------------
3
SQL> 

也可以在浏览器中登录Oracle 19c内置的EM Express来通过图形界面访问和监控。

https://localhost:5500/em

Em express 19c

Popularity of open source DBMS versus commercial DBMS (by DB-Engines)

2018年盘点商业数据库VS.开源数据库 – Updated@2019-05-16

本文来源于DB-Engines的年度特别报告-Popularity of open source DBMS versus commercial DBMS。原文链接如下: https://db-engines.com/en/ranking_osvsc

被定义为开源系统的数据库,是全部能够自由获取源码,并且可以在遵循某种许可协议的情况下进行自由使用和改写的。

数据库个数

NewImage
DB-Engines总共列出了347种数据库,其中178种为商业数据库,169种为开源数据库,基本上可以认为是五五开的局面。

数据库流行度

NewImage
根据当年DB-Engines每个月的数据库排名分数进行的分数计算,商业数据库险胜开源数据库,但是分数相差无几。

流行度趋势

NewImage
从2013年开始统计了每一年的流行度,商业数据库一路下行,而开源数据库扶摇直上,从5年前只有1/3的流行度到今年跟商业数据库几乎平分天下,开源产品可谓是改变了世界。

按照数据库类型区分的流行度

NewImage
在宽列数据库,时间序列数据库,文本数据库,键值数据库,搜索引擎,图像数据库这几类数据库中,开源产品均领先于商业产品,很明显,互联网和互联网应用的兴起,海量数据存储和非结构化数据存储的需求,极大地促进了开源产品的产出和受欢迎程度。而从传统的关系型数据开始往后,包括XML数据库,面向对象的数据库等,仍然是商业数据库更受欢迎。

排名前5的商业数据库

NewImage
Oracle依然排名第一,但是微软在云端的更早发力,让SQL Server已经跟Oracle相差无几。

排名前5的开源数据库

NewImage
MySQL仍然排名第一,但是不可否认从2017年开始,PostgreSQL正在迅速地获得更多关注。

Oracle Announces Availability of Oracle Autonomous Transaction Processing(ATP)

在8月7日,Oracle宣布在云上正式上线自治事务处理数据库,这个名词ATP应对于早前发布的ADW(今年3月份Oracle宣布正式上线的自治数据仓库数据库)。 完整的发布会视频链接在这里

全自动

Atp1
Nothing to Learn, Nothing to Do. 这句宣传语在Larry的演讲中被重复了很多次。我们可以将自治数据库想象成自动驾驶,在没有自动驾驶之前,驾驶员要开一辆车从A地到B地,需要先考取驾照,还需要学习很多技巧,并且在开车的过程中仍然需要集中精力,否则容易出差错。但是有了自动驾驶(理想状态中的自动驾驶),我们不再需要驾驶员,乘客只需要告诉车辆我要从A地到B地,车辆就自动寻找最佳路线行驶了,在这个体验中,乘客完全不需要学些任何驾驶技巧,也不需要做任何驾驶的事情。这就是Nothing to Learn, Nothing to Do,那么在最新的Oracle自治数据库中,传统的DBA工作也不再需要了。安装、扩展、优化、安全、高可用、备份恢复等等,这些以往高深的技巧,现在统统不再需要学习,也不再需要DBA手工去做了。 恭喜大家,在传统DBA的职业道路上离失业又近了一步。

真.弹性

Atp2
Larry在整个演讲中,不知道说了多少次AWS,基本上就是盯着AWS打。只会为使用到的基础架构资源付费,没用到的不用付钱。如果把数据库从AWS上迁移到Oracle Cloud上的自治数据库里,承担的成本将减半,特意重点标注了“Guaranteed”,Larry拍着胸脯保证,账单数字一定会减半。

是骡子是马拉出来遛

Atp3
Atp4
Atp5
Larry接着连续用三页PPT叫板AWS,甚至在已经播放到下一环节以后,又请播放人员退回到上一页,持续嘲讽AWS。这就像60等级时候的魔兽世界,熟悉的“3破输出!”,Larry就是战士坦克,释放3个破甲,建立好仇恨,现在,DPS们可以输出了。 Oracle自治数据库,比AWS数据库快5-10倍。Larry,快5-10倍是什么意思?就是我用10秒钟干活的活儿,你AWS要干1分钟,在按照分钟计费的Cloud数据库中,这就意味着我比AWS要便宜5-10倍。 在纯的在线交易测试中,Oracle ATP比Aurora快12倍,在混合负载场景下,Oracle ATP比Aurora快100倍。在这一页上,Larry疯狂嘲讽了AWS,他说,AWS让别人用Aurora,用Redshift,但是他们自己却在用Oracle,在10年前,AWS就说要弃用Oracle数据库,但是到现在他们也没有做到。Larry还顺道一并嘲讽了SAP和Salesforce,他说这几个哥们儿都特别不想用Oracle数据库,但是怎么去也去不掉,因为这件事情真的很难很难,还因为Oracle数据库真的很优秀很优秀。 Oracle自治数据库可以在运行的同时实现安全补丁安装,可以让数据库获得99.995%的可用性,而AWS则做不到这一点,他们没办法在运行的时候为数据库打补丁。因此Oracle比Amazon稳定100倍。好吧,虽然我不知道这100倍的数字是怎么来的,但是Larry显得自信满满。

就是比AWS便宜

Atp6
整个演讲中间还有不少页,提及了Oracle自治数据库的优点,不过本来在这个世界上也确实没有比Oracle数据库单个解决方案更优秀的数据库产品了,因此Oracle数据库只是不断在超越自己,我们就不看了。我们总说,Oracle数据库除了贵没别的毛病,所以Larry在演讲快结尾的时候又再次提及了,这是我们写下来的承诺,能够满足客户业务负载的成本至少比Amazon便宜50%。

人干不过机器

Atp7
Larry在演讲的最后几分钟,举了NetSuite的案例。虽然PPT上的数字有些语焉不详,比如原来的8151个索引和下面的人类专家使用的4663个索引是什么关系?专家调优过的运行时间1172秒是运行了上面全部17542的SQL的总时长吗?但是,没关系,Oracle想说的是,Oracle ATP能够全自动地分析负载并创建更少更合适的索引,获得跟专家调优相近甚至更优的性能。 虽然为了公平起见,我们还是要提一下NetSuite是Oracle收购了的ERP厂商,但是毋庸置疑,人类传统DBA们面临了越来越多的挑战,就像前几天看到的新闻,DOTA2专业电子竞技高手被AI控制的机器完爆,打得找不到北。

免费品尝

Atp8
从Larry发表演讲的那一刻开始,就可以直接在Oracle Cloud网站申请免费测试Oracle ATP,但是在国内访问Oracle Cloud网站,特别是创建了免费账户以后登录进去的网站,遭遇了很强的阻碍。至少到目前为止,我还没有能成功地打开登录之后的第一个页面。欢迎期待我们测试以后的后续文章。

明年19c

Atp9
Larry简短地预报了Oracle数据库的路线图,在2019年一月份,将会发布Oracle 19c,还停留在11g时代的兄弟们,是不是应该感到惭愧了?

All Secondary die, Primary hang? – SQL Server 2017 on Linux

关于AVAILABILITY_MODE

需要注意的是,与Oracle Data Guard不尽相同的概念是,在Always On AG中每个replica上都可以设置自己的AVAILABILITY_MODE。
AVAILABILITY_MODE参数有三个可选值,分别是SYNCHRONOUS_COMMIT、ASYNCHRONOUS_COMMIT和CONFIGURATION_ONLY。

  • SYNCHRONOUS_COMMIT:同步提交,意味着主replica的事务必须等到备replica将变更日志写入磁盘中才可以提交。可以设置包括主replica在内的最多三个replica处于同步提交状态。
  • ASYNCHRONOUS_COMMIT:异步提交,意味着replica无需等待备replica的动作而可以直接提交成功。
  • CONFIGURATION_ONLY:仅同步AG配置元数据。设置为该值的replica仅会从主replica中将AF配置的元数据同步过来,不会同步任何用户表数据。

在一个多节点replica的AG环境中,如果:

  • 主库和其中任何一个备库设置为SYNCHRONOUS_COMMIT,则主库的日志提交必须等待该备库完成日志写入;
  • 主库设置为SYNCHRONOUS_COMMIT,而所有备库都设置为ASYNCHRONOUS_COMMIT,则主库无需等待;
  • 主库设置为ASYNCHRONOUS_COMMIT,则无视备库上该参数的设置,主库均无需等待。

在多节点的AG环境中,假设一个主库配置了两个同步的secondary,那么是不是要等待这两个secondary都完成日志写入才能提交事务呢?此时又引入了required_synchronized_secondaries_to_commit参数。

关于required_synchronized_secondaries_to_commit

required_synchronized_secondaries_to_commit参数是在SQL Server 2017中引入的,这个参数从直观意义上就可以看得出是指定当commit的时候需要有个几个同步的secondary replica存活。

这个参数在三节点的AG集群中,默认值为1,也就是如果至少要存活一个secondary replica,主库上的事务才可以提交,否则commit就会一直等待。这很好理解。
但是不好理解的是,该参数可以手工修改为0,从字面上看应该是说,即使所有secondary replica都不同步了,也是可以允许commit的。

但是实际情况却并非如此,修改为0是不起作用的。通过以下测试可以知道。

首先,设置required_synchronized_secondaries_to_commit参数为0

sudo pcs resource update ag_cluster required_synchronized_secondaries_to_commit=0
[Kamus@centos1 ~]$ sql
1> select name,required_synchronized_secondaries_to_commit from sys.availability_groups;
2> GO
name required_synchronized_secondaries_to_commit
------------------------------ -------------------------------------------
ag1                            0
(1 rows affected)

现在三个节点都是正常状态。

1> select r.replica_server_name,r.availability_mode_desc,r.session_timeout ,rs.connected_state_desc
2> from sys.availability_replicas r,sys.dm_hadr_availability_replica_states rs
3> where r.replica_id=rs.replica_id;
4> GO
replica_server_name            availability_mode_desc         session_timeout connected_state_desc
------------------------------ ------------------------------ --------------- ------------------------------
centos1                        SYNCHRONOUS_COMMIT             10 CONNECTED
centos2                        SYNCHRONOUS_COMMIT             10 CONNECTED
centos3                        SYNCHRONOUS_COMMIT             10 CONNECTED
(3 rows affected)

在主节点上进行Insert,可以成功,这很好。

1> insert into t1 select * from sys.databases;
2> GO
(6 rows affected)

停掉一个secodary replica。显示第二个节点已经DISCONNECTED。

1> select r.replica_server_name,r.availability_mode_desc,r.session_timeout ,rs.connected_state_desc
2> from sys.availability_replicas r,sys.dm_hadr_availability_replica_states rs
3> where r.replica_id=rs.replica_id;
4> GO
replica_server_name availability_mode_desc session_timeout connected_state_desc
------------------------------ ------------------------------ --------------- ------------------------------
centos1 SYNCHRONOUS_COMMIT 10 CONNECTED
centos2 SYNCHRONOUS_COMMIT 10 DISCONNECTED
centos3 SYNCHRONOUS_COMMIT 10 CONNECTED
(3 rows affected)

在主库上进行Insert,还是可以成功,这很好。

1> insert into t1 select * from sys.databases;
2> GO
(6 rows affected)

再停掉一个secodary replica。显示2、3节点都已经DISCONNECTED。

1> select r.replica_server_name,r.availability_mode_desc,r.session_timeout ,rs.connected_state_desc
2> from sys.availability_replicas r,sys.dm_hadr_availability_replica_states rs
3> where r.replica_id=rs.replica_id;
4> GO
replica_server_name availability_mode_desc session_timeout connected_state_desc
------------------------------ ------------------------------ --------------- ------------------------------
centos1 SYNCHRONOUS_COMMIT 10 CONNECTED
centos2 SYNCHRONOUS_COMMIT 10 DISCONNECTED
centos3 SYNCHRONOUS_COMMIT 10 DISCONNECTED
(3 rows affected)

在主库上执行Insert,此时hang住,这很不好。

1> insert into t1 select * from sys.databases;
2> GO

更讨厌的是,对于该表的查询也会hang住,这就更不好了。

2> select count(*) from t1;
3> GO

现在数据库中的等待是什么呢?确实是HADR_SYNC_COMMIT。

1> select STATUS,COMMAND,DATABASE_ID,WAIT_TYPE,WAIT_TIME from sys.dm_exec_requests where command='INSERT';
2> GO
STATUS COMMAND DATABASE_ID WAIT_TYPE WAIT_TIME
------------------------------ ------------------------------ ----------- ------------------------------ -----------
suspended INSERT 5 HADR_SYNC_COMMIT 1670
(1 rows affected)

如果我们更进一步做一个session的xevent trace,可以看到等待的是WaitForHarden,而Harden的意思即是remote replica的日志写入。现在主库在等待一个备库的日志完成写入,然后自己才能提交成功。在正常情况下,当主库不再需要等待备库而可以自行commit的情况下,在xevent trace中应该出现将备库的commit_policy标志为donothing状态,也就是在xevent中应该要出现hadr_db_partner_set_policy事件才是正常的,然而这里并没有出现。

但是我们明明把required_synchronized_secondaries_to_commit参数设置为0了,这很违反常识,不是吗?

结论

在SQL on Linux中如果设置了availability_mode为SYNCHRONOUS_COMMIT,那么必须至少有一个secondary replica(或者一个config node)是存活的,否则priamry replica中就不再允许任DML操作,而尝试对于某表进行DML之后,还会进一步阻塞对于该表的查询。即使设置了required_synchronized_secondaries_to_commit=0也是无效的。也许微软需要更新一下文档,明确说明在多个sync的secondary存在的情况下,该参数即使修改为0也仍然按照1来处理。
这是一个很奇怪的design,因为这强制去掉了当一个集群中所有备库都崩溃时,主库能够自动转为异步提交模式的功能,从而造成了所有备库失效则会影响主库业务正常进行这样一个大问题。
实际上这个design是在SQL on Linux 2017 CU1之后才修改的,在CU1之前还是允许当所有备库都失效以后,主库仍然是可以正常读写的。甚至在现在的文档中仍然保留了这样的描述。


以上文档描述来自:High availability and data protection for availability group configurations

这意味着在SQL Server 2017 CU 1之后,不再支持单纯的read-scale功能的AG了。虽然不太理解微软的SQL Server程序员是怎么考虑这个问题的,但是现状就是如此。

相比起Oracle的Data Guard而言,也就是现在SQL Server的AG只有同步(等同于DG的Maximum protection),异步(等同于DG的Maximum performance)这样两种方式,而缺少了DG的Maximum availability模式。
我只能认为这是一个设计理念的问题,微软的程序员更倾向于关注数据一致性,但是我期望在未来SQL Server可以对此进行改进。

Configuring Always On Availability Groups in SQL Server 2017 on Linux

在之前的预览版中,Public preview of the next release of SQL Server on Linux,Always On Availability Groups还是不支持的功能,但是在最新的SQL Server 2017 on Linux中,该功能已经引入。

准备测试环境的服务器

在Always On AG中如果需要自动Failover至少需要集群中有3台服务器,但是我只是测试功能,因此只使用了两台服务器。并且本文不涉及任何Pacemaker的设置,完全是数据库层面的AG配置。 我使用的是Google Compute Engine的2台VM,最低配的1vCPU,3.75GB内存。 如果要通过远程客户端配置SQL Server,则需要在VPC network的Firewall rules中将1433端口开放,如果是在虚拟机本地的sqlcmd中操作,则无需配置。 操作系统:CentOS7

$ cat /etc/centos-release CentOS Linux release 7.4.1708 (Core) 

在/etc/hosts中配置双方服务器的名称和IP地址的解析,以保证两台机器可以通过服务器名称互相访问。 重要!服务器主机的hostname必须少于等于15个字符,否则在配置过程会出现各种莫名其妙的权限报错。

安装SQL Server

SQL Server for Linux的安装非常简单,可以参考我之前的这篇文章。 Public preview of the next release of SQL Server on Linux

启用AlwaysOn AG功能

执行范围:在所有机器上执行 安装完的SQL Server,默认是没有启用AlwaysOn AG功能的,需要手工开启,开启的方法很简单。开启该功能需要重启数据库实例。

sudo /opt/mssql/bin/mssql-conf set hadr.hadrenabled 1 sudo systemctl restart mssql-server 

启用AlwaysOn_health事件

执行范围:在所有机器上执行 这一步不是必须的。

ALTER EVENT SESSION AlwaysOn_health ON SERVER WITH (STARTUP_STATE=ON); 

创建数据库复制的用户

执行范围:在所有机器上执行

CREATE LOGIN dbm_login WITH PASSWORD = ‘YourPassword’; CREATE USER dbm_user FOR LOGIN dbm_login; 

创建认证

执行范围:在Primary Replica机器上执行

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'YourPassword'; CREATE CERTIFICATE dbm_certificate WITH SUBJECT = 'dbm'; BACKUP CERTIFICATE dbm_certificate TO FILE = '/var/opt/mssql/data/dbm_certificate.cer' WITH PRIVATE KEY ( FILE = '/var/opt/mssql/data/dbm_certificate.pvk', ENCRYPTION BY PASSWORD = 'YourPassword' ); 

将生成的dbm_certificate.cer和dbm_certificate.pvk文件scp到另外一台服务器的相同位置并修改属主,这台服务器就是Secondary Replica。

cd /var/opt/mssql/data chown mssql:mssql dbm_certificate.* 

然后在这台服务器上导入认证。 执行范围:在Secondary Replica机器上执行

CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'YourPassword'; CREATE CERTIFICATE dbm_certificate AUTHORIZATION dbm_user FROM FILE = '/var/opt/mssql/data/dbm_certificate.cer' WITH PRIVATE KEY ( FILE = '/var/opt/mssql/data/dbm_certificate.pvk', DECRYPTION BY PASSWORD = 'YourPassword' ); 

创建数据库复制的Endpoint

执行范围:在所有机器上执行

CREATE ENDPOINT [Hadr_endpoint] AS TCP (LISTENER_PORT = 5022) FOR DATA_MIRRORING ( ROLE = ALL, AUTHENTICATION = CERTIFICATE dbm_certificate, ENCRYPTION = REQUIRED ALGORITHM AES ); ALTER ENDPOINT [Hadr_endpoint] STATE = STARTED; use master GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [dbm_login]; 

创建Availability Groups

执行范围:在Primary Replica机器上执行

CREATE AVAILABILITY GROUP [ag1] WITH (CLUSTER_TYPE = EXTERNAL) FOR REPLICA ON N'centos1' WITH ( ENDPOINT_URL = N'tcp://centos1:5022', AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, FAILOVER_MODE = EXTERNAL, SEEDING_MODE = AUTOMATIC ), N'centos2' WITH ( ENDPOINT_URL = N'tcp://centos2:5022', AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, FAILOVER_MODE = EXTERNAL, SEEDING_MODE = AUTOMATIC ); ALTER AVAILABILITY GROUP [ag1] GRANT CREATE ANY DATABASE; 

在主库上创建了AG之后,备库需要加入AG。 执行范围:在Secondary Replica机器上执行

ALTER AVAILABILITY GROUP [ag1] JOIN WITH (CLUSTER_TYPE = EXTERNAL); ALTER AVAILABILITY GROUP [ag1] GRANT CREATE ANY DATABASE; 

将数据库加入AG

这里新建一个数据库db1,将它加入到ag1中。由于上面设置的SEEDING_MODE参数为AUTOMATIC,因此这个db1数据库将会在备库实例中自动创建,后续对于该库进行的任何操作也会自动复制到备库中。 执行范围:在Primary Replica机器上执行

CREATE DATABASE [db1]; ALTER DATABASE [db1] SET RECOVERY FULL; BACKUP DATABASE [db1] TO DISK = N'/var/opt/mssql/data/db1.bak'; ALTER AVAILABILITY GROUP [ag1] ADD DATABASE [db1]; 

允许Secondary Replica可以被只读访问

在以上的创建过程中创建出来的AG中的备库是不允许被访问的,如果要访问将会遇到以下错误。 The target database, ‘db1’, is participating in an availability group and is currently not accessible for queries. Either data movement is suspended or the availability replica is not enabled for read access. To allow read-only access to this and other databases in the availability group, enable read access to one or more secondary availability replicas in the group. For more information, see the ALTER AVAILABILITY GROUP statement in SQL Server Books Online. 执行范围:在Primary Replica机器上执行,立刻生效。

use master ALTER AVAILABILITY GROUP ag1 MODIFY REPLICA ON N'centos2' WITH ( SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL ) ); 

测试

在主库中随便创建一张新表,再插入几条记录。

1> use db1 2> select * into t_test from sys.databases; 3> insert into t_test select * from t_test; 4> GO Changed database context to 'db1'. (5 rows affected) (5 rows affected) 

在备库中查询,这张表已经复制成功。

1> use db1 2> select count(*) from t_test; 3> GO Changed database context to 'db1'. ------- 10 (1 rows affected) 

如果在备库中尝试更新数据,将会遇到以下错误。

1> delete from t_test; 2> GO Msg 3906, Level 16, State 2, Server centos2, Line 1 Failed to update database "db1" because the database is read-only. 

监控AG状态

通过以下这些视图可以监控AG中各个部分的状态。 group的监控

select * from sys.availability_groups; select * from sys.availability_groups_cluster; select * from sys.dm_hadr_availability_group_states; 

replica的监控

select * from sys.availability_replicas; select * from sys.dm_hadr_availability_replica_states; select * from sys.dm_hadr_availability_replica_cluster_nodes; select * from sys.dm_hadr_availability_replica_cluster_states; 

在AG中的database的监控

select * from sys.availability_databases_cluster; select * from sys.dm_hadr_database_replica_states; select * from sys.dm_hadr_database_replica_cluster_states; select name,database_id,replica_id,group_database_id from sys.databases; 

参考文档

本文配置步骤的参考文档为: https://docs.microsoft.com/en-us/sql/linux/sql-server-linux-availability-group-configure-ha?view=sql-server-linux-2017

Oracle 18c New Features documentation released

为什么从Oracle 12c直接跳到Oracle 18c

这是很多人还在疑惑的问题。实际上Oracle 18c相当于Oracle 12c Release 2的初始版本12.2.0.1的升级版本12.2.0.2。之所以改名为18c,是因为Oracle修改了版本发布计划,将之前每隔2年发布一个新版本的方式修改为每隔1年发布一个版本,因此18c可以认为是2018年的Oracle数据库版本,之后应该会持续用19,20这样的版本号来发布。

Oracle 12c Release 2有什么新功能

在谈Oracle 18c新功能之前,先回顾一下Oracle 12cR2的新功能。除了增强在12cR1版本中引入的多租户(Multitenant)以及混合列式缓存(Database In-Memory)之外,崭新的功能是Oracle Database Sharding,这是专门为超大事务量应用提供的具备高扩展和高可用性的数据库分片功能。

实际上Oracle 12cR2引入了超过600项新功能,详细信息可以参看官方文档:New Features guide for Oracle Database 12c Release 2

Oracle Database 18c的官方文档在哪里

官方已经释出Oracle Database 18c的系列文档。建议大家从New Features开始阅读,本文后续部分的18c新功能解读也都基于该官方文档。

更多的Oracle Database 18c技术文章

在我们的blog.enmotech.com网站中已经有大量的关于Oracle 18c新特性的介绍文章,并且在18c官方文档放出以后,我们也会持续更新更多的技术细节文章,欢迎大家阅读。Oracle 18c New Features Articles by Enmotech.com

Moving MySQL Group Replication instances to Docker container on macOS

前提条件,已经在自己的macOS操作系统中设置成功了都运行在本地的3节点MySQL Group Replication集群,如何设置,可以参考官方文档中的Deploying Group Replication in Single-Primary Mode。 假设在我们的macOS上已经实现了如下了架构的3节点MGR集群,我们的目标是将这三个数据库全部挪到docker容器中去运行,并且可以互相通信,仍然是MGR集群。

Docker on macOS

接下来我们把MySQL放在一边,先介绍一下在macOS中的docker架构,在macOS中,docker的实现跟在其它Linux系统中略有不同,在其它Linux系统中,操作系统本身就是docker容器的宿主机,docker镜像都是直接存储在宿主机本身的文件系统中,比如我们通过docker info命令可以看到docker的根目录是:

$ docker info|grep "Docker Root Dir" Docker Root Dir: /var/lib/docker 

但是在macOS下,我们直接查看这个目录,其实是根本不存在的。

$ ls /var/lib/docker ls: /var/lib/docker: No such file or directory 

Docker宿主机是谁

那么这个目录到底在哪里?实际上是在一个QEMU虚拟机中,当我们在macOS中安装完docker并启动,就是启动了一个虚拟机,这个虚拟机的整个内容全部都在一个文件里,可以在docker程序属性界面中看到这个文件的路径,比如在我的机器上,路径就是/Users/Kamus/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2,该文件通常比较大,特别是安装了多个docker镜像之后,会轻易占用到数十GB的空间。 如上图所示,就是Disk image location的位置。 从上图中同样可以看到,这个虚拟机被限制只能使用2颗CPU和2GB内存。 那么,我们是否可以登录这个虚拟机来确认这点呢?使用macOS自带的screen命令可以登录该台虚拟机。如下,可以看到这是一个拥有非常新的Linux 4.9.38版本内核的虚拟机,在这个虚拟机中才有/var/lib/docker目录,只有2颗CPU,总共有2GB内存。

$ screen /Users/Kamus/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/tty / # uname -a Linux moby 4.9.38-moby #1 SMP Wed Jul 26 10:02:46 UTC 2017 x86_64 Linux / # hostname moby / # ls /var/lib/docker aufs containers network swarm tmp-old volumes builder image plugins tmp trust / # cat /proc/cpuinfo|grep "processor" processor : 0 processor : 1 / # cat /proc/meminfo |grep "MemTotal" MemTotal: 2047040 kB 

在screen的窗口按组合键control+a d(先按control+a,再按d)可以暂时dettach出这个screen,screen -r可以重新打开窗口。更多的screen命令,可以自行man screen来查看。 所以现在我们可以明确一个概念,macOS本身并不是以后将运行的docker容器的宿主机,而这个Linux虚拟机才是真正的宿主机。这台机器的主机名是moby,这正是docker项目社区版的名称。

Docker宿主机与macOS操作系统的目录共享

由于如下共享文件夹功能的存在,在这个虚拟机中可以访问并更新macOS操作系统本地的目录,这在后面我们将运行在本地的MySQL数据库搬迁到docker容器中起了重要的作用。 我们还是在screen中看一下这些共享目录的情况。

/ # df -h|grep osxfs osxfs 465.1G 324.1G 140.7G 70% /private osxfs 465.1G 324.1G 140.7G 70% /tmp osxfs 465.1G 324.1G 140.7G 70% /Volumes osxfs 465.1G 324.1G 140.7G 70% /Users / # cd /Users /Users # ls Guest Kamus Shared 

可以看到,确实macOS操作系统中的目录在虚拟机中是可以直接访问的,而且更方便的地方是,在虚拟机中自动挂载的目录路径跟macOS中的路径是完全相同的,比如我的个人主目录无论是在macOS中还是在这个虚拟机中,都是/Users/Kamus。 那现在我们的思路基本上有了,就是要将原本运行在macOS操作系统中的MySQL数据库的数据文件和配置文件挪到Docker宿主机可以访问的目录下,然后在docker容器中启动MySQL实例。

安装MySQL docker镜像

先要将MySQL docker镜像安装上,这是运行MySQL docker容器的基础。安装镜像极其简单,只需要一步就可以,以下命令在macOS操作系统中执行。 标准步骤是:

$ docker pull mysql 

但是由于这样会安装latest版本,而我原先在macOS中的MySQL数据库是5.7.17版本,为了避免可能产生的版本升级问题,我做了指定版本的镜像拉取。

$ docker pull mysql:5.7.17 

从官方的docker store中拉取MySQL镜像,大约只需要几分钟时间,完毕以后,可以通过docker images命令来查看。

$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE mysql 5.7.17 9546ca122d3a 4 months ago 407MB store/oracle/database-enterprise 12.1.0.2 f5ffadfccb74 5 months ago 5.27GB alpine latest 13e1761bf172 15 months ago 4.8MB hello-world latest 94df4f0ce8a4 15 months ago 967B 

第一行就是最新拉取的MySQL镜像,这是创建者在4个月前创建的。

运行MySQL docker容器前的准备

设计docker宿主机目录结构

现在我们已经有了docker镜像,也有了在macOS操作系统中运行良好的MySQL MGR集群,开始迁移。首先当然是干净地关闭在macOS操作系统中的三台MySQL数据库,然后我们设计如下的目录结构,将三个数据库的数据文件全部分别挪进去。

$ mkdir /Users/Kamus/mysql_data $ mkdir /Users/Kamus/mysql_data/s1-docker $ mkdir /Users/Kamus/mysql_data/s2-docker $ mkdir /Users/Kamus/mysql_data/s3-docker 

根据之前阐述的共享文件夹功能,可以知道在docker宿主机中是可以通过完全相同的路径访问到这几个目录的,我们将原本在macOS操作系统中的三个数据库的数据文件分别移动到s1-docker,s2-docker,s3-docker目录中,至于哪些文件需要移动,这是MySQL DBA的基本知识,不在这里赘述。 由于目标是能运行MGR集群,那么是有一部分数据库初始化参数要额外设置的,而docker容器中的my.cnf内容无法改动,所以我们再设计一个专门的目录用来存储所有数据库的my.cnf文件。

$ mkdir /Users/Kamus/mysql_data/conf.d $ mkdir /Users/Kamus/mysql_data/conf.d/s1-docker $ mkdir /Users/Kamus/mysql_data/conf.d/s2-docker $ mkdir /Users/Kamus/mysql_data/conf.d/s3-docker 

然后将原本各个数据库的my.cnf文件分别拷贝到conf.d/s1-docker等三个目录下,最后形成了如下的目录结构。

$ tree /Users/Kamus/mysql_data/conf.d/ /Users/Kamus/mysql_data/conf.d/ ├── s1-docker │   └── my.cnf ├── s2-docker │   └── my.cnf └── s3-docker └── my.cnf 3 directories, 3 files 

设计这些目录结构的目的是在运行docker容器的时候通过volume选项将数据文件目录挂载成容器内部的/var/lib/mysql目录,将my.cnf参数文件所在目录挂载成容器内部的/etc/mysql/conf.d目录,从而实现MySQL数据库实例启动所需要的所有关键文件都存储在宿主机中,而docker容器本身只提供MySQL软件镜像。这比将所有文件都存储在docker镜像内部更灵活。

设计docker容器主机名和IP地址

在macOS本机运行的MySQL实例中为MGR配置的各种参数中使用到的主机名往往是localhost,IP地址则是127.0.0.1,MGR集群的节点间通信端口也往往是指定了本机IP上的不同端口,如果我们参照了官方文档中的搭建指南,使用的就是24901,24902,24903三个端口。

group_replication_group_seeds= "127.0.0.1:24901,127.0.0.1:24902,127.0.0.1:24903" 

现在我们既然将数据库搬到了容器中,那么就完全可以重新设计更有意义的主机名和分别的IP地址。 在启动Docker容器的时候,可以通过hostname和ip选项指定主机名和静态IP。可以参照最后运行Docker容器的完整命令。 如果要为Docker容器指定静态IP,则必须要使用手动创建的network,通过以下命令可以创建,比如此处我们创建了名称为mynet的网络。

$ docker network create --subnet=192.168.100.0/24 mynet 

创建完毕以后,通过network ls命令可以查看新创建的网络。可以看到默认是桥接方式。

$ docker network ls NETWORK ID NAME DRIVER SCOPE 45e872bc9b12 bridge bridge local a1e4739cb508 host host local 9617f8d9b8df mynet bridge local b0b9d568e2f9 none null local 

设计docker容器资源占用限制

对于在同一宿主机上运行多个docker容器,进行资源限制是必不可少的需求,虽然在此文中我们只是进行将MySQL实例搬迁到docker容器中的测试,但是也仍然规划了容器资源限制,由于宿主机本身只有2颗CPU和2GB内存,因此做如下规划。 在启动Docker容器的时候,可以通过cpus和memory以及memory-swap选项指定CPU和内存的资源限制。可以参照最后运行Docker容器的完整命令。

根据以上设置修改各数据库的my.cnf配置

因为我们设计了容器启动时候会拥有不同的IP地址,因此在容器中运行的MySQL实例的初始化参数中关于MGR的部分也需要指定这些IP地址。以下是my.cnf文件的设置内容。

[mysqld] # server configuration datadir=/var/lib/mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock port=3306 bind-address=0.0.0.0 server_id=1 gtid_mode=ON enforce_gtid_consistency=ON master_info_repository=TABLE relay_log_info_repository=TABLE binlog_checksum=NONE log_slave_updates=ON log_bin=binlog binlog_format=ROW relay-log=bogon-relay-bin # Group Replication Settings transaction_write_set_extraction=XXHASH64 loose-group_replication_group_name="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" loose-group_replication_start_on_boot=off loose-group_replication_local_address= "192.168.100.11:24901" loose-group_replication_group_seeds= "192.168.100.11:24901,192.168.100.12:24901,192.168.100.13:24901" loose-group_replication_bootstrap_group= off 

三个节点的my.cnf文件内容除了loose-group_replication_local_address参数之外,其它内容都是完全相同的。 对于第一个容器s1-container会使用到的my.cnf文件,这个参数为:

loose-group_replication_local_address= "192.168.100.11:24901" 

对于第二个容器s2-container会使用到的my.cnf文件,这个参数为:

loose-group_replication_local_address= "192.168.100.12:24901" 

对于第三个容器s3-container会使用到的my.cnf文件,这个参数为:

loose-group_replication_local_address= "192.168.100.13:24901" 

运行MySQL docker容器

由于MGR集群要求每个数据库在主机层是可以直接访问到其它节点的主机名的,如果在普通的主机上,只需要修改/etc/hosts文件增加其它节点的主机名和IP地址对应条目即可,但是容器内的/etc/hosts却是无法手动修改的,即使手动增加了条目,只要重新启动容器,该条目就会丢失。 要应对该问题,可以在运行容器时使用add_host选项。在真实的生产环境中,我们可以选择配置专门的DNS服务器来做IP和主机名对应(DNS服务器同样可以是Docker容器,我们后续的测试将增加此部分内容,本文先暂时通过add_host的方式来解决)。 最终运行三个docker容器的完整命令如下:

$ docker run \ --detach \ --name=s1-docker \ --memory=500m --memory-swap=1g \ --cpus=1 \ --hostname=mysql-mgr-server1 \ --net=mynet --ip=192.168.100.11 \ --add-host mysql-mgr-server2:192.168.100.12 --add-host mysql-mgr-server3:192.168.100.13 \ --publish 6601:3306 \ --volume=/Users/Kamus/mysql_data/conf.d/s1-docker:/etc/mysql/conf.d \ --volume=/Users/Kamus/mysql_data/s1-docker:/var/lib/mysql \ mysql:5.7.17 $ docker run \ --detach \ --name=s2-docker \ --memory=500m --memory-swap=1g \ --cpus=1 \ --hostname=mysql-mgr-server2 \ --net=mynet --ip=192.168.100.12 \ --add-host mysql-mgr-server1:192.168.100.11 --add-host mysql-mgr-server3:192.168.100.13 \ --publish 6602:3306 \ --volume=/Users/Kamus/mysql_data/conf.d/s2-docker:/etc/mysql/conf.d \ --volume=/Users/Kamus/mysql_data/s2-docker:/var/lib/mysql \ mysql:5.7.17 $ docker run \ --detach \ --name=s3-docker \ --memory=500m --memory-swap=1g \ --cpus=1 \ --hostname=mysql-mgr-server3 \ --net=mynet --ip=192.168.100.13 \ --add-host mysql-mgr-server2:192.168.100.12 --add-host mysql-mgr-server1:192.168.100.11 \ --publish 6603:3306 \ --volume=/Users/Kamus/mysql_data/conf.d/s3-docker:/etc/mysql/conf.d \ --volume=/Users/Kamus/mysql_data/s3-docker:/var/lib/mysql \ mysql:5.7.17 

全部容器都启动完毕以后,可以通过docker ps命令查看容器的运行状态,或者通过docker logs命令查看MySQL数据库日志的输出。非常方便。

$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2765e9104fe1 mysql:5.7.17 "docker-entrypoint..." 27 hours ago Up 6 seconds 0.0.0.0:6603->3306/tcp s3-docker 1966bea215bc mysql:5.7.17 "docker-entrypoint..." 27 hours ago Up 18 seconds 0.0.0.0:6601->3306/tcp s1-docker 18f68523f3a9 mysql:5.7.17 "docker-entrypoint..." 27 hours ago Up 9 seconds 0.0.0.0:6602->3306/tcp s2-docker 

启动MGR

通过如下命令登录到Docker容器的操作系统中,再进入MySQL实例,启动MGR。我们目前设置的是Single Primary模式的MGR,先启动第一个Primary实例。

$ docker exec -it s1-docker /bin/bash root@mysql-mgr-server1:/# mysql Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 Server version: 5.7.17-log MySQL Community Server (GPL) Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> SET GLOBAL group_replication_bootstrap_group=ON; Query OK, 0 rows affected (0.00 sec) mysql> START GROUP_REPLICATION; Query OK, 0 rows affected (1.07 sec) mysql> SET GLOBAL group_replication_bootstrap_group=OFF; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM performance_schema.replication_group_members; +---------------------------+--------------------------------------+-------------------+-------------+--------------+ | CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | +---------------------------+--------------------------------------+-------------------+-------------+--------------+ | group_replication_applier | 72ad2062-08a3-11e7-a513-5bfce171938d | mysql-mgr-server1 | 3306 | ONLINE | +---------------------------+--------------------------------------+-------------------+-------------+--------------+ 1 row in set (0.00 sec) 

再依次启动第二个只读实例。

$ docker exec -it s2-docker /bin/bash root@mysql-mgr-server2:/# mysql Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 Server version: 5.7.17-log MySQL Community Server (GPL) Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> START GROUP_REPLICATION; Query OK, 0 rows affected (5.60 sec) mysql> SELECT * FROM performance_schema.replication_group_members; +---------------------------+--------------------------------------+-------------------+-------------+--------------+ | CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | +---------------------------+--------------------------------------+-------------------+-------------+--------------+ | group_replication_applier | 72ad2062-08a3-11e7-a513-5bfce171938d | mysql-mgr-server1 | 3306 | ONLINE | | group_replication_applier | 9003e830-08a3-11e7-8ae3-e62d2f6366d2 | mysql-mgr-server2 | 3306 | ONLINE | +---------------------------+--------------------------------------+-------------------+-------------+--------------+ 2 rows in set (0.00 sec) 

启动第三个只读实例。

$ docker exec -it s3-docker /bin/bash root@mysql-mgr-server3:/# mysql Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 Server version: 5.7.17-log MySQL Community Server (GPL) Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> START GROUP_REPLICATION; Query OK, 0 rows affected (2.21 sec) mysql> SELECT * FROM performance_schema.replication_group_members; +---------------------------+--------------------------------------+-------------------+-------------+--------------+ | CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | +---------------------------+--------------------------------------+-------------------+-------------+--------------+ | group_replication_applier | 72ad2062-08a3-11e7-a513-5bfce171938d | mysql-mgr-server1 | 3306 | ONLINE | | group_replication_applier | 9003e830-08a3-11e7-8ae3-e62d2f6366d2 | mysql-mgr-server2 | 3306 | ONLINE | | group_replication_applier | 96fe2b5a-08a3-11e7-b383-d7f02f17e847 | mysql-mgr-server3 | 3306 | ONLINE | +---------------------------+--------------------------------------+-------------------+-------------+--------------+ 3 rows in set (0.00 sec) 

到此为止,我们将原先运行在macOS中的一整套MGR集群全部搬迁到docker容器中。最终实现了如下的系统架构。