• ADADADADAD

    如何证明INNODB辅助索引叶子结点KEY值相同的按照PRIMARY KEY排序[ mysql数据库 ]

    mysql数据库 时间:2024-12-24 19:10:55

    作者:文/会员上传

    简介:

    接http://blog.itpub.net/7728585/viewspace-2126305/
    RR模式下NEXT-KEY LOCK范围到底有多大

    证明观点:
    1、对辅助索引的页中链表进行分析,如果在辅助索引页内的链表按照首

    以下为本文的正文内容,内容仅供参考!本站为公益性网站,复制本文以及下载DOC文档全部免费。

    接http://blog.itpub.net/7728585/viewspace-2126305/
    RR模式下NEXT-KEY LOCK范围到底有多大

    证明观点:
    1、对辅助索引的页中链表进行分析,如果在辅助索引页内的链表按照首先是KEY排序然后KEY相同的按照PRIMARY KEY排序那么基本就验证了我们的说法
    这个随后可以补上

    这篇文章用到了自制工具./bcview和./mysqlblock
    在网盘
    http://pan.baidu.com/s/1num76RJ
    同时很多理论知识来自
    http://blog.itpub.net/7728585/viewspace-2065464/
    http://blog.itpub.net/7728585/viewspace-2063921/
    等文章


    mysql> create table test (a int,b int,primary key(a),key(b));
    Query OK, 0 rows affected (0.08 sec)


    mysql> insert into test values(1,1);
    Query OK, 1 row affected (0.08 sec)


    使用mysqlblock查看得到
    current read blocks is : 3 --This Block is data blocks( index pages)!
    current read blocks is : 4 --This Block is data blocks( index pages)!
    这里面应该是主键的B+树第一个结点和辅助索引B+树的第一个结点
    page 4 应该就是辅助索引,我们进行验证查看
    从38字节到74字节的是INDEX HEADER,查看他的最后8个字节是index ID
    和INNODB_SYS_INDEXES中进行对比
    current block:00000003--Offset:00066--cnt bytes:08--data is:0000000000000029
    current block:00000004--Offset:00066--cnt bytes:08--data is:000000000000002a
    得到INDEX_ID 0X29 0X2A 就是10进制41 42


    mysql> select * from information_schema.INNODB_SYS_INDEXES where index_id in (41,42);
    +----------+---------+----------+------+----------+---------+-------+-----------------+
    | INDEX_ID | NAME| TABLE_ID | TYPE | N_FIELDS | PAGE_NO | SPACE | MERGE_THRESHOLD |
    +----------+---------+----------+------+----------+---------+-------+-----------------+
    |41 | PRIMARY |40 |3 |1 |3 |24 | 50 |
    |42 | b|40 |0 |1 |4 |24 | 50 |
    +----------+---------+----------+------+----------+---------+-------+-----------------+
    2 rows in set (0.01 sec)
    查看
    mysql> select * from information_schema.INNODB_SYS_TABLES where table_id=40;
    +----------+-----------+------+--------+-------+-------------+------------+---------------+------------+
    | TABLE_ID | NAME | FLAG | N_COLS | SPACE | FILE_FORMAT | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE |
    +----------+-----------+------+--------+-------+-------------+------------+---------------+------------+
    |40 | test/test |33 | 5 |24 | Barracuda| Dynamic| 0 | Single |
    +----------+-----------+------+--------+-------+-------------+------------+---------------+------------+
    1 row in set (0.02 sec)
    可以确定41 42 就是test表的主键和辅助索引,同时确认了current block:00000004就是辅助索引存储数据的
    唯一一个叶子结点也是根结点(因为数据很少),
    那么我们对page 04进行查看


    ./bcview test.ibd 16 94 14|more
    current block:00000003--Offset:00094--cnt bytes:14--data is:010002001b696e66696d756d0002
    current block:00000004--Offset:00094--cnt bytes:14--data is:010002001b696e66696d756d0002


    infimum:
    010002
    001b --偏移量
    696e66696d756d0002 --"infimum\0"


    确定了第一行的偏移量0X1b级27 级第一行的位置为99+27
    ./bcview test.ibd 16 126 30|more
    current block:00000003--Offset:00126--cnt bytes:21--data is:80000001000000000707a70000011b011080000001
    current block:00000004--Offset:00126--cnt bytes:21--data is:800000018000000100000000000000000000000000


    这里聚集索引块和辅助索引记录的东西就一样了,
    聚集索引page03
    80000001000000000707a70000011b011080000001
    其中包含了
    offset ----cluster key fields (N bytes)
    transaction id (6 bytes)
    roll pointer(7 bytes)
    non-key fields (M bytes)
    000000000707a70000011b0110这13个字节就是transaction id 和roll pointer
    我们回到主题讨论辅助索引PAGE 4
    80000001 b列
    80000001 a列
    其实就是1和1,第15位的1应该是MYSQL符号位的表示
    我们找到了,然后我们插入
    insert into test values(5,1);
    mysql> insert into test values(5,1);
    Query OK, 1 row affected (0.03 sec)


    mysql> commit;
    通过偏移量进行找到这个记录
    [root@ora12ctest test]# ./bcview test.ibd 16 124 2|more
    current block:00000004--Offset:00124--cnt bytes:02--data is:000e
    偏移量0X0E就是14
    那么5 1 在辅助索引页PAGE 4中的位置是126+14=140
    ./bcview test.ibd 16 140 30|more
    current block:00000004--Offset:00140--cnt bytes:30--data is:800000018000000500000000000000000000000000000000000000000000


    80000001 b列
    80000005 a列


    这个时候实际上是 (B:1 A:1)-->(B:1 A:5)
    这个时候我们插入


    mysql> insert into test values(3,1);
    Query OK, 1 row affected (0.00 sec)


    回过头我们再次查看第一条记录(B:1,A:1)下一条记录的便宜量
    [root@ora12ctest test]# ./bcview test.ibd 16 124 2|more
    current block:00000004--Offset:00124--cnt bytes:02--data is:001c
    发现已经改变了变为了0X1C为28我们找一下看看是不是我们新插入的(B:1,A:3)
    126+28=154
    ./bcview test.ibd 16 154 20|more
    current block:00000004--Offset:00154--cnt bytes:20--data is:8000000180000003
    没有问题,在查看这条记录的下一条的偏移量
    current block:00000004--Offset:00152--cnt bytes:02--data is:fff2
    我们发现fff2明显是负数 补码存在转换为负数为-14
    则下一条就是
    154-14=140
    查看就是
    [root@ora12ctest test]# ./bcview test.ibd 16 140 10|more
    current block:00000004--Offset:00140--cnt bytes:20--data is:8000000180000005


    我们找到了(B:1,A:5)的这条记录。
    那么原始的(B:1 A:1)-->(B:1 A:5) 由于(B:1 A:3)的加入变为了
    (B:1 A:1)-->(B:1 A:3)-->(B:1 A:5)
    由此证明了我们的观点,就是在B+数的叶子结点如果先按照辅助索引的KEY值
    排序然后按照PRIMARY的值排序。及order by 辅助索引KEY,primary key

    刚才肉眼已经看到了(B:1 A:1)-->(B:1 A:3)-->(B:1 A:5)
    然我们在加入一些无规则的来看看。
    mysql> insert into test values(4,2);
    Query OK, 1 row affected (0.59 sec)
    mysql> insert into test values(10,4);
    Query OK, 1 row affected (0.00 sec)
    mysql> insert into test values(7,4);
    Query OK, 1 row affected (0.00 sec)
    mysql> insert into test values(8,5);
    Query OK, 1 row affected (0.01 sec)
    mysql> insert into test values(11,5);
    Query OK, 1 row affected (0.01 sec)
    mysql> insert into test values(20,6);
    Query OK, 1 row affected (0.01 sec)
    mysql> insert into test values(21,6);
    Query OK, 1 row affected (0.00 sec)
    mysql> insert into test values(19,7);
    Query OK, 1 row affected (0.03 sec)
    mysql> insert into test values(16,7);
    Query OK, 1 row affected (0.01 sec)
    用程序跑一下看看

    [root@ora12ctest test]# ./a.out test.ibd 4
    Index_no is:42
    find first one record!
    B:1,A:1-->
    B:1,A:3-->
    B:1,A:5-->
    B:2,A:4-->
    B:4,A:7-->
    B:4,A:10-->
    B:5,A:8-->
    B:5,A:11-->
    B:6,A:20-->
    B:6,A:21-->
    B:7,A:16-->
    B:7,A:19-->

    显然程序的运行也验证我们的结果。。我们插入的顺序是无序的,但是查看到的是
    辅助索引按照B列排序相同的按照主键A进行排序。

    本程序只能用于这个列子,并且数据量不多,如果造成了B+树索引分裂肯定不行,并且插入的值必须为
    正数不要为负数和0,INNODB中正数的最高为符号为1这个和C/C++不同,暂时没有找到他的计算方式
    所以简单的用A^0X80000000来得到,同时只能是Little_endian 平台 如LINUX

    首先你要使用./bcview和./mysqlblock
    来确定辅助索引的PAGE NO才行,就像上面说的。然后使用是./a.out test.ibd 44就是找到的page号。
    表必须是:
    create table test (a int,b int,primary key(a),key(b));
    单独表空间。因为我任何地方都是写死了的,活的只有读取叶子结点内的链表结构而已。我在5.7 INNODB引擎执行没有问题。
    行格式为:
    mysql> select * from INNODB_SYS_TABLES where name='test/test'
    -> ;
    +----------+-----------+------+--------+-------+-------------+------------+---------------+------------+
    | TABLE_ID | NAME | FLAG | N_COLS | SPACE | FILE_FORMAT | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE |
    +----------+-----------+------+--------+-------+-------------+------------+---------------+------------+
    |40 | test/test |33 | 5 |24 | Barracuda| Dynamic| 0 | Single |
    +----------+-----------+------+--------+-------+-------------+------------+---------------+------------+
    但是应该在5.6 INNODB默认的行格式下也没问题,但是没测试过。

    附上代码很简单:

    点击(此处)折叠或打开

      #include<stdio.h>
      #include<stdlib.h>
      #include<string.h>

      void* reverse(void* p,int length) //Little_endianreverse
      {
      int i;
      char* s= (char*)(p);
      char* temp = (char*)calloc(1,length);
      memcpy(temp,s,length);


      for(i=0;i<length;i++)
      {
      s[i] = temp[length-1-i];
      }
      free(temp);
      temp=NULL; return p;
      }



      int main(int argc,char *argv[])
      {
      FILE* fd;
      long blofset;
      short level;
      long int index_no;
      short initof;
      int B;
      int A;
      int reofset;


      if(argc != 3 )
      {
      printf("USEAGE ERROR useage:./tool dbf pageno\n");
      exit(3);
      }

      if(!(fd = fopen(argv[1],"r")))
      {
      perror("error:");
      exit(1);
      }

      sscanf(argv[2],"%ld",&blofset);
      fseek(fd,blofset*16*1024,SEEK_SET);
      fseek(fd,64,SEEK_CUR);
      fread(&level,2,1,fd);
      fread(&index_no,8,1,fd);
      reverse(&level,2);
      reverse(&index_no,8);
      fseek(fd,23,SEEK_CUR);
      fread(&initof,2,1,fd);
      reverse(&initof,2);
      printf("Index_no is:%ld\n",index_no);
      if(initof != 0 )
      {
      printf("find first one record!\n");
      while(1)
      {
      fseek(fd,initof-2,SEEK_CUR);
      fread(&initof,2,1,fd);
      reverse(&initof,2);
      if(initof == 0)
      {
      break;
      }
      else
      {
      fread(&B,4,1,fd);
      fread(&A,4,1,fd);
      fseek(fd,-8,SEEK_CUR);
      reverse(&B,4);
      reverse(&A,4);
      A=A^0X80000000;
      B=B^0X80000000;
      printf("B:%d,A:%d-->\n",B,A);
      }

      }
      }
      else
      {
      printf("no record find!\n");
      exit(2);
      }
      }

    编译用gcc test.c 得到a.out跑就行了.
    是./a.out test.ibd 4

    这里引入另外一个问题
    MYSQL中表记录返回的顺序问题。详细参考下面:
    http://blog.itpub.net/7728585/viewspace-2126470/
    热门标签: innodbkey按照