安装
Oracle in Docker
docke search oracle
,选择相应的 image 拉取
1 | $ docker run -d --name oracle \ |
镜像说明文档 中 Additional options 的意思是自己基于它的基础包重修 build 的时候可以修改的参数。
PS:在给自用 Ubuntu 安装时非常顺利,在给测试平台的Centos7
安装时诡异地出现了docker-entrypoint.sh
中一条chown
语句卡出运行不了,修改文件中相应的语句后成功安装。
Oracle in Centos
vi /etc/sysctl.conf
这里面有很多参数可以优化
内核的 shmall 和 shmmax 参数
1 | SHMMAX= 配置了最大的内存 segment 的大小 ------>这个设置的比 SGA_MAX_SIZE 大比较好。 |
使配置生效
/sbin/sysctl -p
swap 不存在
报错,提示swap空间不足
1 | Checking swap space: 0 MB available, 150 MB required. Failed <<<< |
创建 swap 文件
1 | dd if=/dev/zero of=/swapfile bs=1024 count=512k |
配置监听
1 | netca /silent /responsefile /home/database/response/netca.rsp # 这句话一定要写绝对路径 |
listener.ora 范例
1 | # listener.ora Network Configuration File:/u01/app/oracle/product/12/db_1/network/admin/listener.ora |
参考:
listener/tnsname
listener parameter
host 填 localhost 可能出现其他机子连不到的情况
使用 静态监听 方式时,lsnrctl status
会看到实例的状态为unknown
为正常现象。
- 动态监听:oracle 服务器默认会去绑定
1521
端口。 - 静态监听:在监听器中指定数据库信息(路径),由监听器去搜索服务。
sqlplus 无法使用 backspace 和 history
Cenots 中使用 sqlplus 无法使用删除和方向键,安装 rlwrap 解决。
系统防火墙配置
登录 root 检查防火墙状态
1 | [root@centos7 ~]# firewall-cmd --get-active-zones |
打开相关的端口
1 | [root@centos7 ~]# firewall-cmd --zone=public --add-port=1521/tcp --add-port=5500/tcp --add-port=5520/tcp --add-port=3938/tcp --permanent |
重新加载生效
1 | [root@centos7 ~]# firewall-cmd --reload |
查看端口规则
1 | [root@centos7 ~]# firewall-cmd --list-ports |
开机自启动配置
https://www.cnblogs.com/meiling12/p/8443823.html
- 查看ORACLE_HOME是否设置
1 | $ echo $ORACLE_HOME |
- 执行dbstart 数据库自带启动脚本
1 | [oracle@local ~]$ cd $ORACLE_HOME |
- 编辑
/etc/oratab
文件
dbca建库时都会自动创建/etc/oratab
文件
将 orcl:/u01/app/oracle/product/11.2.0/dbhome_1:N
修改成orcl:/u01/app/oracle/product/11.2.0/dbhome_1:Y
- 编辑
/etc/rc.d/rc.local
启动文件,添加数据库启动脚本 dbstart
1 | [root@local ~]# vi /etc/rc.d/rc.local |
管理
用户管理
创建的新用户是没有任何权限的,甚至连登陆的数据库的权限都没有,需要为其指定相应的权限。给一个用户赋权限使用命令grant
,回收权限使用命令revoke
。
权限分为系统权限和对象权限。
系统权限
用户对数据库的相关权限,connect、resource、dba 等系统权限,如建库、建表、建索引、建存储过程、登陆数据库、修改密码等。
1 | SQL> conn xiaoming/oracle |
注意:准确地说grant connect to xiaoming;
中,connect 不是权限,而是角色
对象权限
用户对其他用户的数据对象操作的权限,insert、delete、update、select、all 等对象权限,数据对象有很多,比如表,索引,视图,触发器、存储过程、包等。
执行SELECT * FROM Dba_Object_Size;
语句可得到 oracle 数据库对象。
希望 xiaoming 用户可以去查询 scott 的 emp 表
1 | grant select on scott.emp to xiaoming |
希望 xiaoming 用户可以去修改 scott 的 emp 表
1 | grant update on scott.emp to xiaoming |
希望 xiaoming 用户可以去修改/删除,查询,添加 scott 的 emp 表
1 | grant all on scott.emp to xiaoming |
scott 希望收回 xiaoming 对 emp 表的查询权限
1 | revoke select on scott.emp from xiaoming |
权限的传递
希望 xiaoming 用户可以去查询 scott 的 emp 表,还希望 xiaoming 可以把这个权限传递给别人。
如果是对象权限,就加入 with grant option
1 | grant select on emp to xiaoming with grant option |
如果 scott 把 xiaoming 对 emp 表的查询权限回收,那么 xiaohong 的权限被回收。
1 | SQL> conn scott/oracle; |
结果显示:小红受到诛连了。
- 如果是系统权限,就加入 with admin option
1 | grant connect to xiaoming with admin option |
收回这个用户的系统权限时,这个用户已经授予其他用户或角色的此系统权限不会因传播无效
代理用户
proxy users, allowing you to access a schema via a different username/password combination.
1 | CONN / AS SYSDBA |
特定 schema 的权限
想要将user1
下所有表的查询权限付给user2
用户,ORACLE 没有提供这个层级的语句。
方法 1:在 all_tables
中查出所有表后,拷贝输出结果执行
1 | select 'Grant all on '||table_name||'to user2 ;' from all_tables |
方法 2:存储过程
1 | create or replace procedure grant_selectAll_sql(v_from in varchar2, v_to in varchar2) is |
表空间管理
创建表空间
oracle 默认最大数据文件为 32G, 想要创建超过这个大小可以使用。一个 表空间不能混用 datafile
和 bigfile
。
1 | create bigfile tablespace ... |
为用户赋予权限,否则会报 ORA-01950
。
1 | ALTER USER userName quota [unlimited/100M] on tablespaceName; |
NAS 数据存储
oracle 对 NAS 的挂载方式有要求,ORACLE 不支持 cifs
NFS 挂载命令
1 | mount -o rw,bg,hard,nointr,rsize=32768,wsize=32768,vers=3,tcp,actimeo=0,timeo=600 192.168.1.2:/ETL /mnt/data |
字符集
server 字符集
查询 server 字符集
1 | SELECT * FROM V$NLS_PARAMETERS; |
client 端字符集
在 windows 平台下,就是注册表里面相应 OracleHome 的 NLS_LANG。还可以在 dos 窗口里面自己设置,这样就只影响这个窗口里面的环境变量,比如:
1 | set nls_lang=AMERICAN_AMERICA.ZHS16GBK |
在 unix 平台下,就是环境变量 NLS_LANG。
1 | echo $NLS_LANG |
如果检查的结果发现 server 端与 client 端字符集不一致,请统一修改为同 server 端相同的字符集
dump 文件字符集
用 oracle 的 exp 工具导出的 dmp 文件也包含了字符集信息,dmp 文件的第 2 和第 3 个字节记录了 dmp 文件的字符集。
如果 dmp 文件不大,比如只有几 M 或几十 M,可以用 UltraEdit 打开 (16 进制方式),看第 2 第 3 个字节的内容,如 0354,然后用以下 SQL 查出它对应的字符集:
1 | SQL> select nls_charset_name(to_number('0354','xxxx')) from dual; |
日期
Oracle 的日期格式
Oracle 数据缺省的时间格式数据的显示形式,与所使用的字符集有关。一般显示年月日,而不显示时分秒。
例如:
-
使用 us7ascii 字符集(或者是其他的英语字符集)时,缺省的时间格式显示为:28-Jan-2003,
-
使用 zhs16gbk 字符集(或其他中文字符集)时时间格式缺省显示为:2003-1 月-28。
向表中插入数据时,如果不使用转换函数,则时间字段的格式必须遵从会话环境的时间格式,否则不能插入。
查看当前会话的时间格式,可以使用以下的 SQL 语句:
1 | SQL> select sysdate from dual; |
修改日期格式的方法
- 在 sql*plus 中修改当前会话的日期格式
1 | SQL> alter session set nls_date_format = 'yyyy-mm-dd hh24:mi:ss'; |
将当前会话的时间格式修改为这种格式: 2003-01-28 15:23:38,这种修改方法,只对当前会话有效。
注意,是对当前会话,而不是当前的 sql*plus 窗口。即如果你这样修改之后,又使用 connect 命令以其他用户连接到数据库或者是连接到其他的数据库,则这个日期格式就失效了,又恢复到缺省的日期格式。
- 修改注册表(只对 windows 系统)
在注册表/hkey_local_machine/software/oracle/home0
主键中增加一个字串 (8i 版本),字串名为 nls_date_format,字串的值为你希望定义的时间格式,如: yyyy-mm-dd hh24:mi:ss ,然后重新启动 sql*plus。
这种修改方法,对 sql*plus 窗口有效,即不论你打开多少个 sql*plus 窗口,缺省的都是这种时间格式。修改服务器端的注册表无效,只有修改客户端的注册表才有效。
- 修改环境变量(Linux)
oracle 用户编辑 .bash_profile
下 加入以下内容,重新登录即可生效
1 | export NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS' |
- 用 sysdba 登录:然后更新 props$这个表里的字段即可
1 | update props$ set value = 'YYYY-MM-DD HH24:MI:SS' where parameter = 'NLS_DATE_FORMAT'; |
- sql developer 修改
工具->首选项->数据库->NLS->日期格式:DD-MON-RR 修改为:YYYY-MM-DD HH24:MI:SS
统计信息
sql developer 中看到的统计信息并不是实时的,只有使用analysis
命令之后才会更新,比如:
1 | ANALYZE TABLE tablename COMPUTE STATISTICS |
数据库恢复与迁移
X医院使用的数据库为 10.2.0.5 in winsows
,想要在Centos下恢复。
恢复步骤
-
安装数据库
-
修改环境变量
ORACLE_SID
-
nomout 启动,恢复 pfile 文件,并修改 pfile 文件中的路径为本地路径
1
2
3
4
5rman target /
rman>startup nomount
rman>restore spfile to pfile '$ORACLE_HOME/dbs/init_$ORACLE_SID.ora' from'/backup/rman/full_09l9esg4_1_1'; -
sql使用 pfile 重启数据库至 nomount 状态,恢复 controlfile,启动到 mount 状态
1
2RMAN> restore controlfile from '...'
RMAN> alter database mount; -
检查备份,设置 datafi 路径,restore datebase
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15RMAN> catalog start with '/home/oracle/rmanbackup';
RMAN> run{
2> allocate channel c1 type disk;
3> set newname for datafile 1 to '/u01/app/oracle/oradata/csdb/system01.dbf';
4> set newname for datafile 2 to '/u01/app/oracle/oradata/csdb/sysaux01.dbf';
5> set newname for datafile 3 to '/u01/app/oracle/oradata/csdb/undotbs1.dbf';
6> set newname for datafile 4 to '/u01/app/oracle/oradata/csdb/users01.dbf';
7> set newname for datafile 5 to '/u01/app/oracle/oradata/csdb/example01.dbf';
8> set newname for datafile 6 to '/u01/app/oracle/oradata/csdb/undotbs2.dbf';
9> set newname for datafile 7 to '/u01/app/oracle/oradata/csdb/testtbs01.dbf';
10> restore database;
11> switch datafile all;
12> release channel c1;
13> } -
recover datafilebase
尝试历程
备份恢复的时候尽量选择相同的数据库版本,即使相同大版本不同小版本(补丁)也有可能出现各种奇怪的错误无法恢复数据。
- 12c 失败
- 10.2.0.1 catalog start with 失败
- 10.2.0.3 restore database 失败
- 10.2.0.5 restore database 成功,recover database 失败
跨平台问题
The Cross-platform redo application is only supported for physical standby but not for general media recovery which is involved while duplicating database using RMAN.
Windows 与 Linux 平台的 Oracle 日志系统不同,无法平台进行恢复,因此想要跨平台恢复存在如下方式:
-
冷备份:即停机备份
-
热备份时备份
standby controlfile
(参考)Let’s modify the RMAN script in windows to backup standby controlfile instead of a normal controlfile.
1
2
3
4
5
6
7
8
9run
{
allocate channel c1 device type disk;
allocate channel c2 device type disk;
backup as compressed backupset database filesperset 1 format 'c:\backup\datafile_%U';
sql "alter database create standby controlfile as ''c:\backup\control_stby.bak''";
release channel c1;
release channel c2;
}Make a few switch log in windows.
1
2
3SQL> alter system switch logfile;
System altered. -
修改配置跳过一致性检查(本次恢复最终使用此方法)
1
alter system set “_allow_resetlogs_corruption”=true scope=spfile;
-
BBED 可以直接修改文件头,骗过一致性检查 (参考)
SQL
in vs exists
in 和 exists 主要是造成了驱动顺序的改变(这是性能变化的关键),如果是 exists,那么以外层表为驱动表,先被访问,如果是 IN,那么先执行子查询,所以我们会以驱动表的快速返回为目标。因此 in 适用于内表小,exists 适用于外表小。
not in vs not exists
对于 not in
如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录。
联表更新
写法 1
1 | update test1 t1 |
写法 2 (inline view 更新法)
1 | update (select t1.object_name, t2.object_name new_object_name |
写法 3 (merge 法)
1 | merge into test1 t1 |
这种写法不能对on
条件中用到的字段进行更新。
写法 2-3 更优,方法 1 会进行两次子查询
写法 4 (快速游标法): 没用过
方案 | 建议 |
---|---|
标准 update 语法 | 单表更新或较简单的语句采用使用此方案更优。 |
inline view 更新法 | 两表关联且被更新表通过关联表主键关联的,采用此方案更优。 |
merge 更新法 | 两表关联且被更新表不是通过关联表主键关联的,采用此方案更优。 |
快速游标更新法 | 多表关联且逻辑复杂的,采用此方案更优。 |
联表删除
创建表 A
key | value |
---|---|
a | 1 |
b | 1 |
创建表 B
key | value |
---|---|
a | 1 |
c | 1 |
从表 A 中删除,与 B 中 key,value 都相等的数据。
方法 1: 无条件
1 | delete from A |
方法 2:要求其中一个表要有主键
1 | delete from |
经测试,删除结果为:当两个表都有主键时删除位置靠前的表的数据,当其中一个表没有主键时删除没有主键的表的数据。
优化
Oracle 数据库性能优化与运维最佳实践
指导思想
自上而下优化以下内容:
- 优化应用程序代码之前先优化设计
- 优化实例之前先优化代码
对可以带来最大潜在好处的方面进行优化并确定: - 最长的等待
- 最慢的 SQL
达到目标时停止优化
优化过程
- 定义问题并陈述目标
- 收集当前性能统计信息
- 考虑一些常见的性能错误
- 制定适用解决方案
- 事实并度量更改
- 是否达到目标?完成:转到步骤 3
实践方法
- 监视和诊断——使用 AWR 报告分析 Oracle 数据库性能
- SQL 优化
- 实例优化
Select Tuning
事实上简单查询时 CBO (Cost-Based Optimization) 已经能够完成优化工作,但在写复杂的嵌套查询是仍需要注意。
from 查询顺序 小表在后
ORACLE 的解析器按照从右到左的顺序处理 FROM 子句中的表名,因此 FROM 子句中写在最后的表(驱动表 driving table) 将被最先处理。在 FROM 子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。当 ORACLE 处理多个表时,会运用排序及合并的方式连接它们。首先,扫描第一个表 (FROM 子句中最后的那个表)并对记录进行排序,然后扫描第二个表 (FROM 子句中最后第二个表), 最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。
如:TAB1 有两千万条,TAB2 中有 1 条
1 | select count(*) from TAB1, TAB2; --20s |
如果有 3 个以上的表连接查询,那就需要选择交叉表 (intersection table) 作为基础表,交叉表是指那个被其他表所引用的表。
例如:EMP 表描述了 LOCATION 表和 CATEGORY 表的交集。
1 | SELECT * |
where 查询顺序
ORACLE 采用自下而上的顺序解析 WHERE 子句,根据这个原理,
- 表之间的连接必须写在其他 WHERE 条件之前,
- 可以过滤掉最大数量记录的条件必须写在 WHERE 子句的末尾。
1 | -- (低效,执行时间 156.3 秒) |
避免使用 *
ORACLE 在解析的过程中,会将’*’ 依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
在 OLAP 场景下作用不大 ,语句运行的时间远大于解析时间 。
减少访问次数
未实践
当执行每条 SQL 语句时,ORACLE 在内部执行了许多工作:解析 SQL 语句,估算索引的利用率,绑定变量 , 读数据块等等。由此可见,减少访问数据库的次数 , 就能实际上减少 ORACLE 的工作量。
在 SQL*Plus , SQL*Forms 和 Pro*C 中重新设置 ARRAYSIZE 参数,可以增加每次数据库访问的检索数据量 , 建议值为 200.
使用 DECODE 函数来减少处理时间
使用 DECODE 函数可以避免重复扫描相同记录或重复连接相同的表。
Insert Tuning
- 停用/删除索引和约束,载入数据后重建
a - Disable/drop indexes and constraints - It’s far faster to rebuild indexes after the data load, all at-once. Also indexes will rebuild cleaner, and with less I/O if they reside in a tablespace with a large block size.
- 管理并行插入时的段头争用
b - Manage segment header contention for parallel inserts - Make sure to define multiple freelist (or freelist groups) to remove contention for the table header. Multiple freelists add additional segment header blocks, removing the bottleneck. You can also use Automatic Segment Space Management (bitmap freelists) to support parallel DML, but ASSM has some limitations.
- 使用 hint 并行加载
c - Parallelize the load - You can invoke parallel DML (i.e. using the PARALLEL and APPEND hint) to have multiple inserts into the same table. For this INSERT optimization, make sure to define multiple freelists and use the SQL “APPEND” option. Mark Bobak notes that if you submit parallel jobs to insert against the table at the same time, using the APPEND hint may cause serialization, removing the benefit of parallel jobstreams.
- 使用
append
使表顺序写入硬盘
d - APPEND into tables - By using the APPEND hint, you ensure that Oracle always grabs “fresh” data blocks by raising the high-water-mark for the table. If you are doing parallel insert DML, the Append mode is the default and you don’t need to specify an APPEND hint. Mark Bobak notes “Also, if you’re going w/ APPEND, consider putting the table into NOLOGGING mode, which will allow Oracle to avoid almost all redo logging.”
insert /*+ append */ into customer values ('hello',';there');
- 使用更大的 blocksize
e - Use a large blocksize - By defining large (i.e. 32k) blocksizes for the target table, you reduce I/O because more rows fit onto a block before a “block full” condition (as set by PCTFREE) unlinks the block from the freelist.
See benchmark test of blocksize and inserts here
See general benefits of multiple blocksizes here
- 使用 NOLOGGING 参数
f - Use NOLOGGING
- 使用告诉的硬盘
g- RAM disk - You can use high-speed solid-state disk (RAM-SAN) to make Oracle inserts run up to 300x faster than platter disk.
- Mark indexes unuasble
- Disable primary key
- Alter table nologging
- Do an insert /*+ append */ into table (select …)
- Enable primary key
- Rebuild indexes nologging
Update Tuning
子查询 update 过慢问题
一下两条语句功能完全相同,但在执行效率有巨大差异
1 | UPDATE scott.mytables a |
适用 autotrace 跟踪实际运行 cost(用 10g 貌似跑不了,执行计划的估计有误),结果如下
使用子查询时以外表为条件,循环对内表进行查询(Nested Loop Join),join 的 cost 很高
使用 merg 语句时,使用了 hash join 进行了优化,join 几乎没有什么成本(这一点 oracle 优化得不错,mysql 都还没实现 hash join)
Nested Loop Join/Merge Join/Hash Join
数据库参数调优
Use Subqueries to Count Distinct 50X Faster
https://www.periscopedata.com/blog/use-subqueries-to-count-distinct-50x-faster
IO 优化
通过将数据分布存储至多个硬盘,可以通过并行 IO 提升效率。在建立 datafile 时就要合理分配
Turning the Tables on Disk I/O
spread a table into multiple disk
错误处理
ORA-00257
ORA-00257: archiver error. Connect internal only, until freed.
ORACLE 默认的日志归档路径为闪回恢复区($ORACLE_BASE/fast_recovery_area)。对于这个路径,Oracle 有一个限制,就是默认只有 4G 的空间,而且不只是归档日志的默认路径,也是备份文件和闪回日志的默认地址,这样的话归档日志锁使用的空间就达不到 4G,在没有设置好这个路径大小的情况下,很多系统都遇到过归档日志满而无法归档导致数据库夯住的问题。
分析
- 查看归档日志路径
1 | SQL> archive log list; |
- 查看 DB_RECOVERY_FILE_DEST 路径
1 | SQL> show parameter db_recovery; |
- 查看闪回恢复区的使用情况
1 | SQL> select * from v$flash_recovery_area_usage; |
确定了确实是因为闪回恢复区的空间不足造成的问题
解决方法
- 手动扩容
1 | alter system set db_recovery_file_dest_size=10G; |
- 修改日志路径
1 | --修改 log_archive_dest_1 值来重新制定路径 |
重启
ORA-22858: 数据类型的变更无效
原因:Oracle 不允许将字段类型修改为:object、REF、nested table、varchar、clob、blob
解决方法:1. 修改该字段的名称。2. 新建一个正确的字段。3. 将数据同步的到新字段。4. 删除错误字段。