暂无图片
暂无图片
9
暂无图片
暂无图片
暂无图片

我的DBA手记(张乐奕)

原创 由迪 2020-10-09
2027

题记

    在学习Oracle技术的过程中,如何掌握正确的方法,推进自己的学习和思考,获得持续的成长是非常重要的。在Kamus的DBA手记中,我们整理收录了他关于自我学习的总结、动手实践的验证、设计分析的解惑,在这样的系列文章中,我们得以学习和借鉴他的思路精华,并最终对大家有所裨益。

5.1 Kamus谈技术、学习与分享

    随着Oracle数据库的发展和广泛使用,整个数据和运维行业的变革也是显而易见的。Oracle数据库为什么如此受欢迎,这些变革将会产生什么样的影响呢?我们来看Kamus在访谈中表达的看法总结。

5.1.1 Oracle的适用场景

    问及当前使用Oracle数据库的应用,Kamus说,可能已经不能问主要有哪些应用采用Oracle数据库了,倒是应该反过来问,有哪些应用不选Oracle数据库呢?除了四大银行核心系统(这是Oracle数据库没有进入的领域)采用IBM大机(实际上很多稍小的银行核心系统也是采用Oracle数据库),当然一些大客户的系统非常多,也绝不仅仅是只采用Oracle一家的产品,比如电信行业的BI目前应该就是被IBM垄断的。另外,现在如火如荼的电子商务(Web网站)领域,也大量采用MySQL等开源数据库,甚或是如MongoDB这样NoSQL数据库,但是通常在重要的订单生成及支付环节,还是在使用Oracle数据库。

5.1.2 为什么Oracle广受欢迎

    这要从Oracle数据库的优点说起。Oracle数据库的优点是见仁见智的问题,每个数据库产品在技术上都有自己的优点,除了技术上的优劣,非技术的原因其实也有很大关系,而Oracle数据库之所以受欢迎,是因为其采用了免费下载的方式,迅速被大量技术人员所熟悉并获得了他们的支持,这一点跟Windows在国内的发展其实可以相提并论。我一直认为其他厂商也完全应该仿效这种模式,免费下载,免费测试,商业使用再付费。另外,每2年一次的大版本升级,也让Oracle数据库始终走在技术前沿,不管如何,混个脸熟,这是必须的。

5.1.3 如何实现灾备

    关于Oracle数据库的灾备技术,Oracle的DataGuard,RAC,GoldenGate已经广泛应用,如果后两者可以算作灾备的方式的话。如今各大型数据库使用灾备是很普遍的,可见数据的安全已经得到了人们的高度重视。可以分享一则我们救援的数据丢失及恢复的真实案例,案例场景如下:客户准备为没有任何备份的数据库添加RMAN备份策略,但是由于空间问题,需要先添加一些磁盘,在Linux操作系统上,添加了一块磁盘,然后重启了主机,想当然的将最后一块磁盘格式化了(意思是之前有/dev/sda和/dev/sdb,添加了新硬盘以后,认为新硬盘必然是/dev/sdc,因此就直接格式化了/dev/sdc,并且创建了文件系统),但是实际上新加的盘在机器重启以后被认成/dev/sda,而原先的sda和sdb则变成了sdb和sdc,所以格式化/dev/sdc的命令就直接将一个包含大量数据的ASM磁盘格式化了。没有任何备份,磁盘被格式化,非常悲惨的一次事件。虽然最后我们通过一系列手段将大部分数据救回,但是客户也为此损失了大量财力、人力和时间。
    这个案例之所以印象深刻,是因为在实施灾备的过程中破坏了数据,就好比在买保险的路上被车撞了,它告诉我们灾难时刻存在,一分一秒都可能碰上,细心+技术可以避免一些问题,但是只有完善的灾备才可以救命于水火。试想如果你的机房部署在2001年9月11日的纽约双子大厦,仅此一份,没有异地备份,前一秒可能还在洋洋自得,系统性能调整得多好,业务又得到长足发展,后一秒一切都化为乌有,再细心再有技术也于事无补。

5.1.4 数据库发展对DBA工作的影响

    数年前,DBA的工作范畴可能还局限在Oracle数据库中,只要登录进SQL*Plus,所有的工作都能完成了。但是如今,DBA已经不仅仅是数据库实例管理员了,而已经可以称为数据管理员。没有任何一个软件,可以替代行业知识,可以替代人对数据的熟悉程度,也没有任何一个软件可以用来决策系统架构,所谓“逆水行舟,不进则退”,如果在DBA这个岗位工作了5年,却还只知道数据库的备份恢复,表空间的整理,索引的调整,那么可能确实要为自己的未来担忧一下了。同时Exadata的出现,意味着DBA的角色前所未有的复杂起来,不仅需要懂数据库,还需要懂主机、存储、网络,更需要懂的是应用的特点、数据的分布特点,所以只要能跟随着技术,不断学习,同时不断积累行业知识,DBA可以做到老,哪怕在中国也是可以做到老的。当然,如果你想DBA做到年薪100万,恐怕还是要先考虑如何进入管理层。

5.1.5 Oracle的学习方法

    学习任何东西唯一一个需要具备的素质就是兴趣,唯一一个不能缺少的素质也是兴趣。如果当你看到一篇跟你技术相关的并且又很有帮助的文档,但是却完全没有兴奋感的话,那么可以考虑一下,是不是应该转型,不要再做技术了。DBA跟其他的技术工种一样,没有什么需要具备的特殊素质,能做的好还是不好,有兴趣没兴趣最重要。Oracle是值得成为兴趣的行当,绝不应该仅仅当成谋生的手段。

    那么如何系统学习Oracle?

    推荐我学习的三部曲,即阅读官方文档+自己动手做实验+论坛中参与讨论,这是快速提高技术的方法。

  • 如果想学习Oracle,但是从来不知道docs.oracle.com这个网址,那么你并没有认真在学习。
  • 如果想学习Oracle,但是自己的机器上都没有Oracle数据库,也没有几个虚拟机环境,那么你并没有认真在学习。
  • 如果想学习Oracle,却从不知道有一个itpub.net可以讨论Oracle,也不知道ACOUG用户组,倒不能说你没有在认真学习,只是自己一个人学习不觉得孤独吗?

    如果你一直在没有兴趣地,不认真地,孤独地学习着Oracle,那么要不改变,要不放弃。因为不合适的学习方法并不利于个人的成长。

