day08_存储

像电信这种业务数据量都比较大,它是以省级为单位有数据。

电信这么大的业务,核算一下,在全国范围内核算一下,某款业务卖了多少钱,营利多少钱?







逻辑结构
tablespace表空间、segments段、extents区扩展、blocks块
【某居民小区、小区内一栋楼、楼内的某一单元、某单元内的某一室】

段名和表名一样

一个普通表占用一个段(分区表不是这样的),小表的段内区少,大表的段内区多

新建一个空表,占用64K(表内有1个段,1个段内有1个区,1个区内有8个块,每个块默认8K)

select * from user_segments where segment_name='空表名'

查看分区、块信息
select BYTES/1024/1024,BLOCKS,EXTENTS from user_segments where segment_name='空表名'



create table ttt as select * from dba_objects;

select bytes/1024/1024 from user_segments where segment_name='TTT';查看表有多大(M)




查看当前库中最大的表
select SEGMENT_NAME,BYTES/1024/1024,BLOCKS from user_segments where bytes =(select max(bytes)from user_segments);




查看分区、块分配具体信息
select * from user_extents where segment_name='TTT'


BYTES/1024/1024     BLOCKS    EXTENTS
--------------- ---------- ----------
              6        768         21




【区的智能化管理】
段中区越多,寻址不方便,存储的数据量达到一定程度,区中的块会扩大,减少区的数量,方便管理。


去超市买东西,超市的方便袋分为:大、中、小
你买2瓶水,买个小袋就可以了。
你买10瓶水和一些小吃,买个中袋就可以了。
你买了一些日常用品、蔬菜和水果、20瓶饮料,买个大袋就可以了。


delete from ttt;
查看分区、块分配具体信息
select * from user_extents where segment_name='TTT'; 【区没有减少】


insert into ttt select * from all_objects;
查看分区、块分配具体信息
select * from user_extents where segment_name='TTT'; 【区没有增加】



虽然数据没了,如果我把区收缩没了,我的删除效率会慢,我要再插入数据时,我再重新建立。
如果不回收,我删除快了,我写入快了。但是影响查询速度。





create table hehe as select * from all_objects;
delete from hehe;
commit;
insert into hehe  select * from all_objects;
commit;
delete from hehe;
commit;
insert into hehe  select * from all_objects;
commit;
delete from hehe;
commit;
insert into hehe  select * from all_objects where rownum<3;
commit;

set autotrace traceonly;
select * from hehe where object_id=20;【 747  consistent gets 逻辑读大些】



create table haha as select * from all_objects where 1=2;
insert into haha  select * from all_objects where rownum<3;
commit;
set autotrace traceonly;
select * from haha where object_id=20;【 8  consistent gets 逻辑读小些】


select * from user_extents where segment_name='HEHE';
select * from user_extents where segment_name='HAHA';

 
hehe表占21个区,haha表占1个区,是咱们生产中常见问题,表碎片。
一个表反复增删改,它就会产生碎片。
碎片就是我的数据不连续【有图】
碎片越多,剩余空间越多,浪费空间。对于查询来说,范围越大,查询越慢。 
 
hehe表和haha存储的数据一模一样的,但是查询成本不同。
hehe表是从21个区中找 object_id=20的记录,haha表是从1个区中找 object_id=20的记录,


alter table 表 move 【tablespace 表空间】--------表收缩(把表内容重新写一下,让表所在的空间减少,索引会失效了)




在生产环境中,怎么去判断?不用总去做,一个月或几个月做一次,一定要在业务不忙时做。
这就是工作经验了,你在公司维护数据库,经常访问的几个主要表,你一定知道。这几个表多做监控,比如说:

你判断2个值

1、表记录的行数   5W----->6M大小(假设正常情况) 
2、表对应的大小   10W---->30M大小(不成正比,肯定不正常了,这里面肯定有碎片了)


表有碎片,与pctfree、pctused值设置有关,后面会讲到。所以你经常做增删改操作,会产生大量碎片,有碎片就要移动,就是把里面的数据重新写一下。
表小移动一下,非常快。如果表中有几百W行,移动一下会非常慢,所以表的移动不能天天弄,可以按月来操作。



举例:
有些兄弟上班后,知道表移动可以加快查询速度。移动后就完蛋了,你的CPU压力变大了,你的I/O压力也变大了,这是为什么呢?
在公司维护一定要记录,移动表索引失效了。
索引的查询方式。索引和表有一个对应关系,通过rowid。


SQL> create table ff as select * from dba_objects;
SQL> set autotrace traceonly;
SQL> select * from ff where object_id=20;【全表扫描】
SQL> create index I_FF on ff (object_id);
SQL> select * from ff where object_id=20;【可以命中索引】
SQL> alter table ff move;
SQL> select * from ff where object_id=20;【全表扫描】
SQL> select status from user_indexes where index_name = 'I_FF';


