2008年5月27日 星期二

Select * From ap2_class_mapping Where class_id in ('0002','0012')
=> 會吃 index
Select * From ap2_class_mapping Where class_id in (0002,0012)
=> Full Scan

=> 有沒有單引號差很多

2008年5月17日 星期六

From ITHome
MySQL漸有大型企業採用,取得認證就業機會高
文/黃彥棻 (記者) 2008-02-01

MySQL在推出5.0版本時,將證照名稱正名,可以從證照看出是資料庫開發或者是資料庫管理員,目前臺灣若要考CMDEV和CMDBA,可以到全省VUE考試中心線上報名及考試。

由開放原始碼撰寫的資料庫產品MySQL,日前雖然被昇陽(Sun)併購,但因為MySQL有免費版和商業版,成為臺灣中小企業和個人用戶使用頻率最高的資料庫產品。但近年有不少大型企業開始評估或已經使用MySQL後,也使得取得MySQL證照的IT人,工作機會水漲船高。

MySQL資料庫因為是開放源碼撰寫的產品,對於許多SOHO或者是中小企業主而言,免費又兼具效能的MySQL,往往是第一也是唯一的資料庫選擇。巨匠電腦PHP與MySQL課程講師梁國亮表示,近來也發現有一些系統廠商或者是中小企業主,將MySQL作為單純進銷存系統或者是ERP(企業資源規畫)的後端資料庫使用。此外,也發現有一些中大型的網站或者是公家機關,也開始使用MySQL,「使用對象不再侷限以往的中小企業或者是SOHO族。」梁國亮說。

不只如此,梁國亮也觀察到,許多重量級廠商已經在部分的產品線上使用MySQL,例如電信業者彙整繳費資料到前端資料庫,就是使用MySQL;搜尋引擎業者某一些前端的資料庫,或者是某些大型的電子商務網站,甚至是美國最著名的網路書店Amazon,都有部分使用MySQL資料庫。

除了這些大型企業也開始使用MySQL資料庫外,梁國亮自身的教學經驗中也發現,有某家DRAM廠商,正在積極評估是否可以部分採用較為便宜的 MySQL資料庫,作為生產數據分析之用。他進一步表示,這家企業主對於MySQL的課程,要求到非常進階的使用功能,對於效能的調校與掌握,也要求務必徹底了解。梁國亮表示,這間DRAM業者為了降低成本,開始評估採用MySQL資料庫的可能性,如果效能上能夠符合該業者的需求,「光是1年資料庫軟體的授權費用,就可以節省上千萬元,」梁國亮說。

目前MySQL有3張相關證照,分別是基礎開發的CMDEV(Certified MySQL Developer)、資料庫管理員的CMDBA(Certified MySQL DBA)和高階叢集運算的CMCDBA(Certified MySQL Cluster DBA)。梁國亮目前已經取得CMDEV和CMDBA兩張證照,而CMCDBA則是去年下半年才推出的新證照。

梁國亮指出,目前證照的名稱是去年底MySQL升級到5.0版後,才重新命名的,原本在MySQL 4.0版本中,分成Core和Profession的兩張證照,難以從證照名稱上就看出原來Core就是資料庫開發工程師,Profession就是資料庫管理員。所以在MySQL 5.0版本中,才從善如流,從證照名稱就可以看出特色。

CMDEV和CMDBA都需要考2科上機的實機考試,單選題和複選題都有。梁國亮經驗表示,在考取CMDEV證照時,因為偏重開發的功能,「考生對於SQL的語法必須非常的了解,」梁國亮依自己考試經驗表示,考題上常出現考你一串語法後,問考生之後的執行結果為何,「這必須對於SQL語法非常熟悉。」他說。

至於CMDBA的考試上,梁國亮表示,主要是考驗考生對資料庫管理的概念和相關程序的了解,不論是使用者、權限管理,資料庫維護,包含備份、還原、目錄(Index)或重整等,都必須相當熟悉。他說,因為考試重視程序,考題就會有類似「資料庫壞了,正確的挽救程序應該如何執行?」的題目。

梁國亮表示,對於一個資料庫管理員而言,MySQL只是其中的一個產品類型,下一步發展的方向,不論是微軟的SQL Server 2005或者是大型的商用資料庫軟體如甲骨文或SAP等,都是進階學習的選擇。

目前臺灣若要考CMDEV和CMDBA,可以到全省VUE考試中心線上報名及考試。文⊙黃彥棻
from http://blog.pixnet.net/miles0722/post/8396830
MySQL截至目前為止有四種認證:

