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时代的兄弟们,是不是应该感到惭愧了?

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

Network Settings Configuration in Oracle Cloud

对于Oracle DBA而言,网络上的配置可能都是短板,而如果在公有云中进行Oracle数据库的部署,那么几乎要求一个DBA变成全栈工程师,因为已经不需要你进行网络基础架构的安装,那么对整个环境进行简单的网络设定配置就成为必不可少的技能之一。

本文会着重介绍Oracle Cloud中关于网络设定的概念和设置方法。

在这之前要先简单介绍Oracle Cloud。

Screen Shot 2016-04-09 at 2.56.16 PM

也许你已经听说过Amazon AWS、Microsoft Azure,甚至你已经在使用阿里云、华为云、腾讯云、青云,而Oracle无疑是这场云大战中的迟到者,但是从过去的2015年看,Oracle在Cloud市场上的表现却是不俗的,Oracle一直宣称自己是全球唯一最全面、最集成的一朵完整的云,提供了IaaS,PaaS,SaaS三个层面的所有产品,而让Oracle Cloud特别引人注意的,无疑是在RDBMS全球市场中占据霸主地位的Oracle数据库的云化。

Oracle Database在Oracle Cloud中目前有以下几种产品方式。

1. Database as a Service 提供一个客户独占的虚拟机,可以选择在其上安装的是11g还是12c数据库,包括可以选择安装一个2节点的RAC;客户对于虚拟机和运行在其上的数据库有完全的管理权限,包括操作系统的root权限,还有数据库的SYSDBA权限,提供一键备份和一键打补丁等简便的管理功能。

2. Exadata Service 提供一个运行在Exadata上的11.2.0.4或者12.1.0.2的数据库,客户可以选择是全配、半配还是四分之一配,还可以选择是否是RAC,同样有操作系统的root权限和数据库的SYSDBA权限。

3. Database Schema Service 提供一个在11g数据库中的Schema,客户可以选择存储空间大小(5GB-50GB),只能通过RESTful网络服务接口来访问该Schema中的表数据,毫无疑问,没有数据库的SYSDBA权限,操作系统也是完全不可见的。

4. Database as a Service – Managed(该服务产品目前还没有正式发布) 提供一个Oracle数据库给客户,客户拥有SYSDBA权限,可以以包括SQL*Net在内的各种方式访问数据库,操作系统不可见,Oracle原厂承担必要的维护工作,比如备份和PITR恢复、打补丁和升级。

本文中使用的产品是Database as a Service,一套Oracle 12.1.0.2 RAC。CDB架构,初始化创建时候设定了一个PDB。

Screen Shot 2016-04-05 at 4.07.20 PM

在数据库创建完毕以后,Oracle也默认创建了承载Oracle数据库的虚拟机,也就是底层的IaaS架构,在本文中是两台2CPU4核,15GB内存的虚拟机,每台机器有161GB存储空间(这其中包括了安装操作系统和Oracle软件的75GB本地空间,以及ASM使用的66GB共享空间)。

Screen Shot 2016-04-05 at 5.19.23 PM

以上这些都不是本文的重点,我们要介绍的是Oracle Cloud的网络设定。要知道,新创建完的虚拟机虽然可以通过SSH来访问,但是数据库默认是无法通过监听来远程访问的,必须要进行网络设定的修改。

在Oracle Cloud中网络层面的设定要涉及到以下几个概念,我们从管理界面中可以看到这些名词。

Screen-Shot-2016-04-05-at-2_15_14-PM

Security Lists 每个Security List包含了一组Oracle Compute Cloud Service实例(每个Oracle Compute Cloud Service实例可以简单地认为就是一台Oracle Cloud虚拟机,用AWS的语言说是一个EC2实例,用阿里云的语言说是一个ECS实例,以下简称为OCCS),在同一个Security List中的机器可以无障碍地通过任何网络端口进行数据交互,但是与外界的机器进行交互就要看这个Security List中对于inbound和outbound的策略定义了。Inbound策略控制了想进入这个Security List中的网络防火墙定义,Outbound策略控制了想传出这个Security List中的网络防火墙定义。 inbound和outbound的策略都有3种,分别是permit,reject和deny。 permit:允许任何数据包传输(在inbound策略中就是传入,在outbound策略中就是传出) reject:丢弃所有数据包,但是有回传信息告知数据包被丢弃 deny:丢弃所有数据包,并且不回传任何信息 如下这张图清晰地表现了不同Security List中不同的inbound和outbound策略下数据的可能流向。

Screen Shot 2016-04-05 at 6.25.13 PM

默认创建的Security List的inbound策略都是deny,下图中有关联实例的命名为oracle-cloud-enmotech/1/db_1/ora_db的Security List是正在使用的,可以看到inbound策略是deny。 Screen Shot 2016-04-09 at 2.02.44 PM

也就意味着所有的数据包不能传入到新建的机器上和数据库里。那么为什么新建的机器可以通过SSH访问,而又确实无法通过数据库监听来远程访问?这就需要提到下面这个概念-Security Rules。

不过在解释Security Rules之前,需要先解释另外两个概念,Security Applications和Security IP Lists。

Security Applications Security Application就是一组网络协议和端口的组合,从下图中可以清晰地看出来。

命名为ssh的Security Application就是tcp协议的22端口,这就是ssh默认的端口。 Screen Shot 2016-04-09 at 1.41.50 PM

命名为ora_scan_listener的Security Application就是tcp协议的1521端口,这就是数据库SCAN监听默认的端口。 Screen Shot 2016-04-09 at 1.42.13 PM

再比如命名为ora_monitor_12c的Security Application就是tcp协议的5500端口,这就是12c新的EM Express默认使用的端口。 Screen Shot 2016-04-09 at 1.43.25 PM

Security IP Lists 另外一个关键的概念,就是Security IP List,这里定义的是一组IP地址或者子网,也就是防火墙设置中在源(source)和目标(destination)中设置的值。 比如在如下图的默认设置中,public-internet就表示了所有的IP地址。

Screen-Shot-2016-04-09-at-1_51_58-PM

最后,进入Security Rules的解释。

Security Rules 一个Security Rule实际上就是一个防火墙规则,用于定义对于特定的应用访问OCCS的防火墙策略。这些防火墙是在Security List之上设置的特例。简单地说,Security List的inbound策略是deny的话,那么只有在Security Rule中明确设置可以通过的端口才被允许通过;如果Security List的inbound策略是permit的话,就不需要再设置任何Security Rule,因为所有端口都被允许了。很明显,Oracle强烈建议第一种设置方法,而不要轻易设置Security List的inbound策略是permit。

为什么新建的机器可以通过SSH访问?因为SSH的Security Rule是默认打开的(Status=Enabled)。 Screen Shot 2016-04-09 at 1.30.38 PM

