InnoDB Plugin Fast Index Creation建索引期间不支持对表的读写

原文链接:http://blog.inkernel.info/archives/15.html


之前认为MySQL可以进行在线增加索引,增加索引时会调用handler::add_index接口,但近期调试时发现,在调用add_index之前,MySQL会关闭所有的handler。因此怀疑可能在add_index期间表是不允许被操作的。InnoDB Plugin的文档说建secondary index时表是可读的,但是实际调试过程中并非如此。

MySQL在线加索引是会关闭所有handler,从show processlist命令可以看到waitint for table。之前看到innodb buildin版本在线加辅助索引时还能对表进行操作是因为buildin版本在线建辅助索引还是进行了表拷贝,其他操作是在旧表上做的。Innodb_plugin的文档上应该不是说add_index时表一定可读(any writes to the table are blocked, but the data in the table may be read)。在manage key期间表也是不可以读的,而manage key比修改mysql的数据字典要慢一些。如果只在修改mysql数据字典时再加X锁应该也是可以的,但上层不是这样子做的。严格意义上来说,这不是真正的在线建索引。虽然在线建索引最终改表元数据还是要加互斥锁,但是这个时间应该是越短越好。至于维护索引一致性,可以通过重做日志实现。

MySQL上层代码分析

MySQL对表的Alter操作分为以下几种:

  • ALTER_TABLE_METADATA_ONLY, No copy needed
  • ALTER_TABLE_DATA_CHANGED, Data changes, copy needed
  • ALTER_TABLE_INDEX_CHANGED, Index changes, copy might be needed

就是说如果need_copy_table变量是ALTER_TABLE_METADATA_ONLY,则不需要拷贝表,只需要修改mysql的数据字典;如果是ALTER_TABLE_DATA_CHANGED,则需要拷贝表;如果是ALTER_TABLE_INDEX_CHANGED,则可能拷贝也可能不拷贝。

分析几段MySQL上层的代码(均来自mysql_alter_table函数),以说明下 InnoDB Plugin建secondary index时的流程:

sql_table.cc:6842

/* 比较新旧表定义,确定是否需要拷贝到新表,need_copy_table_res变量在返回时被修改为ALTER_TABLE_INDEX_CHANGED */
    if (compare_tables(table, alter_info,
                       create_info, order_num,
                       &need_copy_table_res,
                       &key_info_buffer,
                       &index_drop_buffer, &index_drop_count,
                       &index_add_buffer, &index_add_count,
                       &candidate_key_count))
      goto err;  
      if (need_copy_table == ALTER_TABLE_METADATA_ONLY) {
      /* 由于只是增加索引,need_copy_table前面的值为ALTER_TABLE_METADATA_ONLY,在这里被赋值为ALTER_TABLE_INDEX_CHANGED */
         need_copy_table= need_copy_table_res;
      }
      ...

sql_table.cc:7005

/* 下面判断alter_flags中本次在线修改所需要的标志位是否都已经设置,以确定可以进行在线修改 */
      if ((alter_flags & needed_online_flags) == needed_online_flags)
      {
        /* 如果在线增加非唯一性索引时也允许进行写操作,会进入这里 */
        /* All required online flags are present. */
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
        need_lock_for_indexes= FALSE;
      }
      else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
      {
        /* 如果是InnoDB_plugin,在线增加非唯一性索引需要加写锁,会进入这里 */
        /* All required fast flags are present. */
        need_copy_table= ALTER_TABLE_METADATA_ONLY;
      }      
      ..

sql_table.cc:7108

/* 以上代码可以看到need_copy_table被赋值为ALTER_TABLE_METADATA_ONLY,所以不需要拷贝新表,new_table为NULL */
  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
  {
    if (table->s->tmp_table)
    {
        ...
        new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
                            MYSQL_LOCK_IGNORE_FLUSH);
        ...
    }
    else
    {
        ...
        new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
    }
  }

sql_table.cc:7145

if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
  {
    ...
    thd_proc_info(thd, "copy to tmp table");
    error= copy_data_between_tables(table, new_table,
                                    alter_info->create_list, ignore,
                                    order_num, order, &copied, &deleted,
                                    alter_info->keys_onoff,
                                    alter_info->error_if_not_empty);
  }
  else
  {
    /* new_table为NULL,所以进入这里*/
    VOID(pthread_mutex_lock(&LOCK_open));
    wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);//在这里关闭了存储引擎的所有handler
    VOID(pthread_mutex_unlock(&LOCK_open));
    thd_proc_info(thd, "manage keys");
    ...
  }

PS:Innodb_plugin的fast index creation还有一些bug:

  • If any of the indexed columns use the UTF-8 character encoding, MySQL will copy the table. This has been reported as MySQL Bug#33650.
  • ALTER TABLE ADD INDEX may lead to table copying if there's numeric field(s) with non-default display width modificator specified. (Fixed in 5.1.47)

posted on 2011-09-07 23:37  Swimming Fish  阅读(381)  评论(0编辑  收藏  举报

导航