Certified MySQL Associate (CMA): MySQL for Beginners
Certified MySQL 5.0 Developer (CMDEV): MySQL 5.0 for Developers
Certified MySQL 5.0 Database Administrator (CMDBA): MySQL 5.0 for DBAs
Certified MySQL 5.1 Cluster Database Administrator (CMCDBA): MySQL Cluster for High Availability

其中 CMA只需通過一科,而CMEDV與CMDBA皆需通過兩科,CMCDBA則需先有CMDBA認證,加上通過一科才能擁有。

每次考試皆是US$200。

Exam Exam Code Questions Passing Score Exam time CMA

010-* 50 36-38 60 minutes Developer-I

003-* 70 42-44 90 minutes Developer-II

004-* 70 42-44 90 minutes DBA-I

005-* 70 44-46 90 minutes DBA-II

006-* 70 44-46 90 minutes Cluster DBA

009-* 70 42-44 90 minutes Core to Developer upgr.

007-* 60 34-36 75 minutes Professional to DBA upgr.

008-* 60 34-36 75 minutes

除了CMCDBA,其他三個認證可以買 MySQL 5.0 Certification Study Guide 自修,天瓏書局可以買得到。此書依據Developer-I、Developer-II、DBA-I與DBA-II分為四大部份四十二章。若是要考CMA,依據官方網站的說明,則需選讀此書的
Chapter 1: Sections 1.1 and 1.2
Chapter 5: Sections 5.1 through 5.4, 5.5 (but not 5.5.2 and 5.5.3), 5.6 and 5.7
Chapter 7: All
Chapter 8: All
Chapter 9: All, except for 9.6
Chapter 10: 10.5
Chapter 11: All
Chapter 14: Sections 14.1, 14.2 (but not 14.2.1 through 14.2.3), 14.3, 14.4, 14.6
Chapter 15: All, except 15.2.3 and 15.3.1
Chapter 29: Parts 29.4.2 and 29.4.3
Chapter 32: Section 32.4
網站上建議下列章節也要讀
Chapter 2 : All
Chapter 5: parts 5.4.1, 5.4.3
Chapter 20: 20.3
Chapter 25: 25.5, 25.6
Chapter 29: 29.4.6
Chapter 31: All



若是要考CMCDBA,則是要買 MySQL Cluster Certification Study Guide 一書。

附帶一提的是,MySQL 5.0 Certification Study Guide的後面書皮內側有一組號碼,第一次考試使用可以打75折,還不錯說~~不過可是有期限的哦,在最下面一行有寫,小小的字。

點這可以看到世界上有多少人拿到MySQL認證。

MySQL官方網站

2008年5月15日 星期四

from
http://www.itpub.net/114023.html
bitmap 的一点探究

1:bitmap 索引是分段存储的,也就是说很多条记录可能是分做了N段来存储,也就是有N个begin/end ,当新的记录 insert 而使用以前未曾使用过的物理地址的时候,会产生一个bitmap 段来存储,就算只有一条记录

2: 当删除一条记录的时候,在bitmap 索引上做了一个delete 的标记并用一新的记录来标记了,下面请看具体的演示

3: 当 dml发生的时候,会lock住某个值的存储bit的那一rowid所在的记录,参考下面的 row 中 lock ,这样显然会影响并发


SQL> create table tn(a number, b number);

Table created.

SQL> insert into tn select rownum,mod(rownum,5) from all_objects where rownum < 21;

20 rows created.

SQL> commit;

Commit complete.

SQL> create bitmap index tn_bitmap on tn(b);

Index created.

SQL> exec show_space('tn_bitmap',user,'INDEX');
Free Blocks.............................0
Total Blocks............................16
Total Bytes.............................131072
Unused Blocks...........................14
Unused Bytes............................114688
Last Used Ext FileId....................3
Last Used Ext BlockId...................1954
Last Used Block.........................2

PL/SQL procedure successfully completed.

SQL> select * from tn;

A B
---------- ----------
1 1
2 2
3 3
4 4
5 0
6 1
7 2
8 3
9 4
10 0
11 1

A B
---------- ----------
12 2
13 3
14 4
15 0
16 1
17 2
18 3
19 4
20 0

20 rows selected.

SQL> alter system dump datafile 3 block 1955;

System altered.

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66da csc: 0x00.18a0d77 itc: 2 flg: - typ: 2 - INDEX
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x02 xid: 0x0002.040.000000ea uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000