5.1.6 如何成为ACE

    Oracle ACE实际上也并不仅仅是一个技术上的殊荣,而更多的是考虑社区贡献的多少,一个人即使有很强的技术,但是从来不愿意分享,那么仍然不符合ACE的选拔条件。当然实际上这样的人并不会太多,特别是在当今的网络时代,拥有高深技术却不愿意分享的人真的不会太多,所以从这个层面看上去,似乎Oracle ACE都是技术高手,但是实际上,Oracle ACE应该是积极的Oracle社区分享者,分享则包括了写自己的Blog、自己的著作出版、翻译出版、组织社区活动以及积极在社区活动中做主题演讲等等。

    如果你明确了这些,关于如何成为ACE就是很显而易见的事情。

    从认真学习Oracle开始,坚持每天学习,坚持总结心得,坚持分享体会(通过网络中的各种方式,比如blog,比如论坛,比如用户组),大约需要2年的时间,可以有所斩获,然后再坚持2年,大约就有机会成为Oracle ACE了,说上去似乎并不难,但是持之以恒饱含热情地学习并且分享持续4年的时间,大约是现在这个浮躁的社会很少人能够做到的,但是如果你做到了,你就成功了。4年时间在一个人的职业生涯中其实并不算多,只是看你愿不愿意静下心来付出这4年。

    任何事情都没有捷径,如果最初就抱着走捷径的目的去做一件事情,那么一开始就输了。

5.2 以12c Identity类型示范自我探索式学习方法

    题记:这篇文章首先我会从几个方面介绍下我的学习思路和方法,供大家参考。然后借助案例分析的过程来分享在技术研究中的一些技巧。作为一个做技术的人,方法很重要。凡事只有掌握了有效的方法,才能在学习的过程中事半功倍。但我并不主张笼统地谈正确的学习方法是什么,因为不同的技术领域,甚至是不同的知识背景,都有其特殊性,那就要求我们在学习的过程中发现知识的特殊性,并找到最适合的学习方法。

    首先描述一下我自己在学习新知识的时候大概是什么状态,什么思路,因为自认为自己的学习能力还不错,因此也期望这样的学习方法对其他人会有帮助。看这篇文章的时候,你可以同步地想一想如果是你遇到这样的错误,你会怎么处理,怎么发散,怎么研究?

    Oracle Database 12c作为最新一代的Oracle数据库产品,已经广为使用,那么,如果学习一个新版本的数据库,应该如何开始呢?

    我通常是**从**New Features Guide****文档看起,先通览文档的目录,遇到感兴趣的新功能点,就开始做实验来验证这个新功能。当然,这之前需要先把新版本的数据库安装好、新版本的全部文档下载到本地,这样即使你坐在飞机上也有文档可查。

    这次我的计划是**实验一下**Identity****类型的字段,这个字段可以用来作主键,会自动递增,这种类型的字段在SQL Server中早就存在,但是Oracle直到12c才推出这个功能。

    通常我不会用sys用户进行任何实验(除非是验证sysdba的新功能),因此总是会先创建一个我自己的dba用户。

    在12c中创建这个用户首先就遇到了错误(测试环境启用了多租户架构)。

SQL> CREATE USER kamus IDENTIFIED BY oracle DEFAULT tablespace users;

ERROR at line 1:

ORA-65096: invalid common USER OR ROLE name


    对于一个不熟悉的错误,第一件事情不是去Google,而是**用**oerr******实用程序来看看**Oracle****自己对这个错误是怎么解释的。为什么我喜欢非Windows环境中的Oracle?oerr的存在也是很大一个原因。


65096, 00000, "invalid common user or role name"

// *Cause: An attempt was made to create a common user or role with a name

//     that wass not valid for common users or roles. In addition to

//     the usual rules for user and role names, common user and role

//     names must start with C## or c## and consist only of ASCII

//     characters.

// *Action: Specify a valid common user or role name.

 

    错误信息的解析非常明确地告知“试图创建一个通用用户,必需要用C## 或者c##开头”,这时候心里会有疑问,什么是common user?但是我通常不会先急着去翻文档,而是先把手头的事情做完,也就是先把用户创建上。


USER created.

SQL> GRANT dba TO c##kamus; 

GRANT succeeded. 

    创建C##KAMUS用户成功之后,再返回去解决心中的疑问,什么是common user?在联机文档的左上角搜索关键字common user,会得到如图5-1所示的结果。
image.png

图5-1

    通常我会先浏览目录,如果看完觉得心中疑问已经解决,就会返回继续做之前的实验,不会再浏览其他的链接;如果想要查询怎么做,比如说如何创建common user,才会继续去看正文部分。这样的好处是可以保持专注不至于被过多文档分心

    但是由于common user这个概念几乎是崭新的,所以我很有兴趣继续探索一下:跟common user相对的local user该如何创建。继续去看正文当然是个方法,但是这里我选择的是直接去看SQL Language Reference,因为我们知道一定是在Create User语法里面会有不同的定义,进入Create User语法页面,直接搜索common user,就可以看到如下这段话。

CONTAINER Clause:

To create a local user in a pluggable database (PDB), ensure that the current container is that PDB and specify CONTAINER = CURRENT. To create a common user, ensure that the current container is the root and specify CONTAINER = ALL. The name of the common user must begin with C## or c##. If you omit this clause and the current container is a PDB, then CONTAINER = CURRENT is the default. If you omit this clause and the current container is the root, then CONTAINER = ALL is the default.


    也就是说我们一定要先登录进一个PDB,才可以创建本地用户,那么如何知道现在的SQL*Plus是登录进了哪个DB呢?这个疑问其实是一个很简单的联想,既然需要去一个地方,那么一定有方法知道我现在在什么地方,通过简单地查询文档,可以得知以下的方法。现在确实在CDB中。