UNUSABLE 失效
VALID    正常
N/A      分区索引

SQL> alter index I_FF rebuild;



你把表移动完了,表的碎片没了,你把索引也弄失效了,你的主要表查询全表扫描,什么样的服务器也抗不住啊。
move之前应把索引offline或off或删除 【move后rebuild】




【表碎片处理的4种方法】

1、数据导出导入
2、alter move【索引会失效,移动完成的瞬间会阻塞操作】
3、truncate
4、create table 新表 as select * from 旧表;【把旧表、新表的名称互换一下】


当前ttt有碎片,create table fff as select * from ttt;

rename  ttt to nimei;
rename fff to ttt;






create table test as select * from dba_objects;
delete from test;
select count(*) from test;
rollback;【从undo取回来】
select count(*) from test;【数据回来了,之前删除的数据被放在undo中了】






create table test2 as select * from dba_objects;
create table test3 as select * from test2;



set timing on

delete from test2;  【操作慢】
rollback; 
select count(*) from test2;【恢复了数据】


truncate table test3;【操作快】
rollback;
select count(*) from test3;【没有恢复数据】






总结:
delete
   逐行删除、删除慢、
   产生大量日志(块变化就会产生日志,delete会操作很多块)、
   产生大量的undo数据,可以回滚、删除后可以恢复
   太费I/O、
   
占2份I/O,你表删除操作的I/O、同时undo写的I/O
如果删除1000W记录,删除到500W行时,Undo没有空间了,要保持一致性,回滚到删除之前的状态,表还是1000W行,又费了一份I/O,往表中回写的I/O。
所以大表的删除,使用delete操作,容易把库干瘫痪了,太费I/O了。
   
   
   
   可以加where条件


truncate
   删除快、
   产生少量的日志(删除动作会产生日志,删除后会把区收缩了,产生数据很小),
   不产生undo数据、
   不可加where
   
   
   
create table test4 as select * from dba_objects;
create table test5 as select * from test4;
   select count(*) from user_extents where segment_name='TEST4';【test4有多少区?21个区】
   select count(*) from user_extents where segment_name='TEST5';【test5有多少区?21个区】



delete from test4;
commit;
select count(*) from test4;


truncate table test5;
select count(*) from test5;
   
   select count(*) from user_extents where segment_name='TEST4';【还是21个区】
   select count(*) from user_extents where segment_name='TEST5';【1个区,区被回收了】
   
   

生产中删除大表基本用的是truncate,操作起来比较快。但是无法恢复,具有危险性。

删除大表时,如果非要用delete,可以分批删除。
1000W的表,每10W删除一次,把大的动作变小。 

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

操作系统块 ext3 为4k
oracle 块为系统块的整数倍 默认8k

oracle中我们可以设置不同的大小  2,4,8,16,32(32位操作系统不支持32k的块)

mysql 默认块16k  db2 默认块4k

什么时候要改变块大小?对你的操作有什么影响?

对读写效率有影响。

最小块是8K,select * from table where id=1,这条记录可能占10个字节,但是你访问最小范围是1个块,8K可以看成最小存储单元。

你块越小,你寻址时,查询起来越费劲。

举例:
1000W记录的表,块默认是8K大小的,假如占10000个块,你要读1条记录,这条记录可能占10个字节,你访问的范围是1个块,从10000个块中找1个块。
1000W记录的表,块默认是16K大小的,假如占5000个块,你要读1条记录,这条记录可能占10个字节,你访问的范围是1个块,从5000个块中找1个块。

所以针对读动作来说,
1、寻址:块越大越好
2、扫描:块越小越好

OLTP业务:读写比较频繁,你寻址和扫描都多,块大小默认设置成8K就行。
OLAP分析业务:提取数据量大、没有OLTP操作频繁,设置成16K、32K,分析为主。

举例:
我分析一下,全年交易,可能扫10个表,每个表扫描几百W行,所以说OLAP业务做成大块好一些。



9i 开始 允许不同的表空使用 不同的块大小

SQL> create tablespace testnblock
 datafile '/oracle/app/oradata/ecom/testnblock.dbf' size 10M
 blocksize 16k;
create tablespace testnblock
*
ERROR at line 1:
ORA-29339: tablespace block size 16384 does not match configured block sizes


SQL> show parameter db_16k_cache_size;【sga中没有16k的存放空间,默认为0】

SQL> alter system set db_16k_cache_size = 10M;【sga为16K块分配10M空间】

System altered.

SQL> create tablespace testnblock
 datafile '/oracle/app/oradata/ecom/testnblock.dbf' size 10M
  blocksize 16k;


create table af tablespace testnblock as select * from dba_objects;
create table aff as select * from af;

select * from user_extents where segment_name='AF';【20个区】
select * from user_extents where segment_name='AFF';【21个区】