Leaf block dump
===============
header address 125987932=0x7826c5c
kdxcolev 0
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 5
kdxcofbo 46=0x2e
kdxcofeo 7918=0x1eee
kdxcoavs 7872
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8036
row#0[8013] flag: -----, lock: 0
col 0; len 1; (1): 80 ---表示值为0
col 1; len 6; (6): 00 c0 7e 03 00 00 ---rowid 起点的block和行号
col 2; len 6; (6): 00 c0 7e 03 00 17 ---rowid 结束的block和行号,注意17 = 16+7 = 23 ,也就是下面转换后的有效位置截止到23bit
col 3; len 4; (4): ca 10 42 08 ---把该值按照16进制数转化为 11001010 (首字节不表示rowid信息) 00010000 01000010 00001000 ,
凡是从起点到结束点内的1表示该值存在,这里有 一个必须要注意的问题是,这样转化后的位置并不是真实的物理位置,在每个字节内部bit还要颠倒一下顺序,首字节不表示位置信息
也就是说上面的应该转换为 00001000 01000010 00010000 ,发现正好每5个存在一个值为0的记录
row#1[7990] flag: -----, lock: 0
col 0; len 2; (2): c1 02 ---表示值为1
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 0f ---注意这里是f,也就是一共只有16位,因为1是第一条记录开始的,在16的位置就已经有5条了
col 3; len 3; (3): c9 21 84 注意这里的 21 84 正好16位,根据上面描述的规则转换后就是 10000100 00100001,4个1正好表示记录
row#2[7966] flag: -----, lock: 0
col 0; len 2; (2): c1 03 ---表示值为2
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 42 08 01
row#3[7942] flag: -----, lock: 0
col 0; len 2; (2): c1 04 ---表示值为3
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 84 10 02
row#4[7918] flag: -----, lock: 0
col 0; len 2; (2): c1 05 ---表示值为4
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 08 21 04
----- end of leaf block dump -----
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955



SQL> delete from tn where a = 2;

1 row deleted.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

SQL>

Block header dump: 0x00c007a3
Object id on Block? Y
seg/obj: 0x66da csc: 0x00.18a0d77 itc: 2 flg: - typ: 2 - INDEX
fsl: 0 fnx: 0x0 ver: 0x01

Itl Xid Uba Flag Lck Scn/Fsc
0x01 xid: 0x0000.000.00000000 uba: 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x02 xid: 0x0003.047.000000e9 uba: 0x00800dba.00d9.1f --U- 2 fsc 0x001a.018a0d7d

Leaf block dump
===============
header address 125987932=0x7826c5c
kdxcolev 0
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 6
kdxcofbo 48=0x30
kdxcofeo 7894=0x1ed6
kdxcoavs 7846
kdxlespl 0
kdxlende 1
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 8036
row#0[8013] flag: -----, lock: 0
col 0; len 1; (1): 80
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 10 42 08
row#1[7990] flag: -----, lock: 0
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 0f
col 3; len 3; (3): c9 21 84
row#2[7894] flag: -----, lock: 2 ---这是删除后的拷贝,我们发现删除的时候该行已经加锁 lock : 2
col 0; len 2; (2): c1 03
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 40 08 01 ---我们发现 ca 42 已经变成 ca 40 ,也就是已经少掉一位bit了,正好是删除的那一条记录
row#3[7966] flag: ---D-, lock: 2 ---这里我们发现值为2的记录已经有删除过的 ---D- ,D表示delete
col 0; len 2; (2): c1 03
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 42 08 01
row#4[7942] flag: -----, lock: 0
col 0; len 2; (2): c1 04
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 84 10 02
row#5[7918] flag: -----, lock: 0
col 0; len 2; (2): c1 05
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 17
col 3; len 4; (4): ca 08 21 04
----- end of leaf block dump -----
End dump data blocks tsn: 2 file#: 3 minblk 1955 maxblk 1955
再继续补充

首先truncate 表所有数据
truncate table tn;

SQL> exec show_space('tn_bitmap','i');
Free Blocks.............................0
Total Blocks............................16
Total Bytes.............................131072
Unused Blocks...........................14
Unused Bytes............................114688
Last Used Ext FileId....................3
Last Used Ext BlockId...................1954
Last Used Block.........................2

PL/SQL procedure successfully completed.

可以看出索引是空的

然后插入一条数据