SQL> SHOW con_name 

CON_NAME

\------------------------------

CDB$ROOT 

SQL> SELECT SYS_CONTEXT ('USERENV', 'CON_NAME') FROM DUAL; 

SYS_CONTEXT('USERENV','CON_NAME')

\-----------------------------------------------

CDB$ROOT

    dbca建库的时候,有一个新选项是“同时创建PDB”,我勾选过(对于dbca****中出现的新选项,如果不是条件不允许,我都会选中进行测试),创建了名字为pdbtest的PDB,那么现在我想尝试登录这个PDB,去创建一个local user。

    如何登录PDB?Administrator’s Guide中有专门的一个章节“Part VI Managing a Multitenant Environment”来描述如何管理多租户环境,浏览目录就可以直接找到“Connecting to a PDB with SQL*Plus”这部分,如下所示。

You can use the following techniques to connect to a PDB with the SQL*Plus CONNECT command:

Database connection using easy connect

Database connection using a net service name

    那尝试直接使用easy connect来登录PDB。


SQL*Plus: Release 10.2.0.4.0 - Production ON Sat Jul 6 21:44:42 2013

 

Copyright (c) 1982, 2007, Oracle. ALL Rights Reserved.

Connected TO:

Oracle DATABASE 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production

WITH the Partitioning, OLAP, Advanced Analytics AND REAL Application Testing options

    进行如下操作。


SYS_CONTEXT('USERENV','CON_NAME')

\------------------------------------------

PDBTEST

SQL> SELECT NAME,PDB FROM dba_services;

ERROR at line 1:

ORA-01219: DATABASE OR pluggable DATABASE NOT OPEN: queries allowed ON fixed TABLES OR views ONLY

    PDB没有Open?尝试打开。无法使用startup命令。原因是我使用了旧版本的SQL*Plus(如上所示是10.2.0.4.0)连接到12c数据库的PDB中,某些新特性不被支持。

SQL> startup

ORA-24543: instance startup OR shutdown NOT allowed IN pluggable DATABASE

    使用12c自带的SQLPlus登录,就可以使用startup命令将PDB打开,使用SQLPlus管理PDB的详细命令可以参看文档描述。

或者可以使用如下语句打开PDB。

SQL> SHOW USER

USER IS "SYS"

SQL> startup

Pluggable DATABASE opened.

SQL> SHOW con_name

 

CON_NAME

\------------------------------

PDBTEST

 

SQL> ALTER PLUGGABLE DATABASE OPEN;

SQL> SELECT NAME, OPEN_MODE, RESTRICTED, OPEN_TIME FROM V$PDBS;

NAME OPEN_MODE RES OPEN_TIME

--------- ---------- --- --------------------------

PDBTEST  READ WRITE NO 06-JUL-13 09.48.57.260 PM

    到此,可以创建local user了。

SQL> CREATE USER kamus IDENTIFIED BY oracle; 

USER created. 

SQL> GRANT dba TO kamus; 

GRANT succeeded.


    那么在一个PDB中可以看到多少用户呢?可以看到CDB中的用户吗?
    这又是一个简单的联想,学习的过程其实是一个发散再收缩的循环。看来不可以,只能看到自己的用户,当然这里有很多common user。可以看到即使是在PDB中,cdb_视图也是可以使用的。

SQL> SELECT CON_ID,COUNT(*) FROM cdb_users GROUP BY con_id;

  CON_ID  COUNT(*)

---------- ----------

   3   38

    再回到CDB中看一下,会是什么情况?可以看到所有容器数据库中的用户都可以查询到。

SQL> SELECT CON_ID,COUNT(*) FROM cdb_users GROUP BY con_id;

  CON_ID  COUNT(*)

---------- ----------

​     1     36

​     2     35

​     3     38


    终于,我可以回到最开始的实验目标上去了,在PDB中创建了T1表,id列为Identity类型。

SQL> CREATE TABLE t1 (id NUMBER GENERATED AS IDENTITY);

TABLE created.

    根据文档描述,Identity类型仍然是通过Sequence来实现的,那么应该是自动创建了一个Sequence,果然如此。在你学习的过程中会多此一步来查询一下Sequence视图吗?

SQL> SELECT SEQUENCE_NAME FROM user_sequences;

SEQUENCE_NAME

\---------------------------------------------------------------

ISEQ$$_91620

 

默认创建的Sequence,CACHE_SIZE是20,开始值是1,这都跟单独创建的Sequence默认值一样。

SQL> SELECT * FROM user_sequences;

SEQUENCE_NAME INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S K MIN_VALUE MAX_VALUE

------------- ------------------------- - - ---------- ----------- --------------- - -

ISEQ$$_91620 1 N N   20    1     N N

 

插入一条数据试一下,报错报错还是报错。所以是generated always的identity列,如果只有这一列,就没法插入数据。


SQL> INSERT INTO t1 VALUES('');

ERROR at line 1:

ORA-32795: cannot INSERT INTO a generated always IDENTITY COLUMN

 

SQL> INSERT INTO t1 VALUES(ISEQ$$_91620.NEXTVAL);

ERROR at line 1:

ORA-32795: cannot INSERT INTO a generated always IDENTITY COLUMN

SQL> INSERT INTO t1 VALUES(NULL);

ERROR at line 1:

ORA-32795: cannot INSERT INTO a generated always IDENTITY COLUMN



换GENERATED BY DEFAULT ON NULL 类型试一下,Wait,如果删除了表,对应的序列会自动删除吗?理论上应该会,当然还是要测试一下。


SQL> DROP TABLE t1;

TABLE dropped.



序列还在?

SQL> SELECT * FROM user_sequences;

SEQUENCE_NAME INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S KMIN_VALUE MAX_VALUE

\-----------------------------------------------------------------

------------ - - ---------- ----------- --------------- - -

ISEQ$$_91620 1 N N   20    1     N N

Elapsed: 00:00:00.00

 

再建一张测试表。


