前几天有个朋友提出一个“老问题”,数据库上的block能否对应到EXADATA的block上,我答应做个demo,一直没时间,今天闲了,玩了一下:
对于EXADATA来说,这个需求设计两个问题:
1,数据库的block如何对应到asm中
2,exadata上的block如何对应到cell上的物理盘(griddisk,celldisk都是逻辑概念)
首先创建测试表:
create table lunartest as select * from dba_users;
–查找里面用户名为LUNAR的ROWID:
SQL> select username , rowid from lunartest where username='LUNAR'; USERNAME ROWID ------------------------------ ------------------ LUNAR AABCFqAABAAArF6AAW
记录一下这个表的username=’LUNAR’的数据的rowid,便于验证数据。
然后找到该表的第一个block,也就是segment header,方法至少有3种
1,通过dbms_rowid
2,通过dba_extents
3,通过dba_segments
这里我们随便选一种,找到了该block的位置:
select EXTENT_ID, BLOCK_ID, BLOCKS, FILE_ID from dba_extents where SEGMENT_NAME='LUNARTEST'; EXTENT_ID BLOCK_ID BLOCKS FILE_ID ---------- ---------- ---------- ---------- 0 176505 8 1
查看当前ASM的AU尺寸和BLOCK尺寸(通常是缺省的,不排除特殊客户自己设定的或者exadata的情况,因此还是找一下):
[grid@lunar ~]$ kfed read /dev/lunarlun02 ausz=1m aun=0 blkn=0|grep ausize ----1M的AU(一般都是缺省是1M,exadata缺省是4M) kfdhdb.ausize: 1048576 ; 0x0bc: 0x00100000 [grid@lunar ~]$ kfed read /dev/lunarlun02 ausz=1m aun=0 blkn=0|grep blksize ----4k的ASM BLOCK(缺省值) kfdhdb.blksize: 4096 ; 0x0ba: 0x1000 [grid@lunar ~]$ kfed read /dev/lunarlun02 ausz=1m aun=0 blkn=0|grep secsize -----扇区512bytes(缺省值) kfdhdb.secsize: 512 ; 0x0b8: 0x0200 [grid@lunar ~]$
exadata上使用KEFED的例子可以参考《Exadata上验证ASM磁盘头备份的位置》
我的数据库为8k的数据块(db_block_size),那么计算一下对应到ASM是哪一个extent:
19:44:11 SQL> SELECT (176506*8192/1024)/1024 MB FROM DUAL; MB ---------- 1378.95313 Elapsed: 00:00:00.00 19:44:23 SQL>
lunartest表在DATA DG的asm file 1755上:
SQL> select file#,ts#,name from v$datafile WHERE FILE#=1 ; FILE# TS# ---------- ---------- NAME -------------------------------------------------------------------------------- 1 0 +DATA/lunar/datafile/lunar.1755.8764800657 SQL>
如果是exadata,那么输出类似下面的,这里并没有本质区别(区别在通信方式上,后面会讲……):
TABLESPACE_NAME FILE_NAME --------------- ------------------------------------------------------------------------------------------ SYSTEM +DATA_DM01/lunar/datafile/system.264.764800657 SYSAUX +DATA_DM01/lunar/datafile/sysaux.272.764800659 UNDOTBS1 +DATA_DM01/lunar/datafile/undotbs1.271.764800661 UNDOTBS2 +DATA_DM01/lunar/datafile/undotbs2.259.764800671 USERS +DATA_DM01/lunar/datafile/users.267.764800667
根据上面的计算,查找这个表的第一个数据块在哪一个ASM的diskgroup,disk和AU的信息:
select x.GROUP_KFFXP ,x.XNUM_KFFXP ,x.DISK_KFFXP, x.AU_KFFXP, d.path,a.name from x$kffxp x, v$asm_alias a, v$asm_disk d where x.number_kffxp=a.file_number and x.DISK_KFFXP=d.disk_number and x.GROUP_KFFXP=d.GROUP_NUMBER and x.GROUP_KFFXP=a.GROUP_NUMBER and a.name = 'lunar.1755.8764800657' AND x.xnum_kffxp=1378 GROPU# XNUM_KFFXP DISK_KFFXP AU_KFFXP ---------- ---------- ----------- ---------- PATH ------------------------------------------------------------------------------------------------------------------------------------------------------ NAME ------------------------------ 2 1378 4 1000601 /dev/lunarlun02 lunar.1755.8764800657
如果是exadata环境,那么查询到的信息,对应到这里的/dev/lunarlun02可能就是类似下面的:o/192.168.10.3/DATA_DM01_CD_00_dm01cel01:
这里也就对应到cell01(IP为:192.168.10.3)
具体例子可以参考:Exadata更换硬盘的操作过程和解释
NAME PATH STATE TYPE FAILGROUP --------------- ------------------------------------------------------- -------- ------ --------------- DATA_DM01 o/192.168.10.3/DATA_DM01_CD_00_dm01cel01 NORMAL NORMAL DM01CEL01 这里DATA_DM01_CD_00_dm01cel01就是cell01上的griddisk的名字(griddisk是逻辑的概念,经过了exadata划盘时的横切和纵切两种操作,具体介绍exadata磁盘的信息请参考《<a href="http://www.lunar2013.com/?p=897" title="Exadata的数据保护机制(冗余机制)- 1">Exadata的数据保护机制(冗余机制)- 1</a>》)。 file 1,block 176506在磁盘上的物理offset: SQL> select (1000601.95313*1048576)/8192 from dual; (1000601.95313*1048576)/8192 ---------------------------- 128077050 SQL>
使用dd
[grid@lunar ~]$ dd if=/dev/lunarlun02 of=lunartest_1_176505_1.dmp bs=8k skip=128077050 count=1 1+0 records in 1+0 records out 8192 bytes (8.2 kB) copied, 0.00459904 s, 1.8 MB/s [grid@lunar ~]$
我们用dd验证一下数据,:
SQL> select username , rowid from lunartest where username='LUNAR'; USERNAME ROWID ------------------------------ ------------------ LUNAR AABCFqAABAAArF6AAW SQL> SQL> L 1* select * from lunartest where username='LUNAR' SQL> / USERNAME USER_ID PASSWORD ACCOUNT_STATUS LOCK_DATE EXPIRY_DA DEFAULT_TABLESPACE TEMPORARY_TABLESPACE CREATED ------------------------------ ---------- ------------------------------ -------------------------------- --------- --------- ------------------------------ ------------------------------ --------- PROFILE INITIAL_RSRC_CONSUMER_GROUP ------------------------------ ------------------------------ EXTERNAL_NAME -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- PASSWORD E AUTHENTI -------- - -------- LUNAR 125 OPEN USERS TEMP 11-SEP-14 DEFAULT DEFAULT_CONSUMER_GROUP 10G 11G N PASSWORD SQL>
验证数据:这个LUNARTEST是根据DBA_USERS做的CTAS,因此上面我们有一行测试数据,这里可以找到:
[grid@lunar ~]$ strings lunartest_1_176505_1.dmp |grep LUNAR LUNAR [grid@lunar ~]$
因为是别人的生产库,不能使用bbed等工具瞎折腾,因此,我这里使用UltraEdit查看这个块来验证数据:
SQL> SELECT utl_raw.cast_to_raw('LUNAR') FROM dual; UTL_RAW.CAST_TO_RAW('LUNAR') -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 4C554E4152 SQL>
可以看到数据是吻合的。至此,上面将oracle的block对应到ASM是没问题的。
另外,如果要想观察asm的具体操作,还可以使用strace,比如
read64(15, “#\242\0\0\33\0@\2\343\332\177\303s\5\1\4\211\330\0\0\0″…, 8192, 473128960) = 8192
表示每个读取操作以8k(oracle block)的进行读取的偏移量是473128960(451 MB + 27*8KB)
读取的文件FD是15
然后使用ls /proc/30094/fd 就可以找到具体的文件设备名称了,比如上面的/dev/lunarlun02(v$asm_disk.path)
但是要想定位EXADATA,就没那么简单了,因为exadata的cell的盘是做了横切的条带划的(还至少normal冗余,更增加难度……)
其通讯方式跟普通的ASM也不同,参考:在Exadata上,为什么 DUL 和 ODU不能读取ASM数据库的数据,但是Kfed却可以?和Exadata读取数据和传统数据库环境中读取数据的方式有什么关键区别?
但是,如果真的想找到,猜测需要使用strace 定位,应该可以:
11771 0.013455 times({tms_utime=210862, tms_stime=79333, tms_cutime=0, tms_cstime=0}) = 2144885203 11771 0.000143 setsockopt(34, 0x114 /* SOL_?? */, 2, "\0\340f\3\10\0\0\0\0 \0\0\0\0\0\0008\22U\35\0\0\0\0\t\0\0\0\0\0\0\0", 32) = 0 11771 0.000067 sendmsg(32, {msg_name(16)={sa_family=AF_INET, sin_port=htons(50020), sin_addr=inet_addr("192.168.10.3")}, msg_iov(5)=[{"\4\3\2\1~\266\276\346\3\0\0\0MRON\4\3\0\0\0\0\0\0S\355\203>:p\353\n"..., 76}, {"\2+.j.j\0\0\30\1\0\0\230\0\0\0\0 \0\0\0\0\0\0\1\0\0\0", 28}, {"\4\3\2\1\2N\205\0\250\326ew\0\0\0\0S\355\203>\373-\0\0\0\0\1\0\261\32\244\367"..., 64}, {"\4\3\2\1\2\216\264\0\33\27\2\260\0\0\0\0S\355\203>\373-\0\0\0\0\1\0\262\32\244\367"..., 64}, {"\4\3\2\0013\5\230\234\35\0\0\0\0\0\1\0\0 \0\0\0\0\0\0\267\366\0\0\5\0\0\0"..., 152}], msg_controllen=0, msg_flags=0}, 0) = 384 11771 0.000107 setsockopt(34, 0x114 /* SOL_?? */, 2, "\0\340f\3\10\0\0\0\0 \0\0\0\0\0\0\300\203T\35\0\0\0\0\t\0\0\0\0\0\0\0", 32) = 0 11771 0.000039 sendmsg(32, {msg_name(16)={sa_family=AF_INET, sin_port=htons(24635), sin_addr=inet_addr("192.168.10.5")}, msg_iov(5)=[{"\4\3\2\1~\266\276\346\3\0\0\0MRON\4\3\0\0\0\0\0\0S\355\203>1p\353\n"..., 76}, {"\2+\200\370\200\370\0\0\30\1\0\0\230\0\0\0\0 \0\0\0\0\0\0\1\0\0\0", 28}, {"\4\3\2\1\2N%\0\346\"\200L\0\0\0\0S\355\203>\373-\0\0\0\0\1\0\263\32\243\367"..., 64}, {"\4\3\2\1\2\216\3\0007\245\0p\0\0\0\0S\355\203>\373-\0\0\0\0\1\0\264\32\243\367"..., 64}, {"\4\3\2\1\226()\234\34\0\0\0\0\0\1\0\0 \0\0\0\0\0\0\270\366\0\0\5\0\0\0"..., 152}], msg_controllen=0, msg_flags=0}, 0) = 384 11771 0.000119 poll([{fd=33, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}, {fd=32, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}, {fd=34, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 3, 1000) = 1 ([{fd=34, revents=POLLIN|POLLRDNORM}]) 11771 0.000067 recvmsg(34, {msg_name(16)={sa_family=AF_INET, sin_port=htons(9621), sin_addr=inet_addr("192.168.10.3")}, msg_iov(2)=[{"\4\3\2\1\30\215\302\376\3\0\0\0MRON\4\3\0\0\0\0\0\0\220\360\224\23\0\0\0\0"..., 76}, {"\2 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0", 28}], msg_controllen=0, msg_flags=0}, MSG_PEEK) = 104 11771 0.000051 recvmsg(34, {msg_name(16)={sa_family=AF_INET, sin_port=htons(9621), sin_addr=inet_addr("192.168.10.3")}, msg_iov(2)=[{"\4\3\2\1\30\215\302\376\3\0\0\0MRON\4\3\0\0\0\0\0\0\220\360\224\23\0\0\0\0"..., 76}, {"\2 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0", 28}], msg_controllen=0, msg_flags=0}, 0) = 104 11771 0.000050 recvmsg(34, {msg_name(16)={sa_family=AF_INET, sin_port=htons(45218), sin_addr=inet_addr("192.168.10.5")}, msg_iov(2)=[{"\4\3\2\1x\251\302\376\3\0\0\0MRON\4\3\0\0\0\0\0\0\275\243\352f\0\0\0\0"..., 76}, {"\2 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0", 28}], msg_controllen=0, msg_flags=0}, MSG_PEEK) = 104 11771 0.000046 recvmsg(34, {msg_name(16)={sa_family=AF_INET, sin_port=htons(45218), sin_addr=inet_addr("192.168.10.5")}, msg_iov(2)=[{"\4\3\2\1x\251\302\376\3\0\0\0MRON\4\3\0\0\0\0\0\0\275\243\352f\0\0\0\0"..., 76}, {"\2 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\0\0\0\0\0\0\0", 28}], msg_controllen=0, msg_flags=0}, 0) = 104 11771 0.000045 recvmsg(34, {msg_name(16)={sa_family=AF_INET, sin_port=htons(56555), sin_addr=inet_addr("192.168.10.3")}, msg_iov(2)=[{"\4\3\2\1w\0\267\376\3\0\0\0MRON\4\3\0\0\0\0\0\0\346=\213\26x:1\2"..., 76}, {"\2\4\335\24\335\24\0\0\230\0\0\0\0\0\0\0\0\0\0\0\230\0\0\0\1\0\0\0", 28}], msg_controllen=0, msg_flags=MSG_TRUNC}, MSG_PEEK) = 104 11771 0.000063 recvmsg(34, {msg_name(16)={sa_family=AF_INET, sin_port=htons(56555), sin_addr=inet_addr("192.168.10.3")}, msg_iov(3)=[{"\4\3\2\1w\0\267\376\3\0\0\0MRON\4\3\0\0\0\0\0\0\346=\213\26x:1\2"..., 76}, {"\2\4\335\24\335\24\0\0\230\0\0\0\0\0\0\0\0\0\0\0\230\0\0\0\1\0\0\0", 28}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\267\366\210\0\0\0"..., 152}], msg_controllen=0, msg_flags=0}, 0) = 256 11771 0.000055 setsockopt(34, 0x114 /* SOL_?? */, 3, "\33\27\2\260\0\0\0\0\0\0\0\0\0\0\0\0", 16) = -1 EINVAL (Invalid argument) 11771 0.000034 recvmsg(34, {msg_name(16)={sa_family=AF_INET, sin_port=htons(8045), sin_addr=inet_addr("192.168.10.5")}, msg_iov(2)=[{"\4\3\2\1}\0\267\376\3\0\0\0MRON\4\3\0\0\0\0\0\0\324G\26\\\341\223\253-"..., 76}, {"\2\4\203\253\203\253\0\0\230\0\0\0\0\0\0\0\0\0\0\0\230\0\0\0\1\0\0\0", 28}], msg_controllen=0, msg_flags=MSG_TRUNC}, MSG_PEEK) = 104 11771 0.000047 recvmsg(34, {msg_name(16)={sa_family=AF_INET, sin_port=htons(8045), sin_addr=inet_addr("192.168.10.5")}, msg_iov(3)=[{"\4\3\2\1}\0\267\376\3\0\0\0MRON\4\3\0\0\0\0\0\0\324G\26\\\341\223\253-"..., 76}, {"\2\4\203\253\203\253\0\0\230\0\0\0\0\0\0\0\0\0\0\0\230\0\0\0\1\0\0\0", 28}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\270\366\210\0\0\0"..., 152}], msg_controllen=0, msg_flags=0}, 0) = 256 11771 0.000053 setsockopt(34, 0x114 /* SOL_?? */, 3, "7\245\0p\0\0\0\0\0\0\0\0\0\0\0\0", 16) = -1 EINVAL (Invalid argument) 11771 0.000028 recvmsg(34, 0x7fff78c93bb0, MSG_PEEK) = -1 EAGAIN (Resource temporarily unavailable) 11771 0.000083 times({tms_utime=210862, tms_stime=79333, tms_cutime=0, tms_cstime=0}) = 2144885203 11771 0.000037 times({tms_utime=210862, tms_stime=79333, tms_cutime=0, tms_cstime=0}) = 2144885203
这个估计懂C语言,知道recvmsg,sendmsg,setsockopt等操作的程序猿可以解开这个迷,O(∩_∩)O哈哈~
下面从网上找到些recvmsg与sendmsg的介绍,诚然,recvmsg和sendmsg功能更为强大,当然用起来也更为复杂。
我已经看得快睡着了,晚安,杭州……
#include “sys/socket.h”
ssize_t recvmsg(int sockfd, struct msghdr * msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr * msg, int flags);
成功时候返回读写字节数,出错时候返回-1.
这2个函数只用于套接口,不能用于普通的I/O读写,参数sockfd则是指明要读写的套接口。
flags用于传入控制信息,一般包括以下几个
MSG_DONTROUTE send可用
MSG_DONWAIT send与recv都可用
MSG_PEEK recv可用
MSG_WAITALL recv可用
MSG_OOB send可用
MSG_EOR send recv可用
返回信息都记录在struct msghdr * msg中。
struct msghdr {
void * msg_name;//协议地址和套接口信息,在非连接的UDP中,发送者要指定对方地址端口,接受方用于的到数据来源,如果不需要的话可以设置为NULL(在TCP或者连接的UDP中,一般设置为NULL)。
socklen_t msg_namelen;//上面的长度
struct lovec * msg_lov;
ssize_t msg_lovlen;//和readv和writev一样
void * msg_control;
socklen_t msg_controllen;
int msg_flags; //用于返回之前flags的控制信息
}
样例1,在TCP中使用 sendmsg与recvmsg
服务器
……
#define MAXSIZE 100
int main(int argc, char ** argu) {
…….
struct msghdr msg;//初始化struct msghdr
msg.msg_name = NULL; //在tcp中,可以设置为NULL
struct iovec io;//初始化返回数据
io.iov_base = buf; //只用了一个缓冲区
io.iov_len = MAXSIZE; //定义返回数据长度
msg.msg_iov = &io;
msg.msg_iovlen = 1;//只用了一个缓冲区,所以长度为1
……………….
ssize_t recv_size = recvmsg(connfd, &msg, 0);
char * temp = msg.msg_iov[0].iov_base;//获取得到的数据
temp[recv_size] = ‘\0’;//为数据末尾添加结束符
printf(“get message:%s”, temp);
……………………..
}
客户端
………………
#define MAXSIZE 100
int main(int argc, char ** argv) {
……………..
struct msghdr msg;//初始化发送信息
msg.msg_name = NULL;
struct iovec io;
io.iov_base = send_buff;
io.iov_len = sizeof(send_buff);
msg.msg_iov = &io;
msg.msg_iovlen = 1;
if(argc != 2) {
printf(“please input port”);
exit(1);
}
…………
ssize_t size = sendmsg(sockfd, &msg, 0);
close(sockfd);
exit(0);
}