AF使用的表空间是16K的,AFF使用的表空间是默认8K的,可以看出AF区内的块数明显比AFF区内的块数少,所以寻址定位更快了。




公司让你设计库,有多少表,表之前的关联是什么样的?我在一个块内一般存放多少数据比较好一些?

比如:
8k=8*1024=8192字节

一个块8K,一个块中保留10条记录,一条记录最多800字节。我们觉得一行800字节挺少,在生产中正常。

SQL> create table ab as select * from dba_objects;

SQL> analyze table ab compute statistics;(分析表,分析结果存放在动态性能视图库中,我们可以去查询)

SQL> select avg_row_len from user_tables where table_name='AB';
(查看平均行长度,单位字节,一个块 至少能存储一行)

然后用块的默认大小/平均行长 = 一个块内存多少行

8000b/97=80行记录

通过上面实验,能推断一行占多少空间,理论上一个块能装80行。


------------------------------------------
第1块8K大小,使用率100%,有1条记录的姓名=zs,现在要做update操作,将姓名修改成zhangsan。原块使用率100%了,写不下了,oracle不会报错。
oracle继续往下找,发现第10个块有空间,就会把原数据块1中,zs这一行数据迁移到数据块10中,并修改姓名为zhangsan。
然后在在第1块中做个指针定向说明,原zs那条记录行定位到第10块的某某位置了。
你原始记录的rowid还在第1个块中,认为还在第1个块中。产生了行迁移。

如果你频繁update,频繁产生指针。

假如你做一个全表扫描,扫描的过程发现了有指针,再进行一次寻址,跳到第10个数据块,找到数据,然后再跳回第1个块。

如果你的表比较庞大,你经常出现这种跳转情况,回来跳的话,就回来寻址,对于你的查询速度比较慢。


oracle肯定不允许上面的情况出现啊,默认一个块只能使用90%,预留10%空间不存放数据,块写满了占8K的90%。
作用就是防止行迁移,再修改zs修改成zhangsan时,不会出现行迁移,它会占用预留的10%那块空间,就不用再寻址了。
这10%就是pctfree的设置值。






8K=8*1024 字节=8192字节

实验:

create table tab_rc
   (name01 char(1000),
   name02 char(1000),
   name03 char(100))
   pctfree 0;

SQL> insert into tab_rc(name01,name02) values('c','c');
/
/
/
commit
(插入4次  块中有8000字节数据)


SQL> update tab_rc set name03='c';
commit
(更新4行 多出400字节)


analyze table tab_rc compute statistics;(分析表)

select t.table_name,t.chain_cnt from user_tables t where t.table_name='TAB_RC';
(结果 对应链为1)



create table haha as select * from dba_objects;
analyze table haha compute statistics;
select t.table_name,t.chain_cnt from user_tables t where t.table_name='HAHA';











PCTUSED:是指当块里的数据低于多少百分比时,又可以重新被insert,一般默认是40,即40%,
即:当数据低于40%时,又可以写入新的数据。
由于删除的操作而令块的使用率低于PCTUSED,此块又被重新加入到空闲列表中,
它将会一直保留在空闲列表中,只有在到达pctfree时才会将数据块由空闲列表中移走。



当一个块第一次开辟的时候,当然是在空闲列表中的.
随着不断地插入行数据,当使用率达到或者超过 1-PCTFREE%的时候,该块从空闲列表中移出.
所以这时候新的行数据不可能再存放到该块中. 那这个剩余的PCTFREE%部分岂不是浪费了,当然不是了. 
随着UPDATE 活动的增多,某些存在该块中的行数据的就会变大,变大部分的数据就存放在PCTFREE部分中.


Delete活动,会将行数据从块中抹去,这时候块的使用率可能会低于1-pctfree%, 但是该块还是不会
立即回到空闲列表,也就是该块这时候不会接受新的行数据.  那什么时候该块会重新回到空闲列表中呢?
直到该块的使用率低于PCTUSED%的时候,才会回到空闲列表中,也就是说这时候可以接受新的行数据了


所以当你的系统有较多的update活动并且行数据的大小变化较大的时候,应该预留较多的pctfree.
例如一个公文审批系统,一个公文在流转的过程中,数据在不断地变大。
对于一个没有update活动的表,可以将其设置为0.如系统中的组织架构表,更新的可能性几乎为0。

如果你想充分地利用块,则将PCTUsed 设置地高一点,以便在发生Delete活动后,快速地回到空闲列表中.


pctfree=20,pctused=40
当数据量>=80%时,此块不再插入新数据
而如果你之后有delete动作时,数据量>80%
此时能再插入数据吗?
回答是不能
这时是受pctused=40控制
当表中数据占有低要低于40%才会接受新的数据插入





原文地址:https://www.cnblogs.com/xiaoxiao5ya/p/8b4fc79c0cbc8693c48c681d75528151.html