SQL> CREATE TABLE t2 (id NUMBER GENERATED BY DEFAULT AS IDENTITY);

TABLE created.


现在是2个序列了。


SQL> SELECT * FROM user_sequences;

SEQUENCE_NAME  MIN_VALUE MAX_VALUE INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S K

-------------------------------------- - - ---------- ----------- --------------- - -

ISEQ$$_91620  1 1.0000E+28  1 N N   20  1 N N

ISEQ$$_91622  0000E+28    1 N N   20 1 N N


写完整的Drop语句试一下。


SQL> DROP TABLE t2 cascade CONSTRAINT purge;

TABLE dropped.


后面创建的序列已经被自动删除了,之前创建的还在。

SQL> SELECT * FROM user_sequences;

SEQUENCE_NAME  MIN_VALUE MAX_VALUE

-------------------------------------------------------------------------- ----------

INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S K

------------ - - ---------- ----------- --------------- - -

ISEQ$$_9162  1.0000E+28 1 N N  20    1     N N


两者的不同应该是purge,如果被删除的表还在回收站中,序列是会保留的,因为表还可能从回收站里面再restore回来,需要保证序列仍然有效。那么清空回收站实验一下。

SQL> purge recyclebin;

Recyclebin purged.

 

果然,相应的序列也被删除了。


SQL> SELECT * FROM user_sequences;

no ROWS selected


再回到正题,创建T3表,插入一条数据。


SQL> CREATE TABLE t3 (id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY);

TABLE created 

SQL> INSERT INTO t3 VALUES(NULL);

1 ROW created.


序列的LAST_NUMBER已经增加为21。


SQL> SELECT * FROM user_sequences;

SEQUENCE_NAME MIN_VALUE MAX_VALUE

INCREMENT_BY C O CACHE_SIZE LAST_NUMBER PARTITION_COUNT S K

------------ - - ---------- ----------- --------------- - -

ISEQ$$_91624  1.0000E+28  1 N N   20    21     N N


后台如何操作的?使用10046 trace,再插入几条数据。


SQL> INSERT INTO t3 VALUES(NULL);

1 ROW created.

 

SQL> INSERT INTO t3 VALUES(NULL);

1 ROW created.

 

SQL> SELECT * FROM t3;

   ID

\----------

   1

   2

   3

查看10046 trace的结果。可以看到执行计划中直接调用了SEQUENCE,就跟之前插入记录的时候明确指定SEQ.NEXTVAL一样。其实Oracle的实现方法非常简单,这一列其实就是Number类型,然后将这一列的Default值设置为”KAMUS”.”ISEQ$$_91624″.nextval,仅此而已。


insert into t3 values (null)

 

call   count   cpu   elapsed   disk   query  current   rows

-------  ------   ------  -------    --------  --------  -------   --------

Parse   1   0.00    0.00     0     0     0    0

Execute  1   0.00    0.00     0     1     3    1

Fetch   0   0.00    0.00     0     0     0    0

-------  ------   ------   --------    --------   --------   ------- ------

total   2   0.00    0.00     0     1     3     1

 

Misses in library cache during parse: 1

Optimizer mode: ALL_ROWS

Parsing user id: 104

Number of plan statistics captured: 1 

 

Rows (1st) Rows (avg) Rows (max) Row Source Operation

---------- ---------- ---------- -------------------------------------------

​     0     0     0 LOAD TABLE CONVENTIONAL (cr=1 pr=0 pw=0 time=90 us)

​     1     1     1  SEQUENCE ISEQ$$_91624 (cr=0 pr=0 pw=0 time=14 us)

Elapsed times include waiting on following events:

 Event waited on               Times  Max. Wait Total Waited

 ----------------------------------------  Waited ---------- ------------

 SQL*Net message to client            1    0.00     0.00

 SQL*Net message from client           1    5.28     5.28

****************************************************************************


使用DBMS_METADATA.GET_DDL获取到的DDL信息,已经符合12c语法的样式了,显示出了Sequence的具体信息。


SQL> SELECT dbms_metadata.GET_DDL('TABLE','T3') FROM dual;

DBMS_METADATA.GET_DDL('TABLE','T3')

\------------------------------------------------------------------

 CREATE TABLE "KAMUS"."T3"

  ( "ID" NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY MINVALUE 1 MAXVALUE 99

99999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE

 NOT NULL ENABLE,

   "COMMENTS" VARCHAR2(100)

  ) SEGMENT CREATION IMMEDIATE

 PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255

 NOCOMPRESS LOGGING

 STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645

 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1

 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)

 TABLESPACE "USERS"

系统自动产生的序列无法手工修改属性。


SQL> ALTER SEQUENCE "ISEQ$$_91624" INCREMENT BY 10;

ERROR at line 1:

ORA-32793: cannot ALTER a system-generated SEQUENCE

 

SQL> host oerr ora 32793

32793,0000, "cannot alter a system-generated sequence"

// *Cause: An attempt was made TO ALTER a system-generated SEQUENCE.

// *Action: A system-generated SEQUENCE, such AS one created FOR an

//     IDENTITY COLUMN, cannot be altered.


系统自动产生的序列也不允许删除。


SQL> DROP SEQUENCE "ISEQ$$_91624";

ERROR at line 1:

ORA-32794: cannot DROP a system-generated SEQUENCE

 

SQL> host oerr ora 32794

32794,0000, "cannot drop a system-generated sequence"

// *Cause: An attempt was made TO DROP a system-generated SEQUENCE.

// *Action: A system-generated SEQUENCE, such AS one created FOR an

//     IDENTITY COLUMN, cannot be dropped.


在11gR2中,错误信息编号在ORA-32790和ORA-32800之间是空白,而12c使用了这其间的8个错误号作为新特性的报错。

ORA-32791: prebuilt table managed column cannot have a default on null

Cause: An attempt was made to create a materialized view on a prebuilt table that has a managed column with a default on null expression.

Action: Either remove the default on null property, or do not include the column in the materialized view definition.

ORA-32792: prebuilt table managed column cannot be an identity column