解读一下,图中的各个设定的含义。 Security Application=ssh,是这个防火墙规则针对的是tcp协议的22端口; Source=public-internet,是这个防火墙规则的源定义,是一个Security IP List的值,表示任何一个IP; Destination=oracle-cloud-enmotech/1/db_1/ora_db,是这个防火墙规则的目标定义,是一个Security List的值,就是上面提到的inbound策略为deny的那个Security List,这表示在这个Security List中的所有OCCS都是目标。

再通俗易懂地解释一下,就是该防火墙定义允许任何一个IP通过22端口访问本次创建的Database之下的所有OCCS,也就是允许通过SSH来访问RAC的2个节点。

为什么无法通过数据库监听来远程访问?因为跟SCAN Listener相关的Security Rule是默认关闭的(Status=Disabled)。 Screen Shot 2016-04-09 at 1.31.19 PM

修改很简单,将状态从Disabled修改为Enabled即可。

剩下在网络设定管理界面中的另外两个名词,就比较简单了。

IP Reservations 为虚拟主机保留的公网IP,可以通过这个IP直接访问主机。 Screen_Shot_2016-04-09_at_2_07_44_PM

SSH Public Keys 登录OCCS,不是通过用户名和密码,而必须要通过SSH Key,在创建Database的过程中就会要求上传Public Key,也可以在这个界面再次更新或者上传新的Public Key。 Screen_Shot_2016-04-09_at_2_08_10_PM

结论 通过以上的设置,Oracle Cloud基本上组成了一套完善的网络防火墙体系,可以细粒度的控制哪些IP通过哪些端口访问数据库或者数据库主机。

Oracle ACE Spotlight awards

Yes, it’s me. 能够在Oracle官方站点的ACE主页面作为Spotlight人物被介绍,很荣幸。Oracle的技术生涯可以用此做个注解了。

BTW, 上面那张ACE全家福的二排正中间也能找到我,2014年OOW期间的照片,应该是迄今为止中国ACE专家在旧金山会和最全的一届。

Oracle_ACE_Spotlight

Oracle 12.2 Sharded Database Management – Series I

##什么是Sharding

> Sharding is a data tier architecture in which data is horizontally partitioned across independent databases. Each database in such configuration is called a shard. All of the shards together make up a single logical database which is referred to as a sharded database or SDB.

Sharding是指数据层的水平分区,实际上在之前的Oracle版本中,分区已经是数据仓库系统非常常用的技术手段,但是在12.2之前,一个分区表的所有分区只能存储在一个数据库中,而在12.2之后,一个分区表的多个分区可以存储在不同的数据库里,这就被称为Sharding。为什么Sharding这么被大家期待?因为可能很多人都在说,Oracle的水平扩展能力不够强,虽然有RAC,但是集群节点越多内耗就越多,这样的水平扩展能力跟Hadoop之类的方案相比是不足的。我们先不评判这样的看法是不是正确,Oracle 12.2要告诉大家的是,要Sharding?要分库分表?要线性水平扩展?没问题,给你。

假设这样的分库分表一共跨了10个Oracle数据库,那么这10个Oracle数据库对于前端应用来说是透明的,是一个统一的逻辑数据库,称为一个sharded数据库,或者简称为一个SDB,而在这个SDB中每个数据库被称为一个shard。
一张大表可以根据规则被分割到每个shard中,在每个shard里拥有相同的字段结构,但是却拥有不同的数据,这样的一张表被称为sharded table。

##Sharding适合所有的数据库应用吗?
既然Sharding听上去很厉害,那么是不是现在只要遇到有性能问题的数据库,一律都可以使用Sharding技术来解决呢?当然不,Sharding不会也不可能是FAST=TRUE这样的参数。一个适合Sharding技术的应用,必须有非常好的数据模型,和清晰的数据分布策略(比如是一致性哈希,范围或者列表分区),并且访问这些数据也是总要通过shard key来过滤的,只有这样,才能在整个Sharded数据库架构中很好地将请求路由到合适的数据库上。这样的shard key可能会是客户编号,国家编号,身份证号码等。

##Sharding有哪些缺点?
在我们颂扬一项技术之前,先看一下这项技术会带来哪些不便,是更理智的做法,要知道世界上并无十全十美的事物。