SQL> insert into tn values(1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.



row#0[8009] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 1; (1): 00
row#1[8030] flag: ---D-, lock: 2
col 0; NULL
col 1; NULL
col 2; NULL
col 3; NULL




SQL> insert into tn values(1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.



row#0[8009] flag: ---D-, lock: 2 -- 标记删除,下面一份是拷贝
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 1; (1): 00
row#1[7987] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00 00---07 正好表示8 rows
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 03 -- 03 正好表示2条记录被插入



SQL> insert into tn values(1,1);

1 row created.

SQL> alter system dump datafile 3 block 1955;

System altered.

row#0[7987] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 03
row#1[7965] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 07 -- 07 正好表示3条记录被插入



SQL> insert into tn values(1,1);

1 row created.

SQL> insert into tn values(1,1);

1 row created.

SQL> insert into tn values(1,1);

1 row created.

SQL> insert into tn values(1,1);

1 row created.

SQL> insert into tn values(1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

我们在同一个session中同一个事务连续插入5条记录,发现在bitmap中居然做了5个拷贝

row#0[7987] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 03
row#1[7965] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 07
row#2[7943] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 0f
row#3[7921] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 1f
row#4[7899] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 3f
row#5[7877] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 7f
row#6[7855] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 ff -- ff 正好表示8条记录被插入


SQL> insert into tn values(1,1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system dump datafile 3 block 1955;

System altered.

SQL>

-- 上一个bitmap段存储表示8条记录,我们再插入第9条记录再来看

row#0[7855] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 00
col 2; len 6; (6): 00 c0 7e 03 00 07
col 3; len 2; (2): c8 ff -- 8条记录已满,也把前面的前8条的多拷贝给清除掉了
row#1[7834] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 c0 7e 03 00 08
col 2; len 6; (6): 00 c0 7e 03 00 0f
col 3; len 1; (1): 00 新插入的第9条记录被新的从 08 -- 0f 这8个字节用来存储


综合上面的实验可以看出,当单条insert发生的时候,会以8条记录为一个bitmap row 来存储,这正好是一个字节的bit,并且就算是
相同事务中的insert也会导致大量的拷贝和lock产生,严重影响性能,甚至可能发生行迁移等严重问题,所以在经常发生变化的表中
我们不应该采用 bitmap index ,当发生update 的时候情形更为复杂,暂时不予讨论了
///////////////
SQL> truncate table tn;

表已截掉。

SQL> exec show_space('tn_bitmap',user,'INDEX');
Free Blocks.............................0
Total Blocks............................3
Total Bytes.............................12288
Unused Blocks...........................1
Unused Bytes............................4096
Last Used Ext FileId....................1
Last Used Ext BlockId...................26474
Last Used Block.........................2

PL/SQL 过程已成功完成。

SQL> alter system dump datafile 1 block 26475;

系统已更改。

Leaf block dump
===============
header address 83060828=0x4f3685c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 0
kdxcofbo 36=0x24
kdxcofeo 3940=0xf64
kdxcoavs 3904
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 3940
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 26475 maxblk 26475
索引已经清空

SQL> insert into tn values (1, 1);

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

SQL> /

已创建 1 行。

依次插入10行数据。

SQL> commit;

提交完成。

SQL> alter system dump datafile 1 block 26475;

系统已更改。

Leaf block dump
===============
header address 83060828=0x4f3685c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 11
kdxcofbo 58=0x3a
kdxcofeo 3716=0xe84
kdxcoavs 3658
kdxlespl 0
kdxlende 9
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 3940
row#0[3913] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 1; (1): 00
row#1[3891] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 03
row#2[3869] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 07
row#3[3847] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 0f
row#4[3825] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 1f
row#5[3803] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 3f
row#6[3781] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 7f
row#7[3759] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 07
col 3; len 2; (2): c8 ff
row#8[3738] flag: ---D-, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 08
col 2; len 6; (6): 00 40 67 68 00 0f
col 3; len 1; (1): 00
row#9[3716] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 08
col 2; len 6; (6): 00 40 67 68 00 0f
col 3; len 2; (2): c8 03
row#10[3934] flag: ---D-, lock: 2
col 0; NULL
col 1; NULL
col 2; NULL
col 3; NULL
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 26475 maxblk 26475

oracle对每次插入的数据都进行索引。


SQL> truncate table tn;

表已截掉。

SQL> alter system dump datafile 1 block 26475;

系统已更改。

Leaf block dump
===============
header address 83060828=0x4f3685c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 0
kdxcofbo 36=0x24
kdxcofeo 3940=0xf64
kdxcoavs 3904
kdxlespl 0
kdxlende 0
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 3940
----- end of leaf block dump -----

一次性插入10条数据
SQL> insert into tn select 1,1 from user_objects where rownum < 11;

已创建10行。

SQL> commit;

提交完成。

SQL> alter system dump datafile 1 block 26475;

系统已更改。

Leaf block dump
===============
header address 83060828=0x4f3685c
kdxcolev 0
KDXCOLEV Flags = - - -
kdxcolok 0
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y
kdxconco 4
kdxcosdc 0
kdxconro 2
kdxcofbo 40=0x28
kdxcofeo 3911=0xf47
kdxcoavs 3871
kdxlespl 0
kdxlende 1
kdxlenxt 0=0x0
kdxleprv 0=0x0
kdxledsz 0
kdxlebksz 3940
row#0[3911] flag: -----, lock: 2
col 0; len 2; (2): c1 02
col 1; len 6; (6): 00 40 67 68 00 00
col 2; len 6; (6): 00 40 67 68 00 0f
col 3; len 3; (3): c9 ff 03 一次性插入的数据,oracle会对整批数据进行bitmap索引。
row#1[3934] flag: ---D-, lock: 2
col 0; NULL
col 1; NULL
col 2; NULL
col 3; NULL
----- end of leaf block dump -----
End dump data blocks tsn: 0 file#: 1 minblk 26475 maxblk 26475

因此我认为对于包含bitmap索引的表应当减少对数据库操作的数量。运用批量入库的方法会使性能得到较大的提高。

有什么不对的地方还请指正。


my reply:

综合上面的实验可以看出,当单条insert发生的时候,会以8条记录为一个bitmap row 来存储,这正好是一个字节的bit,并且就算是
相同事务中的insert也会导致大量的拷贝和lock产生,严重影响性能,甚至可能发生行迁移等严重问题,所以在经常发生变化的表中
我们不应该采用 bitmap index ,当发生update 的时候情形更为复杂,暂时不予讨论了



结合
前面的第一条:
:bitmap 索引是分段存储的,也就是说很多条记录可能是分做了N段来存储,也就是有N个begin/end ,当新的记录 insert 而使用以前未曾使用过的物理地址的时候,会产生一个bitmap 段来存储,就算只有一条记录


所以我的实验中可能没有很好的做归纳,但是现象都是有的,描述的比较零散



就是说假如你们每天入库 1000万条记录
那创建 bitmap 索引的这个列有多少不同的值,如果有10万个不同的值以上,那采用 bitmap 是否合适就值得考虑了

2008年5月13日 星期二

Oracle Partion

form
http://jzhua.javaeye.com/blog/166078

一、Oracle分区简介
ORACLE的分区是一种处理超大型表、索引等的技术。分区是一种“分而治之”的技术,通过将大表和索引分成可以管理的小块,从而避免了对每个表作为一个大的、单独的对象进行管理,为大量数据提供了可伸缩的性能。分区通过将操作分配给更小的存储单元,减少了需要进行管理操作的时间,并通过增强的并行处理提高了性能,通过屏蔽故障数据的分区,还增加了可用性。
二、Oracle分区优缺点
 优点:
增强可用性:如果表的某个分区出现故障,表在其他分区的数据仍然可用;
维护方便:如果表的某个分区出现故障,需要修复数据,只修复该分区即可;
均衡I/O:可以把不同的分区映射到磁盘以平衡I/O,改善整个系统性能;
改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索速度。
 缺点:
分区表相关:已经存在的表没有方法可以直接转化为分区表。不过 Oracle 提供了在线重定义表的功能。
三、Oracle分区方法
 范围分区:
范围分区就是对数据表中的某个值的范围进行分区,根据某个值的范围,决定将该数据存储在哪个分区上。如根据序号分区,根据业务记录的创建日期进行分区等。
 Hash分区(散列分区):
散列分区为通过指定分区编号来均匀分布数据的一种分区类型,因为通过在I/O设备上进行散列分区,使得这些分区大小一致。
 List分区(列表分区):
当你需要明确地控制如何将行映射到分区时,就使用列表分区方法。与范围分区和散列分区所不同,列表分区不支持多列分区。如果要将表按列分区,那么分区键就只能由表的一个单独的列组成,然而可以用范围分区或散列分区方法进行分区的所有的列,都可以用列表分区方法进行分区。
 范围-散列分区(复合分区):
有时候我们需要根据范围分区后,每个分区内的数据再散列地分布在几个表空间中,这样我们就要使用复合分区。复合分区是先使用范围分区,然后在每个分区内再使用散列分区的一种分区方法(注意:先一定要进行范围分区)
 范围-列表分区(复合分区):
范围和列表技术的组合,首先对表进行范围分区,然后用列表技术对每个范围分区再次分区。与组合范围-散列分区不同的是,每个子分区的所有内容表示数据的逻辑子集,由适当的范围和列表分区设置来描述。(注意:先一定要进行范围分区)

四、Oracle表分区表操作
--Partitioning 是否为true
select * from v$option s order by s.PARAMETER desc

--创建表空间
CREATE TABLESPACE "PARTION_03"
LOGGING
DATAFILE 'D:\ORACLE\ORADATA\JZHUA\PARTION_03.dbf' SIZE 50M
EXTENT MANAGEMENT LOCAL SEGMENT SPACE MANAGEMENT AUTO

--删除表空间
drop tablespace partion_01


--范围 分区技术
create table Partition_Test
(
PID number not null,
PITEM varchar2(200),
PDATA date not null
)
partition by range(PID)
(
partition part_01 values less than(50000) tablespace dinya_space01,
partition part_02 values less than(100000) tablespace dinya_space02,
partition part_03 values less than(maxvalue) tablespace dinya_space03
)

create table Partition_TTest
(
PID number not null,
PITEM varchar2(200),
PDATA date not null
)
partition by range(PDATA)
(
partition part_t01 values less than(to_date('2004-01-01','yyyy-mm-dd')) tablespace dinya_space01,
partition part_t02 values less than(to_date('2008-01-01','yyyy-mm-dd')) tablespace dinya_space02,
partition part_t03 values less than(maxvalue) tablespace dinya_space03
)

insert into Partition_Test(PID,PITEM,PDATA) select h.id,h.userid,h.rectime from st_handle h

select * from Partition_Test partition(part_01) t where t.pid = '1961'

--hash 分区技术
create table Partition_HashTest
(
PID number not null,
PITEM varchar2(200),
PDATA date not null
)
partition by hash(PID)
(
partition part_h01 tablespace dinya_space01,
partition part_h02 tablespace dinya_space02,
partition part_h03 tablespace dinya_space03
)

insert into Partition_HashTest(PID,PITEM,PDATA) select h.id,h.userid,h.rectime from st_handle h

select * from Partition_HashTest partition(part_h03) t where t.pid = '1961'


--复合分区技术
create table Partition_FHTest
(
PID number not null,
PITEM varchar2(200),
PDATA date not null
)
partition by range(PDATA) subpartition by hash(PID) subpartitions 3 store in (dinya_space01,dinya_space02,dinya_space03)
(
partition part_fh01 values less than(to_date('2004-01-01','yyyy-mm-dd')) tablespace dinya_space01,
partition part_fh02 values less than(to_date('2008-01-01','yyyy-mm-dd')) tablespace dinya_space02,
partition part_fh03 values less than(maxvalue) tablespace dinya_space03
)

insert into Partition_FHTest(PID,PITEM,PDATA) select h.id,h.userid,h.rectime from st_handle h

select * from Partition_FHTest partition(part_fh02) t where t.pid = '1961'

select * from Partition_FHTest partition(part_fh03) t

--速度比较
select * from st_handle h where h.rectime > to_date('2008-01-01','yyyy-mm-dd');

select * from Partition_FHTest partition(part_fh03) t where t.pdata > to_date('2008-01-01','yyyy-mm-dd');


--分区表操作

--增加一个分区
alter table Partition_Test add partition part_05 values less than (10020) tablespace dinya_space03

--查询分区数据
select * from Partition_FHTest partition(part_fh02) t

--修改分区里的数据
update Partition_FHTest partition(part_fh02) t set t.PITEM = 'JZHUA' where t.pid = '1961'

--删除分区里的数据
delete from Partition_FHTest partition(part_fh02) t where t.pid = '1961'

--合并分区
create table Partition_HB
(
PID number not null,
PITEM varchar2(200),
PDATA date not null
)
partition by range(PID)
(
partition part_01 values less than(50000) tablespace dinya_space01,
partition part_02 values less than(100000) tablespace dinya_space02,
partition part_03 values less than(maxvalue) tablespace dinya_space03
)

insert into Partition_HB(PID,PITEM,PDATA) select h.id,h.userid,h.rectime from st_handle h

select * from Partition_HB partition(part_03) t where t.pid = '100001'

alter table Partition_HB merge partitions part_01,part_02 into partition part_02;

--拆分分区
-- spilt partition 分区名 at(这里是一个临界区,比如:50000就是说小于50000的放在part_01,而大于50000的放在part_02中)
alter table Partition_HB split Partition part_02 at (50000) into (Partition part_01 tablespace dinya_space01, Partition part_02 tablespace dinya_space02);

--更改分区名
alter table Partition_HB rename Partition part_01_test to part_02;
五、Oracle索引分区表操作
分区表和一般表一样可以建立索引,分区表可以创建局部索引和全局索引。当分区中出现许多事务并且要保证所有分区中的数据记录的唯一性时采用全局索引。全局索引建立时 global 子句允许指定索引的范围值,这个范围值为索引字段的范围值。其实理论上有3中分区索引。

 Global索引(全局索引):
对于 global 索引,可以选择是否分区,而且索引的分区可以不与表分区相对应。当对分区进行维护操作时,通常会导致全局索引的 Invalid,必须在执行完操作后 Rebuild。Oracle9i 提供了 Update Global Indexes 语句,可以在进行分区维护的同时重建全局索引。
1:索引信息的存放位置与父表的Partition(分区)信息完全不相干。甚至父表是不是分区表都无所谓的。

create index dinya_idx_t on dinya_test(item_id) global partition by range(item_id) (
partition idx_1 values less than (1000) tablespace dinya_space01,
partition idx_2 values less than (10000) tablespace dinya_space02,
partition idx_3 values less than (maxvalue) tablespace dinya_space03
);
2:但是在这种情况下,如果父表是分区表,要删除父表的一个分区都必须要更新Global Index ,否则索引信息不正确
ALTER TABLE TableName DROP PARTITION PartitionName Update Global Indexes

 Local索引(局部索引):

对于 local 索引,每一个表分区对应一个索引分区(就是说一个分区表一个字段只可以建一个局部索引),当表的分区发生变化时,索引的维护由 Oracle 自动进行;
1:索引信息的存放位置依赖于父表的Partition(分区)信息,换句话说创建这样的索引必须保证父表是Partition(分区),索引信息存放在父表的分区所在的表空间。
2:但是仅可以创建在父表为HashTable或者composite分区表的。
3:仅可以创建在父表为HashTable或者composite分区表的。并且指定的分区数目要与父表的分区数目要一致。

create index dinya_idx_t on dinya_test(item_id) local (
partition idx_1 tablespace dinya_space01,
partition idx_2 tablespace dinya_space02,
partition idx_3 tablespace dinya_space03
);

不指定索引分区名直接对整个表建立索引
create index dinya_idx_t on dinya_test(item_id);

2008年5月7日 星期三

有空來 try ~try~
http://msdn.microsoft.com/zh-tw/library/system.data.oracleclient.oracletype(VS.80).aspx
http://www.dba-oracle.com/t_storing_insert_photo_pictures_tables.htm
http://www.dba-oracle.com/t_blob.htm

Oracle基本数据类型存储格式浅析(一)——字符类型
===========================================================
作者: yangtingkun(http://yangtingkun.itpub.net)
发表于: 2004.12.09 23:47
分类: ORACLE
出处: http://yangtingkun.itpub.net/post/468/9287
---------------------------------------------------------------

前一阵看完文档,对oracle的基本数据类型的存储格式有了一些了解,最近有做了一些测试进行了验证。


打算整理总结一下,这一篇主要说明字符类型的存储格式。主要包括char、varchar2和long等几种类型。








SQL> create table test_char (char_col char(10), varchar_col varchar2(10), long_col long);


表已创建。


SQL> insert into test_char values ('abc', '123', ',fd');


已创建 1 行。


SQL> commit;


提交完成。


SQL> select rowid from test_char;


ROWID
------------------
AAAB3LAAFAAAAAgAAA


根据rowid的定义规则,第7~9位是表示的是数据文件,F表示5,而10~15位表示的是在这个数据文件中的第几个BLOCK,g表示32。(rowid编码相当于64进制。用A~Z a~z 0~9 + /共64个字符表示。A表示0,B表示1,……,a表示26,……,0表示52,……,+表示62,/表示63。)


我们根据计算的结果去dump这个block。


SQL> ALTER SYSTEM DUMP DATAFILE 5 BLOCK 32;


系统已更改。


打开产生的trace文件:


data_block_dump,data header at 0x3421064
===============
tsiz: 0x1f98
hsiz: 0x14
pbl: 0x03421064
bdba: 0x01400020
76543210
flag=--------
ntab=1
nrow=1
frre=-1
fsbo=0x14
fseo=0x1f82
avsp=0x1f6e
tosp=0x1f6e
0xe:pti[0] nrow=1 offs=0
0x12:pri[0] offs=0x1f82
block_row_dump:
tab 0, row 0, @0x1f82
tl: 22 fb: --H-FL-- lb: 0x1 cc: 3
col 0: [10] 61 62 63 20 20 20 20 20 20 20
col 1: [ 3] 31 32 33
col 2: [ 3] 2c 66 64
end_of_block_dump
End dump data blocks tsn: 5 file#: 5 minblk 32 maxblk 32


观察dump出来的结果,可以发现以下几点:


1.对于每个字段,除了保存字段的值以外,还会保存当前字段中数据的长度。而且,oracle显然没有把字段的长度定义或类型定义保存在block中,这些信息保存在oracle的数据字典里面。


2. 根据dump的结果,可以清楚的看到,字符类型在数据库中是以ascii格式存储的。


SQL> select chr(to_number('61', 'xx')) from dual;


CH
--
a


3.char类型为定长格式,存储的时候会在字符串后面填补空格,而varchar2和long类型都是变长的。


SQL> SELECT DUMP(CHAR_COL, 16) D_CHAR FROM TEST_CHAR;


D_CHAR
-------------------------------------------------------------
Typ=96 Len=10: 61,62,63,20,20,20,20,20,20,20


SQL> SELECT DUMP(VARCHAR_COL, 16) D_VARCHAR2 FROM TEST_CHAR;


D_VARCHAR2
-------------------------------------------------------------
Typ=1 Len=3: 31,32,33


SQL> SELECT DUMP(LONG_COL, 16) D_VARCHAR2 FROM TEST_CHAR;
SELECT DUMP(LONG_COL, 16) D_VARCHAR2 FROM TEST_CHAR
*
ERROR 位于第 1 行:
ORA-00997: 非法使用 LONG 数据类型


由于DUMP不支持LONG类型,因此我们使用了alter system dump block的方式,通过比较两种方式得到的结果,发现DUMP()函数不但方便,结果清晰,而且指出了进行DUMP的数据类型,在以后的例子中,除非必要的情况,否则都会采用DUMP()函数的方式进行说明。


下面看一下插入中文的情况,首先看一下数据库的字符集


SQL> select name, value$ from sys.props$ where name like '%CHARACTERSET%';


NAME VALUE$
------------------------------ ------------------------------
NLS_CHARACTERSET ZHS16GBK
NLS_NCHAR_CHARACTERSET AL16UTF16


SQL> insert into test_char values ('定长', '变长', null);


已创建 1 行。


SQL> SELECT DUMP(CHAR_COL, 16) D_CHAR FROM TEST_CHAR;


D_CHAR
----------------------------------------------------------------
Typ=96 Len=10: 61,62,63,20,20,20,20,20,20,20
Typ=96 Len=10: b6,a8,b3,a4,20,20,20,20,20,20


SQL> SELECT DUMP(VARCHAR_COL, 16) D_VARCHAR2 FROM TEST_CHAR;


D_VARCHAR2
----------------------------------------------------------------
Typ=1 Len=3: 31,32,33
Typ=1 Len=4: b1,e4,b3,a4


根据dump结果,可以清楚的看出,普通英文字符和标点用一个字节表示,而中文字符或中文标点需要两个字节来表示。


下面,对比一下nchar和nvarchar2与char、varchar2类型有什么不同。


SQL> create table test_nchar (nchar_col nchar(10), nvarchar_col nvarchar2(10));


表已创建。


SQL> insert into test_nchar values ('nchar定长', 'nvarchar变长');


已创建 1 行。


从这里已经可以看出一些不同了,如果按照刚才中文的计算方法,'nvarchar变长'的长度是8+2*2=12已经超过了数据类型定义的大小,可是为什么插入成功了?


还是dump一下看看结果吧。


SQL> select dump(nchar_col, 16) from test_nchar;


DUMP(NCHAR_COL,16)
--------------------------------------------------------------
Typ=96 Len=20: 0,6e,0,63,0,68,0,61,0,72,5b,9a,95,7f,0,20,0,20,0,20


SQL> select dump(nvarchar_col, 16) from test_nchar;


DUMP(NVARCHAR_COL,16)
--------------------------------------------------------------
Typ=1 Len=20: 0,6e,0,76,0,61,0,72,0,63,0,68,0,61,0,72,53,d8,95,7f


这下就明白了,虽然仍然是采用ascii码存储,但是nchar使用的AL16UTF16字符集,编码长度变为2个字节。这样中文使用两个字节,对于可以用一个字节就表示的英文字符,采用了高位补0的方式凑足2位,这样,对于采用AL16UTF16字符集的nchar类型,无论中文还是英文都用2位字符表示。因此'nvarchar变长'的长度是10,并没有超过数据类型的限制。
1 bit
1 byte = 8 bits , in C language , called character or CHAR
=> can break a byte into two groups , one is called nibble , nnnn nnnn => 2 nibble.
1 word = 2 bytes = 16 bits , in C language , called an Integer , unsign 0~65535
=> MySQL smallint(5)
1 long word = 2 words = 4 bytes = 32 bits , unsign 0 ~ 4,294,967,295
=> MySQL int(10)