Cause: An attempt was made to create a materialized view on a prebuilt table that has a managed column that is an identity column.

Action: Either remove the identity property, or do not include the column in the materialized view definition.

ORA-32793: cannot alter a system-generated sequence

Cause: An attempt was made to alter a system-generated sequence.

Action: A system-generated sequence, such as one created for an identity column, cannot be altered.

ORA-32794: cannot drop a system-generated sequence

Cause: An attempt was made to drop a system-generated sequence.

Action: A system-generated sequence, such as one created for an identity column, cannot be dropped.

ORA-32795: cannot insert into a generated always identity column

Cause: An attempt was made to insert a value into an identity column created with GENERATED ALWAYS keywords.

Action: A generated always identity column cannot be directly inserted. Instead, the associated sequence generator must provide the value.

ORA-32796: cannot update a generated always identity column

Cause: An attempt was made to update an identity column created with GENERATED ALWAYS keywords.

Action: A generated always identity column cannot be directly updated.

ORA-32797: identity column sequence mismatch in ALTER TABLE EXCHANGE PARTITION

Cause: The two tables specified in the EXCHANGE have identity columns with sequences that are neither both increasing nor decreasing.

Action: Ensure that the identity columns have sequences with INCREMENT BY having the same sign.

ORA-32798: cannot use ANSI RIGHT or FULL outer join with a left correlation

Cause: An attempt was made to use a lateral view with a left correlation to the first operand of an ANSI RIGHT or FULL outer join.

Action: Rewrite the query without the left correlation.

到此为止可以休息一下了,从ORA-65096开始大概花费了1个多小时的时间,我学习到了:

(1)什么是common user,什么是local user?

(2)如何查询现在的环境是CDB还是某个PDB?

(3)如何登录PDB?

(4)如何启动PDB?

(5)PDB和CDB中视图看到的内容有怎样的不同?

(6)如何创建Identity类型的列?

(7)删除表以后,对应的Sequence如何处理?

(8)Oracle后台对于Identity列是如何处理的?

你是不是也是这样学习的呢?

5.3 DUMP Block是否会引起Block读入内存

    中国有一句古话说“熟视无睹,常见不疑”,指的是我们可能忽视那些随时可见的事物,并且对常见之处深信不疑,这期间可能存在巨大的误区。能够基于常见问题提出辨析和思考,并通过实践验证,是最为考验一个人知识体系的。

在一次讨论中,以下问题被提出:当我们使用DUMP Block方式进行数据块转储时,是否需要将数据读入内存呢?

这是个常用的操作,可是很少有人思考过这个问题,有了思考还要有方法去验证,这整个过程代表着一个工程师在技术上的成熟。

我们立即动手,通过实例来求解一下这个问题的答案。

(1)重启一下数据库,这样buffer cache中几乎就没什么用户数据了,方便测试。

(2)找一张表看看是在哪个file哪个block里面(测试表,一行数据)。


SQL> select dbms_rowid.rowid_relative_fno(rowid) fno,dbms_rowid.rowid_block_number (rowid) block# from t1;

 

FNO   BLOCK#

---------- ----------

1   103001



(3)T1表在数据文件1中,第一个block是103001,检查v$bh,看看这个block有没有在buffer cache中。


SQL> select count(*)

 2  from v$bh

 3  where file# = 1 and block# = 103001;

 

 COUNT(*)

\----------

​     0


v$bh视图保存着buffer cache中每一个block****的信息,是一个重要视图。

(4)目前buffer cache中没有这个block,做一次dump再看看有没有。


SQL> alter system dump datafile 1 block 103001;

System altered

SQL> select count(*)

 2  from v$bh where file# = 1 and block# = 103001;

COUNT(*)

\----------

​     0


(5)这就验证了做block dump不会把数据块先读入buffer cache。

(6)继续做一次select看看,这次一定是读进buffer cache了。


SQL> select * from ops$kamus.t1;

SQL> select count(*)

 2  from v$bh

 3  where file# =1 and block# =103001;

 COUNT(*)

\----------

​     1



这就证明了我们的结论:DUMP Block操作不会引发Block读入Buffer Cache。

5.4 DUMP Block是否会引起脏数据写入磁盘

    伴随着上一个问题,随之而来的问题是:Dump Block会否触发脏数据写入磁盘?

这一次我们尝试一个不同的工具BBED。BBED(Oracle Block Browser and Editor)工具是Oracle内部提供的数据块级别查看和修改工具。借助这个工具,我们可以方便的查看Oracle块block级别的存储细节信息,更好地了解Oracle Internal结构的技术细节,DBA们应当了解这个工具的简单使用方法。

掌握尽量多的工具,会让我们具备选择的基础,这也是DBA的基本技能要求。

首先亮出结论:****Dump Block******不会引起**Buffer cache****中的脏数据回写入磁盘。然后我们使用bbed工具来验证一下。

(1)创建一个测试表。


SQL> CREATE TABLE t (n NUMBER);

TABLE created



(2)插入一条数据,提交,然后强制checkpoint。


SQL> INSERT INTO t VALUES(1);

1 ROW inserted

SQL> commit;

Commit complete

SQL> ALTER system checkpoint;

System altered


(3)此时这条数据一定已经写回磁盘,这个无需验证,继续插入另外一条数据,提交,但是不checkpoint。


SQL> INSERT INTO t VALUES(2);

1 ROW inserted 

SQL> commit;

Commit complete



(4)此时这条脏数据在buffer cache中,我们可以通过dump block来验证。


tab 0, row 0, @0x1f9a

tl: 6 fb: --H-FL-- lb: 0x1  cc: 1

col  0: [ 2]  c1 02

tab 0, row 1, @0x1f94

tl: 6 fb: --H-FL-- lb: 0x2  cc: 1

col  0: [ 2]  c1 03

end_of_block_dump


(5)通过dbms_rowid包取得T表中记录的文件号和block号,本例中取得是file#=58, block#=570。

(6)关键步骤到了,现在我们要用bbed来获取磁盘上的数据块内容,然后跟dump block的结果比较一下。