在Wikipedia([https://en.wikipedia.org/wiki/Shard_(database_architecture)])中对于Shard的坏处有如下的定义。

> •Increased complexity of SQL – Increased bugs because the developers have to write more complicated SQL to handle sharding logic.
> •Sharding introduces complexity – The sharding software that partitions, balances, coordinates, and ensures integrity can fail.
> •Single point of failure – Corruption of one shard due to network/hardware/systems problems causes failure of the entire table.
> •Failover servers more complex – Failover servers must themselves have copies of the fleets of database shards.
> •Backups more complex – Database backups of the individual shards must be coordinated with the backups of the other shards.
> •Operational complexity added – Adding/removing indexes, adding/deleting columns, modifying the schema becomes much more difficult.

1. 增加了SQL的复杂性。因为开发人员必须要写更复杂的SQL来处理sharding的逻辑。
2. Sharding本身带来的复杂性。sharding软件需要照顾分区,数据平衡,访问协调,数据完整性。
3. 单点故障。一个shard损坏可能导致整张表不可访问。
4. 失效接管服务器也更复杂。因为负责失效接管的服务器必须拥有任何可能损坏的shard上的数据。
5. 备份也更复杂。多个shard可能都需要同时备份。
6. 维护也更复杂。比如增加删除索引,增减删除字段,修改表定义等等,都变得更困难。

庆幸的是,这上面的大部分缺点,在Oracle 12.2 Sharding中都无需担心。

##Oracle Sharding Architecture
跟那些NoSQL数据库架构不一样,Oracle Sharding在提供sharding的同时,并没有牺牲掉关系型数据库所带来的优秀特性,比如说关系型数据建模,SQL编程接口,丰富的数据类型,在线的表结构变更,充分利用CPU多核的扩展性,高级安全,压缩,高可用,ACID特征,一致读,所有的Oracle数据库的优势仍然还在那里,但是,额外,提供了sharding的优势。

对于Oracle Sharding的上层来说,使用的是Oracle GDS(Global Data Services)框架来实现自动部署和shading的管理以及拓扑复制。GDS还同时提供了对于整个SDB访问的负载均衡和基于位置的路由功能。在GDS框架中,global service manager负责将应用过来的请求转发到合适的shard上,另外还有一个shard catalog数据库,支持跨shard的查询功能,同时SDB的配置数据也都存在这个catalog数据库中。

对于Oracle Sharding的底层来说,使用的是Oracle长久以来一直存在的分区(partitioning)技术。Oracle Sharding就其本质上来说,实际上就是分布式分区,将以前的分区扩展支持到跨不同的物理数据库上。

因此创建一个sharded table的语法,跟以前创建一张分区表的语法也是非常相像的,以前称为分区键的字段现在被称为“sharding key”。

CREATE SHARDED TABLE customers
( cust_id NUMBER NOT NULL
, name VARCHAR2(50)
, address VARCHAR2(250)
, region VARCHAR2(20)
, class VARCHAR2(3)
, signup DATE
CONSTRAINT cust_pk PRIMARY KEY(cust_id) )     
PARTITION BY CONSISTENT HASH (cust_id) 
TABLESPACE SET ts1
PARTITIONS AUTO
;

在上面的语法中出现了一些新的关键字,比如“CONSISTENT HASH”,“TABLESPACE SET”,后面会详细解释。

Sharded table的每个分区是在表空间这个层面分布到不同的数据库(shard)中的,每个分区都必须在一个单独的表空间中。当使用consistent hash来进行分区以后,表空间会自动分布到不同的shard中,最终提供一个平均分布的数据架构。

除了consistent hash方式之外,Oracle还提供了基于range和list,和两层复合分区(range-consistent hash和list-consistent hash)的方式来进行Sharding,这对于熟悉Oracle分区技术的人来说一点儿也不陌生。

Oracle Sharding整合了Oracle很多成熟的技术,比如复制技术,Oracle Data Guard和Oracle Goldengate;比如高可用技术,Oracle RAC;比如连接池技术,connection pool;当然还有在12.1中新发的GDS框架。

这个章节最后用2张PPT来结束,这是Eygel放出的在昨天旧金山OOW上Oracle介绍的Sharding技术的PPT。

_Oracle Sharding的实现_
640

_Sharding如何实现数据访问路由_
640-1

接下来的章节会继续介绍Oracle Sharding的详细实现和使用方法。

How to Analyse Row Lock Contention in Oracle 10gR2 and later

我们有一道面试题,原以为很简单,但是却发现面试者能够完美解出的几乎没有,一部分人有思路,但是可能是因为面试紧张,很难在指定时间内完成解题,而更大一部分人连思路也不清晰。

题目是:请将emp.empno=7369的记录ename字段修改为“ENMOTECH”并提交,你可能会遇到各种故障,请尝试解决。

其实题目的设计非常简单,一个RAC双节点的实例环境,面试人员使用的是实例2,而我们在实例1中使用select for update将EMP表加锁。

SQL> select * from emp for update;

此时在实例2中,如果执行以下SQL语句尝试更新ename字段,必然会被行锁堵塞。

SQL> update emp set ename='ENMOTECH' where empno=7369;

这道面试题中包含的知识点有:
1. 如何在另外一个session中查找被堵塞的session信息;
2. 如何找到产生行锁的blocker;
3. 在杀掉blocker进程之前会不会向面试监考人员询问,我已经找到了产生堵塞的会话,是不是可以kill掉;
4. 在获得可以kill掉进程的确认回复后,正确杀掉另一个实例上的进程。

这道题我们期待可以在5分钟之内获得解决,实际上大部分应试者在15分钟以后都完全没有头绪。

正确的思路和解法应该如下:

检查被阻塞会话的等待事件

更新语句回车以后没有回显,明显是被锁住了,那么现在这个会话经历的是什么等待事件呢?

SQL> select sid,event,username,sql.sql_text 
  2  from v$session s,v$sql sql
  3  where s.sql_id=sql.sql_id
  4  and sql.sql_text like 'update emp set ename%';

       SID EVENT                          USERNAME   SQL_TEXT
---------- ------------------------------ ---------- ----------------------------------------------------------------------
        79 enq: TX - row lock contention  ENMOTECH   update emp set ename='ENMOTECH' where empno=7369

以上使用的是关联v$sql的SQL语句,实际上通过登录用户名等也可以快速定位被锁住的会话。

查找blocker

得知等待事件是enq: TX – row lock contention,行锁,接下来就是要找到谁锁住了这个会话。在10gR2以后,只需要gv$session视图就可以迅速定位blocker,通过BLOCKING_INSTANCE和BLOCKING_SESSION字段即可。

SQL> select SID,INST_ID,BLOCKING_INSTANCE,BLOCKING_SESSION from gv$session where INST_ID=2 and SID=79;

       SID    INST_ID BLOCKING_INSTANCE BLOCKING_SESSION
---------- ---------- ----------------- ----------------
        79          2                 1               73

上述方法是最简单的,如果是使用更传统的方法,实际上也并不难,从gv$lock视图中去查询即可。

SQL> select TYPE,ID1,ID2,LMODE,REQUEST from v$lock where sid=79;

TY        ID1        ID2      LMODE    REQUEST
-- ---------- ---------- ---------- ----------
TX     589854      26267          0          6
AE        100          0          4          0
TM      79621          0          3          0

SQL> select INST_ID,SID,TYPE,LMODE,REQUEST from gv$Lock where ID1=589854 and ID2=26267;

   INST_ID        SID TY      LMODE    REQUEST
---------- ---------- -- ---------- ----------
         2         79 TX          0          6
         1         73 TX          6          0

乙方DBA需谨慎

第三个知识点是考核作为乙方的谨慎,即使你查到了blocker,是不是应该直接kill掉,必须要先征询客户的意见,确认之后才可以杀掉。

清除blocker

已经确认了可以kill掉session之后,需要再找到相应session的serail#,这是kill session时必须输入的参数。

SQL> select SID,SERIAL# from gv$session where INST_ID=1 and SID=73;

       SID    SERIAL#
---------- ----------
        73      15625

如果是11gR2数据库,那么直接在实例2中加入@1参数就可以杀掉实例1中的会话,如果是10g,那么登入实例1再执行kill session的操作。

SQL> alter system kill session '73,15625,@1';

System altered.

再检查之前被阻塞的更新会话,可以看到已经更新成功了。

SQL> update emp set ename='ENMOTECH' where empno=7369;

1 row updated.

对于熟悉整个故障解决过程的人,5分钟之内就可以解决问题。

深入一步

对于TX锁,在v$lock视图中显示的ID1和ID2是什么意思? 解释可以从v$lock_type视图中获取。

SQL> select ID1_TAG,ID2_TAG from V$LOCK_TYPE where type='TX';

ID1_TAG         ID2_TAG
--------------- ----------
usn<<16 | slot  sequence

所以ID1是事务的USN+SLOT,而ID2则是事务的SQN。这些可以从v$transaction视图中获得验证。

SQL> select taddr from v$session where sid=73;

TADDR
----------------
000000008E3B65C0

SQL> select XIDUSN,XIDSLOT,XIDSQN from v$transaction where addr='000000008E3B65C0';

    XIDUSN    XIDSLOT     XIDSQN
---------- ---------- ----------
         9         30      26267

如何和ID1=589854 and ID2=26267对应呢? XIDSQN=26267和ID2=26267直接就对应了,没有问题。 那么ID1=589854是如何对应的?将之转换为16进制,是0x9001E,然后分高位和低位分别再转换为10进制,高位的16进制9就是十进制的9,也就是XIDUSN=9,而低位的16进制1E转换为10进制是30,也就是XIDSLOT=30。

文章写到这里,忽然感觉网上那些一气呵成的故障诊断脚本其实挺误人的,只需要给一个参数,运行一下脚本就列出故障原因。所以很少人愿意再去研究这个脚本为什么这么写,各个视图之间的联系是如何环环相扣的。所以当你不再使用自己的笔记本,不再能迅速找到你赖以生存的那些脚本,你还能一步一步地解决故障吗?

Oracle ASM Filter Driver (ASMFD) – New Features for Oracle ASM 12.1.0.2

什么是Oracle ASM Filter Driver(ASMFD)?

简单地说,这是一个可以取代ASMLIB和udev设置的新功能,并且还增加了I/O Filter功能,这也体现在该功能的命名中。ASMFD目前只在Linux操作系统中有效,并且必须要使用最新版的Oracle ASM 12.1.0.2。在之前,由于Linux操作系统对于块设备的发现顺序不定,所以在系统重启以后,无法保证原来的/dev/sda还是sda,所以不能直接使用这样原始的设备名来做ASM Disk的Path,因此出现了ASMLIB,以Label的方式给予设备一个固定的名字,而ASM则直接使用这个固定的名字来创建ASM磁盘,后来ASMLIB必须要ULN帐号才可以下载了,于是大家全部转到了udev方式,我还因此写了几篇文章来阐述在Linux中如何设置udev rule。比如:

How to use udev for Oracle ASM in Oracle Linux 6

Oracle Datafiles & Block Device & Parted & Udev

现在Oracle推出了ASMFD,可以一举取代ASMLIB和手动设置udev rules文件的繁琐,并且最重要的是I/O Filter功能。

什么是I/O Filter功能?

文档原文如下:

The Oracle ASM Filter Driver rejects any I/O requests that are invalid. This action eliminates accidental overwrites of Oracle ASM disks that would cause corruption in the disks and files within the disk group. For example, the Oracle ASM Filter Driver filters out all non-Oracle I/Os which could cause accidental overwrites.

意思是:该功能可以拒绝所有无效的I/O请求,最主要的作用是防止意外覆写ASM磁盘的底层盘,在后面的测试中可以看到对于root用户的dd全盘清零这样的变态操作也都是可以过滤的。

真是不错,那么该怎么启用这个功能呢?

通常我们原先的ASM中都应该使用的是ASMLIB或者udev绑定的设备,这里就直接描述如何将原先的设备名重新命名为新的AFD设备名。

--确认目前ASMFD模块(以下简称AFD)的状态,未加载。
[grid@dbserver1 ~]$ asmcmd afd_state
ASMCMD-9526: The AFD state is 'NOT INSTALLED' and filtering is 'DEFAULT' on host 'dbserver1.vbox.com'

--获取当前ASM磁盘发现路径,我这里是使用udev绑定的名称
[grid@dbserver1 ~]$ asmcmd dsget
parameter:/dev/asm*
profile:/dev/asm*

--设置ASM磁盘路径,将新的Disk String加入
--可以看到在设置该参数时,ASM会检查现有已经加载的磁盘,如果不在发现路径上,将会报错。
[grid@dbserver1 ~]$ asmcmd dsset AFD:*
ORA-02097: parameter cannot be modified because specified value is invalid
ORA-15014: path '/dev/asm-disk7' is not in the discovery set (DBD ERROR: OCIStmtExecute)

--因此我们必须将新的路径加在原始路径后面,设置成多种路径,该操作会运行一段时间,视ASM磁盘多少而定
[grid@dbserver1 ~]$ asmcmd dsset '/dev/asm*','AFD:*'

[grid@dbserver1 ~]$ asmcmd dsget
parameter:/dev/asm*, AFD:*
profile:/dev/asm*,AFD:*

--检查现在GI环境中的节点。
[grid@dbserver1 ~]$ olsnodes -a
dbserver1       Hub
dbserver2       Hub

--以下命令必须在所有Hub节点上都运行,可以使用Rolling的方式。以下命令有些需要root用户,有些需要grid用户,请注意#或者$不同的提示符表示不同的用户。
--先停止crs
[root@dbserver1 ~]# crsctl stop crs

--作AFD Configure,实际上这是一个解压程序包,安装,并加载Driver的过程,需要消耗一些时间
[root@dbserver1 ~]# asmcmd afd_configure
Connected to an idle instance.
AFD-627: AFD distribution files found.
AFD-636: Installing requested AFD software.
AFD-637: Loading installed AFD drivers.
AFD-9321: Creating udev for AFD.
AFD-9323: Creating module dependencies - this may take some time.
AFD-9154: Loading 'oracleafd.ko' driver.
AFD-649: Verifying AFD devices.
AFD-9156: Detecting control device '/dev/oracleafd/admin'.
AFD-638: AFD installation correctness verified.
Modifying resource dependencies - this may take some time.

--结束以后,可以再次检查AFD状态,显示已加载。
[root@dbserver1 ~]# asmcmd afd_state
Connected to an idle instance.
ASMCMD-9526: The AFD state is 'LOADED' and filtering is 'DEFAULT' on host 'dbserver1.vbox.com'

--接下来需要设置AFD自己的磁盘发现路径了,其实这里很像以前ASMLIB的操作。
--设置AFD磁盘发现路径,必须先启动CRS,否则将会遇到下面的错误。同时也可以看到这个信息是存储在每个节点自己的OLR中,因此必须在所有节点中都设置。
[root@dbserver1 ~]# asmcmd afd_dsget
Connected to an idle instance.
ASMCMD-9511: failed to obtain required AFD disk string from Oracle Local Repository
[root@dbserver1 ~]#
[root@dbserver1 ~]# asmcmd afd_dsset '/dev/sd*'
Connected to an idle instance.
ASMCMD-9512: failed to update AFD disk string in Oracle Local Repository.

--启动CRS
[root@dbserver1 ~]# crsctl start crs
CRS-4123: Oracle High Availability Services has been started.

--此时查看后台的ASM告警日志,可以看到加载的磁盘仍然使用的是原始路径。但是也可以看到libafd已经成功加载。
2014-11-20 17:01:04.545000 +08:00
NOTE: Loaded library: /opt/oracle/extapi/64/asm/orcl/1/libafd12.so
ORACLE_BASE from environment = /u03/app/grid
SQL> ALTER DISKGROUP ALL MOUNT /* asm agent call crs *//* {0:9:3} */
NOTE: Diskgroup used for Voting files is:
  CRSDG
Diskgroup with spfile:CRSDG
NOTE: Diskgroup used for OCR is:CRSDG
NOTE: Diskgroups listed in ASM_DISKGROUP are
  DATADG
NOTE: cache registered group CRSDG 1/0xB8E8EA0B
NOTE: cache began mount (first) of group CRSDG 1/0xB8E8EA0B
NOTE: cache registered group DATADG 2/0xB8F8EA0C
NOTE: cache began mount (first) of group DATADG 2/0xB8F8EA0C
NOTE: Assigning number (1,2) to disk (/dev/asm-disk3)
NOTE: Assigning number (1,1) to disk (/dev/asm-disk2)
NOTE: Assigning number (1,0) to disk (/dev/asm-disk1)
NOTE: Assigning number (1,5) to disk (/dev/asm-disk10)
NOTE: Assigning number (1,3) to disk (/dev/asm-disk8)
NOTE: Assigning number (1,4) to disk (/dev/asm-disk9)
NOTE: Assigning number (2,3) to disk (/dev/asm-disk7)
NOTE: Assigning number (2,2) to disk (/dev/asm-disk6)
NOTE: Assigning number (2,1) to disk (/dev/asm-disk5)
NOTE: Assigning number (2,5) to disk (/dev/asm-disk12)
NOTE: Assigning number (2,0) to disk (/dev/asm-disk4)
NOTE: Assigning number (2,6) to disk (/dev/asm-disk13)
NOTE: Assigning number (2,4) to disk (/dev/asm-disk11)

--将afd_ds设置为ASM磁盘的底层磁盘设备名,这样以后就不再需要手工配置udev rules了。
[grid@dbserver1 ~]$ asmcmd afd_dsset '/dev/sd*'

[grid@dbserver1 ~]$ asmcmd afd_dsget
AFD discovery string: /dev/sd*

--我在测试的时候,上面犯了一个错误,将路径设置为了“dev/sd*”,缺少了最开始的根目录。因此此处没有发现任何磁盘,如果你的测试中,这一步已经可以发现磁盘,请告诉我。
[grid@dbserver1 ~]$ asmcmd afd_lsdsk
There are no labelled devices.

--再次提醒,到此为止的所有命令,都必须要在集群环境的所有节点中都执行。
--接下来就是将原先的ASM磁盘路径从udev转到AFD
--先检查现在的磁盘路径
[root@dbserver1 ~]# ocrcheck -config
Oracle Cluster Registry configuration is :
         Device/File Name         :     +CRSDG

[root@dbserver1 ~]# crsctl query css votedisk
##  STATE    File Universal Id                File Name Disk group
--  -----    -----------------                --------- ---------
 1. ONLINE   4838a0ee7bfa4fbebf8ff9f58642c965 (/dev/asm-disk1) [CRSDG]
 2. ONLINE   72057097a36e4f02bfc7b5e23672e4cc (/dev/asm-disk2) [CRSDG]
 3. ONLINE   7906e2fb24d24faebf9b82bba6564be3 (/dev/asm-disk3) [CRSDG]
Located 3 voting disk(s).

[root@dbserver1 ~]# su - grid
[grid@dbserver1 ~]$ asmcmd lsdsk -G CRSDG
Path
/dev/asm-disk1
/dev/asm-disk10
/dev/asm-disk2
/dev/asm-disk3
/dev/asm-disk8
/dev/asm-disk9

--由于要修改OCR所在的磁盘,因此修改之前需要停止Cluster。
[root@dbserver1 ~]# crsctl stop cluster -all

--直接修改会报错,因为/dev/asm-disk1已经存在在ASM中了。
[grid@dbserver1 ~]$ asmcmd afd_label asmdisk01 /dev/asm-disk1
Connected to an idle instance.
ASMCMD-9513: ASM disk label set operation failed.
disk /dev/asm-disk1 is already provisioned for ASM

--必须要增加migrate关键字,才可以成功。
[grid@dbserver1 ~]$ asmcmd afd_label asmdisk01 /dev/asm-disk1 --migrate
Connected to an idle instance.
[grid@dbserver1 ~]$ asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                   ENABLED   /dev/asm-disk1

--在我的测试ASM中,一共有13块磁盘,因此依次修改。
asmcmd afd_label asmdisk01 /dev/asm-disk1 --migrate
asmcmd afd_label asmdisk02 /dev/asm-disk2 --migrate
asmcmd afd_label asmdisk03 /dev/asm-disk3 --migrate
asmcmd afd_label asmdisk04 /dev/asm-disk4 --migrate
asmcmd afd_label asmdisk05 /dev/asm-disk5 --migrate
asmcmd afd_label asmdisk06 /dev/asm-disk6 --migrate
asmcmd afd_label asmdisk07 /dev/asm-disk7 --migrate
asmcmd afd_label asmdisk08 /dev/asm-disk8 --migrate
asmcmd afd_label asmdisk09 /dev/asm-disk9 --migrate
asmcmd afd_label asmdisk10 /dev/asm-disk10 --migrate
asmcmd afd_label asmdisk11 /dev/asm-disk11 --migrate
asmcmd afd_label asmdisk12 /dev/asm-disk12 --migrate
asmcmd afd_label asmdisk13 /dev/asm-disk13 --migrate

[grid@dbserver1 ~]$ asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                   ENABLED   /dev/asm-disk1
ASMDISK02                   ENABLED   /dev/asm-disk2
ASMDISK03                   ENABLED   /dev/asm-disk3
ASMDISK04                   ENABLED   /dev/asm-disk4
ASMDISK05                   ENABLED   /dev/asm-disk5
ASMDISK06                   ENABLED   /dev/asm-disk6
ASMDISK07                   ENABLED   /dev/asm-disk7
ASMDISK08                   ENABLED   /dev/asm-disk8
ASMDISK09                   ENABLED   /dev/asm-disk9
ASMDISK10                   ENABLED   /dev/asm-disk10
ASMDISK11                   ENABLED   /dev/asm-disk11
ASMDISK12                   ENABLED   /dev/asm-disk12
ASMDISK13                   ENABLED   /dev/asm-disk13

--在另外的节点中,不再需要作label,而是直接scan即可,这跟使用ASMLIB的操作非常相像。
[grid@dbserver2 ~]$ asmcmd afd_scan
Connected to an idle instance.
[grid@dbserver2 ~]$ asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK12                   ENABLED   /dev/asm-disk12
ASMDISK09                   ENABLED   /dev/asm-disk9
ASMDISK08                   ENABLED   /dev/asm-disk8
ASMDISK11                   ENABLED   /dev/asm-disk11
ASMDISK10                   ENABLED   /dev/asm-disk10
ASMDISK13                   ENABLED   /dev/asm-disk13
ASMDISK01                   ENABLED   /dev/asm-disk1
ASMDISK04                   ENABLED   /dev/asm-disk4
ASMDISK06                   ENABLED   /dev/asm-disk6
ASMDISK07                   ENABLED   /dev/asm-disk7
ASMDISK05                   ENABLED   /dev/asm-disk5
ASMDISK03                   ENABLED   /dev/asm-disk3
ASMDISK02                   ENABLED   /dev/asm-disk2

--重新启动Cluster
[root@dbserver1 ~]# crsctl start cluster -all

--可以看到ASM告警日志中已经显示开始使用新的名称。关于其中WARNING的含义表示目前AFD还不支持Advanced Format格式的磁盘,普通磁盘格式一个扇区是512字节,而高级格式则为4K字节。
2014-11-20 17:46:16.695000 +08:00
* allocate domain 1, invalid = TRUE
* instance 2 validates domain 1
NOTE: cache began mount (not first) of group CRSDG 1/0x508D0B98
NOTE: cache registered group DATADG 2/0x509D0B99
* allocate domain 2, invalid = TRUE
* instance 2 validates domain 2
NOTE: cache began mount (not first) of group DATADG 2/0x509D0B99
WARNING: Library 'AFD Library - Generic , version 3 (KABI_V3)' does not support advanced format disks
NOTE: Assigning number (1,0) to disk (AFD:ASMDISK01)
NOTE: Assigning number (1,1) to disk (AFD:ASMDISK02)
NOTE: Assigning number (1,2) to disk (AFD:ASMDISK03)
NOTE: Assigning number (1,3) to disk (AFD:ASMDISK08)
NOTE: Assigning number (1,4) to disk (AFD:ASMDISK09)
NOTE: Assigning number (1,5) to disk (AFD:ASMDISK10)
NOTE: Assigning number (2,0) to disk (AFD:ASMDISK04)
NOTE: Assigning number (2,1) to disk (AFD:ASMDISK05)
NOTE: Assigning number (2,2) to disk (AFD:ASMDISK06)
NOTE: Assigning number (2,3) to disk (AFD:ASMDISK07)
NOTE: Assigning number (2,4) to disk (AFD:ASMDISK11)
NOTE: Assigning number (2,5) to disk (AFD:ASMDISK12)
NOTE: Assigning number (2,6) to disk (AFD:ASMDISK13)

--检查磁盘加载路径,以及功能全部是AFD样式了。
[grid@dbserver1 ~]$ asmcmd lsdsk
Path
AFD:ASMDISK01
AFD:ASMDISK02
AFD:ASMDISK03
AFD:ASMDISK04
AFD:ASMDISK05
AFD:ASMDISK06
AFD:ASMDISK07
AFD:ASMDISK08
AFD:ASMDISK09
AFD:ASMDISK10
AFD:ASMDISK11
AFD:ASMDISK12
AFD:ASMDISK13

--但是我们可以看到在数据字典中仍然存在之前的磁盘路径。
SQL> select NAME,LABEL,PATH from V$ASM_DISK;

NAME                 LABEL                           PATH
-------------------- ------------------------------- --------------------------------------------------
                                                     /dev/asm-disk7
                                                     /dev/asm-disk6
                                                     /dev/asm-disk13
                                                     /dev/asm-disk12
                                                     /dev/asm-disk11
                                                     /dev/asm-disk4
                                                     /dev/asm-disk2
                                                     /dev/asm-disk9
                                                     /dev/asm-disk3
                                                     /dev/asm-disk5
                                                     /dev/asm-disk10
                                                     /dev/asm-disk8
                                                     /dev/asm-disk1
CRSDG_0000           ASMDISK01                       AFD:ASMDISK01
CRSDG_0001           ASMDISK02                       AFD:ASMDISK02
CRSDG_0002           ASMDISK03                       AFD:ASMDISK03
DATADG_0000          ASMDISK04                       AFD:ASMDISK04
DATADG_0001          ASMDISK05                       AFD:ASMDISK05
DATADG_0002          ASMDISK06                       AFD:ASMDISK06
DATADG_0003          ASMDISK07                       AFD:ASMDISK07
CRSDG_0003           ASMDISK08                       AFD:ASMDISK08
CRSDG_0004           ASMDISK09                       AFD:ASMDISK09
CRSDG_0005           ASMDISK10                       AFD:ASMDISK10
DATADG_0004          ASMDISK11                       AFD:ASMDISK11
DATADG_0005          ASMDISK12                       AFD:ASMDISK12
DATADG_0006          ASMDISK13                       AFD:ASMDISK13

26 rows selected.

--需要将ASM磁盘发现路径(注意,这跟设置AFD磁盘发现路径不是一个命令)中原先的路径去除,只保留AFD路径。
[grid@dbserver1 ~]$ asmcmd dsset 'AFD:*'
[grid@dbserver1 ~]$ asmcmd dsget
parameter:AFD:*
profile:AFD:*

--再次重启ASM,一切正常了。
SQL> select NAME,LABEL,PATH from V$ASM_DISK;

NAME                 LABEL                           PATH
-------------------- ------------------------------- ------------------------------------------------------------
CRSDG_0000           ASMDISK01                       AFD:ASMDISK01
CRSDG_0001           ASMDISK02                       AFD:ASMDISK02
CRSDG_0002           ASMDISK03                       AFD:ASMDISK03
DATADG_0000          ASMDISK04                       AFD:ASMDISK04
DATADG_0001          ASMDISK05                       AFD:ASMDISK05
DATADG_0002          ASMDISK06                       AFD:ASMDISK06
DATADG_0003          ASMDISK07                       AFD:ASMDISK07
CRSDG_0003           ASMDISK08                       AFD:ASMDISK08
CRSDG_0004           ASMDISK09                       AFD:ASMDISK09
CRSDG_0005           ASMDISK10                       AFD:ASMDISK10
DATADG_0004          ASMDISK11                       AFD:ASMDISK11
DATADG_0005          ASMDISK12                       AFD:ASMDISK12
DATADG_0006          ASMDISK13                       AFD:ASMDISK13

13 rows selected.

--收尾工作,将原先的udev rules文件移除。当然,这要在所有节点中都运行。以后如果服务器再次重启,AFD就会完全接管了。
[root@dbserver1 ~]# mv /etc/udev/rules.d/99-oracle-asmdevices.rules ~oracle/

还有什么发现?

其实,AFD也在使用udev。囧。

[grid@dbserver1 ~]$ cat /etc/udev/rules.d/53-afd.rules
#
# AFD devices
KERNEL=="oracleafd/.*", OWNER="grid", GROUP="asmdba", MODE="0770"
KERNEL=="oracleafd/*", OWNER="grid", GROUP="asmdba", MODE="0770"
KERNEL=="oracleafd/disks/*", OWNER="grid", GROUP="asmdba", MODE="0660"

Label过后的磁盘在/dev/oracleafd/disks目录中可以找到。

[root@dbserver2 disks]# ls -l /dev/oracleafd/disks
total 52
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK01
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK02
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK03
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK04
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK05
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK06
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK07
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK08
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK09
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK10
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK11
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK12
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK13

这里有一个很大不同,所有磁盘的属主变成了root,并且只有root才有写入的权限。很多文章认为,这就是AFD的filter功能体现了,因为现在用oracle或者grid用户都没有办法直接对ASM磁盘进行写入操作,自然就获得了一层保护。比如以下命令会直接报权限不足。

[oracle@dbserver1 disks]$ echo "do some evil" > ASMDISK99
-bash: ASMDISK99: Permission denied

但是如果你认为这就是AFD的保护功能,那也太小看Oracle了,仅仅是这样也对不起名字中Filter字样。且看后面分解。

操作系统中也可以看到AFD磁盘和底层磁盘的对应关系。

[grid@dbserver1 /]$ ls -l /dev/disk/by-label/
total 0
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK01 -> ../../sdc
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK02 -> ../../sdd
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK03 -> ../../sde
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK04 -> ../../sdf
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK05 -> ../../sdg
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK06 -> ../../sdh
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK07 -> ../../sdi
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK08 -> ../../sdj
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK09 -> ../../sdk
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK10 -> ../../sdl
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK11 -> ../../sdm
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK12 -> ../../sdn
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK13 -> ../../sdo

再次重启服务器以后,afd_lsdsk的结果中显示的路径都已经变为底层磁盘,但是Filtering却变成了DISABLED。不要在意这里的Label和Path的对应和上面的不一样,因为有些是在节点1中执行的结果,有些是在节点2中执行的结果,而这也是AFD功能的展示,不管两边机器发现块设备的顺序是不是一样,只要绑定了AFD的Label,就没问题了。

ASMCMD> afd_lsdsk
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                  DISABLED   /dev/sdd
ASMDISK02                  DISABLED   /dev/sde
ASMDISK03                  DISABLED   /dev/sdf
ASMDISK04                  DISABLED   /dev/sdg
ASMDISK05                  DISABLED   /dev/sdh
ASMDISK06                  DISABLED   /dev/sdi
ASMDISK07                  DISABLED   /dev/sdj
ASMDISK08                  DISABLED   /dev/sdk
ASMDISK09                  DISABLED   /dev/sdl
ASMDISK10                  DISABLED   /dev/sdm
ASMDISK11                  DISABLED   /dev/sdn
ASMDISK12                  DISABLED   /dev/sdo
ASMDISK13                  DISABLED   /dev/sdp

最后,该来测试一下I/O Filter功能了吧,等好久了!

对,这才是重点。

先看一下如何启用或者禁用Filter功能。在我的测试中,单独设置某块盘启用还是禁用是不生效的,只能全局启用或者禁用。

[grid@dbserver1 ~]$ asmcmd help afd_filter
afd_filter
        Sets the AFD filtering mode on a given disk path.
        If the command is executed without specifying a disk path then
        filtering is set at node level.

Synopsis
        afd_filter {-e | -d } [<disk-path>]

Description
        The options for afd_filter are described below

        -e      -  enable  AFD filtering mode
        -d      -  disable AFD filtering mode

Examples
        The following example uses afd_filter to enable AFD filtering
        on a given diskpath.

        ASMCMD [+] >afd_filter -e /dev/sdq

See Also
       afd_lsdsk afd_state

启用Filter功能。

[grid@dbserver1 ~]$ asmcmd afd_filter -e
[grid@dbserver1 ~]$ asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                   ENABLED   /dev/sdb
ASMDISK02                   ENABLED   /dev/sdc
ASMDISK03                   ENABLED   /dev/sdd
ASMDISK04                   ENABLED   /dev/sde
ASMDISK05                   ENABLED   /dev/sdf
ASMDISK06                   ENABLED   /dev/sdg
ASMDISK07                   ENABLED   /dev/sdh
ASMDISK08                   ENABLED   /dev/sdi
ASMDISK09                   ENABLED   /dev/sdj
ASMDISK10                   ENABLED   /dev/sdk
ASMDISK11                   ENABLED   /dev/sdl
ASMDISK12                   ENABLED   /dev/sdm
ASMDISK13                   ENABLED   /dev/sdn

为了以防万一,不破坏我自己的实验环境,增加了一块磁盘来作测试。

[root@dbserver1 ~]# asmcmd afd_label asmdisk99 /dev/sdo
Connected to an idle instance.
[root@dbserver1 ~]# asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                   ENABLED   /dev/sdb
ASMDISK02                   ENABLED   /dev/sdc
ASMDISK03                   ENABLED   /dev/sdd
ASMDISK04                   ENABLED   /dev/sde
ASMDISK05                   ENABLED   /dev/sdf
ASMDISK06                   ENABLED   /dev/sdg
ASMDISK07                   ENABLED   /dev/sdh
ASMDISK08                   ENABLED   /dev/sdi
ASMDISK09                   ENABLED   /dev/sdj
ASMDISK10                   ENABLED   /dev/sdk
ASMDISK11                   ENABLED   /dev/sdl
ASMDISK12                   ENABLED   /dev/sdm
ASMDISK13                   ENABLED   /dev/sdn
ASMDISK99                   ENABLED   /dev/sdo

创建一个新的磁盘组。

[grid@dbserver1 ~]$ sqlplus / as sysasm
SQL> create diskgroup DGTEST external redundancy disk 'AFD:ASMDISK99';

Diskgroup created.

先用KFED读取一下磁盘头,验证一下确实无误。

[grid@dbserver1 ~]$ kfed read AFD:ASMDISK99
kfbh.endian:                          1 ; 0x000: 0x01
kfbh.hard:                          130 ; 0x001: 0x82
kfbh.type:                            1 ; 0x002: KFBTYP_DISKHEAD
kfbh.datfmt:                          1 ; 0x003: 0x01
kfbh.block.blk:                       0 ; 0x004: blk=0
kfbh.block.obj:              2147483648 ; 0x008: disk=0
kfbh.check:                  1854585587 ; 0x00c: 0x6e8abaf3
kfbh.fcn.base:                        0 ; 0x010: 0x00000000
kfbh.fcn.wrap:                        0 ; 0x014: 0x00000000
kfbh.spare1:                          0 ; 0x018: 0x00000000
kfbh.spare2:                          0 ; 0x01c: 0x00000000
kfdhdb.driver.provstr:ORCLDISKASMDISK99 ; 0x000: length=17
kfdhdb.driver.reserved[0]:   1145918273 ; 0x008: 0x444d5341
kfdhdb.driver.reserved[1]:    961237833 ; 0x00c: 0x394b5349
kfdhdb.driver.reserved[2]:           57 ; 0x010: 0x00000039
kfdhdb.driver.reserved[3]:            0 ; 0x014: 0x00000000
kfdhdb.driver.reserved[4]:            0 ; 0x018: 0x00000000
kfdhdb.driver.reserved[5]:            0 ; 0x01c: 0x00000000
kfdhdb.compat:                168820736 ; 0x020: 0x0a100000
kfdhdb.dsknum:                        0 ; 0x024: 0x0000
kfdhdb.grptyp:                        1 ; 0x026: KFDGTP_EXTERNAL
kfdhdb.hdrsts:                        3 ; 0x027: KFDHDR_MEMBER
kfdhdb.dskname:               ASMDISK99 ; 0x028: length=9
kfdhdb.grpname:                  DGTEST ; 0x048: length=6
kfdhdb.fgname:                ASMDISK99 ; 0x068: length=9

直接使用dd尝试将整个磁盘清零。dd命令本身没有任何错误返回。

[root@dbserver1 ~]# dd if=/dev/zero of=/dev/sdo
dd: writing to `/dev/sdo': No space left on device
409601+0 records in
409600+0 records out
209715200 bytes (210 MB) copied, 19.9602 s, 10.5 MB/s

之后重新mount磁盘组,如果磁盘被清零,在重新mount的时候一定会出现错误,而现在正常挂载。

SQL>  alter diskgroup DGTEST dismount;

Diskgroup altered.

SQL>  alter diskgroup DGTEST mount;

Diskgroup altered.

觉得不过瘾?那再创建一个表空间,插入一些数据,做一次checkpoint,仍然一切正常。

SQL> create tablespace test datafile '+DGTEST' size 100M;

Tablespace created.

SQL> create table t_afd (n number) tablespace test;

Table created.

SQL> insert into t_afd values(1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system checkpoint;

System altered.

SQL> select count(*) from t_afd;

  COUNT(*)
----------
         1

但是诡异的是,这时候在操作系统级别直接去读取/dev/sdo的内容,会显示全部都已经被清空为0了。

[root@dbserver1 ~]# od -c -N 256 /dev/sdo
0000000  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0000400

使用strings命令也完全看不到任何有意义的字符。

[root@dbserver1 disks]# strings /dev/sdo
[root@dbserver1 disks]# 

但是,千万不要被这样的假象迷惑,以为磁盘真的被清空了,在dd的时候,/var/log/message会产生大量日志,明确表示这些在ASM管理的设备上的IO操作都是不被支持,这才是Filter真正起作用的场合。

afd_mkrequest_fn: write IO on ASM managed device (major=8/minor=224)  not supported

使用kfed,仍然可以读取到正常的信息。

[grid@dbserver1 ~]$ kfed read AFD:ASMDISK99
kfbh.endian:                          1 ; 0x000: 0x01
kfbh.hard:                          130 ; 0x001: 0x82
kfbh.type:                            1 ; 0x002: KFBTYP_DISKHEAD
kfbh.datfmt:                          1 ; 0x003: 0x01
kfbh.block.blk:                       0 ; 0x004: blk=0
kfbh.block.obj:              2147483648 ; 0x008: disk=0
kfbh.check:                  1854585587 ; 0x00c: 0x6e8abaf3
kfbh.fcn.base:                        0 ; 0x010: 0x00000000
kfbh.fcn.wrap:                        0 ; 0x014: 0x00000000
kfbh.spare1:                          0 ; 0x018: 0x00000000
kfbh.spare2:                          0 ; 0x01c: 0x00000000
kfdhdb.driver.provstr:ORCLDISKASMDISK99 ; 0x000: length=17
......

直到重新启动服务器(重新启动ASM,重新启动Cluster,在操作系统仍然看到的是清零后的数据),所有的数据又回来了。目前还不确认Oracle是使用了怎样的重定向技术实现了这样的神奇效果。

[root@dbserver1 ~]# od -c -N 256 /dev/sdo
0000000 001 202 001 001  \0  \0  \0  \0  \0  \0  \0 200   u 177   D   I
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040   O   R   C   L   D   I   S   K   A   S   M   D   I   S   K   9
0000060   9  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000100  \0  \0 020  \n  \0  \0 001 003   A   S   M   D   I   S   K   9
0000120   9  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000140  \0  \0  \0  \0  \0  \0  \0  \0   D   G   T   E   S   T  \0  \0
0000160  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000200  \0  \0  \0  \0  \0  \0  \0  \0   A   S   M   D   I   S   K   9
0000220   9  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000240  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0000300  \0  \0  \0  \0  \0  \0  \0  \0 022 257 367 001  \0   X  \0 247
0000320 022 257 367 001  \0   h 036 344  \0 002  \0 020  \0  \0 020  \0
0000340 200 274 001  \0 310  \0  \0  \0 002  \0  \0  \0 001  \0  \0  \0
0000360 002  \0  \0  \0 002  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000400
[root@dbserver1 ~]# 
[root@dbserver1 ~]# strings /dev/sdo | grep ASM
ORCLDISKASMDISK99
ASMDISK99
ASMDISK99
ORCLDISKASMDISK99
ASMDISK99
ASMDISK99
ASMDISK99
ASMDISK99
ASMPARAMETERFILE
ASMPARAMETERBAKFILE
ASM_STALE

最后将Filter禁用之后再测试。

[root@dbserver1 ~]# asmcmd afd_filter -d
Connected to an idle instance.
[root@dbserver1 ~]# asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                  DISABLED   /dev/sdb
ASMDISK02                  DISABLED   /dev/sdc
ASMDISK03                  DISABLED   /dev/sdd
ASMDISK04                  DISABLED   /dev/sde
ASMDISK05                  DISABLED   /dev/sdf
ASMDISK06                  DISABLED   /dev/sdg
ASMDISK07                  DISABLED   /dev/sdh
ASMDISK08                  DISABLED   /dev/sdi
ASMDISK09                  DISABLED   /dev/sdj
ASMDISK10                  DISABLED   /dev/sdk
ASMDISK11                  DISABLED   /dev/sdl
ASMDISK12                  DISABLED   /dev/sdm
ASMDISK13                  DISABLED   /dev/sdn
ASMDISK99                  DISABLED   /dev/sdo

同样使用dd命令清零整个磁盘。

[root@dbserver1 ~]# dd if=/dev/zero of=/dev/sdo
dd: writing to `/dev/sdo': No space left on device
409601+0 records in
409600+0 records out
209715200 bytes (210 MB) copied, 4.46444 s, 47.0 MB/s

重新mount磁盘组,如期报错,磁盘组无法加载。

SQL> alter diskgroup DGTEST dismount;

Diskgroup altered.

SQL> alter diskgroup DGTEST mount;
alter diskgroup DGTEST mount
*
ERROR at line 1:
ORA-15032: not all alterations performed
ORA-15017: diskgroup "DGTEST" cannot be mounted
ORA-15040: diskgroup is incomplete

重新启动数据库,也会发现由于表空间中数据库不存在而导致数据库无法正常Open。

SQL> startup
ORACLE instance started.

Total System Global Area  838860800 bytes
Fixed Size                  2929936 bytes
Variable Size             385878768 bytes
Database Buffers          226492416 bytes
Redo Buffers                5455872 bytes
In-Memory Area            218103808 bytes
Database mounted.
ORA-01157: cannot identify/lock data file 15 - see DBWR trace file
ORA-01110: data file 15: '+DGTEST/CDB12C/DATAFILE/test.256.864163075'

有结论吗?

以上还不够吗?就酱!