zoukankan      html  css  js  c++  java
  • 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%才会接受新的数据插入





  • 相关阅读:
    “爆奇葩”项目之索引页
    android 的生命周期自我理解
    Jquery Mobile 中文API站
    根据两点经纬度计算距离
    sql语句查询经纬度范围
    Asp.net core 笔记
    Docker 笔记
    IOC和DI
    PHP学习笔记十、图像处理
    PHP学习笔记九、cookie与session
  • 原文地址:https://www.cnblogs.com/xiaoxiao5ya/p/8b4fc79c0cbc8693c48c681d75528151.html
Copyright © 2011-2022 走看看