像电信这种业务数据量都比较大,它是以省级为单位有数据。
电信这么大的业务,核算一下,在全国范围内核算一下,某款业务卖了多少钱,营利多少钱?
逻辑结构
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%才会接受新的数据插入