《收获,不止Oracle》--梁敬彬【读书笔记】


前言

这本书这里是第二遍开始读了,第一遍只看了三分之一,感觉写的挺不错的,对于我这种半路出家的人来说内容还是比较友好的通俗易懂。这次重读一是经常出差在外晚上有时候没事做,二是第一遍都是手机上看,看过就忘了,这次就想把一些比较重要的知识点记录一下,以后也方便查阅。


`提示:以下记录内容一级标题按章节分类,二级标题按知识点分类,看一点记录一点,更新时间都会记录在文章中,看看能不能看完吧,加油!

开始时间:2022年9月13日22点08分

二、Oracle的体系结构


Oracle由实例和数据库组成。
实例是由SGA和一系列后台进程组成的。
SGA被划分为共享池(share pool)、数据缓冲区(db cache)、日志缓冲区(log buffer)。
后台进程包括:PMON、SMON、LCKn、RECO、CKPT、DBWR、LGWR、ARCH等系列进程。
数据库是由:数据文件、参数文件、日志文件、控制文件、归档日志文件等组成

还有一块单独开辟的内存区域PGA(program global area),PGA不是共享内存,用户对数据库发起的无论查询还是更新的任何操作都是在PGA中预处理,然后在进入实例区域。
PGA的作用:
1.保存用户的连接信息,如会话属性、绑定变量等。
2.保存用户权限信息,当用户与数据库建立连接时,系统会将这个用户相关的权限查询出来,保存在PGA中。
3.如果这个连接的操作需要进行排序,也可在pga中进行排序,如果pga中空间不足以完成排序则超出部分就在临时表空间中完成。

上图中将Oracle划分成3块区域分别为1区:PGA、2区:实例instance、3区:数据库
那么我们的用户请求从发起到完成,在Oracle3个区域中的经过路径是怎样的呢?
第一种情况:1区>2区>3区>1区
第二种情况:1区>2区>1区(这种情况说明在内存数据缓冲区中有用户想要得到的数据所以就不用去数据文件中查询)

现在是2022年9月14日19点53分

刚刚想到,反正要写点什么,就顺便记录一下每天都干了什么吧。今天上午去一个数据中心升级项目上看了一下机房环境,这个机房虽小但是五脏俱全,还是个双活机房,两个机房各放置了一台rac节点和一台双活存储节点。下午就是远程处理一些小问题,都是一些琐事。中午还被领导怼了一顿,感觉挺不值的。算了算了,加油加油。开始吧。

一个查询的执行时长的变化

idle>conn test/test
已连接。
test@ORCL>drop table t;

表已删除。

test@ORCL>create table t as select * from all_objects;

表已创建。

test@ORCL>create index idx_object_id on t(object_id);

索引已创建。

test@ORCL>set autotrace on
test@ORCL>set linesize 1000
test@ORCL>set timing on
test@ORCL>select object_name from t where object_id=29;  **--第一次执行**

OBJECT_NAME
------------------------------
C_COBJ#

**已用时间:  00: 00: 00.08**

执行计划
----------------------------------------------------------
Plan hash value: 2041828949

---------------------------------------------------------------------------------------------
| Id  | Operation		    | Name	    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	    |		    |	  1 |	 30 |	  2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T 	    |	  1 |	 30 |	  2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN	    | IDX_OBJECT_ID |	  1 |	    |	  1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=29)

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
	 11  recursive calls
	  0  db block gets
	 81  consistent gets
	340  physical reads
	  0  redo size
	535  bytes sent via SQL*Net to client
	519  bytes received via SQL*Net from client
	  2  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
	  1  rows processed

test@ORCL>


test@ORCL>select object_name from t where object_id=29;  **--第二次执行**

OBJECT_NAME
------------------------------
C_COBJ#

**已用时间:  00: 00: 00.01**

执行计划
----------------------------------------------------------
Plan hash value: 2041828949

---------------------------------------------------------------------------------------------
| Id  | Operation		    | Name	    | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT	    |		    |	  1 |	 30 |	  2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T 	    |	  1 |	 30 |	  2   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN	    | IDX_OBJECT_ID |	  1 |	    |	  1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJECT_ID"=29)

Note
-----
   - dynamic sampling used for this statement (level=2)


统计信息
----------------------------------------------------------
	  0  recursive calls  **--递归调用**
	  0  db block gets   **--db块取:表示当前读。在发生INSERT、DELETE、UPDATE和SELECT FOR UPDATE的时候,数据库缓冲区中的数据库块的个数。在SELECT语句中一般为0**
	  4  consistent gets   **--一致性读(逻辑读的一种)**
	  0  physical reads   **--物理读**
	  0  redo size
	535  bytes sent via SQL*Net to client
	519  bytes received via SQL*Net from client
	  2  SQL*Net roundtrips to/from client
	  0  sorts (memory)
	  0  sorts (disk)
	  1  rows processed

可以看到上面两次的执行时长从第一次的0.08s缩短到了第二次的0.01s
为什么两次执行时间会有差距呢:
原因:(1)第一次执行时,需要从磁盘中获取用户的连接信息和相关权限信息,而这些信息在第一次执行完会保存在pga中,第二次执行的时候就不需要再次从磁盘中获取了,减少了物理读。
(2)第一次执行完这条sql后,sga的共享池中已经保存了这条sql的hash值,避免了再次去重新进行语法语义等相关检查和硬解析该sql可以直接软解析。
(3)第一次执行时,所需数据一般不会存放在sga的数据缓存区中(除非被执行别的sql时读取),只能从磁盘中获得,但是在第二次执行时,因为第一次的执行将数据存放在了sga的数据缓存中,所以可以在数据缓存中直接读取,完全避免了从磁盘中取数据的物理读。

逻辑读、当前读、一致性读和物理读
Logical read:所谓逻辑读,就是从内存中读取数据块,包含current read 与 consistent read
current read:属于Oracle版本读取方式的一种机制,就是说当进行DML操作时,我们需要获取数据块最新的状态,只对最新状态进行操作,操作期间锁定数据行。
consistent read:当进行select查询时,我们需要获取查询那一刻数据块状态,不管查询了多长时间,我们只要查询那一瞬间的结果,如果查询期间数据块被修改,那么我们就去undo segment读取旧映像来呈现(如果旧映像被覆盖产生ora-01555错误)。
公式:logical read=db block gets(current read) + consistent gets(consistent read)
Physical read:所谓物理读,就是从磁盘中读取数据块

详细说一下各种读的意思
什么是逻辑读? cpu在内存中读这些block的过程就叫做逻辑读(consistent get),在读的过程中产生的IO就是逻辑IO.逻辑读的过程中,是非sql语句执行时,产生一致性读。

什么是逻辑读? cpu在内存中读这些block的过程就叫做逻辑读(consistent get),在读的过程中产生的IO就是逻辑IO.逻辑读的过程中,是非常消耗cpu资源的.因此,执行sql的逻辑读越少越好.sql调优必须调整buffer get很大的sql语句

logical reads= consistent gets + db block gets,逻辑读其实是DB BLOCK GETS 和 consistents Gets 之和

DB block gets—当前请求的块数目

Consistent gets—数据请求总数在回滚段buffer中的数据一致性读所需要的数据块

Physical Reads—从磁盘读取数据块的数量(主要原因:buffer中不存在这些块;全表扫描;磁盘排序)

Oracle读取数据的方式有2种

1.物理读即是把数据从磁盘读入到buffer cache的过程。

通常情况下是,如果需要数据的时候发现不存在于buffer cache当中,即oracle就会执行物理读。

2.逻辑读取

逻辑读指的就是从(或者视图从)Buffer Cache中读取数据块。按照访问数据块的模式不同,可以分为即时读(Current Read)和一致性读(Consistent Read)。

(1)即时读 db block gets或者叫current读

即时读即读取数据块当前的最新数据。任何时候在Buffer Cache中都只有一份当前数据块。即时读通常发生在对数据进行修改、删除操作时。这时,进程会给数据加上行级锁,并且标识数据为“脏”数据。

(2)一致性读 consistent get

Oracle是一个多用户系统。当一个会话开始读取数据还未结束读取之前,可能会有其他会话修改它将要读取的数据。如果会话读取到修改后的数据,就会造成数据的不一致。

一致性读就是为了保证数据的一致性。在Buffer Cache中的数据块上都会有最后一次修改数据块时的SCN。

2.为什么要一致性读,为了保持数据的一致性:下面这段话好好理解一下

如果一个事务需要修改数据块中数据,会先在回滚段中保存一份修改前数据和SCN的数据块,然后再更新Buffer Cache中的数据块的数据及其SCN,并标识其为“脏”数据。
当其他进程读取数据块时,会先比较数据块上的SCN和进程自己的SCN。如果数据块上的SCN小于等于进程本身的SCN,则直接读取数据块上的数据;
如果数据块上的SCN大于进程本身的SCN,则会从回滚段中找出修改前的数据块读取数据。通常,普通查询都是一致性读。

**用白话文解释一下:**我现在想要修改一条数据,我会在没修改前先将这条数据保存一份在回滚段undo中并标记好没修改时状态的scn号。然后再到buffer cache中去更新这条数据,更新完成后给这条数据打上scn的标签。其实scn就是个时间标志。如果另外一个人过来查这条数据,不确定他这个查询进程是何时发起的,如果是在我修改这条数据之前他就发起了查询,但是因为这条数据所在的表非常大又没有索引或者其他原因导致,在我修改完后他还没查出结果,这是oracle为了保证数据的一致性,就要对比他这个查询进程的scn和buffer cache中这条数据的scn的大小,如果进程的scn比buffer cache中数据块的scn小则表明,在我修改完这条数据之前他就发起了查询,所以oracle会将回滚段中的快照数据给他。如果发现进程的scn比buffer cache中数据块的scn大,则表明他在我修改完数据后才发起查询,这样oracle就把buffer cache中修改过的数据给他。

简单列举一下执行计划中的名词解释,有些我也不是太懂,后面讲到执行计划的时候再看
1.recursive calls
递归调用,表示执行SQL的时候的产生的递归调用的次数。Oracle在执行SQL的时候,有时候会生成很多额外的SQL语句,这个就称为递归调用。这个参数和访问数据字典的次数有很大的关系,一般来说,这个参数值不会很大。
2.db block gets
DB块取,表示当前读。在发生INSERT、DELETE、UPDATE和SELECT FOR UPDATE的时候,数据库缓冲区中的数据库块的个数。在SELECT语句中一般为0。
3.consistent gets
一致性读,表示除了SELECT FOR UPDATE的时候,从数据库缓冲区中读取的数据块的个数(注意,实际上并不是块的个数),可能会读取回滚段的信息,一般来说,逻辑读(Logical Reads) = 当前读(db block gets) + 一致性读(consistent gets)。
4.physical reads
物理读,在执行SQL的过程中,从硬盘上读取的数据块个数。
5.redo size
SQL语句在执行过程中产生的Redo的字节数。
6.bytes sent via SQLNet to client
服务器利用SQL
Net发送到客户端的字节数。
7.bytes received via SQLNet from client
服务器利用SQL
Net从客户端接收的字节数。
8.SQLNet roundtrips to/from client
从客户端发送和接收的SQL
Net消息的总数,包括从多行的结果集中提取的往返消息。
9.sorts (memory)
在内存执行的排序次数。
10.sorts (disk)
在磁盘上执行的排序次数,如果内存空间不足,那么会使用磁盘空间。
11.rows processed
更改或选择返回的行数。

了解Oracle数据缓存区的刷盘机制–断电了数据缓存区的脏数据会怎么样

存放在数据缓存区的脏数据是不会实施刷入磁盘的,如果实时刷入磁盘会大大降低数据的性能,那么一点数据库服务器遭遇断电,数据缓存区必定会存放一部分脏数据,那这部分脏数据怎么办呢?我们用shutdown abort模拟一下数据库断电,断电后再startup开机看一下数据库在此期间都进行了什么操作来保证数据的一致性和完整性。

SQL> shutdown abort
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
Total System Global Area 167772160 bytes
Fixed Size 1218316 bytes
Variable Size 71305460 bytes
Database Buffers 92274688 bytes
Redo Buffers 2973696 bytes
Database mounted.
Database opened.
[root@RedHat ~]# tail-f /u01/app/oracle/admin/orcl/bdump/alert_orcl.log
starting up 1 dispatcher(s) for network address '(ADDRESS=(PARTIAL=YES)(PROTOCOL=TCP))'...
starting up 1 shared server(s) ...
Tue Sep 3 17:55:33 2013
ALTER DATABASE MOUNT
Tue Sep 3 17:55:37 2013
Setting recovery target incarnation to 2
Tue Sep 3 17:55:37 2013
Successful mount of redo thread 1, with mount id 1352977301
Tue Sep 3 17:55:37 2013
Database mounted in Exclusive Mode
Completed: ALTER DATABASE MOUNT
Tue Sep 3 17:55:37 2013
ALTER DATABASE OPEN
Tue Sep 3 17:55:37 2013
Beginning crash recovery of 1 threads
Tue Sep 3 17:55:37 2013
Started redo scan
Tue Sep 3 17:55:37 2013
Completed redo scan
91 redo blocks read, 41 data blocks need recovery     **检查REDO日志,发现有41个数据块需要恢复**
Tue Sep 3 17:55:38 2013
Started redo application at
Thread 1: logseq 124, block 1372   **然后开始恢复第3号日志组中124号日志的第1372块**
Tue Sep 3 17:55:38 2013
Recovery of Online Redo Log: Thread 1 Group 3 Seq 124 Reading mem 0
Mem# 0 errs 0: /u01/app/oracle/oradata/orcl/redo03.log
Tue Sep 3 17:55:38 2013
Completed redo application
Tue Sep 3 17:55:38 2013
Completed crash recovery at
Thread 1: logseq 124, block 1463, scn 1714580
41 data blocks read, 41 data blocks written, 91 redo blocks read
Tue Sep 3 17:55:38 2013
Beginning crash recovery of 1 threads
Completed crash recovery at
Thread 1: logseq 124, block 1463, scn 1714580
41 data blocks read, 41 data blocks written, 91 redo blocks read    **共恢复了41个数据块,读了91个重做日志块。**

从这个警告日志中可以看出:
当断电后启动数据库的时候,为了是数据库能够打开,ORACLE自动的进行实例操作同步数据库文件。
实例恢复(INSTANCE RECOVERY):在启动数据库的时候发现文件不同步后,自动利用在线日志中的重作记录自动对陈旧的数据文件进行恢复的过程。
总结:在数据库强制关闭之后再开启,会做一下步骤
1.管理员发出startup命令
2.打开参数文件,启动实例
3.打开控制文件
4.检查在线重做日志和数据文件是否同步,结果为不同步
5.对已经提交事务实施前滚,对没有写如数据文件的脏块写进数据文件。
6.打开数据库,可以接受客户请求
7.对没有提交的事务实施回滚,将相当于sql命令中的rollback,将相对应的已经写入数据文件的块给修改掉,完成之后数据文件中不会存在由于上一次强制关闭而留下的,未提交的脏数据块。

上面内容是复制别人的,下面是自己的理解
断电时候的几种情况:
1.我修改的数据已经commit了,突然断电,但是数据还在数据缓存区,没有被刷入磁盘。
脏数据:还存放在数据缓存区db cache中,断电后这部分脏数据全部丢失。
logbuffer中的数据操作记录:因为我已经commit提交了,log buffer中的数据已经被刷入redolog
恢复:数据库重新启动后,会根据redolog中的操作记录将修改数据过程重新执行一遍,这时断电前的脏数据会回到dbcache中等待刷盘。

2.我修改的数据没有commit,突然断电,但是数据还在数据缓存区,没有被刷入磁盘。
恢复:没有commit也就没有提交成功,有可能这部分操作记录还没被写到redolog中,也有可能写到了redolog中,无论如何这部分数据都会被回滚掉。

3.会不会出现我修改了数据,提交了,但是log buffer中的操作记录没来得及写入redolog的情况
恢复:不会出现这种情况,提交commit完成,代表与此事务相关的操作记录已经被写入到了redolog中。InnoDB存储引擎的事务采用了WAL技术(Write-Ahead Logging),这种技术就是先写日志,再写磁盘,只有日志写入成功,才算事务提交成功,这里的日志就是redo log。当发生宕机且数据未刷新到磁盘的时候,可以通过redo log恢复过来,保证事务的持久性。

4.会不会出现,我修改的这条脏数据,已经被写入了磁盘,操作记录也被刷入了redo,此时断电重启后,又重新修改一遍数据。
恢复:不会,数据库重启后会对比redo中和数据文件中的数据记录,如果一致就不做修改。

logbuffer的刷盘机制
1、前台进程触发,包括两种情况。最显而易见的一种情况就是用户发出commit或rollback语句进行提交时,需要触发LGWR将内存里的日志信息写入联机日志文件,因为提交的数据必须被保护而不被丢失;另外一种情况就是在日志缓冲区中找不到足够的内存来放日志信息时,也会触发LGWR进程将一些日志信息写入联机日志文件以后,从而释放一些空间。
2、 每隔三秒钟,LGWR启动一次。
3、在DBWn启动时,如果发现脏数据块所对应的重做条目还没有写入联机日志文件,则DBWn触发LGWR进程并等待LRWR写完以后才会继续。
4、日志信息的数量达到整个日志缓冲区的1/3时,触发LGWR。
5、 日志信息的数量达到1MB时,触发LGWR。

现在是2022年9月14日23:30 今天先到这,关于db cache和log buffer越看内容越多,看不完了,睡觉!


新的一天开始了,现在是2022年9月15日8:00上午不用去客户那,可以在宾馆休息,那就看一会书吧。

简单描述一下Oracle主要进程的作用:
PMON:processes monitor,进程监视器。如果你在执行某些更新语句,未提交时进程崩溃了,这时候PMON会执行回滚操作,无需你手工rollback。除此之外PMON还可干预后台进程,比如RECO进程异常,PMON会重启RECO进程,如果遇到LGWR进程失败这种严重故障,PMON则会直接终止实例。
SMON:system monitor,系统监视器,SMON监视的是系统操作,主要工作是instance recovery实例恢复,比如数据库异常掉电重启后就需要SMON来进行实例恢复,除此之外还有清理临时表空间、回滚段的功能。
CKPT:该进程由FAST_START_MTTR_TARGET参数控制,用于触发DBWR从数据缓冲区写出数据到磁盘。CKPT执行越频繁,DBWR写出也就越频繁,DBWR写出越频繁,数据区性能就越低,但是这样数据库异常恢复就越快,所以要合理设置CKPT触发频率。
DBWR:负责将数据缓存区的脏数据写到磁盘中,它和CKPT相辅相成,因为CKPT促成DBWR去写数据。DBWR也和LGWR密切相关,因为write-ahead-log机制迫使,在DBWR将脏数据写入到磁盘前,必须将和该脏数据相关的所有操作日志通过LGWR写入到redolog中,只有日志写完才可以将脏数据写入磁盘。
LGWR:将操作记录顺序由log buffer输入redolog。
ARCH:将redolog写入归档日志。

现在是2022年9月15日9:00 休息一会,吃个饭,下午干活。

现在是2022年9月17日9:24 周六在家早上忙一会,偷闲看一会。后面记录的内容会偏向与需要理解的东西,和重要的知识点。

提升速度的集中方法
先创建,一个过程用来生成一个有数据的表:

创建一个过程,用来往t表插入数据
create or replace procedure proc1
as 
begin
  for i in 1..100000
    loop
      execute immediate
      'insert into t values('||i||')';
      commit;
      end loop;
 end;

创建t表
 drop table t purge;
 create table t(x number(9));
清空共享池,避免之前已经解析过,排除其他因素的干扰
test@ORCL>alter system flush shared_pool;
打开时间显示
test@ORCL>set timing on;
执行过程,开始往表里插入数据
test@ORCL>exec proc1;
PL/SQL 过程已成功完成。
已用时间:  00: 00: 11.74
test@ORCL>select count(*) from t;
  COUNT(*)
----------
    100000

上面的过程中我们看到,往t表插入100000条数据,所需的时间是00: 00: 11.74

下面介绍集中提升插入速度的方法:

 select t.sql_text,t.sql_id,t.PARSE_CALLS,t.EXECUTIONS
  from v$sql t
 where sql_text like'%insert into t values%';

在这里插入图片描述
可以看到,我们执行插入的过程后,插入语句在共享池中解析了10万次,极大地浪费了时间。

方法一:绑定变量,将10万次硬解析,变成一次
绑定变量的方法可以将10万条insert写成一条:insert into t values(:X)

改进后的插入过程如下
create or replace procedure proc2
as 
begin
  for i in 1..100000
    loop
      execute immediate
      'insert into t values(:x)' using i;
      commit;
      end loop;
 end;
 下面一样的流程
 drop table t purge;
 create table t(x number(9));
test@ORCL>alter system flush shared_pool;
test@ORCL>set timing on;
执行过程,开始往表里插入数据
test@ORCL>exec proc2;
PL/SQL 过程已成功完成。
已用时间:  00: 00: 04.25

从上述过程可以看到,执行时间大幅减少,主要节省的时间就是insert语句硬解析的时间
解析次数,也变成了一次
在这里插入图片描述
方法二:将动态sql,改写成静态sql
这个过程中,静态和动态的区别就在于 execute immediate, execute immediate是一种动态sql的写法,但是过程中’insert into t values(‘||i||’)'是包含表明的,直接可以用静态的写法,可以将 execute immediate去掉。

create or replace procedure proc3
as 
begin
  for i in 1..100000
    loop
      insert into t values(i);
      commit;
      end loop;
 end;
  下面一样的流程
 drop table t purge;
 create table t(x number(9));
test@ORCL>alter system flush shared_pool;
test@ORCL>set timing on;
执行过程,开始往表里插入数据
test@ORCL>exec proc2;
PL/SQL 过程已成功完成。
已用时间:  00: 00: 03.89

这边我们没有使用绑定变量,为什么速度还这么快呢?因为静态sql是默认使用绑定变量的,下面的查询可以看出,插入语句只解析了一次.
动态sql的特点是在执行过程中解析,而静态sql是在编译过程中就已经解析好了.

方法三:批量提交

只要将commit一道loop外面来,之前来loop里面是插一条数据提交一次,现在移到外面来之后就可以在所有数据都插完后批量提交
create or replace procedure proc4
as 
begin
  for i in 1..100000
    loop
      insert into t values(i);
      end loop;
       commit;
 end;

方法四:集合写法

 insert into t select rownum from dual connect by level<=100000;

这种方法自行了解,大概意思就是,之前10万块砖头是一块一块搬得,现在10万块砖头,来了一辆大卡车一下搬走了

方法五:直接路径写法

create table t as select rownum from dual connect by level<=100000;

这种写法为什么快呢,是因为,insert into t select这种写法是先把数据写到data buffer缓存中,然后再刷到磁盘中去。而create table t的方式是直接将数据写到磁盘中去了,这种方法又称为直接路径读写方式。

方法六:并行设置

create table t nologging parallel 64
 as select rownum x from dual connect by level<=100000;

这次改进的主要地方就是创建表的时候,我们给表设置了并行,这种改进方式有利有弊,这种方式会大量消耗cpu资源,如果是一个并发环境,有很多应用在运行,就会出现资源不足的问题。

现在是2022年9月16日10:53 看一会,下午回家,

三、Oracle的逻辑体系

在这里插入图片描述

数据文件的逻辑结构:表空间(tablespace)、段(segment)、区(extent)、块(block)。
这些元素的组成关系:一系列block组成extent,一个或多个extent组成了segment,一个或多个segment组成了tablespcae。

表空间又包含:系统表空间、回滚段undo表空间、临时temp表空间、用户表空间。

我们平时在数据库中建的表就是和段(segment)直接对应的,T表对应T段,T2表对应T2段、T3表对应T3段(一个表可以对应一个段或者多个段,比如表中如果包含lob类型列,则lob至少会有两个段,数据段和索引段,再者如果是分区表,则每个分区又都独立成段)。

create table t(id int) tablespace tbs_test; 
create table t2(id int) tablespace tbs_test;
create table t3(id int) tablespace tbs_test;

我们在创建表的时候可以选择将表创建在指定表空间,这里可以直观的看出表空间是由段组成的。
段则是由区extent组成的,区则是又一些列块block组成的,每个区块的数量是固定的,块的的大小自数据库创建之日起也是固定的。

块block的大小选择,一般操作系统OS的块容量为512字节大小或者其整数备,而数据库块一般设置为8KB,也可以设置为2KB、4KB、16KB等,设置数据库块大小为操作系统块大小整数倍的目的是为了减少I/O操作,节省空间。

查看数据库块大小方法

方法1:查看系统参数
test@ORCL>show parameter db_block_size

NAME				     TYPE	 VALUE
------------------------------------ ----------- ------------------------------
db_block_size	
方法2:查看系统表空间视图
test@ORCL>select block_size from dba_tablespaces where tablespace_name='SYSTEM';
BLOCK_SIZE
----------
      8192

现在是2022年9月119日22:03 看一会
数据库的块大小是我们在安装数据库的时候设定的,大小默认为8KB
那么块大小是不是就不能变了呢?。。。是可以变得
我们可以通过下面的参数,生成制定大小的块大小,2KB/4KB/16KB都是可以的

test@ORCL>show parameter cache_size

NAME				     TYPE	 VALUE
------------------------------------ ----------- ------------------------------
db_16k_cache_size		     big integer 0
db_2k_cache_size		     big integer 0
db_32k_cache_size		     big integer 0
db_4k_cache_size		     big integer 0
db_8k_cache_size		     big integer 0
db_cache_size			     big integer 0

可以测试一下

test@ORCL>alter system set db_16k_cache_size=20m;
系统已更改。
test@ORCL>show parameter 16k
NAME				     TYPE	 VALUE
------------------------------------ ----------- ------------------------------
db_16k_cache_size		     big integer 20M
这个参数设置为20M大小就意味着,在数据库的DATABUFFER数据缓存区中有20m的大小可以以16KB的大小进行访问。
测试一下
test@ORCL>create tablespace TBS_16K blocksize 16k datafile'/u01/app/oracle/orcl/16K.dbf' size 100m autoextend on;
表空间已创建。
test@ORCL>select tablespace_name,block_size from dba_tablespaces where tablespace_name in('TEST','TBS_16K');
TABLESPACE_NAME 	       BLOCK_SIZE
------------------------------ ----------
TBS_16K 			    16384
TEST				     8192
可以看到test表空间是我们之前用常规方法建的,TBS_16K是以16k大小的块建的

block块有个重要属性就是PCTFREE,PCTFREE的默认值是10,但是这个值也是可以改的,我们可以根据不同的SEGMENT设置不同的PCTFREE,可以用alter table T pctfree 20;。只读表的PCTFREE可以为0,因为它没有数据插入,不会产生行迁移。

9月25日 17点31

四、表的设计

全局临时表分为两种:1.基于事务的全局临时表。 2.基于会话的全局临时表。
基于事务的全局临时表commit或者session连接退出后,临时表记录自动删除;基于会话的全局临时表则是session连接退出后,临时表记录自动删除。

分区表:范围分区(partition by range)、列表分区(partition by list)、HASH分区(partition by hash)及组合分区四种。

下次看第4.3节 第189页

10月26日 21点55
一晃一个月没看了,家里发生了一些事,都过去了,重新开始吧。

五、索引

创建索引的时候,到底发生了什么?
现在有一个表test,我们要在表id列上建一个索引,create index idx_id on test(id);
1.首先,创建索引的话表中数据需要按照索引列先在内存中排序,每一个索引列在表中都有相对应的rowid(伪列),每一行的rowid值绝不会重复,可定位到行的记录在数据库中的位置。内存中索引列和相对应的rowid会被存放到索引块中,每个索引块的存储空间是有限的,一个索引块存满后,会存进另一个索引块中,两个索引块需要有上一级索引块平衡,就这样一级一级往上就形成了b-tree索引的树形结构。

下次看211页

时间:2022年111日晚八点
分区表索引:如果要想发挥分区索引的性能,在sql中必须使用分区字段条件,不然分区索引的性能还不如普通索引。

关于count()的优化
我们创建表t: create table t as select * from dba_objects;并且在object_id列上创建索引idx1_id
select count(
) from t;
count(*)

    55614

那么select count(*) from t;会不会走索引?
如果不在sql中声明索引列为非空,就不会走索引,加上非空条件或者直接将object_id设为非空,就可以走索引,如果object_id为主键也可以走索引。

MAX函数和MIN函数不加非空条件也会走索引,而且逻辑读数量等于索引数的高度。

索引回表优化
执行计划中出现 TABLE ACCESS BY INDEX ROWID就表示有索引回表读产生,索引回表指的是根据条件找到对应的索引块后根据这部分索引块的rowid定位到表所在的数据块,然后从数据块中获取到其他字段的记录。
索引回表势必会产生逻辑读,因为根据索引块的rowid访问表的其他数据需要访问更多的数据块。如果要避免索引回表,可以将非索引字段去掉不查询,或者可以使用组合索引(联合索引也不宜过多,联合索引的联合列太多,必然导致索引过大,索引块过多,在索引中的查询可能就要遍历更多的block,所以联合索引不宜包含过多的列,一般不宜超过3个字段)。

下次看245页

忙里偷闲 时间:2022年11月4日 下午3点
表列的聚合因子值越大,表列就越无序,和该列索引排序相差就越大,索引指向的数据块就越多,通过该列索引查询表数据产生的逻辑读就越大。

INDEX FAST FULL SCAN和INDEX FULL SCAN的区别INDEX FAST FULL SCAN一次可读取多个索引块但不一定保证有序,可减少逻辑读但不能减少排序,INDEX FULL SCAN一次只读取一个索引库但较易保证有序。不需要排序的查询一般都使用INDEX FAST FULL SCAN。

组合索引的两列,当一列是范围查询,一列是等值查询的情况下,创建索引的时候,等值查询列在前,范围查询列在后,这样的索引才最高效。

一个插入优化的小tips,如果表的数据量很大,表上有多个索引,插入数据量也比较大,插入数据时间很长,可以考虑先将表上的索引失效,等数据插入完成再将索引生效,这样就可以大大缩短数据插入时间。

位图索引的使用场景:1.位图索引列大量重复;2.该表极少更新。

外键一定要加索引
不加索引会引发下面的问题:
1.死锁以及并发问题。
对于父表的delete、update操作都会导致子表锁定(表级锁)。这指的是发起指令到指令结束这一段过程中,会锁表。这个全表锁可能会导致死锁,以及带来的子表并发性的问题。注意这里明显的hang,只会发生在特定场合。
这里强调一下,对于父表的delete、update操作,发起指令到指令结束这一段过程中,会锁表。也就是说若delete from 父表 where x = 1; 那么子表在会话显示出 xxx row deleted.结果之前是锁定的,显示出之后又是释放的。这个很关键,有助于理解一些例子。

2.删除变慢。
若索引是ON DELETE CASCADE,也就是级联外键。即使在不会导致1的情况,也会在删除过程中,每删除父表的一行就会对子表进行一次全表扫描,造成删除变慢。

3.查询变慢。
父表查询子表通过外键联合查询下,没有添加外键索引会导致查询变慢。

又一个月没看了,最近在搞opengauss的打卡,现在时间12.5下午3点32分

第六章讲的是表连接,不太看得懂,先略过,后面要用再看吧,本书的下半部分比较偏实战,接下来看第七章。

七、砍掉一些不必要的东西

调优也不都需要专注于sql语法,也需某些模块中的sql,甚至是整个模块都是不需要的,直接砍掉就行了。

一个数据库系统如果运行缓慢,经常提示临时表空间耗尽,则有可能是有大量sql一直在进行排序,耗尽了PGA中的空间,然后去临时表空间中排序(磁盘中),可以去掉不必要的排序。如果order by排序避免不了的话,可以把需要排序的表适当缩小,砍掉部分记录,变成小表。

后面几张主要介绍的是案例及处理问题方法,一带而过,基本算看完了吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值