oracle数据块如何定位到ASM中?在exadata定位block的思路是什么?

联系:QQ(5163721)

标题:oracle数据块如何定位到ASM中?在exadata定位block的思路是什么?

作者:Lunar©版权所有[文章允许转载,但必须以链接方式注明源地址,否则追究法律责任.]

前几天有个朋友提出一个“老问题”,数据库上的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> 

1


可以看到数据是吻合的。至此,上面将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);
}

此条目发表在 ASM, 内部机制 分类目录,贴了 , , 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注