创建一个filelist文件,命名为files.lst。


$ cat files.lst

58 /fin/u06/cnctest2data/system12.dbf 1048576000



创建一个参数文件par.bbd,用以被bbed调用。

$ cat par.bbd

blocksize=8192

listfile=/home/oraaux/files.lst

mode=browse

 

执行bbed。


$ bbed parfile=par.bbd

Password: 




BBED: Release 2.0.0.0.0 - Limited Production on Mon Mar 13 17:35:32 2006

Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.

************* !!! For Oracle Internal Use only !!! ***************

 

BBED> set dba 58,570

​    DBA       0x0e80023a (243270202 58,570)

 

BBED> x /*rn rowdata

rowdata[0]                  @8182   

\----------

flag@8182: 0x2c (KDRHFL, KDRHFF, KDRHFH)

lock@8183: 0x01

cols@8184:   1

col 0[2] @8185: 1 --只有一条记录,值是1

tailchk                   @8188   

\-------

BBED-00210: no row at this offset


到目前为止我们已经验证了dump block并不会把脏数据写回磁盘,为了看一下checkpoint的效果,我们继续往下。

(7)做checkpoint

SQL> ALTER system checkpoint;

System altered

(8)再次运行bbed。

$ bbed parfile=par.bbd

Password: 


 

BBED: Release 2.0.0.0.0 - Limited Production on Mon Mar 13 17:35:32 2006

Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.

************* !!! For Oracle Internal Use only !!! ***************

 

BBED> set dba 58,570

​    DBA       0x0e80023a (243270202 58,570) 

BBED> x /*rn rowdata

rowdata[0]                  @8176   

\----------

flag@8176: 0x2c (KDRHFL, KDRHFF, KDRHFH)

lock@8177: 0x02

cols@8178:   1

col   0[2] @8179: 2  --这是后来插入的记录,值是2

rowdata[6]                  @8182   

\----------

flag@8182: 0x2c (KDRHFL, KDRHFF, KDRHFH)

lock@8183: 0x01

cols@8184:   1 



col 0[2] @8185: 1 --这是第一条记录,值是1


tailchk                   @8188   

\-------

BBED-00210: no row at this offset


checkpoint将buffer cache中的脏数据写回数据文件了。

5.5 如何验证ASM的块头备份块的位置

大家都知道,在Oracle 10.2.0.5之前,ASM磁盘的头块并没有自己的备份,因此一旦头块损坏,如果没有以前kfed read备份出来的信息,也就没有办法使用kfed merge来作头块恢复,特别是如果一个磁盘组中所有的磁盘头块都出现问题(比如被人为地创建了PV),恢复ASM磁盘头块的操作就会非常麻烦。

但是从Oracle 10.2.0.5之后,ASM磁盘的头块会自动备份在另外一个块中,这实际上是Oracle 11g出现的功能,不过经过测试,在Oracle 10.2.0.5版本中,这个备份也是存在的。正是因为存在这个备份,所以Oracle 10.2.0.5之后的kfed程序才有了新的repair命令,该命令将备份块直接覆盖到磁盘头块,完成修复工作。

在Oracle 10.2.0.4中,如果尝试执行kfed repair,则会报错说命令行参数不正确,此报错说明并不存在repair命令。


$ kfed repair 

KFED-00101: LRM error [102] while parsing command line arguments



但是在Oracle 10.2.0.5中,执行kfed repair,则会说无法打开文件,而这正说明repair命令是存在的,报错是因为还需要明确指定要修复哪块磁盘。


KFED-00303: unable to open file ''

那么这个备份块具体存在哪里呢?**在学习**Oracle****技术的过程中,好奇心是驱使我们进步的强大动力,设问、思考、解答,这是获得自我提升的根本。养成动手的习惯,通过动手找出真相,这是成长的必经之路。

在Solaris下的测试,我们使用truss来进行跟踪。


$ truss -o tracedisk2.out kfed repair /asmdisks/vdisk2



在trace文件中,找到下面这段,可以明确地看到kfed程序从第510个块中读出4096字节,然后再写回到第0个块中。


stat("/asmdisks/vdisk2", 0xFFFFFD7FFFDFDB20)  = 0

open("/asmdisks/vdisk2", O_RDWR)        = 7

**lseek(7, 0x001FE000, SEEK_SET)         = 2088960 <-- 1FE is 510**

**read(7, "01820101FE\0\0\0\0\0\080".., 4096)   = 4096  <-- read 4096 bytes**

lseek(7, 0, SEEK_SET)              = 0

read(7, "01820101\0\0\0\0\0\0\080".., 4096)   = 4096

lseek(7, 0, SEEK_SET)              = 0  <-- 0 is 0

**write(7, "01820101\0\0\0\0\0\0\080".., 4096)  = 4096  <-- write 4096 bytes**

close(7)


同样如果是在Linux下用裸设备作为ASM磁盘,并且用strace进行repair命令的跟踪,也可以得到类似结果。


open("/dev/raw/raw3", O_RDWR)      = 5

lseek(5, 2088960, SEEK_SET)       = 2088960  <-- 2088960/4096=510

read(5, "\1\202\1\1\0\0\0\0\0\0\0\200evx\257\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096

lseek(5, 0, SEEK_SET)          = 0

read(5, "\1\202\1\1\0\0\0\0\0\0\0\200evx\257\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096

lseek(5, 0, SEEK_SET)          = 0

write(5, "\1\202\1\1\0\0\0\0\0\0\0\200evx\257\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096) = 4096

close(5)                = 0


那么通过kfed命令再来验证一下这两个块是否都标志为头块。验证结果表示块类型都为DISKHEAD。


$ kfed read /asmdisks/vdisk2 blkn=0 | grep KFBTYP

kfbh.type:              1 ; 0x002: KFBTYP_DISKHEAD

$ kfed read /asmdisks/vdisk2 blkn=510 | grep KFBTYP

kfbh.type:              1 ; 0x002: KFBTYP_DISKHEAD <-- this is the backup!!


那么下一个疑问是,在11gR2以后,ASM磁盘组的AU Size可以指定不同的大小,是不是不同的AU Size下的磁盘头块备份都是在第510个块呢?还是用truss来跟踪一下,这里的vdisk3属于一个AU Size=8M的磁盘组,此时repair命令需要明确指定aus,否则会报KFED-00320错误。


truss -o tracedisk3.out kfed repair /asmdisks/vdisk3 aus=8388608



在trace文件中,可以发现已经不是读第510个块,而是改为读第4094个块。


stat("vdisk3", 0xFFFFFD7FFFDFDB10)       = 0

open("vdisk3", O_RDWR)             = 7

lseek(7, 0x00FFE000, SEEK_SET)         = 16769024 <--FFE is 4094

read(7, "01820101FE07\0\0\0\0\080".., 4096)   = 4096

lseek(7, 0, SEEK_SET)              = 0

read(7, "01820101\0\0\0\0\0\0\080".., 4096)   = 4096

lseek(7, 0, SEEK_SET)              = 0

write(7, "01820101\0\0\0\0\0\0\080".., 4096)  = 4096

close(7)


用kfed验证第4094个块,确实标志为DISKHEAD。


$ kfed read /asmdisks/vdisk3 blkn=4094 | grep KFBTYP

kfbh.type:      1 ; 0x002: KFBTYP_DISKHEAD


那么也就是AU 1M的磁盘组头块备份在第510个块上,而AU 8M的磁盘组头块备份在第4094个块上,备份块的存储位置有规律吗?有的,始终保存在第2个AU的倒数第2个块上。下面来验证这个观点。

对于默认的磁盘组,AU Size=1M,每个AU中可以存储256个块,块号为0~255。第1个AU存储256个块,第2个AU最后1个块号为255,倒数第2个块号是254,也就是整体的第510个块(从第1个AU的第1个块往后算起)。


kfdhdb.ausize:         1048576 ; 0x0bc: 0x00100000

$ kfed read /asmdisks/vdisk2 blkn=0 | grep blksize

kfdhdb.blksize:          4096 ; 0x0ba: 0x1000

$ let r=1048576/4096;echo $r

256

$ let r=256+255-1;echo $r

510

 

对于AU Size=8M的磁盘组,每个AU可以存储2048个块,块号为0-2047。第1个AU存储2048个块,第2个AU最后1个块号为2047,倒数第2个块号是2046,也就是整体的第4094个块(从第1个AU的第1个块往后算起)。

$ kfed read /asmdisks/vdisk3 blkn=0 | grep ausize

kfdhdb.ausize:         8388608 ; 0x0bc: 0x00800000

$ kfed read /asmdisks/vdisk3 blkn=0 | grep blksize

kfdhdb.blksize:          4096 ; 0x0ba: 0x1000

$ let r=8388608/4096;echo $r

2048

$ let r=2048+2047-1;echo $r

4094

 

对于其他AU Size磁盘组的验证,看到文章的朋友有兴趣可以自己做一下。

结论:从Oracle 10.2.0.5开始,ASM磁盘已经开始自动将头块进行备份,备份块的位置在第2个AU的倒数第2个块上(对于默认1M的AU来说,是第510个块),如果头块损坏,可以用kfed repair命令来修复。

5.6 如何利用文件句柄恢复误删除的文件

动手、动手,还是动手,看到有兴趣的案例、方法,就坐言起行,通过实践将这些知识变成自己的知识储备。

这一次是客户的数据库意外被删除了整个目录中的数据文件,操作系统级别的删除,然而幸运的是这个数据库没有崩溃。仍然处于open状态的时候,客户就发现了问题,并求助到我们,最终完整地恢复了所有数据文件。

在Linux下大致重新演示一下恢复的过程,恢复的步骤与数据库版本没有太大关系,但是会因操作系统的不同有所改变。

(1)在数据库open的时候,直接删除users表空间中的数据文件。

SQL> select name from v$datafile;

NAME

\--------------------------------------------------------------

/app/oracle/oradata/ORCL/datafile/o1_mf_system_555wqbnk_.dbf

/app/oracle/oradata/ORCL/datafile/o1_mf_undotbs1_555wqxgl_.dbf

/app/oracle/oradata/ORCL/datafile/o1_mf_sysaux_555wr5p6_.dbf

/app/oracle/oradata/ORCL/datafile/o1_mf_users_555wrj4o_.dbf

SQL> host rm /app/oracle/oradata/ORCL/datafile/o1_mf_users_555wrj4o_.dbf

 

(2)尝试在users表空间中创建表,开始报错。

SQL> create table t tablespace users as select * from dual;

ERROR at line 1:

ORA-01116: error in opening database file 4

ORA-01110: data file 4:

'/app/oracle/oradata/ORCL/datafile/o1_mf_users_555wrj4o_.dbf'

ORA-27041: unable to open file

Linux Error: 2: No such file or directory

Additional information: 3


在警告日志中,同样也可以看到类似信息。


Mon Dec 19 21:48:17 CST 2011

Errors in file /app/oracle/admin/orcl/bdump/orcl_m000_3897.trc:

ORA-01116: error in opening database file 4

ORA-01110: data file 4: '/app/oracle/oradata/ORCL/datafile/o1_mf_users_555wrj4o_.dbf'

ORA-27041: unable to open file

Linux Error: 2: No such file or directory

Additional information: 3



(3)检查dbwr的进程PID。

$ ps -ef|grep dbw0|grep -v grep

oracle  2879   1 0 21:38 ?    00:00:00 ora_dbw0_orcl



(4)dbwr会打开所有数据文件的句柄。在proc目录中可以查到,目录名是进程PID,fd表示文件描述符。


$ cd /proc/2879/fd

$ ls -l

total 0

lr-x------ 1 oracle dba 64 Dec 19 21:50 0 -> /dev/null

lr-x------ 1 oracle dba 64 Dec 19 21:50 1 -> /dev/null

lr-x------ 1 oracle dba 64 Dec 19 21:50 10 -> /dev/zero

lr-x------ 1 oracle dba 64 Dec 19 21:50 11 -> /dev/zero

lr-x------ 1 oracle dba 64 Dec 19 21:50 12 -> /app/oracle/product/10.2.0/db_1/rdbms/mesg/oraus.msb

lrwx------ 1 oracle dba 64 Dec 19 21:50 13 -> /app/oracle/product/10.2.0/db_1/dbs/ hc_orcl.dat

lrwx------ 1 oracle dba 64 Dec 19 21:50 14 -> /app/oracle/product/10.2.0/db_1/dbs/lkORCL

lrwx------ 1 oracle dba 64 Dec 19 21:50 15 -> /app/oracle/oradata/ORCL/controlfile/o1_mf_ 555wq3ng_.ctl

lrwx------ 1 oracle dba 64 Dec 19 21:50 16 -> /app/oracle/oradata/ORCL/datafile/o1_mf_ system_555wqbnk_.dbf

lrwx------ 1 oracle dba 64 Dec 19 21:50 17 -> /app/oracle/oradata/ORCL/datafile/o1_mf_ undotbs1_555wqxgl_.dbf

lrwx------ 1 oracle dba 64 Dec 19 21:50 18 -> /app/oracle/oradata/ORCL/datafile/o1_mf_ sysaux_555wr5p6_.dbf

lrwx------ 1 oracle dba 64 Dec 19 21:50 19 -> /app/oracle/oradata/ORCL/datafile/o1_mf_ users_555wrj4o_.dbf (deleted)

lr-x------ 1 oracle dba 64 Dec 19 21:50 2 -> /dev/null

lrwx------ 1 oracle dba 64 Dec 19 21:50 20 -> /app/oracle/oradata/ORCL/datafile/o1_mf_temp_ 555wrbnz_.tmp

lr-x------ 1 oracle dba 64 Dec 19 21:50 21 -> /app/oracle/product/10.2.0/db_1/rdbms/mesg/ oraus.msb

lr-x------ 1 oracle dba 64 Dec 19 21:50 3 -> /dev/null

lr-x------ 1 oracle dba 64 Dec 19 21:50 4 -> /dev/null

l-wx------ 1 oracle dba 64 Dec 19 21:50 5 -> /app/oracle/admin/orcl/udump/orcl_ora_2871.trc

l-wx------ 1 oracle dba 64 Dec 19 21:50 6 -> /app/oracle/admin/orcl/bdump/alert_orcl.log

lrwx------ 1 oracle dba 64 Dec 19 21:50 7 -> /app/oracle/product/10.2.0/db_1/dbs/lkinstorcl (deleted)

l-wx------ 1 oracle dba 64 Dec 19 21:50 8 -> /app/oracle/admin/orcl/bdump/alert_orcl.log

lrwx------ 1 oracle dba 64 Dec 19 21:50 9 -> /app/oracle/product/10.2.0/db_1/dbs/ hc_orcl.dat


注意其中“/app/oracle/oradata/ORCL/datafile/o1_mf_users_555wrj4o_.dbf (deleted)”字样,表示该文件已经被删除,如果是Solaris操作系统,ls命令不会有如此清晰地显示,为了在Solaris系统中确认哪个句柄对应哪个文件,则需要使用lsof程序。

(5)直接cp该句柄文件名回原位置。


cp 19 /app/oracle/oradata/ORCL/datafile/o1_mf_users_555wrj4o_.dbf


(6)进行数据文件recover。


SQL> alter database datafile 4 offline;

Database altered.

SQL> recover datafile 4;

Media recovery complete.

SQL> alter database datafile 4 online; 

Database altered.



完成数据文件恢复。

恢复的原理是:在Linux操作系统中,如果文件从操作系统级别被rm掉,之前打开该文件的进程仍然持有相应的文件句柄,所指向的文件仍然可以读写,并且该文件的文件描述符可以从/proc目录中获得。但是要注意的是,此时如果关闭数据库,则此句柄会消失,那么除了扫描磁盘进行文件恢复之外就没有其他方法了,因此在数据库出现问题的时候,如果不确认情况的复杂程度,千万不要随便关闭数据库。重启数据库往往是没有意义的,甚至是致命的。

5.7 从一道面试题看分析问题的思路

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

题目是:请将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分钟以后都完全没有头绪。

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

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

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


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语句,实际上通过登录用户名等也可以快速定位被锁住的会话。

5.7.2 查找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


5.7.3 乙方DBA需谨慎

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

5.7.4 清除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分钟之内就可以解决问题。

5.7.5 深入一步

对于TX锁,在vlock视图中显示的ID1和ID2是什么意思? 解释可以从vlock_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视图中获得验证。


    如何和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。

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

5.8 涓涓细流终聚海

    整理这一章最主要的目的是想把我多年的经验和方法介绍给大家,让大家有所借鉴,并阐释我们欣赏的优秀技术人员的一些特质,最后我在这里简单总结一下。

(1)合适的学习方法。就像我在前面说过的,对于一个学习技术的人而言,掌握正确的学习方法很重要,虽然方法可能因人而异因事而异,但正确的方法总能让你在技术的道路上走得更快些。

(2)有用的学习工具。学会学习就是要充分利用一切可用的资源。Oracle内部提供了很多有用的工具,能够很好地帮助我们,除此而外,还有很多有用的外部工具也能够有效提升我们的效率。

(3)相关的各方面知识。所有的知识都不是单一的存在,要掌握一门技术或者意向能力,要求学习者掌握与其相关的领域和行业知识,扩展知识面,这样才能以全局的思维去认识事情的本质。

(4)解决问题的思路。在掌握了一定知识后,学会在实际问题中去应用很重要。灵活分析,善于思考,才能学以致用,让我们学的东西的价值最大化。

(5)解决问题的经验。无论是作为一个运维者还是其他工作的人,积累经验很重要。我们总会遇到各种各样的case,这些case之间有相同的地方也有不同的地方,当我们处理完一件事,要善于总结,吸取教训和经验,让每一次故障都成为我们成功路上的铺路石。

这就是我想要说的话。

最后修改时间:2020-10-10 15:38:34
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论