如何写MySQL存储引擎

 mysql教程:如何写MySQL存储引擎
[日期:2009-04-02]     来源:  作者:     

在MySQL 5.1中开发一个存储引擎已经是比较方便了。所谓存储引擎实际上是按照MySQL的约定,提供某些接口的实现而已,如MySQL插入一条记录时将调用 write_row方法,通过索引检索时将调用index_read和index_next方法等。MySQL与存储引擎之间的接口主要由两个类/结构约定,分别是在sql/handler.h中定义的handlerton结构和handler类。handlerton结构主要是约定事务操作接口,handler类主要约定表、索引及记录操作接口。对不支持事务的存储引擎只需要创建一个handler的派生类,并重载该引擎需要支持的方法。对于需要支持事务的存储引擎,还需要实现handlerton结构中指定的接口。

  实现了存储引擎本身功能后,还需要按一定格式“声明”一下存储引擎的存在。首先需要定义一个类型为st_mysql_storage_engine的变量,其值为MYSQL_HANDLERTON_INTERFACE_VERSION。如下所示:

struct st_mysql_storage_engine foo_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };


  然后需要用mysql_declare_plugin宏来给出存储引擎的声明,如下所示:

mysql_declare_plugin(foo)          /* foo为存储引擎名称 */
{
 MYSQL_STORAGE_ENGINE_PLUGIN,
 &foo_storage_engine,            /* 这里即为上面定义的st_mysql_storage_engine变量 */
 "FOO",                        /* SHOW ENGINES中显示的存储引擎名称 */
 "Author Here",                  /* 作者名 */
 "Foo storage engine",            /* SHOW ENGINES中显示的存储引擎说明 */
 PLUGIN_LICENSE_GPL,
 init_func,              /* 某函数,将在加载存储引擎时调用 */
 done_func,              /* 某函数,将在卸载存储引擎时调用 */
 0x0001 /* 版本号0.1 */,
 NULL,                     /* status variables */
 NULL,                     /* system variables */
 NULL                     /* config options */
}
mysql_declare_plugin_end;


  照此声明后,在加载存储引擎时MySQL将调用init_func函数,此时会传入一个handlerton变量,存储引擎至少需要设置其中的 state、db_type、create、flags等属性,其中create为一函数指针,将随后被调用。若存储引擎支持事务,则还需要设置 handlerton中事务接口函数指针指向相应的实现函数。

  然后MySQL调用上面设置的create函数,该函数中,存储引擎实现者构建一个该引擎对应的handler派生类实例并返回给 MySQL。至此存储引擎初始化过程完成,存储引擎与MySQL之间的接口也已经确定,此后MySQL即会在相应的时刻调用handlerton结构或 handler类约定的方法。

  MySQL源代码中的storage/example目录给出了很好的示例,该目录实现了一个“最小”的存储引擎EXAMPLE,只是能加载进来玩玩,不能用来存储记录。开发新的存储引擎时,最方便的方法也是拷贝一份example的代码来改.

 安装自己写的我就不知道了。
Mysql>INSTALL PLUGIN  Archive    SONAME ‘ha_archive.so’;
       Mysql>INSTALL PLUGIN  Blackhole  SONAME ‘ha_blackhole.so’;
       Mysql>INSTALL PLUGIN  Falcon     SONAME ‘ha_falcon.so’;
       Mysql>INSTALL PLUGIN  InnoDB    SONAME ‘ha_innodb.so’;

楼主自己写了个存储引擎吗?我看了十多天的源代码,还是一头雾水。楼主有什么好的资料介绍一下,或者介绍一下经验啊!如何写存储引擎?尤其是如何处理blob这种大的二进制类型!

在你编译的时候使用下面的命令:
./configure  --prefix=/usr/local/mysql --with-my_storage_engine-storage-engine
make
make install

然后,以mysql的root身份进入Mysql命令行
mysql>INSTALL PLUGIN  my_storage_engine    SONAME ‘ha_archive.so’;
其中my_storage_engine 是你自己写的存储引擎,要换为实际的名字,并且在编译之前要把这个文件夹放在storage目录下!
Mysql源代码分析系列(2): 源代码结构
Mysql源代码主要包括客户端程序代码,服务器端代码,测试工具和一些库构成,下面我们对比较重要的目录做些介绍。
    * BUILD
这个目录在本系列的上篇文章中我们仔细看过,内含各种平台的编译脚本,这里就不仔细说了。
    * client
这个目录下有如下比较让人眼熟的文件: mysql.cc, mysqlcheck.c, mysqladmin.cc, mysqlshow.c,等等,如果你编译一下就会发现那些眼熟的程序也出现了,比如 mysql。明白了吧,这个目录就是那些客户端程序所在的目录。这个目录的内容也比较少,而且也不是我们阅读的重点。
    * Docs
这个目录包含了文档。
    * storage
这个目录包含了所谓的Mysql存储引擎 (storage engine)。存储引擎是数据库系统的核心,封装了数据库文件的操作,是数据库系统是否强大最重要的因素。Mysql实现了一个抽象接口层,叫做 handler(sql/handler.h),其中定义了接口函数,比如:ha_open, ha_index_end, ha_create等等,存储引擎需要实现这些接口才能被系统使用。这个接口定义超级复杂,有900多行 :-(,不过我们暂时知道它是干什么的就好了,没必要深究每行代码。对于具体每种引擎的特点,我推荐大家去看mysql的在线文档: http://dev.mysql.com/doc/refman/5.1/en/storage-engines.html
应该能看到如下的目录:
* innobase, innodb的目录,当前最流行的存储引擎
* myisam, 最早的Mysql存储引擎,一直到innodb出现以前,使用最广的引擎。
* heap, 基于内存的存储引擎
* federated, 一个比较新的存储引擎
* example, csv,这几个大家可以作为自己写存储引擎时的参考实现,比较容易读懂
    * mysys
包含了对于系统调用的封装,用以方便实现跨平台。大家看看文件名就大概知道是什么情况了。
    * sql
这个目录是另外一个大块头,你应该会看到mysqld.cc,没错,这里就是数据库主程序mysqld所在的地方。大部分的系统流程都发生在这里。你还能看到sql_insert.cc, sql_update.cc, sql_select.cc,等等,分别实现了对应的SQL命令。后面我们还要经常提到这个目录下的文件。
大概有如下及部分:
SQL解析器代码: sql_lex.cc, sql_yacc.yy, sql_yacc.cc, sql_parse.cc等,实现了对SQL语句的解析操作。
"handler"代码: handle.cc, handler.h,定义了存储引擎的接口。
"item"代码:item_func.cc, item_create.cc,定义了SQL解析后的各个部分。
SQL 语句执行代码: sql_update.cc, sql_insert.cc sql_select.cc, sql_show.cc, sql_load.cc,执行SQL对应的语句。当你要看"SELECT ..."的执行的时候,直接到sql_select.cc去看就OK了。
辅助代码: net_serv.cc实现网络操作
还有其他很多代码。
    * vio
封装了virtual IO接口,主要是封装了各种协议的网络操作。
    * plugin
插件的目录,目前有一个全文搜索插件(只能用在myisam存储引擎)。
    * libmysqld
Mysql连接库源代码。
    * 开源函数库目录
和所有的开源项目一样,Mysql也使用了一些开源的库,在其代码库中我们能看到dbug、pstack、strings、 zlib等。
多说无益,主要是对于mysql的代码目录有个概念,要找的时候也有个方向。万一要找某个东西找不到了就只能grep了...

第16章:编写自定义存储引擎

目录

16.1. 前言
16.2. 概述
16.3. 创建存储引擎源文件
16.4. 创建handlerton
16.5. 对处理程序进行实例化处理
16.6. 定义表扩展
16.7. 创建表
16.8. 打开表
16.9. 实施基本的表扫描功能

    16.9.1. 实施store_lock()函数
    16.9.2. 实施external_lock()函数
    16.9.3. 实施rnd_init()函数
    16.9.4. 实施info()函数
    16.9.5. 实施extra()函数
    16.9.6. 实施rnd_next()函数

16.10. 关闭表
16.11. 为存储引擎添加对INSERT的支持
16.12. 为存储引擎添加对UPDATE的支持
16.13. 为存储引擎添加对DELETE的支持
16.14. API引用

    16.14.1. bas_ext
    16.14.2. close
    16.14.3. create
    16.14.4. delete_row
    16.14.5. delete_table
    16.14.6. external_lock
    16.14.7. extra
    16.14.8. info
    16.14.9. open
    16.14.10. rnd_init
    16.14.11. rnd_next
    16.14.12. store_lock
    16.14.13. update_row
    16.14.14. write_row

16.1. 前言

对于MySQL 5.1,MySQL AB公司引入了插件式存储引擎体系结构,这样,就能创建新的存储引擎,并将它们添加到正在运行的MySQL服务器上,而不必重新编译服务器本身。

该体系结构简化了新存储引擎的开发和部署。

本章的意图是作为指南,用于帮助你为新的插件式存储引擎体系结构开发存储引擎。
关于MySQL插件式存储引擎体系结构的更多信息,请参见第14章:插件式存储引擎体系结构。
16.2. 概述
MySQL服务器采用了模块化风格。

图16.1:MySQL体系结构
MySQL architecture
存储引擎负责管理数据存储,以及MySQL的索引管理。通过定义的API,MySQL服务器能够与存储引擎进行通信。

每个存储引擎均是1个继承类,每个类实例作为处理程序而被引用。

针对需要与特殊表一起工作的每个线程,处理程序是在1个处理程序的基础上实例化的。例如,如果3个连接全都在相同的表上工作,需要创建3个处理程序实例。

一旦创建了处理程序实例,MySQL服务器将向处理程序发送命令,以便执行数据存储和检索任务,如打开表、操纵行和管理索引等。

能够以累进方式创建定制存储引擎:开发人员能够以只读存储引擎启动,随后添加对INSERT、UPDATE和DELETE操作的支持,甚至能够增加对索引功能、事务和其他高级操作的支持。
16.3. 创建存储引擎源文件

实施新存储引擎的最简单方法是,通过拷贝和更改EXAMPLE存储引擎开始。在MySQL 5.1源码树的sql/examples/目录下可找到文件ha_example.cc和ha_example.h。关于如何获得5.1源码树的说明,请参见2.8.3节,“从开发源码树安装”。

复制文件时,将名称从ha_example.cc和ha_example.h更改为与存储引擎相适应的名称,如ha_foo.cc和ha_foo.h。

拷贝并重命名了这些文件后,必须更换所有的EXAMPLE示例,以及具有存储引擎名称的示例。如果你熟悉sed,也能自动完成这些步骤:

sed s/EXAMPLE/FOO/g ha_example.h | sed s/example/foo/g ha_foo.h

sed s/EXAMPLE/FOO/g ha_example.cc | sed s/example/foo/g ha_foo.cc

16.4. 创建handlerton

handlerton(“单个处理程序”的简称)定义了存储引擎,并包含指向函数的函数指针,它以整体方式作用在引擎上,而函数工作在单独的处理程序实例中。在这类函数的一些示例中,包含用于处理注释和回滚的事务函数。

下面给出了一个来自EXAMPLE存储引擎的示例:

handlerton example_hton= {

  "EXAMPLE",

  SHOW_OPTION_YES,

  "Example storage engine",

  DB_TYPE_EXAMPLE_DB,

  NULL,    /* Initialize */

  0,       /* slot */

  0,       /* savepoint size. */

  NULL,    /* close_connection */

  NULL,    /* savepoint */

  NULL,    /* rollback to savepoint */

  NULL,    /* release savepoint */

  NULL,    /* commit */

  NULL,    /* rollback */

  NULL,    /* prepare */

  NULL,    /* recover */

  NULL,    /* commit_by_xid */

  NULL,    /* rollback_by_xid */

  NULL,    /* create_cursor_read_view */

  NULL,    /* set_cursor_read_view */

  NULL,    /* close_cursor_read_view */

  example_create_handler,    /* Create a new handler */

  NULL,    /* Drop a database */

  NULL,    /* Panic call */

  NULL,    /* Release temporary latches */

  NULL,    /* Update Statistics */

  NULL,    /* Start Consistent Snapshot */

  NULL,    /* Flush logs */

  NULL,    /* Show status */

  NULL,    /* Replication Report Sent Binlog */

  HTON_CAN_RECREATE

};

下面给出了来自handler.h的handlerton定义:

typedef struct

  {

    const char *name;

    SHOW_COMP_OPTION state;

    const char *comment;

    enum db_type db_type;

    bool (*init)();

    uint slot;

    uint savepoint_offset;

    int  (*close_connection)(THD *thd);

    int  (*savepoint_set)(THD *thd, void *sv);

    int  (*savepoint_rollback)(THD *thd, void *sv);

    int  (*savepoint_release)(THD *thd, void *sv);

    int  (*commit)(THD *thd, bool all);

    int  (*rollback)(THD *thd, bool all);

    int  (*prepare)(THD *thd, bool all);

    int  (*recover)(XID *xid_list, uint len);

    int  (*commit_by_xid)(XID *xid);

    int  (*rollback_by_xid)(XID *xid);

    void *(*create_cursor_read_view)();

    void (*set_cursor_read_view)(void *);

    void (*close_cursor_read_view)(void *);

    handler *(*create)(TABLE *table);

    void (*drop_database)(char* path);

    int (*panic)(enum ha_panic_function flag);

    int (*release_temporary_latches)(THD *thd);

    int (*update_statistics)();

    int (*start_consistent_snapshot)(THD *thd);

    bool (*flush_logs)();

    bool (*show_status)(THD *thd, stat_print_fn *print, enum ha_stat_type stat);

    int (*repl_report_sent_binlog)(THD *thd, char *log_file_name, my_off_t end_offset);

    uint32 flags;                                

  } handlerton;  

共有30个handlerton元素,但只有少量元素是强制性的(明确地讲是前4个元素和第21个元素)。

1.    存储引擎的名称。这是创建表时将使用的名称(CREATE TABLE ... ENGINE = FOO;)。

2.    确定使用SHOW STORAGE ENGINES命令时是否列出存储引擎。

3.    存储引擎注释,对使用SHOW STORAGE ENGINES命令时显示的存储引擎的描述。

4.    在MySQL服务器内唯一识别存储引擎的整数。内置存储引擎使用的常数定义在handler.h文件中。作为创建常数的可选方法,可使用大于25的整数。

5.    指向存储引擎初始化程序的指针。仅当启动服务器时才调用该函数,以便在实例化处理程序之前,存储引擎类能执行必要的内务操作。

6.    插槽。保存每连接的信息时,每个存储引擎在thd中有自己的内存区域(实际上为指针)。它是作为thd->ha_data[foo_hton.slot]访问的。插槽编号在调用foo_init()后由MySQL初始化。

7.    保存点偏移。为了保存每个savepoint数据,为存储引擎提供了请求的大小(典型情况下为0)。

必须以静态方式初始化savepoint偏移,使其具有所有的内存大小,以便保存每个savepoint的信息。在foo_init之后,它被更改为savepoint存储区域的偏移,存储引擎不需要使用它。

8.    由事务性存储引擎使用,清理其存储段内分配的内存,和/或回滚任何未完成的事务。

9.    由事务性存储引擎选择性使用,创建savepoint(保存点),并将其保存到提供的内存中。

10.指向处理程序rollback_to_savepoint()函数的函数指针。它用于在事务期间返回savepoint。仅对支持保存点的存储引擎才会填充它。

11.指向处理程序release_savepoint()函数的函数指针。它用于在事务期间释放保存点的资源。仅对支持保存点的存储引擎才会填充它。

12.指向处理程序commit()函数的函数指针。它用于提交事务。仅对支持事务的存储引擎才会填充它。

13.指向处理程序rollback()函数的函数指针。它用于回滚交易。仅对支持事务的存储引擎才会填充它。

14.XA事务性存储引擎所需。为提交操作准备事务。将XID与事务关联起来。

15.XA事务性存储引擎所需。恢复由XID标识的事务。

16.XA事务性存储引擎所需。提交由XID标识的事务。

17.XA事务性存储引擎所需。回滚由XID标识的事务。

18.与服务器端光标一起使用,尚未实施。

19.与服务器端光标一起使用,尚未实施。

20.与服务器端光标一起使用,尚未实施。

21.MANDATORY:构造并返回处理程序实例。

22.撤销方案时,如果存储引擎需要执行特殊步骤时使用(如在使用表空间的存储引擎中使用)。

23.清理在服务器关闭和崩溃时调用的函数。

24.InnoDB特殊函数。

25.在启动SHOW STATUS时调用InnoDB特殊函数。

26.调用InnoDB特殊函数以开始连续读取。

27.调用它,指明应将日志刷新为可靠的存储。

28.在存储引擎上提供可被人员读取的状态信息。

29.InnoDB特殊函数用于复制。

30.Handlerton标志,通常与ALTER TABLE相关。可能的值定义于sql/handler.h文件中,并在此列出;

31.       #define HTON_NO_FLAGS                 0

32.       #define HTON_CLOSE_CURSORS_AT_COMMIT (1 << 0)

33.       #define HTON_ALTER_NOT_SUPPORTED     (1 << 1)

34.       #define HTON_CAN_RECREATE            (1 << 2)

35.       #define HTON_FLUSH_AFTER_RENAME      (1 << 3)

36.       #define HTON_NOT_USER_SELECTABLE     (1 << 4)

HTON_ALTER_NOT_SUPPORTED由FEDERATED存储引擎使用,用以指明存储引擎不接受AFTER TABLE语句。

HTON_FLUSH_AFTER_RENAME指明,重命名表后 ,必须调用FLUSH LOGS。

HTON_NOT_USER_SELECTABLE指明存储引擎不能由用户选择,而是用作系统存储引擎,如用于二进制日志的伪存储引擎。
16.5. 对处理程序进行实例化处理

调用存储引擎的第1个方法是调用新的处理程序实例。

在存储引擎源文件中定义handlerton之前,必须定义用于函数实例化的函数题头。下面给出了1个来自CSV引擎的示例:

static handler* tina_create_handler(TABLE *table);

正如你所见到的那样,函数接受指向处理程序准备管理的表的指针,并返回处理程序对象。

定义了函数题头后,用第21个handlerton元素中的函数指针命名函数,指明函数负责生成新的处理程序实例。

下面给出了MyISAM存储引擎的实例化函数示例:

static handler *myisam_create_handler(TABLE *table)

  {

    return new ha_myisam(table);

  }

该调用随后与存储引擎的构造程序一起工作。下面给出了来自FEDERATED存储引擎的1个示例:

ha_federated::ha_federated(TABLE *table_arg)

  :handler(&federated_hton, table_arg),

  mysql(0), stored_result(0), scan_flag(0),

  ref_length(sizeof(MYSQL_ROW_OFFSET)), current_position(0)

  {}

下面给出了来自EXAMPLE存储引擎的另一个示例:

ha_example::ha_example(TABLE *table_arg)

  :handler(&example_hton, table_arg)

  {}  

FEDERATED示例中的附加元素是处理程序的额外初始化要素。所要求的最低实施是EXAMPLE示例中显示的handler()初始化。
16.6. 定义表扩展

就给定的表、数据和索引,要求存储引擎为MySQL服务器提供存储引擎所使用的扩展列表。

扩展应采用以Null终结的字符串数组形式。下面给出了CSV引擎使用的数组:

static const char *ha_tina_exts[] = {

  ".CSV",

  NullS

};

调用bas_ext()函数时返回该数组。

const char **ha_tina::bas_ext() const

{

  return ha_tina_exts;

}

通过提供扩展信息,你还能忽略DROP TABLE功能的实施,这是因为,通过关闭表并用你指定的扩展删除所有文件,MySQL服务器能实现该功能。
16.7. 创建表

一旦实例化了处理程序,所需的第1个操作很可能是创建表。

你的存储引擎必须实现create()虚拟函数:

virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;

该函数应创建所有必须的文件,然后关闭表。MySQL服务器将调用随后需打开的表。

*name参数是表的名称。*form参数是st_table结构,该结构定义了表并与MySQL服务器已创建的tablename.frm文件的内容匹配。在大多数情况下,存储引擎不需要更改tablename.frm文件,也没有支持该操作的预置功能。

*info参数是包含CREATE TABLE语句用于创建表所需信息的结构。该结构定义于handler.h文件中,并为了便于参考列于下面:

typedef struct st_ha_create_information

  {

    CHARSET_INFO *table_charset, *default_table_charset;

    LEX_STRING connect_string;

    const char *comment,*password;

    const char *data_file_name, *index_file_name;

    const char *alias;

    ulonglong max_rows,min_rows;

    ulonglong auto_increment_value;

    ulong table_options;

    ulong avg_row_length;

    ulong raid_chunksize;

    ulong used_fields;

    SQL_LIST merge_list;

    enum db_type db_type;

    enum row_type row_type;

    uint null_bits;                       /* NULL bits at start of record */

    uint options;                                               /* OR of HA_CREATE_ options */

    uint raid_type,raid_chunks;

    uint merge_insert_method;

    uint extra_size;                      /* length of extra data segment */

    bool table_existed;                                     /* 1 in create if table existed */

    bool frm_only;                        /* 1 if no ha_create_table() */

    bool varchar;                         /* 1 if table has a VARCHAR */

  } HA_CREATE_INFO;

基本的存储引擎能忽略*form和*info的内容,这是因为,真正所需的是创建存储引擎所使用的数据文件,以及对数据文件的可能初始化操作(假定存储文件是基于文件的)。

下面给出了来自CSV存储引擎的实施示例:

int ha_tina::create(const char *name, TABLE *table_arg,

  HA_CREATE_INFO *create_info)

  {

    char name_buff[FN_REFLEN];

    File create_file;

    DBUG_ENTER("ha_tina::create");

 

    if ((create_file= my_create(fn_format(name_buff, name, "", ".CSV",

          MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,

          O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)

    DBUG_RETURN(-1);

 

    my_close(create_file,MYF(0));

 

    DBUG_RETURN(0);

  }

在前面的例子中,CSV引擎未引用*table_arg或*create_info参数,而是简单地创建了所需的数据文件,关闭它们,并返回。

my_create和my_close函数是定义于src/include/my_sys.h文件中的助手函数。
16.8. 打开表

在表上执行任何读或写操作之前,MySQL服务器将调用open()方法打开表数据和索引文件(如果存在的话)。

int open(const char *name, int mode, int test_if_locked);

第1个参数是要打开的表的名称。第2个参数确定了要打开的文件或准备执行的操作。它们的值定义于handler.h中,并为了方便起见列在下面:

#define HA_OPEN_KEYFILE                 1

#define HA_OPEN_RNDFILE                 2

#define HA_GET_INDEX               4

#define HA_GET_INFO                  8     /* do a ha_info() after open */

#define HA_READ_ONLY               16    /* File opened as readonly */

#define HA_TRY_READ_ONLY         32    /* Try readonly if can't open with read and write */

#define HA_WAIT_IF_LOCKED        64      /* Wait if locked on open */

#define HA_ABORT_IF_LOCKED     128       /* skip if locked on open.*/

#define HA_BLOCK_LOCK              256   /* unlock when reading some records */

#define HA_OPEN_TEMPORARY        512

最后一个选项规定了是否要在打开表之前检查表上的锁定。

在典型情况下,存储引擎需要实施某种形式的共享访问控制,以防止在多线程环境下的文件损坏。关于如何实施文件锁定的示例,请参见sql/examples/ha_tina.cc的get_share()和free_share()方法。
16.9. 实施基本的表扫描功能

16.9.1. 实施store_lock()函数
16.9.2. 实施external_lock()函数
16.9.3. 实施rnd_init()函数
16.9.4. 实施info()函数
16.9.5. 实施extra()函数
16.9.6. 实施rnd_next()函数

最基本的存储引擎能实现只读表扫描功能。这类引擎可用于支持SQL日志查询、以及在MySQL之外填充的其他数据文件。

本节介绍的方法实施提供了创建更高级存储引擎的基础。

下面给出了在CSV引擎的9行表扫描过程中进行的方法调用:

ha_tina::store_lock
ha_tina::external_lock
ha_tina::info
ha_tina::rnd_init
ha_tina::extra - ENUM HA_EXTRA_CACHE   Cache record in HA_rrnd()
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::rnd_next
ha_tina::extra - ENUM HA_EXTRA_NO_CACHE   End cacheing of records (def)
ha_tina::external_lock
ha_tina::extra - ENUM HA_EXTRA_RESET   Reset database to after open

16.9.1. 实施store_lock()函数

在执行任何读取或写操作之前,调用store_lock()函数。

将锁定添加到表锁定处理程序之前(请参见thr_lock.c),mysqld将用请求的锁调用存储锁定。目前,存储锁定能将写锁定更改为读锁定(或其他锁定),忽略锁定(如果不打算使用MySQL锁定的话),或为很多表添加锁定(就像使用MERGE处理程序时作的那样)。

例如,Berkeley DB能将所有的WRITE锁定更改为TL_WRITE_ALLOW_WRITE(表示我们正在执行WRITES,但我们仍允许其他人员进行操作)。

释放锁定时,也将调用store_lock(),在这种情况下,通常不需做任何事。

在某些特殊情况下,MySQL可能会发送对TL_IGNORE的请求。这意味着我们正在请求与上次相同的锁定,这也应被忽略(当我们打开了表的某一部分时,如果其他人执行了表刷新操作,就会出现该情况,此时,mysqld将关闭并再次打开表,然后获取与上次相同的锁定)。我们打算在将来删除该特性。

可能的锁定类型定义于includes/thr_lock.h中,并列在下面:

enum thr_lock_type

{

         TL_IGNORE=-1,

                    TL_UNLOCK,                             /* UNLOCK ANY LOCK */

                    TL_READ,                                 /* Read lock */

                    TL_READ_WITH_SHARED_LOCKS,  

         TL_READ_HIGH_PRIORITY,      /* High prior. than TL_WRITE. Allow concurrent insert */

         TL_READ_NO_INSERT,                /* READ, Don't allow concurrent insert */

         TL_WRITE_ALLOW_WRITE,                   /*   Write lock, but allow other threads to read / write. */

         TL_WRITE_ALLOW_READ,        /*       Write lock, but allow other threads to read / write. */

         TL_WRITE_CONCURRENT_INSERT, /* WRITE lock used by concurrent insert. */

         TL_WRITE_DELAYED,                 /* Write used by INSERT DELAYED.  Allows READ locks */

         TL_WRITE_LOW_PRIORITY,            /* WRITE lock that has lower priority than TL_READ */

         TL_WRITE,                         /* Normal WRITE lock */

         TL_WRITE_ONLY               /* Abort new lock request with an error */

};  

实际的锁定处理因锁定实施的不同而不同,你可以选择某些请求的锁定类型或不选择任何锁定类型,并根据情况恰当地代入你自己的方法。下面给出了1个CSV存储引擎实施示例:

THR_LOCK_DATA **ha_tina::store_lock(THD *thd,

                                     THR_LOCK_DATA **to,

                                     enum thr_lock_type lock_type)

 {

   if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)

     lock.type=lock_type;

   *to++= &lock;

   return to;

 }  

16.9.2. 实施external_lock()函数

external_lock()函数是在事务开始时调用的,或发出LOCK TABLES语句时调用的,用于事务性存储引擎。

在sql/ha_innodb.cc和sql/ha_berkeley.cc文件中,可找到使用external_lock()的示例,但大多数存储引擎简单地返回0,就像EXAMPLE存储引擎那样:

int ha_example::external_lock(THD *thd, int lock_type)

 {

   DBUG_ENTER("ha_example::external_lock");

   DBUG_RETURN(0);

 }

16.9.3. 实施rnd_init()函数

在任何表扫描之前调用的函数是rnd_init()函数。函数rnd_init()用于为表扫描作准备,将计数器和指针复位为表的开始状态。

下述示例来自CSV存储引擎:

  int ha_tina::rnd_init(bool scan)
    {
      DBUG_ENTER("ha_tina::rnd_init");
 
      current_position= next_position= 0;
      records= 0;
      chain_ptr= chain;
 
      DBUG_RETURN(0);
    }  

16.9.4. 实施info()函数
执行表扫描操作之前,将调用info()函数,以便为优化程序提供额外信息。

优化程序所需的信息不是通过返回值给定的,你需填充存储引擎类的特定属性,当info()调用返回后,优化程序将读取存储引擎类。

除了供优化程序使用外,在调用info()函数期间,很多值集合还将用于SHOW TABLE STATUS语句。

在sql/handler.h中列出了完整的公共属性,下面给出了一些常见的属性:

ulonglong data_file_length;           /* Length off data file */

ulonglong max_data_file_length;       /* Length off data file */

ulonglong index_file_length;

ulonglong max_index_file_length;

ulonglong delete_length;              /* Free bytes */

ulonglong auto_increment_value;

ha_rows records;                      /* Records in table */

ha_rows deleted;                      /* Deleted records */

ulong raid_chunksize;

ulong mean_rec_length;         /* physical reclength */

time_t create_time;                   /* When table was created */

time_t check_time;

time_t update_time;  

对于表扫描,最重要的属性是“records”,它指明了表中的记录数。当存储引擎指明表中有0或1行时,或有2行以上时,在这两种情况下,优化程序的执行方式不同。因此,当你在执行表扫描之前不清楚表中有多少行时,应返回大于等于2的值,这很重要(例如,数据是在外部填充的)。
16.9.5. 实施extra()函数

执行某些操作之前,应调用extra()函数,以便为存储引擎就如何执行特定操作予以提示。

额外调用中的提示实施不是强制性的,大多数存储引擎均返回0:

int ha_tina::extra(enum ha_extra_function operation)
 {
   DBUG_ENTER("ha_tina::extra");
   DBUG_RETURN(0);
 }

16.9.6. 实施rnd_next()函数

完成表的初始化操作后,MySQL服务器将调用处理程序的rnd_next()函数,每两个扫描行调用1次,直至满足了服务器的搜索条件或到达文件结尾为止,在后一种情况下,处理程序将返回HA_ERR_END_OF_FILE。

rnd_next()函数有一个名为*buf的单字节数组参数。对于*buf参数,必须按内部MySQL格式用表行的内容填充它。

服务器采用了三种数据格式:固定长度行,可变长度行,以及具有BLOB指针的可变长度行。对于每种格式,各列将按照它们由CREATE TABLE语句定义的顺序显示(表定义保存在.frm文件中,优化程序和处理程序均能从相同的源,即TABLE结构,访问表的元数据)。

每种格式以每列1比特的"NULL bitmap"开始。对于含6个列的表,其bitmap为1字节,对于含9~16列的表,其bitmap为2字节,依此类推。要想指明特定的值是NULL,应将该列NULL位设置为1。

当NULL bitmap逐个进入列后,每列将具有MySQL手册的“MySQL数据类型”一节中指定的大小。在服务器中,列的数据类型定义在sql/field.cc文件中。对于固定长度行格式,列将简单地逐个放置。对于可变长度行,VARCHAR列将被编码为1字节长,后跟字符串。对于具有BLOB列的可变长度行,每个blob由两部分表示:首先是表示BLOB实际大小的整数,然后是指向内存中BLOB的指针。

在任何表处理程序中从rnd_next()开始,可找到行转换(或“包装”)的示例。例如,在ha_tina.cc中,find_current_row()内的代码给出了使用TABLE结构(由表指向的)和字符串对象(命名缓冲)包装字符数据(来自CSV文件)的方法。将行写回磁盘需要反向转换,从内部格式解包。

下述示例来自CSV存储引擎:

int ha_tina::rnd_next(byte *buf)

 {

   DBUG_ENTER("ha_tina::rnd_next");

 

   statistic_increment(table->in_use->status_var.ha_read_rnd_next_count, &LOCK_status);

 

   current_position= next_position;

   if (!share->mapped_file)

     DBUG_RETURN(HA_ERR_END_OF_FILE);

   if (HA_ERR_END_OF_FILE == find_current_row(buf) )

     DBUG_RETURN(HA_ERR_END_OF_FILE);

 

   records++;

   DBUG_RETURN(0);

 }  

对于从内部行格式到CSV行格式的转换,它是在find_current_row()函数中执行的。

int ha_tina::find_current_row(byte *buf)

 {

   byte *mapped_ptr= (byte *)share->mapped_file + current_position;

   byte *end_ptr;

   DBUG_ENTER("ha_tina::find_current_row");

 

   /* EOF should be counted as new line */

   if ((end_ptr=  find_eoln(share->mapped_file, current_position,

                            share->file_stat.st_size)) == 0)

     DBUG_RETURN(HA_ERR_END_OF_FILE);

 

   for (Field **field=table->field ; *field ; field++)

   {

     buffer.length(0);

     mapped_ptr++; // Increment past the first quote

     for(;mapped_ptr != end_ptr; mapped_ptr++)

     {

       // Need to convert line feeds!

       if (*mapped_ptr == '"' &&

           (((mapped_ptr[1] == ',') && (mapped_ptr[2] == '"')) ||

            (mapped_ptr == end_ptr -1 )))

       {

         mapped_ptr += 2; // Move past the , and the "

         break;

       }

       if (*mapped_ptr == '//' && mapped_ptr != (end_ptr - 1))

       {

         mapped_ptr++;

         if (*mapped_ptr == 'r')

           buffer.append('/r');

         else if (*mapped_ptr == 'n' )

           buffer.append('/n');

         else if ((*mapped_ptr == '//') || (*mapped_ptr == '"'))

           buffer.append(*mapped_ptr);

         else  /* This could only happed with an externally created file */

         {

           buffer.append('//');

           buffer.append(*mapped_ptr);

         }

       }

       else

         buffer.append(*mapped_ptr);

     }

     (*field)->store(buffer.ptr(), buffer.length(), system_charset_info);

   }

   next_position= (end_ptr - share->mapped_file)+1;

   /* Maybe use /N for null? */

   memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */

 

   DBUG_RETURN(0);

 }  

16.10. 关闭表
当MySQL服务器完成表操作时,它将调用close()方法关闭文件指针并释放任何其他资源。

对于使用共享访问方法的存储引擎(如CSV引擎和其他示例引擎中显示的方法),必须将它们自己从共享结构中删除:

int ha_tina::close(void)

 {

   DBUG_ENTER("ha_tina::close");

   DBUG_RETURN(free_share(share));

 }  

对于使用其自己共享管理系统的存储引擎,应使用任何所需的方法,在它们的处理程序中,从已打开表的共享区删除处理程序实例。
16.11. 为存储引擎添加对INSERT的支持
一旦在你的存储引擎中有了读支持,下一个需要实施的特性是对INSERT语句的支持。有了INSERT支持,存储引擎就能处理WORM(写一次,读多次)应用程序,如用于以后分析的日志和归档应用等。

所有的INSERT操作均是通过write_row()函数予以处理的:

int ha_foo::write_row(byte *buf)  

*buf参数包含将要插入的行,采用内部MySQL格式。基本的存储引擎将简单地前进到数据文件末尾,并直接在末尾处添加缓冲的内容,这样就能使行读取变得简单,这是因为,你可以读取行并将其直接传递到rnd_next()函数的缓冲参数中。

写入行的进程与读取行的进程相反:从MySQL内部行格式获取数据,并将其写入数据文件。下述示例来自CSV存储引擎:

int ha_tina::write_row(byte * buf)

 {

   int size;

   DBUG_ENTER("ha_tina::write_row");

 

   statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status);

 

   if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)

     table->timestamp_field->set_time();

 

   size= encode_quote(buf);

 

   if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))

     DBUG_RETURN(-1);

 

   if (get_mmap(share, 0) > 0)

     DBUG_RETURN(-1);

   DBUG_RETURN(0);

 }

前述示例中的两条注释包括,更新关于写入操作的表统计,以及在写入行之前设置时间戳。
16.12. 为存储引擎添加对UPDATE的支持

通过执行表扫描操作,在找到与UPDATE语句的WHERE子句匹配的行后,MySQL服务器将执行UPDATE语句,然后调用update_row()函数:

int ha_foo::update_row(const byte *old_data, byte *new_data)

*old_data参数包含更新前位于行中的数据,而*new_data参数包含行的新内容(采用MySQL内部行格式)。

更新的执行取决于行格式和存储实施方式。某些存储引擎将替换恰当位置的数据,而其他实施方案则会删除已有的行,并在数据文件末尾添加新行。

非事务性存储引擎通常会忽略*old_data参数的内容,仅处理*new_data缓冲。事务性存储引擎可能需要比较缓冲,以确定在上次回滚中出现了什么变化。

如果正在更新的表中包含时间戳列,对时间戳的更新将由update_row()调用管理。下述示例来自CSV引擎:

int ha_tina::update_row(const byte * old_data, byte * new_data)

 {

   int size;

   DBUG_ENTER("ha_tina::update_row");

 

   statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,

                     &LOCK_status);

 

   if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)

     table->timestamp_field->set_time();

 

   size= encode_quote(new_data);

 

   if (chain_append())

     DBUG_RETURN(-1);

 

   if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))

     DBUG_RETURN(-1);

   DBUG_RETURN(0);

 }

请注意上例中的时间戳设置。
16.13. 为存储引擎添加对DELETE的支持

MySQL服务器采用了与INSERT语句相同的方法来执行DELETE语句:服务器使用rnd_next()函数跳到要删除的行,然后调用delete_row()函数删除行。

int ha_foo::delete_row(const byte *buf)

*buf参数包含要删除行的内容。对于大多数存储引擎,该参数可被忽略,但事务性存储引擎可能需要保存删除的数据,以供回滚操作使用。

下述示例来自CSV存储引擎:

int ha_tina::delete_row(const byte * buf)

 {

   DBUG_ENTER("ha_tina::delete_row");

   statistic_increment(table->in_use->status_var.ha_delete_count,

                       &LOCK_status);

 

   if (chain_append())

     DBUG_RETURN(-1);

 

   --records;

 

   DBUG_RETURN(0);

 }

前述示例的步骤是更新delete_count统计,并记录计数。
16.14. API引用

16.14.1. bas_ext
16.14.2. close
16.14.3. create
16.14.4. delete_row
16.14.5. delete_table
16.14.6. external_lock
16.14.7. extra
16.14.8. info
16.14.9. open
16.14.10. rnd_init
16.14.11. rnd_next
16.14.12. store_lock
16.14.13. update_row
16.14.14. write_row

16.14.1. bas_ext
目的

定义存储引擎所使用的文件扩展。
概要
virtual const char ** bas_ext (    );    
     ;
描述

这是bas_ext方法。调用它,可为MySQL服务器提供存储引擎所使用的文件扩展列表。该列表将返回以Null终结的字符串数组。

通过提供扩展列表,在很多情况下,存储引擎能省略delete_table()函数,这是因为MySQL服务器将关闭所有对表的引用,并使用指定的扩展删除所有文件。
参数

该函数无参数。
返回值

    *

      返回值是存储引擎扩展的以Null终结的字符串数组。下面给出了CSV引擎的示例:

      static const char *ha_tina_exts[] =
       {
         ".CSV",
         NullS
       };

用法

static const char *ha_tina_exts[] =
 {
   ".CSV",
   NullS
 };
        
const char **ha_tina::bas_ext() const
 {
   return ha_tina_exts;
 }  

默认实施

static const char *ha_example_exts[] = {
   NullS
 };
 
const char **ha_example::bas_ext() const
 {
   return ha_example_exts;
 }

16.14.2. close
目的

关闭打开的表。
概要
virtual int close (    void);    
     void ;
描述

这是close方法。

关闭表。这是释放任何已分配资源的恰当时机。

从sql_base.cc、sql_select.cc和table.cc调用它。在sql_select.cc中,它仅用于关闭临时表,或在将临时表转换为myisam表的过程中关闭表。关于sql_base.cc,请查看close_data_tables()。
参数

    *

      void

返回值

无返回值。
用法

取自CSV引擎的示例:

int ha_example::close(void)
{
  DBUG_ENTER("ha_example::close");
  DBUG_RETURN(free_share(share));
}

16.14.3. create
目的

创建新表。
概要
virtual int create (    name,     
     form,     
     info);    
const char *     name ;
TABLE *     form ;
HA_CREATE_INFO *     info ;
描述

这是create方法。

调用create()以创建表。变量名称为表的名称。调用create()时,不需要打开表。此外,由于已创建了.frm文件,不推荐调整create_info。

由ha_create_table()从handle.cc中调用。
参数

    *

      name
    *

      form
    *

      info

返回值

无返回值。
用法

CSV搜索引擎示例:

int ha_tina::create(const char *name, TABLE *table_arg,
                    HA_CREATE_INFO *create_info)
{
  char name_buff[FN_REFLEN];
  File create_file;
  DBUG_ENTER("ha_tina::create");

  if ((create_file= my_create(fn_format(name_buff, name, "", ".CSV",
                                        MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
                              O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
    DBUG_RETURN(-1);

  my_close(create_file,MYF(0));

  DBUG_RETURN(0);
}          

16.14.4. delete_row
目的

删除行。
概要
virtual int delete_row (    buf);    
const byte *     buf ;
描述

这是delete_row方法。

Buf包含删除行的副本。调用了当前行后,服务器将立刻调用它(通过前一个rnd_next()或索引调用)。如果存在指向上一行的指针,或能够访问 主键,删除操作将更为容易。请记住,服务器不保证连续删除。可以使用ORDER BY子句。

在sql_acl.cc和sql_udf.cc中调用,以管理内部的表信息。在sql_delete.cc、sql_insert.cc和sql_select.cc中调用。在sql_select中,它用于删除副本,而在插入操作中,它用于REPLACE调用。
参数

    *

      buf

返回值

无返回值。
用法

          
        

默认实施

{ return  HA_ERR_WRONG_COMMAND; }

16.14.5. delete_table
目的

用来自bas_ext()的扩展删除所有文件。
概要
virtual int delete_table (    name);    
const char *     name ;
描述

这是delete_table方法。

用于删除表。调用delete_table()时,所有已打开的对该表的引用均将被关闭(并释放全局共享的引用)。变量名称为表名。此时,需要删除任何已创建的文件。

如果未实施它,将从handler.cc调用默认的delete_table(),并用bas_ext()返回的文件扩展删除所有文件。假定处理程序返回的扩展比文件实际使用的多。

由delete_table和ha_create_table()从handler.cc调用。如果为存储引擎指定了table_flag HA_DROP_BEFORE_CREATE,仅在创建过程中使用。
参数

    *

      name: 表的基本名称

返回值

·         如果成功地从base_ext删除了至少1个文件而且未出现除ENOENT之外的错误,返回0。

·         #: Error
用法
大多数存储引擎均会忽略该函数的实施。
16.14.6. external_lock
目的

为事务处理表锁定。
概要
virtual int external_lock (    thd,     
     lock_type);    
THD *     thd ;
int     lock_type ;
描述

这是external_lock方法。

在lock.cc中“用于mysql的锁定函数”一节,给出了关于该议题的额外注释,值的一读。

在表上创建锁定。如果实施了能处理事务的存储引擎,请查看ha_berkely.cc,以了解如何执行该操作的方法。否则,应考虑在此调用flock()。

由lock_external()和unlock_external()从lock.cc中调用。也能由copy_data_between_tables()从sql_table.cc中调用。
参数

    *

      thd
    *

      lock_type

返回值

无返回值。
默认实施

{ return 0; }

16.14.7. extra
目的

将提示从服务器传递给存储引擎。
概要
virtual int extra (    operation);    
enum ha_extra_function     operation ;
描述

这是extra方法。

无论何时,当服务器希望将提示发送到存储引擎时,将调用extra()。MyISAM引擎实现了大多数提示。ha_innodb.cc给出了最详尽的提示列表。
参数

    *

      operation

返回值

无返回值。
用法

          
        

默认实施

默认情况下,存储引擎倾向于不实施任何提示。

{ return 0; }

16.14.8. info
目的

提示存储引擎通报统计信息。
概要
virtual void info (    uint);    
     uint ;
描述

这是info方法。

::info()用于将信息返回给优化程序。目前,该表处理程序未实施实际需要的大多数字段。SHOW也能利用该数据。注意,或许你打算在你的代码中包含下述内容“if (records > 2) records = 2”。原因在于,服务器仅优化具有一条记录的情形。如果在表扫描过程中,你不清楚记录的数目,最好将记录数设为2,以便能够返回尽可能多的所需记录。除了记录外,你或许还希望设置其他变量,包括:删除的记录,data_file_length,index_file_length,delete_length,check_time。更多信息,请参见handler.h中的公共变量。

在下述文件中调用:filesort.cc ha_heap.cc item_sum.cc opt_sum.cc sql_delete.cc sql_delete.cc sql_derived.cc sql_select.cc sql_select.cc sql_select.cc sql_select.cc sql_select.cc sql_show.cc sql_show.cc sql_show.cc sql_show.cc sql_table.cc sql_union.cc sql_update.cc
参数

    *

      uint

返回值

无返回值。
用法

该示例取自CSV存储引擎:

void ha_tina::info(uint flag)
{
  DBUG_ENTER("ha_tina::info");
  /* This is a lie, but you don't want the optimizer to see zero or 1 */
  if (records < 2)
    records= 2;
  DBUG_VOID_RETURN;
}          

16.14.9. open
目的

打开表。
概要
virtual int open (    name,     
     mode,     
     test_if_locked);    
const char *     name ;
int     mode ;
uint     test_if_locked ;
描述

这是open方法。

用于打开表。名称是文件的名称。在需要打开表时打开它。例如,当请求在表上执行选择操作时(对于每一请求,表未打开并被关闭,对其进行高速缓冲处理)。

由handler::ha_open()从handler.cc中调用。通过调用ha_open(),然后调用处理程序相关的open(),服务器打开所有表。

对于处理程序对象,将作为初始化的一部分并在将其用于正常查询之前打开它(并非总在元数据变化之前)。如果打开了对象,在删除之前还将关闭它。

这是open方法。调用open以打开数据库表。

第1个参数是要打开的表的名称。第2个参数决定了要打开的文件或将要执行的操作。这类值定义于handler.h中,为了方便起见在此列出:

        #define HA_OPEN_KEYFILE                 1

        #define HA_OPEN_RNDFILE                 2

        #define HA_GET_INDEX              4

        #define HA_GET_INFO                 8     /* do a ha_info() after open */

        #define HA_READ_ONLY              16    /* File opened as readonly */

        #define HA_TRY_READ_ONLY        32    /* Try readonly if can't open with read and write */

        #define HA_WAIT_IF_LOCKED       64      /* Wait if locked on open */

        #define HA_ABORT_IF_LOCKED    128       /* skip if locked on open.*/

        #define HA_BLOCK_LOCK             256   /* unlock when reading some records */

        #define HA_OPEN_TEMPORARY       512

      

最后的选项规定了在打开表之前是否应检查表上的锁定。

典型情况下,存储引擎需要实现某种形式的共享访问控制,以防止多线程环境下的文件损坏。关于如何实现文件锁定的示例,请参见sql/examples/ha_tina.cc的get_share()和free_share()方法。
参数

    *

      name
    *

      mode
    *

      test_if_locked

返回值

无返回值。
用法

该示例取自CSV存储引擎:

        int ha_tina::open(const char *name, int mode, uint test_if_locked)
        {
        DBUG_ENTER("ha_tina::open");
        
        if (!(share= get_share(name, table)))
        DBUG_RETURN(1);
        thr_lock_data_init(&share->lock,&lock,NULL);
        ref_length=sizeof(off_t);
        
        DBUG_RETURN(0);
        }          
      

16.14.10. rnd_init
目的

为表扫描功能初始化处理程序。
概要
virtual int rnd_init (    scan);    
bool     scan ;
描述

这是rnd_init方法。

当系统希望存储引擎执行表扫描时,将调用rnd_init()。

与index_init()不同,rnd_init()可以调用两次,两次调用之间不使用rnd_end()(仅当scan=1时才有意义)。随后,第2次调用应准备好新的表扫描。例如,如果rnd_init分配了光标,第2次调用应将光标定位于表的开始部分,不需要撤销分配并再次分配。

从下述文件调用:filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, 和sql_update.cc。
参数

    *

      scan

返回值

无返回值。
用法

该示例取自CSV存储引擎:

int ha_tina::rnd_init(bool scan)
{
  DBUG_ENTER("ha_tina::rnd_init");

  current_position= next_position= 0;
  records= 0;
  chain_ptr= chain;
  DBUG_RETURN(0);
}          

16.14.11. rnd_next
目的

从表中读取下一行,并将其返回服务器。
概要
virtual int rnd_next (    buf);    
byte *     buf ;
描述

这是rnd_next方法。

对于表扫描的每一行调用它。耗尽记录时,应返回HA_ERR_END_OF_FILE。用行信息填充buff。表的字段结构是以服务器能理解的方式将数据保存到buf中的键。

从下述文件调用:filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc, 和sql_update.cc。
参数

    *

      buf

返回值

无返回值。
用法

下述示例取自ARCHIVE存储引擎:

int ha_archive::rnd_next(byte *buf)
{
  int rc;
  DBUG_ENTER("ha_archive::rnd_next");

  if (share->crashed)
      DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
 
  if (!scan_rows)
    DBUG_RETURN(HA_ERR_END_OF_FILE);
  scan_rows--;

  statistic_increment(table->in_use->status_var.ha_read_rnd_next_count,
              &LOCK_status);
    current_position= gztell(archive);
  rc= get_row(archive, buf);
 
  if (rc != HA_ERR_END_OF_FILE)
    records++;

  DBUG_RETURN(rc);
}          

16.14.12. store_lock
目的

创建和释放表锁定。
概要
virtual THR_LOCK_DATA ** store_lock (    thd,     
     to,     
     lock_type);    
THD *     thd ;
THR_LOCK_DATA **     to ;
enum thr_lock_type     lock_type ;
描述

这是store_lock方法。

下面介绍了关于handler::store_lock()的概念:

该语句决定了在表上需要何种锁定。对于updates/deletes/inserts,我们得到WRITE锁定;对于SELECT...,我们得到读锁定。

将锁定添加到表锁定处理程序之前(请参见thr_lock.c),mysqld将用请求的锁定调用存储锁定。目前,存储锁定能将写锁定更改为读锁定(或某些其他锁定),忽略锁定(如果不打算使用MySQL表锁定),或为很多表添加锁定(就像使用MERGE处理程序时那样)。

例如,Berkeley DB能够将所有的WRITE锁定更改为TL_WRITE_ALLOW_WRITE(表明正在执行WRITES操作,但我们仍允许其他人执行操作)。

释放锁定时,也将调用store_lock()。在这种情况下,通常不需要作任何事。

在某些特殊情况下,MySQL可能会发送对TL_IGNORE的请求。这意味着我们正在请求与上次相同的锁定,这也应被忽略(当我们打开了表的某一部分时,如果其他人执行了表刷新操作,就会出现该情况,此时,mysqld将关闭并再次打开表,然后获取与上次相同的锁定)。我们打算在将来删除该特性。

由get_lock_data()从lock.cc中调用。
参数

    *

      thd
    *

      to
    *

      lock_type

返回值

无返回值。
用法

下述示例取自ARCHIVE存储引擎:

/*
  Below is an example of how to setup row level locking.
*/
THR_LOCK_DATA **ha_archive::store_lock(THD *thd,
                                       THR_LOCK_DATA **to,
                                       enum thr_lock_type lock_type)
{
  if (lock_type == TL_WRITE_DELAYED)
    delayed_insert= TRUE;
  else
    delayed_insert= FALSE;

  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
  {
    /*
      Here is where we get into the guts of a row level lock.
      If TL_UNLOCK is set
      If we are not doing a LOCK TABLE or DISCARD/IMPORT
      TABLESPACE, then allow multiple writers
    */

    if ((lock_type >= TL_WRITE_CONCURRENT_INSERT &&
         lock_type <= TL_WRITE) && !thd->in_lock_tables
        && !thd->tablespace_op)
      lock_type = TL_WRITE_ALLOW_WRITE;

    /*
      In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
      MySQL would use the lock TL_READ_NO_INSERT on t2, and that
      would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
      to t2. Convert the lock to a normal read lock to allow
      concurrent inserts to t2.
    */

    if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables)
      lock_type = TL_READ;

    lock.type=lock_type;
  }

  *to++= &lock;
 
  return to;
}          

16.14.13. update_row
目的

更新已有行的内容。
概要
virtual int update_row (    old_data,     
     new_data);    
const byte *     old_data ;
byte *     new_data ;
描述

这是update_row方法。

old_data将保存前一行的记录,而new_data将保存最新的数据。

如果使用了ORDER BY子句,服务器能够根据排序执行更新操作。不保证连续排序。

目前,new_data不会拥有已更新的auto_increament记录,或已更新的时间戳字段。你可以通过下述方式(例如)完成该操作:if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) table->timestamp_field->set_time(); if (table->next_number_field && record == table->record[0]) update_auto_increment();

从sql_select.cc, sql_acl.cc, sql_update.cc和sql_insert.cc调用。
参数

    *

      old_data
    *

      new_data

返回值

无返回值。
用法

          
        

默认实施

           { return  HA_ERR_WRONG_COMMAND; }
        

16.14.14. write_row
目的

为表添加新行。
概要
virtual int write_row (    buf);    
byte *     buf ;
描述

这是write_row方法。

write_row()用于插入行。目前,如果出现大量加载,不会给出任何extra()提示。buf是数据的字节数组,大小为table->s->reclength。

可以使用字段信息从本地字节数组类型提取数据。例如:

for (Field **field=table->field ; *field ; field++) { ... }

BLOB必须特殊处理:

    

for (ptr= table->s->blob_field, end= ptr + table->s->blob_fields ; ptr != end ; ptr++)

  {

        char *data_ptr;

        uint32 size= ((Field_blob*)table->field[*ptr])->get_length();

        ((Field_blob*)table->field[*ptr])->get_ptr(&data_ptr);

        ...

  }

关于以字符串形式提取所有数据的示例,请参见ha_tina.cc。在ha_berkeley.cc中,对于ha_berkeley自己的本地存储类型,给出了一个通过“包装功能”完整保存它的例子。

请参见update_row()关于auto_increments和时间戳的注释。该情形也适用于write_row()。

从item_sum.cc、item_sum.cc、sql_acl.cc、sql_insert.cc、sql_insert.cc、sql_select.cc、sql_table.cc、sql_udf.cc、以及sql_update.cc调用。
参数

    *

      数据的buf字节数组

返回值

无返回值。
用法

          
        

默认实施

           { return  HA_ERR_WRONG_COMMAND; }
        

这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访问dev.mysql.com。 原始参考手册为英文版,与英文版参考手册相比,本翻译版可能不是最新的。
Top / Previous / Next / Up / Table of Contents
自动翻页分割符。  当前页(第    2    页)。  跳到页面顶部  上翻  下翻  跳到页面底部

    * Documentation Library
          o Table of Contents

Search manual:

Additional languages

    * Chinese
    * English
    * German
    * Japanese

MySQL 5.1参考手册 :: 17. MySQL簇
? 16. 编写自定义存储引擎
18. 分区 ?
Section Navigation      [Toggle]

    * 前言
    * 1. 一般信息
    * 2. 安装MySQL
    * 3. 教程
    * 4. MySQL程序概述
    * 5. 数据库管理
    * 6. 中的复制
    * 7. 优化
    * 8. 客户端和实用工具程序
    * 9.语言结构
    * 10. 字符集支持
    * 11. 列类型
    * 12. 函数和操作符
    * 13. SQL语句语法
    * 14. 插件式存储引擎体系结构
    * 15. 存储引擎和表类型
    * 16. 编写自定义存储引擎
    * 17. MySQL簇
    * 18. 分区
    * 19. 中的空间扩展
    * 20. 存储程序和函数
    * 21. 触发程序
    * 22. 视图
    * 23. INFORMATION_SCHEMA信息数据库
    * 24. 精度数学
    * 25. API和库
    * 26. 连接器
    * 27. 扩展MySQL
    * A. 问题和常见错误
    * B. 错误代码和消息
    * C. 感谢
    * D. MySQL变更史
    * E. 移植到其他系统
    * F. 环境变量
    * G. 正则表达式
    * H. Limits in MySQL
    * I. 特性限制
    * J. GNU通用公共许可
    * K. MySQL FLOSS许可例外
    * 索引

第17章:MySQL簇

目录

17.1. MySQL簇概述
17.2. MySQL簇的基本概念
17.3. 多计算机的简单基础知识

    17.3.1. 硬件、软件和联网
    17.3.2. 安装
    17.3.3. 配置
    17.3.4. 首次启动
    17.3.5. 加载示例数据并执行查询
    17.3.6. 安全关闭和重启

17.4. MySQL簇的配置

    17.4.1. 从源码创建MySQL簇
    17.4.2. 安装软件
    17.4.3. MySQL簇的快速测试设置
    17.4.4. 配置文件

17.5. MySQL簇中的进程管理

    17.5.1. 用于MySQL簇的MySQL服务器进程使用
    17.5.2. ndbd,存储引擎节点进程
    17.5.3. ndb_mgmd,“管理服务器”进程
    17.5.4. ndb_mgm,“管理客户端”进程
    17.5.5. 用于MySQL簇进程的命令选项

17.6. MySQL簇的管理

    17.6.1. MySQL簇的启动阶段
    17.6.2. “管理客户端”中的命令
    17.6.3. MySQL簇中生成的事件报告
    17.6.4. 单用户模式
    17.6.5. MySQL簇的联机备份

17.7. 使用与MySQL簇的高速互连

    17.7.1. 配置MySQL簇以使用SCI套接字
    17.7.2. 理解簇互连的影响

17.8. MySQL簇的已知限制
17.9. MySQL簇发展的重要历程

    17.9.1. MySQL 5.0中的MySQL簇变化
    17.9.2. 关于MySQL簇的MySQL 5.1发展历程

17.10. MySQL簇常见问题解答
17.11. MySQL簇术语表

MySQL簇是MySQL适合于分布式计算环境的高实用、高冗余版本。它采用了NDB簇存储引擎,允许在1个簇中运行多个MySQL服务器。在MySQL 5.1二进制版本中、以及与最新的Linux版本兼容的RPM中提供了该存储引擎。(注意,要想获得MySQL簇的功能,必须安装mysql-server和mysql-max RPM)。

目前能够运行MySQL簇的操作系统有Linux、Mac OS X和Solaris。(一些用户通报成功地在FreeBSD上运行了MySQL簇,但MySQL AB公司尚未正式支持该特性)。我们正在努力,以便使MySQL簇能运行在MySQL支持的所有操作系统上,包括Windows,而且当支持新的平台时,将更新该页面。

本章介绍了正在进行的工作,其内容将随着MySQL簇的不断演化而变化。关于MySQL簇的更多信息,请访问MySQL AB公司的网站http://www.mysql.com/products/cluster/。

或许你也希望使用MySQL AB提供的两种额外资源:

·         MySQL簇邮件列表。

·         MySQL用户论坛上的簇主题区。

关于簇的一些常见问题,请参见17.10节,“MySQL簇常见问题解答”。如果你是MySQL簇的新手,请阅读我方开发人员区的文章如何为两个服务器设置MySQL簇,这会有所帮助。
17.1. MySQL簇概述

MySQL簇是一种技术,该技术允许在无共享的系统中部署“内存中”数据库的簇。通过无共享体系结构,系统能够使用廉价的硬件,而且对软硬件无特殊要求。此外,由于每个组件有自己的内存和磁盘,不存在单点故障。

MySQL簇将标准的MySQL服务器与名为NDB的“内存中”簇式存储引擎集成了起来。在我们的文档中,术语NDB指的是与存储引擎相关的设置部分,而术语“MySQL簇”指的是MySQL和NDB存储引擎的组合。

MySQL簇由一组计算机构成,每台计算机上均运行着多种进程,包括MySQL服务器,NDB簇的数据节点,管理服务器,以及(可能)专门的数据访问程序。关于簇中这些组件的关系,请参见下图:

MySQL Cluster Components

所有这些程序一起构成了MySQL簇。将数据保存到NDB簇存储引擎中时,表将保存在数据节点内。能够从簇中所有其他MySQL服务器直接访问这些表。因此,在将数据保存在簇内的工资表应用程序中,如果某一应用程序更新了1位雇员的工资,所有查询该数据的其他MySQL服务器能立刻发现这种变化。

对于MySQL簇,保存在数据节点内的数据可被映射,簇能够处理单独数据节点的故障,除了少数事务将因事务状态丢失而被放弃外,不会产生其他影响。由于事务性应用程序能够处理事务失败事宜,因而它不是问题源。

通过将MySQL簇引入开放源码世界,MySQL为所有需要它的人员提供了具有高可用性、高性能和可缩放性的簇数据管理。
17.2. MySQL簇的基本概念

NDB是一种“内存中”存储引擎,它具有可用性高和数据一致性好的特点。

能够使用多种故障切换和负载平衡选项配置NDB存储引擎,但以簇层面上的存储引擎开始最简单。MySQL簇的NDB存储引擎包含完整的数据集,仅取决于簇本身内的其他数据。

下面,我们介绍了设置由NDB存储引擎和一些MySQL服务器构成的MySQL簇的设置方法。

目前,MySQL簇的簇部分可独立于MySQL服务器进行配置。在MySQL簇中,簇的每个部分被视为1个节点。

注释:在很多情况下,术语“节点”用于指计算机,但在讨论MySQL簇时,它表示的是进程。在单台计算机上可以有任意数目的节点,为此,我们采用术语簇主机。

有三类簇节点,在最低的MySQL簇配置中,至少有三个节点,这三类节点分别是:

·         管理(MGM)节点:这类节点的作用是管理MySQL簇内的其他节点,如提供配置数据、启动并停止节点、运行备份等。由于这类节点负责管理其他节点的配置,应在启动其他节点之前首先启动这类节点。MGM节点是用命令ndb_mgmd启动的。

·         数据节点:这类节点用于保存簇的数据。数据节点的数目与副本的数目相关,是片段的倍数。例如,对于两个副本,每个副本有两个片段,那么就有4个数据节点。没有必要有一个以上的副本。数据节点是用命令ndbd启动的。

·         SQL节点:这是用来访问簇数据的节点。对于MySQL簇,客户端节点是使用NDB簇存储引擎的传统MySQL服务器。典型情况下,SQL节点是使用命令mysqld –ndbcluster启动的,或将ndbcluster添加到my.cnf后使用mysqld启动。

簇配置包括对簇中单独节点的配置,以及设置节点之间的单独通信链路。对于目前设计的MySQL簇,其意图在于,从处理器的能力、内存空间和带宽来讲,存储节点是同质的,此外,为了提供单一的配置点,作为整体,簇的所有配置数据均位于1个配置文件中。

管理服务器(MGM节点)负责管理簇配置文件和簇日志。簇中的每个节点从管理服务器检索配置数据,并请求确定管理服务器所在位置的方式。当数据节点内出现有趣的事件时,节点将关于这类事件的信息传输到管理服务器,然后,将这类信息写入簇日志。

此外,可以有任意数目的簇客户端进程或应用程序。它们分为两种类型:

·         标准MySQL客户端:对于MySQL簇,它们与标准的(非簇类)MySQL没有区别。换句话讲,能够从用PHP、Perl、C、C++、Java、Python、Ruby等编写的现有MySQL应用程序访问MySQL簇。

·         管理客户端:这类客户端与管理服务器相连,并提供了优雅地启动和停止节点、启动和停止消息跟踪(仅对调试版本)、显示节点版本和状态、启动和停止备份等的命令。
17.3. 多计算机的简单基础知识

17.3.1. 硬件、软件和联网
17.3.2. 安装
17.3.3. 配置
17.3.4. 首次启动
17.3.5. 加载示例数据并执行查询
17.3.6. 安全关闭和重启

本节介绍了如何规划、安装、配置和运行MySQL簇的基本知识。与17.4节,“MySQL簇的配置”中给出的示例不同,按照下面介绍的步骤和指南,所得的结果应是有用的MySQL簇,它满足对数据可用性和安全防护的最低要求。

在本节中,我们介绍了下述内容:硬件和软件要求,联网事宜,MySQL簇的安装,配置事宜,簇的启动、停止和重启,加载样本数据库,以及执行查询的方法。

基本假定

本节作了如下假定:

1.    我们将建立具有4个节点的簇,每个节点位于不同的主机上,而且在典型的以太网中具有固定的网络地址,如下所述:

节点
    

IP地址

管理(MGM)节点
    

192.168.0.10

MySQL服务器(SQL)节点
    

192.168.0.20

数据(NDBD)节点"A"
    

192.168.0.30

数据(NDBD)节点"B"
    

192.168.0.40

2.    通过下图可更清楚的表明这点:

   3. MySQL Cluster Multi-Computer Setup

4.    注释:出于简单性(以及可靠性)方面的考虑,在本基本知识介绍中我们仅使用数值IP地址。但是,如果在你的网络中具备DNS解析功能,在配置簇的过程中,可使用主机名代替IP地址。作为可选方式,也能使用/etc/hosts文件,或能提供主机查询的操作系统的等效物(如果可用的话)。

5.    在我们的场景中,每台主机均是基于Intel的桌面PC,PC上运行的是常见的一般性Linux版本,操作系统以标准配置安装在磁盘上,未运行任何不必要的服务。具备标准TCP/IP联网客户端的核心操作系统应足以符合我们的要求。此外,为了简单性,我们还假定所有主机上的文件系统是等同的。如果这些主机上的文件系统不同,就需对这些说明作相应的调整。

6.    在每台机器上安装了标准的100 Mbps或1吉比特以太网卡,为每块网卡安装了恰当的驱动程序,并用标准的以太网联网装置(如交换器等)将4台主机连接起来(所有机器应使用具有相同容量的网卡,也就是说,簇中的所有4台机器应全部使用100M网卡,或全部使用1G网卡)。MySQL簇将工作在100 Mbps网络中,但吉比特以太网能提供更好的性能。

注意,MySQL簇不适合于连通性低于100 Mbps的网络。出于该原因(尤其是),在公共网络如Internet上运行MySQL簇很难成功,也不推荐这样做。

7.    对于样本数据,我们将使用世界数据库,该数据库可从MySQL AB公司的网站上下载。由于该数据库占用的空间相对较小,我们假定每台机器有256 MB RAM,这足以运行操作系统、主机NDB进程、以及存储数据库(对于数据节点)。

尽管在本基本介绍中采用的是Linux操作系统,但对这里给出的说明和步骤来说,仅过简单的修改,也能适用于Solaris或Mac OS X。此外,我们还假定你已掌握了安装和配置具备联网功能的操作系统的基本知识,或能够在需要的时候获得帮助。

下一节,我们更详细地讨论了MySQL簇的硬件、软件和联网要求。(请参见17.3.1节,“硬件、软件和联网”)。
17.3.1. 硬件、软件和联网

MySQL簇的一个强大优点在于,它能运行在普通硬件上,除了需要较大的RAM外在这点上没有特殊要求,这是因为实际的数据存储均是在内存中进行的。(注意,未来这点会改变,我们打算在未来的MySQL簇版本中实现基于磁盘的存储)。显然,多个CPU和更快的CPU能增强性能。对于簇进程来说,对内存的要求相对较少。

簇的软件要求程度适中。主机操作系统不需要任何特殊模块、服务、应用程序、或配置就能支持MySQL簇。对于Mac OS X或Solaris,标准安装就已足够。对于Linux,标准的“即开即用”安装应是所需的全部。MySQL软件要求很简单:MySQL-max 5.1的生产版就是所需的全部,要想获得簇支持,必须使用MySQL的-max版本。无需自己编译MySQL就能使用簇。在本节中,我们假定你使用了与Linux相适应的-max二进制版本。对于Solaris或Mac OS X操作系统,相应的部分可通过MySQL软件下载页面获得,http://dev.mysql.com/downloads/。

对于节点之间的通信,簇支持采用标准拓扑方案的TCP/IP联网,对于每台主机的预期最低要求是1块标准的100 Mbps以太网卡,对于作为整体的簇,还需加上交换器、网络集线器或路由器以提供网络连通性。我们强烈建议,应在其自己的子网内运行MySQL簇,不与非簇机器共享该子网,原因如下:

·         安全性:簇节点之间的通信未采用任何特殊加密或防护。对MySQL簇内传输的唯一保护方法是,在受保护的网络上运行簇。如果打算将MySQL簇用于Web应用,簇应明确地位于防火墙后面,而且不应位于网络的非军事区(DMZ)或其他地方。

·         效率:在专有的或受保护的网络上设置MySQL簇,这样,簇就能独享簇主机之间的带宽。为MySQL簇使用单独的交换器不仅能防止对簇数据的非法访问,而且还能确保簇节点不受网络上其他计算机之间信息传输的干扰。为了增强可靠性,可以使用双交换器和双卡,以防止网络出现单点故障,对于这类通信链路,很多设备驱动均支持故障切换功能。
也能与MySQL簇一起使用高速SCI(规模可扩展的计算机接口),但这不是要求的。关于该协议的更多信息,以及它与MySQL簇的用法,请参见17.7节,“使用与MySQL簇的高速互连”
17.3.2. 安装

对于每台运行存储或SQL节点的MySQL簇主机计算机,必须在其上安装MySQL-max二进制版本。对于管理节点,没有必要安装MySQL服务器二进制版本,但应安装MGM服务器端口监督程序和客户端二进制版本(分别是ndb_mgmd和ndb_mgm)。在本节中,我们介绍了为每种簇节点安装正确的二进制版本所需的步骤。

MySQL AB提供了预编译的二进制文件,它们支持簇,你不需要自己编译这些文件(如果你确实需要定制的二进制文件,请参见2.8.3节,“从开发源码树安装”)。因此,对于每台簇主机,安装进程的第一步是从MySQL下载区下载文件mysql-max-5.1.2-alpha-pc-linux-gnu-i686.tar.gz。我们假定你将该文件放在各机器的/var/tmp目录下。

对于32位和64位Linux平台,均有相应的RPM,RPM安装的-max二进制文件支持NDB簇存储引擎。如果你选择使用它们而不是二进制文件,务必在运行簇节点的所有机器上安装-server和-max软件包(关于使用RPM安装MySQL的更多信息,请参见2.4节,“在Linux下安装MySQL”)。使用RPM完成安装后,仍需对簇进行配置,请参见17.3.3节,“配置”。

注释:完成安装后,不启动任何二进制文件。配置完所有节点后,我们将向你介绍执行这类操作的方法。

存储节点和SQL节点安装

在设计为运行存储节点或SQL节点的三台机器的每一台上,以系统根用户身份执行下述步骤:

1.    检查你的/etc/passwd和/etc/group文件(或使用操作系统提供的用于管理用户和组的工具),查看在系统上是否已存在mysql组和mysql用户,这是因为某些操作系统会将其作为安装进程的一部分予以创建。如果它们不存在,创建新的mysql用户组,然后为该组添加1个mysql用户。

2.           groupadd mysql

3.           useradd -g mysql mysql

4.    进入包含下载文件的目录,解包档案文件,并创建与mysql-max可执行文件的symlink。注意,根据MySQL的版本号,实际的文件名和目录名会有所不同。

5.           cd /var/tmp

6.           tar -xzvf -C /usr/local/bin mysql-max-5.1.2-alpha-pc-linux-gnu-i686.tar.gz

7.           ln -s /usr/local/bin/mysql-max-5.1.2-alpha-pc-linux-gnu-i686 mysql

8.    进入mysql目录,运行所提供的用于创建系统数据库的脚本:

9.           cd mysql

10.       scripts/mysql_install_db --user=mysql

11.为MySQL服务器和数据目录设置必要的权限:

12.       chown -R root .

13.       chown -R mysql data

14.       chgrp -R mysql .

注意,在每台运行数据节点的机器上,数据目录是/usr/local/mysql/data。配置管理节点时将用到这类信息(请参见17.3.3节,“配置”)。

15.将MySQL启动脚本拷贝到恰当的目录下,使之成为可执行的脚本,并设置它以便在启动操作系统时启动:

16.       cp support-files/mysql.server /etc/rc.d/init.d/

17.       chmod +x /etc/rc.d/init.d/mysql.server

18.       chkconfig --add mysql.server

在此,我们使用Red Hat的chkconfig来创建与启动脚本的链接,请在你的操作系统上使用恰当的用于该目的的方式,如Debian上的update-rc.d。

请记住,对于存储节点或SQL节点所在的每台机器,必须分别指向上述步骤。

管理节点安装

对于MGM(管理)节点,不需要安装mysqld可执行文件,仅需安装用于MGM服务器和客户端的二进制文件,这类文件可在下载的-max档案中找到。再次假定你将该文件放在了/var/tmp目录下,引导系统时(也就是说使用sudo, su root或系统的等效命令后,假定具有系统管理员账户的权限),执行下述步骤,在簇管理节点主机上安装ndb_mgmd和ndb_mgm:

1.    即如/var/tmp目录,从档案文件中将ndb_mgm和ndb_mgmd提取到恰当的目录下,如/usr/local/bin:

2.           cd /var/tmp

3.           tar -zxvf mysql-max-5.1.2-alpha-pc-linux-gnu-i686.tar.gz /usr/local/bin '*/bin/ndb_mgm*'

4.    进入解包文件所在的目录,然后使这两个文件成为可执行的:

5.           cd /usr/local/bin

6.           chmod +x ndb_mgm*

在17.3.3节,“配置”中,我们将为示例簇中的所有节点创建和编写配置文件。
17.3.3. 配置

对于我们的4节点、4主机MySQL簇,需要编写4个配置文件,每个节点/主机1个。

·         每个数据节点或SQl节点需要1个my.cnf文件,该文件提供了两类信息:connectstring(连接字符串),用于通知节点到哪里找到MGM节点;以及一行,用于通知该主机(容纳数据节点的机器)上的MySQL服务器运行在NDB模式下。

关于连接字符串的更多信息,请参见17.4.4.2节,“MySQL簇连接字符串”。

·         管理节点需要config.ini文件,该文件通知节点有多少需要维护的副本,需要在每个数据节点上为数据和索引分配多少内存,数据节点的位置,在每个数据节点上保存数据的磁盘位置,以及SQL节点的位置。

配置存储节点和SQL节点

数据节点所需的my.cnf文件相当简单。配置文件应位于/etc目录下,并能用任何文本编辑器进行编辑(如有必要,创建该文件),例如:

vi /etc/my.cnf

对于本示例中的每个数据节点和SQL节点,my.cnf文件类似于:

# Options for mysqld process:

[MYSQLD]                        

ndbcluster                      # run NDB engine

ndb-connectstring=192.168.0.10  # location of MGM node

 

# Options for ndbd process:

[MYSQL_CLUSTER]                 

ndb-connectstring=192.168.0.10  # location of MGM node

输入上述内容后,保存文件并退出文本编辑器。在容纳数据节点“A”、数据节点“B”和SQL节点的机器上分别执行上述操作。

配置管理节点

配置MGM节点的第一步是创建目录,该目录用于存放配置文件,然后创建配置文件本身。例如(以根用户身份运行):

mkdir /var/lib/mysql-cluster

cd /var/lib/mysql-cluster

vi config.ini

在此使用了vi来创建文件,不过,任何文本编辑器均应能胜任。

对于我们的典型设置,config.ini文件应类似于:

# Options affecting ndbd processes on all data nodes:

[NDBD DEFAULT]    

NoOfReplicas=2    # Number of replicas

DataMemory=80M    # How much memory to allocate for data storage

IndexMemory=18M   # How much memory to allocate for index storage

                  # For DataMemory and IndexMemory, we have used the

                  # default values. Since the "world" database takes up

                  # only about 500KB, this should be more than enough for

                  # this example Cluster setup.

 

# TCP/IP options:

[TCP DEFAULT]     

portnumber=2202   # This the default; however, you can use any

                  # port that is free for all the hosts in cluster

                  # Note: It is recommended beginning with MySQL 5.0 that

                  # you do not specify the portnumber at all and simply allow

                  # the default value to be used instead

 

# Management process options:

[NDB_MGMD]                      

hostname=192.168.0.10           # Hostname or IP address of MGM node

datadir=/var/lib/mysql-cluster  # Directory for MGM node logfiles

 

# Options for data node "A":

[NDBD]                          

                                # (one [NDBD] section per data node)

hostname=192.168.0.30           # Hostname or IP address

datadir=/usr/local/mysql/data   # Directory for this data node's datafiles

 

# Options for data node "B":

[NDBD]                          

hostname=192.168.0.40           # Hostname or IP address

datadir=/usr/local/mysql/data   # Directory for this data node's datafiles

 

# SQL node options:

[MYSQLD]                        

hostname=192.168.0.20           # Hostname or IP address

                                # (additional mysqld connections can be

                                # specified for this node for various

                                # purposes such as running ndb_restore)

(注释:"world"数据库可从站点http://dev.mysql.com/doc/下载,它列在“示例”栏目下)。

一旦创建了所有的配置文件并指定了这些最低选项,可启动簇,并验证所有进程均能正常运行。关于这方面的讨论,请参见17.3.4节,“首次启动”。

关于可用MySQL簇配置参数以及其用法的更多信息,请参见17.4.4节,“配置文件”和17.4节,“MySQL簇的配置”。关于与进行备份有关的MySQL簇配置,请参见17.6.5.4节,“簇备份的配置”。

注释:簇管理节点的默认端口是1186,数据节点的默认端口2202。从MySQL 5.0.3开始,该限制已被放宽,簇能够根据空闲的端口自动地为数据节点分配端口。
17.3.4. 首次启动

完成配置后,启动簇并不很困难。必须在数据节点所在的主机上分别启动每个簇节点进程。尽管能够按任何顺序启动节点,但我们建议,应首先启动管理节点,然后启动存储节点,最后启动SQL节点:

1.    在管理主机上,从系统shell发出下述命令以启动MGM节点进程:

2.           shell> ndb_mgmd -f /var/lib/mysql-cluster/config.ini

注意,必须用“-f”或“--config-file”选项,告诉ndb_mgmd到哪里找到配置文件(详情请参见17.5.3节,“ndb_mgmd,“管理服务器”进程”)。

3.    在每台数据节点主机上,对于首次启动,运行下述命令启动NDBD进程:

4.           shell> ndbd --initial

注意,仅应在首次启动ndbd时,或在备份/恢复或配置变化后重启ndbd时使用“--initial”参数,这很重要。原因在于,该参数会使节点删除由早期ndbd实例创建的、用于恢复的任何文件,包括恢复用日志文件。

5.    如果使用RPM在SQL节点所在的簇主机上安装了MySQL,能够(也应当)使用安装在/etc/init.d下的启动脚本在SQL节点上启动MySQL服务器进程。注意,要想运行“-max”服务器二进制文件,除了标准的RPM外,还需要安装-max服务器RPM。

如果一切顺利,并已正确设置了簇,那么簇现在应能运行。通过调用ndb_mgm管理节点客户端,可对其进行测试。其输出应类似于:

shell> ndb_mgm

-- NDB Cluster -- Management Client --

ndb_mgm> SHOW

Connected to Management Server at: localhost:1186

Cluster Configuration

---------------------

[ndbd(NDB)]     2 node(s)

id=2    @192.168.0.30  (Version: 5.1.2-alpha, Nodegroup: 0, Master)

id=3    @192.168.0.40  (Version: 5.1.2-alpha, Nodegroup: 0)

 

[ndb_mgmd(MGM)] 1 node(s)

id=1    @192.168.0.10  (Version: 5.1.2-alpha)

 

[mysqld(SQL)]   1 node(s)

id=4   (Version: 5.1.2-alpha)

具体的输出内容可能会略有不同,这取决于你所使用的MySQL版本。

注释:如果你正在使用较早的MySQL版本,你或许会看到引用为‘[mysqld(API)]’的SQL节点。这是一种早期的用法,现已放弃。

现在,应能在MySQL簇中处理数据库,表和数据。关于这方面的简要讨论,请参见17.3.5节,“加载示例数据并执行查询”。
17.3.5. 加载示例数据并执行查询

与没有使用簇的MySQL相比,在MySQL簇内操作数据的方式没有太大的区别。执行这类操作时应记住两点:

·         表必须用ENGINE=NDB或ENGINE=NDBCLUSTER选项创建,或用ALTER TABLE选项更改,以使用NDB簇存储引擎在簇内复制它们。如果使用mysqldump的输出从已有数据库导入表,可在文本编辑器中打开SQL脚本,并将该选项添加到任何表创建语句,或用这类选项之一替换任何已有的ENGINE(或TYPE)选项。例如,假定在另一个MySQL服务器(不支持MySQL簇)上有样本世界数据库,而且你打算导出城市表的定义:

·                shell> mysqldump --add-drop-table world City > city_table.sql

在所得的city_table.sql文件中,将包含这条表创建语句(以及导入表数据所需的INSERT语句):

DROP TABLE IF EXISTS City;

CREATE TABLE City (

ID int(11) NOT NULL auto_increment,

Name char(35) NOT NULL default '',

CountryCode char(3) NOT NULL default '',

District char(20) NOT NULL default '',

Population int(11) NOT NULL default '0',

PRIMARY KEY  (ID)

) ENGINE=MyISAM;

 

INSERT INTO City VALUES (1,'Kabul','AFG','Kabol',1780000);

INSERT INTO City VALUES (2,'Qandahar','AFG','Qandahar',237500);

INSERT INTO City VALUES (3,'Herat','AFG','Herat',186800);

# (remaining INSERT statements omitted)

需要确认MySQL为该表使用了NDB存储引擎。有两种完成该任务的方法。其中一种方法是,在将表导入簇数据库之前更改其定义,使其类似于(仍使用“城市”作为示例):

DROP TABLE IF EXISTS City;

CREATE TABLE City (

ID int(11) NOT NULL auto_increment,

Name char(35) NOT NULL default '',

CountryCode char(3) NOT NULL default '',

District char(20) NOT NULL default '',

Population int(11) NOT NULL default '0',

PRIMARY KEY  (ID)

) ENGINE=NDBCLUSTER;

 

INSERT INTO City VALUES (1,'Kabul','AFG','Kabol',1780000);

INSERT INTO City VALUES (2,'Qandahar','AFG','Qandahar',237500);

INSERT INTO City VALUES (3,'Herat','AFG','Herat',186800);

# (etc.)

对于将成为簇数据库组成部份的每个表,均需要为其定义执行上述操作。完成该任务的最简单方法是,简单地在world.sql文件上执行“查找-替换”,并用ENGINE=NDBCLUSTER替换所有的TYPE=MyISAM实例。如果你不打算更改该文件,也可使用ALTER TABLE。详情请参见下面的介绍。

假定你已在簇的SQL节点上创建了名为“world”的数据库,随后可使用mysql命令行客户端读取city_table.sql,并按通常方式创建和填充对应的表:

shell> mysql world < city_table.sql

请记住,上述命令必须在运行SQL节点的主机上执行,这点十分重要。对于本例,应在IP地址为192.168.0.20的机器上执行。

要想在SQL节点上创建世界数据库的副本,请将文件保存到/usr/local/mysql/data,然后运行:

shell> cd /usr/local/mysql/data

shell> mysql world < world.sql

当然,SQL脚本必须能被mysql系统用户读取。如果将文件保存到了不同的目录下,请作相应的调整。

注意,在MySQL 5.1中,NDB簇不支持自动发现数据库的功能,这点很重要(请参见17.8节,“MySQL簇的已知限制”)。这意味着,一旦在一个数据节点上创建了世界(world)数据库和它的表,在簇中的每个SQL节点上还需要发出命令CREATE DATABASE world(从MySQL 5.0.2开始,可以使用CREATE SCHEMA world取而代之),后跟FLUSH TABLES。这样,节点就能识别数据库并读取其表定义。

在SQL节点上运行SELECT查询与在MySQL服务器的任何其他实例上运行查询没有区别。要想从命令行运行查询,首先应按照通常方式登录到MySQL监视器:

shell> mysql -u root -p

Enter password:

Welcome to the MySQL monitor.  Commands end with ; or /g.

Your MySQL connection id is 1 to server version: 5.1.2-alpha

 

键入’help;’或’/h’获取帮助。键入’/c’清空缓冲区。

 

mysql>

如果在导入MySQL脚本之前未更改表定义中的ENGINE=子句,应在此时运行下述命令:

mysql> USE world;

mysql> ALTER TABLE City ENGINE=NDBCLUSTER;

mysql> ALTER TABLE Country ENGINE=NDBCLUSTER;

mysql> ALTER TABLE CountryLanguage ENGINE=NDBCLUSTER;

注意,在这里我们简单地使用了MySQL服务器密码为空的默认根用户账户。当然,在生产设置下,安装MySQL服务器时,总应遵守标准的安全方法措施,包括设置牢靠的根用户密码,并为用户创建具有完成任务所需的权限的用户账户。关于这方面的更多信息,请参见5.7节,“MySQL访问权限系统”。

需要关注的是,当簇节点彼此访问时不使用MySQL的权限系统,设置或更改MySQL用户账户(包括根用户账户)不影响节点之间的交互,它们仅对访问SQL节点的应用程序有效。

能够以通常的方式选择数据库,并对表执行SELECT查询,就像退出MySQL监视器一样:

mysql> USE world;

mysql> SELECT Name, Population FROM City ORDER BY Population DESC LIMIT 5;

+-----------+------------+

| 名称      | 人口 |

+-----------+------------+

| 孟买      |   10500000 |

| 汉城      |    9981619 |

| 圣保罗    |    9968485 |

| 上海      |    9696300 |

| 雅加达    |    9604900 |

+-----------+------------+

5 rows in set (0.34 sec)

 

mysql> /q

Bye

 

shell>

使用MySQL的应用程序能够使用标准的API。重要的是应记住,你的应用程序必须访问SQL节点,而不是MGM或存储节点。在下面的简单示例中,介绍了使用PHP 5的mysqli扩展(运行在位于网络中其他位置的Web服务器上)执行相同查询的方法:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

  "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

  <meta http-equiv="Content-Type"

        content="text/html; charset=iso-8859-1">

  <title>SIMPLE mysqli SELECT</title>

</head>

<body>

<?php

  # connect to SQL node:

  $link = new mysqli('192.168.0.20', 'root', '', 'world');

  # parameters for mysqli constructor are:

  #   host, user, password, database

 

  if( mysqli_connect_errno() )

    die("Connect failed: " . mysqli_connect_error());

 

  $query = "SELECT Name, Population

            FROM City

            ORDER BY Population DESC

            LIMIT 5";

 

  # if no errors...

  if( $result = $link->query($query) )

  {

?>

<table border="1" width="40%" cellpadding="4" cellspacing ="1">

  <tbody>

  <tr>

    <th width="10%">City</th>

    <th>Population</th>

  </tr>

<?

    # then display the results...

    while($row = $result->fetch_object())

      printf(<tr>/n  <td align=/"center/">%s</td><td>%d</td>/n</tr>/n",

              $row->Name, $row->Population);

?>

  </tbody

</table>

<?

  # ...and verify the number of rows that were retrieved

    printf("<p>Affected rows: %d</p>/n", $link->affected_rows);

  }

  else

    # otherwise, tell us what went wrong

    echo mysqli_error();

 

  # free the result set and the mysqli connection object

  $result->close();

  $link->close();

?>

</body>

</html>

我们假定运行在Web服务器上的进程能够访问SQL节点的IP地址。

采用类似的风格,可以使用MySQL C API、Perl-DBI、Python-mysql、或MySQL AB自己的连接器来执行数据定义和操控任务,就像正常使用MySQL那样。

·         另外还请记住,每个NDB表必须有一个主键。如果在创建表时用户未定义主键,NDB簇存储引擎将自动生成隐含的主键。(注释:该隐含 键也将占用空间,就像任何其他的表索引一样。由于没有足够的内存来容纳这些自动创建的键,出现问题并不罕见)。
17.3.6. 安全关闭和重启

要想关闭簇,可在MGM节点所在的机器上,在Shell中简单地输入下述命令:

shell> ndb_mgm -e shutdown

该命令将恰当地中止ndb_mgm、ndb_mgmd以及任何ndbd进程。使用mysqladmin shutdown或其他方法,可中止SQL节点。注意,这里的“-e”选项用于将命令从shell传递到ndb_mgm客户端。请参见4.3.1节,“在命令行上使用选项”。

要想重启簇,可简单地运行下述命令:

·         在管理主机上(本设置中为192.168.0.10):

·                shell> ndb_mgmd -f /var/lib/mysql-cluster/config.ini

·         在每台数据节点主机上(192.168.0.30和192.168.0.40):

·                shell> ndbd

请记住,正常重启NDBD节点时,不要用“--initial”选项调用该命令。

·         在SQL主机上(192.168.0.20):

·                shell> mysqld &

关于创建簇备份的更多信息,请参见17.6.5.2节,“使用管理服务器创建备份”。

要想从备份中恢复簇,需要使用ndb_restore命令。请参见17.6.5.3节,“如何恢复簇备份”。

关于配置MySQL簇的更多信息,请参见17.4节,“MySQL簇的配置”。
17.4. MySQL簇的配置

17.4.1. 从源码创建MySQL簇
17.4.2. 安装软件
17.4.3. MySQL簇的快速测试设置
17.4.4. 配置文件

作为MySQL簇组成部份的MySQL服务器仅在一个方面上与正常的(非簇式)MySQL服务器不同,它采用了NDB簇存储引擎。该引擎也简单地称为NDB,这两个术语是同义词。

为了避免不必要的资源分配,默认情况下,在服务器的配置中将禁止NDB存储引擎。要想启用NDB,需要更改服务器的my.cnf配置文件,或使用“—ndbcluster”选项启动服务器。

由于MySQL服务器是簇的一部分,它也需要知道如何访问MGM节点,以便获得簇配置数据。默认行为是查找本地主机上MGM节点。但是,如果需要另外指定它的位置,可在my.cnf文件或MySQL服务器命令行上进行。能够使用NDB存储引擎之前,至少应有一个MGM节点是可操作的,而且还应有所需的数据节点。
17.4.1. 从源码创建MySQL簇

对于Linux、Mac OS X和Solaris,在其二进制分发版中均提供了NDB簇存储引擎。在Windows平台上尚不支持它,但我们打算在不远的将来使其能用于win32和其他平台。

如果选择从源码tarball或MySQL 5.1 BitKeeper树创建它,运行configure时,务必使用“--with-ndbcluster”选项。也可以使用BUILD/compile-pentium-max创建脚本。注意,该脚本包含OpenSSL,因此,要想成功创建,必须有或获得OpenSSL,如不然,需要更改“compile-pentium-max”以便将该要求排除在外,当然,也能采用标准步骤来编译你自己的二进制文件,然后执行常规测试和安装步骤。请参见2.8.3节,“从开发源码树安装”。
17.4.2. 安装软件

在下面的数节内,假定你已熟悉了MySQL的安装方法,在此,我们仅介绍了MySQL簇配置与不具备簇功能的MySQL配置之间的差别。如果希望了解关于后者的更多信息,请参见第2章:安装MySQL。

如果首先运行了所有的管理和数据节点,你将发现簇配置最简单,这或许是最花时间的配置部分。编辑my.cnf文件相对直接,在本节中,仅讨论与不具备簇功能的MySQL配置不同的部分。
17.4.3. MySQL簇的快速测试设置
为了帮助你熟悉基本概念,我们将介绍功能性MySQL簇的最简单的可能配置。然后,按照本章相关部分提供的信息,你应能设计自己所需的配置。

首先,应以系统根用户身份通过执行下述命令创建配置目录,如/var/lib/mysql-cluster:

shell> mkdir /var/lib/mysql-cluster

在该目录下,使用下述信息创建名为config.ini的文件,针对系统的情况,用恰当的值替换HostName和DataDir。

# file "config.ini" - showing minimal setup consisting of 1 data node,

# 1 management server, and 3 MySQL servers.

# The empty default sections are not required, and are shown only for

# the sake of completeness.

# Data nodes must provide a hostname but MySQL Servers are not required

# to do so.

# If you don't know the hostname for your machine, use localhost.

# The DataDir parameter also has a default value, but it is recommended to

# set it explicitly.

# Note: DB, API, and MGM are aliases for NDBD, MYSQLD, and NDB_MGMD

# respectively. DB and API are deprecated and should not be used in new

# installations.

[NDBD DEFAULT]

NoOfReplicas= 1

 

[MYSQLD DEFAULT]

[NDB_MGMD DEFAULT]

[TCP DEFAULT]

 

[NDB_MGMD]

HostName= myhost.example.com

 

[NDBD]

HostName= myhost.example.com

DataDir= /var/lib/mysql-cluster

 

[MYSQLD]

[MYSQLD]

[MYSQLD]

现在,能够按下述方式启动管理服务器:

shell> cd /var/lib/mysql-cluster

shell> ndb_mgmd

接下来,通过运行ndbd启动单个DB节点。首次为给定的DB节点启动ndbd时,应使用“—initial”选项,如下所示:

shell> ndbd --initial

对于后续的ndbd启动,通常不需要使用该选项:

shell> ndbd

这是因为,--initial选项将删除该数据节点的所有已有数据和日志文件(以及所有的表元数据),并创建新的数据和日志文件。该规则的一项例外是:添加了新数据节点后重启簇并从备份进行恢复之时。

默认情况下,ndbd将在端口1186上查找本地主机上的管理服务器。

注释:如果从二进制tarball安装了MySQL,需要明确指定ndb_mgmd和ndbd服务器的路径。(正常情况下,它们位于/usr/local/mysql/bin目录下)。

最后,进入MySQL数据目录(通常是/var/lib/mysql或/usr/local/mysql/data),并确保my.cnf文件包含启用NDB存储引擎所需的选项:

[mysqld]

ndbcluster

现在,你能按通常方式启动MySQL服务器:

shell> mysqld_safe --user=mysql &

等待一段时间,确认MySQL服务器正在恰当运行。如果发现通知用mysql停止,请检查服务器的.err文件,找出错误。

如果到目前为止一切正常,可使用簇启动它:

shell> mysql

Welcome to the MySQL monitor.  Commands end with ; or /g.

Your MySQL connection id is 1 to server version: 5.1.2-alpha-Max

 

键入’help;’或’/h’获取帮助。键入’/c’清空缓冲区。

 

mysql> SHOW ENGINES/G

 

...

*************************** 12. row ***************************

Engine: NDBCLUSTER

Support: YES

Comment: Clustered, fault-tolerant, memory-based tables

*************************** 13. row ***************************

Engine: NDB

Support: YES

Comment: Alias for NDBCLUSTER

...

(注意,上例输出中显示的行号可能与你的系统上显示的不同,具体情况取决于使用的MySQL版本,以及配置它的方式)。

shell> mysql

Welcome to the MySQL monitor.  Commands end with ; or /g.

Your MySQL connection id is 1 to server version: 5.1.2-alpha-Max

 

键入’help;’或’/h’获取帮助。键入’/c’清空缓冲区。

 

mysql> USE test;

Database changed

 

mysql> CREATE TABLE ctest (i INT) ENGINE=NDBCLUSTER;

Query OK, 0 rows affected (0.09 sec)

 

mysql> SHOW CREATE TABLE ctest /G

*************************** 1. row ***************************

       Table: ctest

Create Table: CREATE TABLE `ctest` (

  `i` int(11) default NULL

) ENGINE=ndbcluster DEFAULT CHARSET=latin1

1 row in set (0.00 sec)

要想检查是否恰当设置了节点,可启动管理客户端:

shell> ndb_mgm

随后,为了获得关于簇状态的报告,可从管理客户端内使用SHOW命令:

NDB> SHOW

Cluster Configuration

---------------------

[ndbd(NDB)]     1 node(s)

id=2    @127.0.0.1  (Version: 3.5.3, Nodegroup: 0, Master)

 

[ndb_mgmd(MGM)] 1 node(s)

id=1    @127.0.0.1  (Version: 3.5.3)

 

[mysqld(API)]   3 node(s)

id=3    @127.0.0.1  (Version: 3.5.3)

id=4 (not connected, accepting connect from any host)

id=5 (not connected, accepting connect from any host)

此时,你成功地设置了工作的MySQL簇。现在,你能使用由ENGINE=NDBCLUSTER或其别名ENGINE=NDB创建的表,将数据保存到簇中。
17.4.4. 配置文件

17.4.4.1. MySQL簇的配置示例
17.4.4.2. MySQL簇连接字符串
17.4.4.3. 定义构成MySQL簇的计算机
17.4.4.4. 定义MySQL簇管理服务器
17.4.4.5. 定义MySQL簇数据节点
17.4.4.6. 定义MySQL簇内的SQL节点
17.4.4.7. MySQL簇TCP/IP连接
17.4.4.8. 使用直接连接的MySQL簇TCP/IP连接
17.4.4.9. MySQL簇共享内存连接
17.4.4.10. MySQL簇SCI传输连接

配置MySQL簇需要与两个文件打交道:

·         my.cnf:为所有的MySQL簇可执行文件指定了选项。你应熟悉了前面介绍的使用MySQL的方式,通过运行在簇中的每个可执行文件,必须能够访问该文件。

·         config.ini:该文件仅由MySQL簇管理服务器读取,随后管理服务器会将包含该文件的信息分配给簇中的所有进程。config.ini文件包含对簇中各节点的描述。包括数据节点的配置参数,以及簇中所有节点间连接的配置参数。

我们正在不断改进簇配置,并努力简化该进程。尽管我们将尽量维护向后兼容性,但在某些时候,可能也需要引入不兼容的变动。在这种情况下,我们将尽量让簇用户事先了解该变动是否是向后兼容的。如果你发现了尚未记录在文档中的这类变动,请使用我们的缺陷数据库通报它。
17.4.4.1. MySQL簇的配置示例

为了支持MySQL簇,需要更新文件my.cnf,如下例所示。注意,不应将这里给出的选项与config.ini文件中出现的选项混淆起来。此外,从命令行调用可执行文件时,或许也应指定这些参数。

# my.cnf

# example additions to my.cnf for MySQL Cluster

# (valid in MySQL 5.1)

 

# enable ndbcluster storage engine, and provide connectstring for

# management server host (default port is 1186)

[mysqld]

ndbcluster

ndb-connectstring=ndb_mgmd.mysql.com

 

 

# provide connectstring for management server host (default port: 1186)

[ndbd]

connect-string=ndb_mgmd.mysql.com

 

# provide connectstring for management server host (default port: 1186)

[ndb_mgm]

connect-string=ndb_mgmd.mysql.com

 

# provide location of cluster configuration file

[ndb_mgmd]

config-file=/etc/config.ini

(关于连接字符的更多信息,请参见17.4.4.2节,“MySQL簇连接字符串”)。

# my.cnf

# example additions to my.cnf for MySQL Cluster

# (will work on all versions)

 

# enable ndbcluster storage engine, and provide connectstring for management

# server host to the default port 1186

[mysqld]

ndbcluster

ndb-connectstring=ndb_mgmd.mysql.com:1186

或许,你也可以使用簇my.cnf中单独的[mysql_cluster]部分,设置可被所有可执行文件读取的设置,并影响所有的可执行文件:

# cluster-specific settings

[mysql_cluster]

ndb-connectstring=ndb_mgmd.mysql.com:1186

目前,配置文件采用的是INI格式,默认情况下被命名为config.ini。该文件在启动时由ndb_mgmd读取,并能被置于任何地方。在命令行上与ndb_mgmd一起使用--config-file=[<path>]<filename>,可指定其位置和名称。如果未指定配置文件,默认情况下,ndb_mgmd将尝试读取位于当前工作目录下的文件config.ini。

对于大多数参数,均定义了默认值,也能在config.ini文件中指定默认值。要想创建默认值部分,可简单地将单词DEFAULT添加到该部分的名称上。例如,数据节点是使用[NDBD]部分配置的。如果所有的数据节点使用相同大小的数据内存,而且该内存大小不同于默认的大小,应创建包含DataMemory行的[NDBD DEFAULT]部分,为所有数据节点指定默认的数据内存大小。

INI格式包含多个部分,每一部分以该部分的标题(用方括号括住)开始,后跟恰当的参数名和值。与标准格式的不同之处在于,不能用冒号“:”和等号“=”隔开参数名和值;另一处不同是,这些部分并不是用名称唯一标识的。其唯一性条目(如具有相同类型的两个不同节点)是由唯一ID标识的。

作为最低要求,配置文件必须定义簇中的计算机和节点,以及这些节点所在的计算机。下面给出了一个简单的簇配置文件示例,该簇包含1个管理服务器,2个数据节点和2个MySQL服务器:

# file "config.ini" - 2 data nodes and 2 SQL nodes

# This file is placed in the startup directory of ndb_mgmd (the management

# server)

# The first MySQL Server can be started from any host. The second can be started

# only on the host mysqld_5.mysql.com

 

[NDBD DEFAULT]

NoOfReplicas= 2

DataDir= /var/lib/mysql-cluster

 

[NDB_MGMD]

Hostname= ndb_mgmd.mysql.com

DataDir= /var/lib/mysql-cluster

 

[NDBD]

HostName= ndbd_2.mysql.com

 

[NDBD]

HostName= ndbd_3.mysql.com

 

[MYSQLD]

[MYSQLD]

HostName= mysqld_5.mysql.com

在该配置文件中,有6个不同部分:

·         [COMPUTER]:定义了簇主机。

·         [NDBD]:定义了簇的数据节点。

·         [MYSQLD]:定义了簇的MySQL服务器节点。

·         [MGM]或[NDB_MGMD]:定义了簇的管理服务器节点。

·         [TCP]:定义了簇中节点间的TCP/IP连接,TCP/IP是默认的连接协议。

·         [SHM]:定义了节点间的共享内存连接。以前,这类连接仅能在使用“--with-ndb-shm”选项创建的二进制文件中使用。在MySQL 5.1-Max中,默认情况下它是允许的,但仍应将其视为试验性的。

注意,每个节点在config.ini文件中有自己的部分。例如,由于该簇有两个数据节点,在配置文件中,也包含定义这些节点的部分。

可以为每个部分定义DEFAULT值。所有的簇参数名称均区分大小写。
17.4.4.2. MySQL簇连接字符串

除了MySQL簇管理服务器(ndb_mgmd),构成MySQL簇的每个节点均需要1个连接字符串,该连接字符串指向管理服务器所在的位置。它用于建立与管理服务器的连接,并执行其他任务,这类其他任务取决于节点在簇内扮演的角色。连接字符串的语法如下:

<connectstring> :=

    [<nodeid-specification>,]<host-specification>[,<host-specification>]

    

<nodeid-specification> := node_id

 

<host-specification> := host[:port]

node_id是大于1的整数,用于确定config.ini中的节点。port是引用正常Unix端口的整数。host是代表有效Internet地址的字符串。

example 1 (long):    "nodeid=2,myhost1:1100,myhost2:1100,192.168.0.3:1200"

example 2 (short):   "myhost1"

如果未提供,所有节点均将使用localhost:1186作为默认的连接字符串值。如果在连接字符串中省略了<port>,默认端口为1186。该端口在网络上总应是可用的,这是因为它是由IANA为该目的而指定的(详情请参见http://www.iana.org/assignments/port-numbers)。

通过列出多个<host-specification>值,能够指定数个冗余管理服务器。簇节点将按照指定的顺序尝试连接到每台主机上的连续管理服务器,直至成功建立起连接为止。

有多种指定连接字符串的不同方法:

·         每个可执行文件有自己的命令行选项,使用它,能够在启动时指定管理服务器(关于各可执行程序的介绍,请参见相应的文档)。

·         也能一次性地为簇中的所有节点设置连接字符串,方法是将其放在管理服务器的my.cnf文件的[mysql_cluster]部分。

·         为了向后兼容性,还提供了两种其他选项,其使用的语法相同:

1.    设置NDB_CONNECTSTRING环境变量,使之包含connectstring(连接字符串)。

2.    将针对各可执行文件的connectstring(连接字符串)写入名为Ndb.cfg的文本文件,并将该文件放在可执行文件的启动目录下。

但是,这些方法目前已不再受重视,对于新安装,不应使用它们。

指定连接字符串时,推荐的方法是在命令行上设置它,或为每个可执行文件在my.cnf文件中设置它。
17.4.4.3. 定义构成MySQL簇的计算机

除了用于避免为系统中的每个节点定义主机名外,[COMPUTER]部分没有实际的重要意义。这里所提到的所有参数都是需要的。

·         [COMPUTER]Id

这是整数值,用于引用位于配置文件中别处的主机计算机。

·         [COMPUTER]HostName

这是计算机的主机名或IP地址。
17.4.4.4. 定义MySQL簇管理服务器

[NDB_MGMD]部分(或其别名[MGM])用于配置管理服务器的行为。下面列出的所有参数均能被忽略,如果是这样,将使用其默认值。注释:如果ExecuteOnComputer和HostName参数均未出现,会为它们指定默认值localhost。

·         [NDB_MGMD]Id

簇中的每个节点都有唯一的标识,由从1到63的整数表示。所有的内部簇消息使用该ID来定址结点。

·         [NDB_MGMD]ExecuteOnComputer

它引用在[COMPUTER]部分中定义的计算机之一。

·         [NDB_MGMD]PortNumber

这是管理服务器用于监听配置请求和管理命令的端口号。

·         [NDB_MGMD]LogDestination

该参数指定了将簇登录信息发送到哪里。有三种选项,CONSOLE、SYSLOG和FILE:

o        CONSOLE,将日志输出到标准输出设备(stdout):

o                     CONSOLE

o        SYSLOG,将日志发送到syslog(系统日志)软设备,可能的值包括:auth、authpriv、cron、daemon、ftp、kern、lpr、mail、news、syslog、user、uucp、local0、local1、local2、local3、local4、local5、local6或local7。

注释:并非所有的操作系统均支持所有的软设备。

SYSLOG:facility=syslog

o        FILE,将簇日志输出导向相同机器上的正规文件。可指定下述值:

§         filename:日志文件的名称。

§         maxsize:日志记录切换到新文件之前,文件能增长到的最大尺寸。出现该情况时,将通过在文件名上添加.x,重命名日志文件,其中,x是该名称尚未使用的下一个数字。

§         maxfiles:日志文件的最大数目。

o                     FILE:filename=cluster.log,maxsize=1000000,maxfiles=6

使用由分号分隔的字符串,可以指定多个日志目标,如下所示:

CONSOLE;SYSLOG:facility=local0;FILE:filename=/var/log/mgmd

FILE参数的默认值是FILE:filename=ndb_node_id_cluster.log,maxsize=1000000,maxfiles=6,其中,node_id是节点的ID。

·         [NDB_MGMD]ArbitrationRank

该参数用于定义哪个节点将扮演仲裁程序的角色。只有MGM节点和SQL节点能扮演仲裁程序的角色。ArbitrationRank可以取下述值之一:

o        0:该节点永远不会用作仲裁程序。

o        1:该节点具有高的优先级,也就是说,与低优先级节点相比,它更容易成为仲裁程序。

o        2:表明节点具有低的优先级,仅当具有高优先级的节点无法用于该目的时,才能成为仲裁程序。

通常情况下,应将ArbitrationRank设置为1(默认值),并将所有的SQL节点设置为0,将管理服务器配置为仲裁程序。

·         [NDB_MGMD]ArbitrationDelay

整数值,以毫秒为单位规定了管理服务器对仲裁请求的延迟时间。默认情况下,该值为0,通常不需要改变它。

·         [NDB_MGMD]DataDir

它用于设置保存管理服务器输出文件的位置。这些文件包括簇日志文件、进程输出文件、以及端口监督程序的pid文件(对于日志文件,可通过设置[NDB_MGMD]LogDestination的FILE参数覆盖它,请参见本节前面的讨论)。
17.4.4.5. 定义MySQL簇数据节点

[NDBD]部分用于配置簇数据节点的行为。有很多可用于控制缓冲区大小、池大小、超时等的参数。强制性参数包括:

·         ExecuteOnComputer或HostName.

·         参数NoOfReplicas

这些参数需要在[NDBD DEFAULT]部分中定义。

大多数数据节点参数是在[NDBD DEFAULT]部分中设置的。只有那些明确声明为能设置本地值的参数才能在[NDBD]部分中被更改。HostName、Id以及ExecuteOnComputer必须在本地[NDBD]部分中定义。

识别数据节点

启动节点时,可在命令行上分配Id值(即数据节点ID),也能在配置文件中分配Id值。

对于各参数,能够使用后缀k、M或G用于指明单位,分别表示1024、1024*1024或1024*1024*1024(例如,100k表示100 * 1024 = 102400)。目前,参数和值区分大小写。

·         [NBDB]Id

这是用作节点地址的节点ID,供有的簇内部消息使用。这是介于1~63之间的整数。簇中的每个节点均有唯一的ID。

·         [NDBD]ExecuteOnComputer

用于引用在COMPUTER部分中定义的计算机(主机)。

·         [NDBD]HostName

指定该参数的效果类似于指定ExecuteOnComputer。它定义了存储节点所在计算机的主机名。指定除localhost之外的其他主机名时,需要该参数或ExecuteOnComputer。

·         (OBSOLETE) [NDBD]ServerPort

簇中的各节点使用端口来与其他节点相连。该端口也用于连接建立阶段中的非TCP传输器。由于默认端口是动态分配的,同一台计算机上的两个节点具有不同的端口号,正常情况下不需要为该参数指定值。

·         [NDBD]NoOfReplicas

该全局参数仅能在[NDBD DEFAULT]中设置,它定义了簇中每个表保存的副本数。该参数还指定了节点组的大小。节点组指的是保存相同信息的节点集合。

节点组是以隐式方式构成的。第1个节点组由具有最低节点ID的数据节点集合构成,下一个节点组由具有次低节点ID的数据节点集合构成,依此类推。作为示例,截顶我们有4个数据节点,并将NoOfReplicas设置为2。这四个数据节点的ID分别是2、3、4、5。那么第1个节点组由节点2和3构成,第2个节点组由节点4和5构成。重要的是对簇进行相应的配置,使得同一节点组中的节点位于不同的计算机上,这是因为,如果位于相同的计算机上,单个硬件故障会导致整个簇崩溃。

如果未提供节点ID,那么数据节点的顺序将是节点组的决定因素。无论是否进行了明确的分配,可在管理客户端SHOW命令的输出中查看它们。

NoOfReplicas没有默认值,最大的可能值为4。

·         [NDBD]DataDir

该参数指定了存放跟踪文件、日志文件、pid文件以及错误日志的目录。

·         [NDBD]FileSystemPath

该参数指定了存放为元数据创建的所有文件、REDO日志、UNDO日志和数据文件的目录。默认目录是由DataDir指定的。注意,启动ndbd进程之前,该目录必须已存在。

为MySQl簇推荐的目录层次包括/var/lib/mysql-cluster,在其下为节点的文件系统创建1个为目录。该子目录包含节点ID。例如,如果节点ID为2,该子目录的名称为ndb_2_fs。

·         [NDBD]BackupDataDir

也能指定存放备份的目录。默认情况下,该目录是FileSystemPath/BACKUP(请参见前面的介绍)。

数据内存和索引内存

参数DataMemory和IndexMemory指定了存放实际记录及其索引的内存段的大小。这是它们的值时,重要的是应掌握使用DataMemory和IndexMemory的方式,这是因为,为了反映簇的实际使用情况,常常需要更新它们:

·         [NDBD]DataMemory

该参数定义了用于保存数据库记录的空间大小。全部空间均是分配在内存中的,因此,机器应具有足够的物理内存来容纳该值,这点极其重要。

由DataMemory分配的内存用于保存实际记录和索引。目前,每条记录具有固定的大小(甚至VARCHAR列也保存为固定宽度列)。每条记录的开销为16字节,此外,每条记录还需要额外的空间,这是因为,这类记录保存在具有128字节页面开销的32KB页中(请参见下面的介绍)。由于每条记录仅保存在1个页中,因而每页有少量的浪费。目前,最大记录大小为8052字节。

由DataMemory定义的内存空间也用于保存有序索引,对于每条记录,索引约使用10字节。在有序索引中,表示了每个表行。用户常犯的一个错误是,想当然地认为所有的索引均保存在由IndexMemory分配的内存中,但情况并非如此:只有主键和唯一性混编索引使用该内存,有序索引使用的是由DataMemory分配的内存。然而,创建主键或唯一性混编索引时,也会在相同的 键上创建有序索引,除非在索引创建语句中指定了USING HASH。通过在管理客户端中运行ndb_desc -d db_name table_name,可对其进行验证。

DataMemory分配的内存空间由多个32KB页构成,它们是为表片段分配的。通常情况下,为每一表划分的表片段数目与簇中的节点数目相同。因此,对于每一节点,片段数目与在NoOfReplicas中设置的相同。一旦分配了1页,目前无法将其返回到自由页池中,除非删除表。执行节点恢复也将压缩分区,这是因为,所有记录均会被插入到其他活动节点的空分区中。

DataMemory内存空间也包含UNDO信息:对于每一更新,未改变记录的副本将被分配到DataMemory中。在有序表索引中,还有对每一副本的引用。仅当更新唯一性索引列时,才会更新唯一性混编索引,在该情况下,将在索引表中插入新的条目,并在提交时删除旧的条目。因此,也有必要分配足够的内存,以便处理由使用簇的应用程序执行的最大事务。在任何情况下,执行少量大的事务并不比使用众多小的事务占优,原因如下:

o        大事务的速度没有较小事务的速度快。

o        大的事务会增加丢失操作的数目,一旦事务失败,必须重复执行。

o        大的事务使用更多的内存。

DataMemory的默认值是80MB,最小为1MB。没有最大尺寸限制,但在实际使用过程中,最大限制应恰当,以便当达到最大限制时,进程不会启动交换功能。该限制由机器上可用的物理RAM量、以及操作系统能提交给任何进程的内存量决定。对于32位操作系统,该限制值为每进程2~4GB,对于64位操作系统,该限制值更大。对于大的数据库,出于该原因,最好使用64位操作系统。此外,在每台机器上也能运行一个以上的ndbd进程,在使用多CPU的机器上,该特性颇具优势。

·         [NDBD]IndexMemory

该参数用于控制MySQL簇中哈希(混编)索引所使用的存储量。哈希(混编)索引总用于主键索引、唯一性索引、以及唯一性约束。注意,定义主键和唯一性索引时,将创建两条索引,其中一条是用于所有tuple访问和锁定处理的哈希(混编)索引。此外,它还能用于增强唯一性约束。

哈希(混编)索引的大小是每记录25字节,再加上主键的大小。对大于32字节的主键,还需加上8字节。

考虑下例定义的表:

CREATE TABLE example (

  a INT NOT NULL,

  b INT NOT NULL,

  c INT NOT NULL,

  PRIMARY KEY(a),

  UNIQUE(b)

) ENGINE=NDBCLUSTER;

有12字节的开销(无可空列将节省4字节的开销)加上每记录12字节的数据。此外,在列a和b上有两个有序索引,假定每记录分别耗用约10字节的空间。在每记录约使用29字节的基表上有1条主键哈希索引。唯一性约束由以b作为主键以及a作为列的单独表实现。对于该表,每记录将耗用额外的29字节索引内存,在示例表中,还包括12字节的开销再加上8字节的记录数据。

因此,对于100万条记录,需要58MB的索引内存来处理用于主键和唯一性约束的哈希索引。还需要64 MB来处理基表和唯一索引表、以及两个有序索引表的记录。

由此可见,哈希索引占用了相当大的内存空间,但作为回报,它们提供了对数据的极快访问。在MySQl簇中,它们也用于处理唯一性约束。

目前仅有的分区算法是散列法,有序索引对每个节点来说都是局部性的。因此,有序索引不能用于处理一般情况下的唯一性约束。

对于IndexMemory和DataMemory,重要的是,总的数据库大小是各节点组的所有数据内存和所有索引内存之和。每个节点组用于保存复制信息,因此,如果有4个节点和2个副本,将有2个节点组。对于每个数据节点,可用的总数据内存是2*DataMemory。

强烈建议为所有的节点设置相同的DataMemory值和IndexMemory值。由于数据是平均分布在簇中的所有节点上,任何节点可用的最大空间不超过簇中最小节点的可用空间。

DataMemory和IndexMemory可被更改,但降低任何一个的值均会导致危险,如果这样做,很容易使某一节点甚至整个簇因缺少足够的内存空间而无法重启。增加它们的值应是可接受的,但建议采用与软件升级相同的方式升级它,首先更新配置文件,然后重启管理服务器,最后依次重启每个数据节点。

更新不会增加所用的索引内存。插入将立刻生效,但是,在提交事务之前并不会实际删除行。

IndexMemory的默认值是18MB。最小值为1MB。

事务参数

下面讨论的三个参数十分重要,这是因为,它们会影响并发事务的数目,以及系统能够处理的事务的大小。MaxNoOfConcurrentTransactions用于设置节点内可能的并发事务数目。MaxNoOfConcurrentOperations用于设置能同时出现在更新阶段或同时锁定的记录数目。

对于打算设定特定值、不使用默认值的用户,这两个参数可能正是他们所需的(尤其是MaxNoOfConcurrentOperations)。默认值是为使用小型事务的系统而设置的,为的是确保这类事务不会使用过多的内存。

·         [NDBD]MaxNoOfConcurrentTransactions

对于簇中的每个活动事务,必须在簇节点之一中有1条记录。对事务的协调任务是在各节点间进行的:在簇中,事务记录的总数等于任意给定节点中的事务数乘以簇中的节点数。

事务记录被分配给单独的MySQL服务器。正常情况下,对于使用簇中任何表的每个连接,必须为其分配至少1条事务记录。出于该原因,应确保簇中的事务记录数大于簇中所有MySQL服务器的并发连接数。

对于所有的簇节点,必须将该参数设置为相同的值。

更改该参数不安全,如果这样做,会导致簇崩溃。当某一节点崩溃时,簇中的一个节点(实际上是生存时间最久的节点)将为崩溃之时正在崩溃节点中运行的所有事务建立事务状态。因此,重要的是,该节点的事务记录数不低于失效节点中的事务记录数。

该参数的默认值为4096.

·         [NDBD]MaxNoOfConcurrentOperations

根据事务的大小和数目调整该参数的值,这个想法不错。执行仅包含少量操作且不涉及很多记录的事务时,不需要将该参数设置得很高。但在执行涉及大量记录的大事务时,需要将该参数设置得较高。

对于每次事务更新的簇数据,均会保存记录,并会将它们保存在事务协调器中以及执行实际更新的节点中。这些记录包含所需的状态信息,这类信息可用于为回滚操作找到UNDO记录,用于锁定查询或其他目的。

该参数应被设置为:事务中同时更新的记录数除以簇数据节点的数目。例如,在包含4个数据节点的簇中,如果预期处理的、使用事务的并发更新数为1000000,就应将该值设置为1000000 / 4 = 250000。

设置锁定的读请求也会导致操作记录的创建。在单独节点内也会分配一些额外的空间,以便处理在节点间分配不完美的问题。

当查询使用唯一性哈希索引时,对于事务中的每条记录,实际上将使用两条操作记录。第1条记录代表在索引表中的读,第2条记录负责处理基表上的操作。

该参数的默认值为32768.

该参数实际上处理的是能分别配置的两个值。第1个值指定了将多少操作记录放到事务协调器中,第2个值指定了多少操作记录是数据库的本地记录。

对于在8节点簇上执行的特大事务,它要求事务协调器中的操作记录数不少于事务中涉及的读取、更新和删除次数。然而,簇中的操作记录分布在所有的8个节点上。因此,如果有必要为特大事务配置系统,良好的方法是分别配置该参数的两个部分。MaxNoOfConcurrentOperations总会被用于计算节点的事务协调器部分中的操作记录数。

应了解操作记录对内存的要求,这点也很重要。每记录约消耗1KB。

·         [NDBD]MaxNoOfLocalOperations

默认情况下,将按照1.1 * MaxNoOfConcurrentOperations计算该参数,它适合于具有很多并发事务但不存在特大事务的系统。如果需要在某一时间处理特大事务而且有很多节点,最好通过明确指定该参数以覆盖默认值。

事务临时存储

下一组参数用于决定执行作为簇事务组成部分的查询时所需的临时存储空间。查询完成后将释放所有记录,簇将等待提交或回滚事件。

对于大多数情况,这些参数的默认值是恰当的。但是,如果需要支持涉及大量行或操作的事务,用户或许应增大这些参数的值,以便在系统中获得更好的平行性。对于需要相对较少事务的应用程序,用户可降低这些参数的值,以便节省内存。

·         [NDBD]MaxNoOfConcurrentIndexOperations

对于使用唯一性哈希索引的查询,在查询执行期间,将使用操作记录的另一个临时集合。该参数用于设置记录池的大小。因此,仅当执行查询的某一部分时才会分配该记录,一旦该部分执行完成,将释放记录。对于处理放弃和提交所需的状态,它是由正常的操作记录负责处理的,这类记录的池大小由参数MaxNoOfConcurrentOperations设置。

该参数的默认值为8192。只有在极其罕见的情况下,需要使用唯一性哈希索引执行极高的并行操作时,才有必要增大该值。如果DBA(数据库管理员)确信该簇不需要高的并行操作,可以使用较小的值并节省内存。

·         [NDBD]MaxNoOfFiredTriggers

MaxNoOfFiredTriggers的默认值是4000,它足以应付大多数情况。在某些情况下,如果DBA认为在簇中对并行操作的要求并不高,甚至还能降低它。

执行会影响唯一哈希索引的操作时,将创建记录。在具有哈希索引的表中插入或删除记录时,或更新作为唯一哈希索引组成部分的列时,均会触发索引表中的插入或删除操作。所获得的记录用于代表该索引表操作,同时等待促使其完成的初始操作。该操作的时间很短,但对于在基表(包含唯一哈希索引)上有很多并发写操作的情形,仍需要在记录池中有大量的记录。

·         [NDBD]TransactionBufferMemory

该参数影响的内存用于跟踪更新索引表和读取唯一索引时执行的操作。该内存用于保存关于这类操作的键和列信息。几乎不需要更改该参数的默认值。

正常的读和写操作使用类似的缓冲区,其使用时间甚至更短。编译时间参数ZATTRBUF_FILESIZE(在ndb/src/kernel/blocks/Dbtc/Dbtc.hpp中)被设为4000*128字节(500KB)。用于 键信息的类似缓冲区,ZDATABUF_FILESIZE(也在Dbtc.hpp中)包含4000 * 16 = 62.5KB的缓冲空间。Dbtc是用于处理事务协调的模块。

扫描和缓冲

在Dblqh模块中(在ndb/src/kernel/blocks/Dblqh/Dblqh.hpp内)有很多附加参数,这些参数会影响读和写操作。这些参数包括:ZATTRINBUF_FILESIZE,默认值为10000*128字节(1250KB);以及ZDATABUF_FILE_SIZE,默认的缓冲空间大小为10000*16字节(约156KB)。到目前为止,没有任何迹象表明应增加这类编译时间限制参数的值,无论是用户报告还是我们自己的大量测试。

TransactionBufferMemory的默认值是1MB。

·         [NDBD]MaxNoOfConcurrentScans

该参数用于控制可在簇中执行的并行扫描的数目。每个事务协调程序均能处理为该参数定义的并行扫描。对于每次执行的扫描查询,将以并行方式扫描所有分区。每次分区扫描将使用分区所在节点内的扫描记录,记录数等于该参数的值乘以节点数。簇应能支持从簇内所有节点同时执行的MaxNoOfConcurrentScans扫描。

扫描实际上是在两种情况下执行的。第1种情况是,处理查询时不存在哈希或有序索引,在该情况下,查询是通过执行全表扫描进行的。第2种情况是,没有支持查询的哈希索引,但存在有序索引。使用有序索引意味着将执行并发范围扫描。由于顺序仅保存在本地分区上,需要在所有分区上执行索引扫描。

MaxNoOfConcurrentScans的默认值是256。最大值为500。

该参数指定了事务协调器中的可能扫描数。如果未提供本地扫描记录的数目,会对其进行计算,等于MaxNoOfConcurrentScans乘以系统中数据节点的数目。

·         [NDBD]MaxNoOfLocalScans

如果很多扫描不是完全并行化的,指定本地扫描记录的数目。

·         [NDBD]BatchSizePerLocalScan

该参数用于计算锁定记录的数目,要想处理很多并发扫描操作,需要这类记录。

默认值是64,该值与SQL节点中定义的ScanBatchSize关系密切。

·         [NDBD]LongMessageBuffer

这是用于在单独节点内和节点之间传递消息的内部缓冲。尽管几乎不需要改变它,但它仍是可配置的。默认情况下,它被设置为1MB。

日志和Checkpointing

·         [NDBD]NoOfFragmentLogFiles

该参数用于设置节点的REDO日志文件的大小。REDO日志文件是按循环方式组织的。第1个和最后1个日志文件(有时也分别称为“头”日志文件和“尾”日志文件)不应相遇,这点极其重要,当它们彼此过于接近时,由于缺少新日志记录的空间,节点将开始放弃所有事务,包括更新。

自插入日志记录开始,在三个本地检查点完成之前,不会删除REDO日志记录。检查点的频率由其自己的配置参数集决定,请参见本章的相应部分。

默认的参数值为8,它表示有8个集合,每个集合有4个16MB文件,总容量为512MB。换句话讲,REDO日志空间必须按64MB的块大小分配。在需要大量更新的情况下,可能需要将NoOfFragmentLogFiles的值增加到300或更高,以便为REDO日志提供足够的空间。

如果checkpointing很慢,并有很多对数据库的写操作以至于日志文件已满,而且在没有jeapo rdising恢复功能的情况下无法截断日志尾部,那么所有的更新日志均将被放弃,并给出错误代码410或缺少临时日志空间。该状况将一直持续,直至完成了检查点操作并能将日志尾部向前移动为止。

·         [NDBD]MaxNoOfSavedMessages

该参数用于设置跟踪文件的最大数目,在覆盖旧文件之前,将保留这些跟踪文件。无论出于何种原因,当节点崩溃时将创建跟踪文件。

默认为25个跟踪文件。

元数据对象

下一组参数为元数据对象定义了池的大小,可用于定义最大属性数,表,索引,索引使用的触发程序对象,事件,以及簇之间的复制。注意,这些参数仅是对簇的“建议”,任何未指定的参数均将采用其默认值。

·         [NDBD]MaxNoOfAttributes

定义了可在簇中定义的属性数目。

该参数的默认值为1000,最小的可能值为32。没有最大值限制。对于每一属性,每节点约需200字节的存储空间,这是应为,所有的元数据将完整地复制到服务器上。

设置MaxNoOfAttributes时,应实现准备好打算在将来执行的任何ALTER TABLE命令,这点很重要。这是因为下述事实,在簇表上执行ALTER TABLE的过程中,所使用的属性数目是原始表中的3倍。例如,如果某一表需要100个属性,而且你打算在以后更改它,那么就需要将MaxNoOfAttributes的值设为300。有一个良好的经验规则,如果你能在不出现问题的情况下创建所有所需的表,请将最大表中属性数目的两倍加到MaxNoOfAttributes上。完成该设置后,应通过执行实际的ALTER TABLE操作,验证该数目是足够的。如果失败,将原始值的倍数加到MaxNoOfAttributes上,并再次测试。

·         [NDBD]MaxNoOfTables

表对象是为每个表、唯一哈希索引和有序索引分配的。该参数为作为整体的簇设置了最大表对象数目。

对于具有BLOB数据类型的每个属性,将使用额外的表来保存大部分BLOB数据。定义表的总数时,必须将这些表考虑在内。

该参数的默认值为128。最小值为8,最大值为1600。每个表对象每节点约需20KB的空间。

·         [NDBD]MaxNoOfOrderedIndexes

对于簇中的每个有序索引,将分配1个对象,该对象描述了编入索引的是什么以及其存储段。默认情况下,每个这样定义的索引还将定义1个有序索引。每个唯一索引和主键既有1个有序索引还有1个哈希索引。

该参数的默认值为128。每个对象每节点约需10KB的数据。

·         [NDBD]MaxNoOfUniqueHashIndexes

对于每个不是主键的唯一索引,将分配1个特殊表,该表将唯一键映射到索引表的主键上。默认情况下,对于每个唯一索引,还将定义1个有序索引。为了防止该情况,定义唯一索引时,必须使用USING HASH选项。

默认值是64。每个索引每节点约需15KB的空间。

·         [NDBD]MaxNoOfTriggers

对于每个唯一性哈希索引,将分配内部更新、插入、和删除触发程序(这意味着对于每个唯一性哈希索引,将创建三个触发程序)。但是,1个有序索引仅需要1个触发程序对象。对于簇中每个正常表,备份也将使用三个触发程序对象。

注释:支持簇之间的复制时,也将使用内部触发程序。

该参数用于设置簇中触发程序对象的最大数目。

该参数的默认值为768.

·         [NDBD]MaxNoOfIndexes

在MySQL 5.1中,该参数已被放弃,应使用MaxNoOfOrderedIndexes和MaxNoOfUniqueHashIndexes取而代之。

该参数仅供唯一性哈希索引使用。对于在簇中定义的每个唯一性哈希索引,在该池中需要有1条记录。

该参数的默认值为128.

布尔参数

数据节点的行为也会受具有布尔值的一组参数的影响。将其设为“1”或“Y”,可将这类参数设置为“真”,将其设为“0”或“N”,可将这类参数设置为“假”。

·         [NDBD]LockPagesInMainMemory

对于包括Solaris和Linux在内的很多操作系统,能够将进程锁定在内存中,以避免与磁盘的交换。使用它,可确保簇的实时特性。

默认情况下,该特性是被禁止的。

·         [NDBD]StopOnError

出现错误时,该参数指定了NDBD进程是退出还是执行自动重启。

默认情况下,允许该特性。

·         [NDBD]Diskless

能够将MySQL簇的表指定为“无磁盘的”,这意味着不会在磁盘上对表执行检查点操作,也不会出现日志操作。这类表仅存在于主内存中。使用“无磁盘”表的一个结果是,出现崩溃侯,不会保留这类表,也不会保留这类表中的任何记录。但是,当工作在“无磁盘”模式下时,能够在无盘计算机上运行ndbd。

要点:该特性会使整个簇运行在“无磁盘”模式下。

允许该特性时,可执行备份操作,但不会实际保存备份数据。

将“Diskless”设置为“1”或“Y”可允许该特性。默认情况下,禁止该特性。

·         [NDBD]RestartOnErrorInsert

仅当创建调试版时才能访问该特性,在执行作为测试组成部份的代码块的过程中,可以插入错误。

默认情况下,该特性是被禁止的。

控制超时、间隔、和磁盘分页

有多种用于指定超时以及簇数据节点中各种动作间时间间隔的参数。大多数超时值以毫秒为单位指定。任何例外均将在适用时指明。

·         [NDBD]TimeBetweenWatchDogCheck

为了防止主线程在某一点上陷入无限循环,采用了“看门狗”线程来检查主线程。该参数以毫秒为单位指定了检查之间的时间间隔。如果三次检查之后进程仍保持在相同的状态,它将被“看门狗”线程中止。

出于试验目的,可方便地更改该参数,也可以对其进行调整以适合本地条件。也可以按节点指定它,虽然这样作的理由很少。

默认超时为4000毫秒(4秒)。

·         [NDBD]StartPartialTimeout

该参数指定了在调用簇初始化子程序之前,簇等待所有存储节点出现的时间。该超时参数由于防止部分簇启动。

默认值是30000毫秒(30秒)。0表示无限超时,换句话讲,仅当所有节点均可能时才会启动簇。

·         [NDBD]StartPartitionedTimeout

等待了StartPartialTimeout毫秒后,如果簇做好了启动准备但仍可能处于隔离状态,簇将等待该超时时间结束。

默认超时为60000毫秒(60秒)。

·         [NDBD]StartFailureTimeout

如果数据节点在该参数指定的时间内未完成其启动序列,节点启动将失败。如果将该参数设置为0,表示不采用数据节点超时。

默认值是60000毫秒(60秒)。对于包含大量数据的数据节点,应增加该参数。例如,对于包含数GB数据的存储节点,为了执行节点重启,可能需要10~15分钟(即600000~1000000毫秒)。

·         [NDBD]HeartbeatIntervalDbDb

发现失败节点的主要方法之一是使用“心跳”数。该参数指明了心跳信号的发送频率,以及接收它们的频率。如果在1行内丢失了三次心跳,节点将被宣告为死亡。因此,通过心跳机制发现故障的最大时间是心跳间隔的四倍。

默认的心跳间隔为1500毫秒(1.5秒)。不得大幅度更改该参数,各节点间该参数的变化范围也不得过宽。例如,如果某一节点使用了5000毫米的值,而观察它的节点采用1000毫秒,很显然,该节点很快就会被宣布为死亡。能够在软件升级期间更改该参数,但增量应较小。

·         [NDBD]HeartbeatIntervalDbApi

每个数据节点会将心跳信号发送到各MySQL服务器(SQL节点),以确保保持接触。如果某一MySQL服务器未能及时发出心跳信号,它将被宣布为死亡。在这种情况下,所有正在进行的事务将结束并释放所有资源。SQL节点不能重新连接,直至由以前的MySQL实例初始化的所有活动完成为止。用于该判断的3心跳判据与HeartbeatIntervalDbDb描述的相同。

默认时间间隔为1500毫秒(1.5秒)。不同的数据节点之间,该间隔可以有所不同,这是因为,每个存储节点均会独立于所有其他数据节点观察与之相连的MySQL服务器。

·         [NDBD]TimeBetweenLocalCheckpoints

该参数是一个例外,它未指定启动新的本地检查前应等待的时间,相反,它用于确保在出现相对较少更新的簇内未执行本地检查点操作。在具有较高更新率的大多数簇内,很可能在前一个本地检查点操作完成后立刻启动一个新的检查点操作。

从前一个本地检查点启动后,所有已执行写操作的大小将增加。该参数也是一个例外,原因在于它被指定为4字节字总数的以2为底数的对数,因此,默认值20表示4MB (4 × 220)写操作,21表示8MB,依此类推,直至等同于8GB写操作的最大值31。

簇中所有的写操作将加在一起。将TimeBetweenLocalCheckpoints设置为6或更小表示本地检查点操作将不停顿地连续执行,与簇的工作负荷无关。

·         [NDBD]TimeBetweenGlobalCheckpoints

提交事务时,它被提交到存有镜像数据的所有节点的主内存中。但是,事务日志记录不会作为提交进程的一部分写入磁盘。其原因在于,在至少两台独立主机机器上安全体提交事务应能满足关于关于持久性的合理标准。

另一个很重要的方面是,应确保即使在最差情况下(簇完全崩溃),也能进行恰当地处理。为了确保这点,在给定时间间隔内出现的所有事务均会被放到全局检查点,可将其视为写入磁盘的已提交事务的集合。换句话讲,作为提交进程的组成部分,事务将被放入全局检查点组;稍后,该组的日志记录将被写入磁盘,然后将整个事务组安全地提交到簇内所有计算机的磁盘上。。

该参数定义了全局检查点操作之间的时间间隔。默认值为2000毫秒。 milliseconds.

·         [NDBD]TimeBetweenInactiveTransactionAbortCheck

对于该参数指定的每个时间间隔,通过检查每个事务的定时器来执行超时处理。因此,如果该参数被设为1000毫秒,每隔1秒就会对事务进行检查。

该参数的默认值为1000毫秒(1秒)。

·         [NDBD]TransactionInactiveTimeout

如果事务目前未执行任何查询,而是等待进一步的用户输入,该参数指明了放弃事务之前用户能够等待的最长时间。

该参数的默认值是0(无超时)。对于需要确保无任何事务锁定了过长时间的数据库,应将参数设置为较小的值。单位为毫秒。

·         [NDBD]TransactionDeadlockDetectionTimeout

当节点执行涉及事务的查询时,在继续之前,节点将等待簇中其他节点作出回应。如果出现下述原因,将无法予以回应:

1.    节点“死亡”。

2.    操作进入锁定队列。

3.    被请求执行动作的节点负荷过重。

该超时参数指明了放弃事务之前,事务协调器等候另一节点执行查询的时间长短,该参数在节点失败处理和死锁检测方面十分重要。在涉及死锁和节点失败的情形下,如果将其设置的过高,会导致不合需要的行为。

默认的超时值为1200毫秒(1.2秒)。

·         [NDBD]NoOfDiskPagesToDiskAfterRestartTUP

执行本地检查点操作时,相应的算法会将所有数据页写入磁盘。如果追求尽快完成该操作而不是适中,很可能会对处理器、网络和磁盘带来过重负担。为了控制写入速度,该参数指明了每100毫秒可写入多少数据页。在本情形下,1个数据页定义为8KB,因而该参数的单位是每秒80KB。因此,如果将NoOfDiskPagesToDiskAfterRestartTUP设置为20,那么在执行本地检查点操作期间,要求每秒想磁盘写入1.6MB的数据。该值包括针对数据页的UNDO日志记录写入,也就是说,该参数能处理来自数据内存的写入限制。置于针对索引页的UNDO日志记录,它们是由参数NoOfDiskPagesToDiskAfterRestartACC处理的(关于索引页的更多信息,请参见关于IndexMemory的条目)。

简而言之,该参数指定了执行本地检查点操作的速度,并能与NoOfFragmentLogFiles、DataMemory和IndexMemory一起使用。

默认值是40(每秒3.2MB的数据页)。

·         [NDBD]NoOfDiskPagesToDiskAfterRestartACC

该参数使用的单位与NoOfDiskPagesToDiskAfterRestartTUP的相同,工作方式也类似,但限制的是从索引内存进行的索引页写入速度。

该参数的默认值为每秒20个索引内存页(1.6MB每秒)。

·         [NDBD]NoOfDiskPagesToDiskDuringRestartTUP

该参数的工作方式类似于NoOfDiskPagesToDiskAfterRestartTUP和NoOfDiskPagesToDiskAfterRestartACC,但仅对重启节点时在节点内执行的本地检查点操作有效。作为所有节点重启的组成部份,总会执行本地检查点操作。在节点重启过程中,能够以比其他时间更快的速度执行磁盘写入操作,这是因为,此时在节点内执行的活动数较少。

该参数涉及从数据内存写入的页。

默认值是40(3.2MB每秒)。

·         [NDBD]NoOfDiskPagesToDiskDuringRestartACC

在节点重启的本地检查点阶段,对能够写入到磁盘的索引内存页的数目进行控制。

与NoOfDiskPagesToDiskAfterRestartTUP和NoOfDiskPagesToDiskAfterRestartACC一样,该参数的值采用的单位也是每100毫秒写入8KB(80KB/秒)。

默认值是20 (1.6MB每秒)。

·         [NDBD]ArbitrationTimeout

该参数指定了数据节点等待仲裁程序对仲裁消息的回应的时间。如果超过了该时间,将假定网络已断开。

默认值是1000毫秒(1秒)。

缓冲和日志功能

一些与以前的编译时间参数对应的配置参数仍可用。使用这些参数,高级用户能够对节点进程使用的资源进行更多的控制,并能根据需要调整各种缓冲区大小。

将日志记录写入磁盘时,这些缓冲区用作文件系统的前端。如果节点运行在无盘模式下,那么可以将这些参数设置为它们的最小值而不会造成负面影响,这是因为,磁盘写入是由NDB存储引擎的文件系统提取层虚拟的。

·         [NDBD]UndoIndexBuffer

该缓冲用于本地检查点操作执行期间。NDB存储引擎采用了一种恢复方案,该方案建立在检查点一致性以及操作性REDO日志值上。为了在不隔断整个系统的写操作的情况下获得一致的检查点,在执行本地检查点操作的同时,将执行UNDO日志操作。UNDO日志功能每次是在单个表偏短上触发的。由于表全部保存在主内存中,该优化是可能的。

UNDO索引缓冲用于主键哈希索引上的更新。插入和删除操作会导致哈希索引的重新排列,NDB存储引擎将映射了所有物理变化的UNDO日志记录写入索引页,以便能在系统重启时撤销这些变化。它还能记录启动本地检查点操作时对每个偏短的所有插入操作。

读取和更新能够设置锁定位,并更新哈希索引条目中的标题。这类变更由页写入算法负责处理,以确保这些操作不需要UNDO日志。

该缓冲的默认大小为2MB。最小值为1MB,对于大多数应用,最小值已足够。对于执行极大和/或大量插入和删除操作、并处理大事务和大主键的应用程序,或许有必要增大该缓冲。如果该缓冲过小,NDB存储引擎会发出错误代码677“索引UNDO缓冲过载”。

·         [NDBD]UndoDataBuffer

UNDO数据缓冲的作用与UNDO索引缓冲的相同,不同之处在于,它作用在数据内存上而不是索引内存上。对于插入、删除和更新,该缓冲是在片段的本地检查点阶段使用的。

由于UNDO日志条目会随着所记录操作的增加而增大,该缓冲大于与之对应的索引内存缓冲,默认值为16MB。

对于某些应用程序,该内存可能过大。在这种情况下,可降低它的值,最小为1MB。

需要增加该缓冲的情况十分罕见。如果确实有这方面的要求,较好的方式是,检查磁盘是否能实际处理数据库更新活动所产生的负荷。如果缺少足够的磁盘空间,即使增加该缓冲的大小也不能解决问题。

如果该缓冲过小并变得“拥挤不堪”,NDB存储引擎将发出错误代码891“数据UNDO缓冲过载”。

·         [NDBD]RedoBuffer

所有的更新活动也需要被记录到日志中。使用这类日志,当系统重启时,能够重现这类更新。NDB恢复算法采用了“模糊”数据检查点和UNDO日志,然后使用REDO日志再现所有变化直至到达恢复点。

该缓冲的默认大小是8MB。最小值为1MB。

如果该缓冲过小,NDB存储引擎将发出错误代码1221“REDO日志缓冲过载”。

在管理簇的过程中,应能控制为各种事件类型发送至标准输出装置的日志消息的数目,这点十分重要。有16种可能的事件级别(编号从0到15)。如果将给定事件类别的事件通报级别设置为15,那么该类别中的所有事件报告均会被发送至标准输出装置,如果将其设置为0,表示在该类别中的没有事件报告。

默认情况下,仅会将启动消息发送至标准输出装置,其余的事件通报级别默认为0。这样做的原因在于,这些消息也会被发送至管理服务器的簇日志。

对于管理客户端,也能设置类似的级别,用以确定在簇日志中记录哪些级别的事件。

·         [NDBD]LogLevelStartup

通报级别,用于进程启动过程中生成的事件。

默认级别为1.

·         [NDBD]LogLevelShutdown

通报级别,用于作为节点恰当关闭进程组成部分而生成的事件。

默认级别为0.

·         [NDBD]LogLevelStatistic

通报级别,用于统计事件,如主键法读取次数,更新数目,插入数目,与缓冲使用有关的信息等。

默认级别为0.

·         [NDBD]LogLevelCheckpoint

通报级别,用于由本地和全局检查点操作生成的事件。

默认级别为0.

·         [NDBD]LogLevelNodeRestart

通报级别,用于在节点重启过程中生成的事件。

默认级别为0.

·         [NDBD]LogLevelConnection

通报级别,用于由簇节点间的连接生成的事件。

默认级别为0.

·         [NDBD]LogLevelError

通报级别,用于由在整个簇内的错误和警告生成的事件。这类错误不会导致任何节点失败,当仍值得通报。

默认级别为0.

·         [NDBD]LogLevelInfo

通报级别,用于为簇的一般状态信息而生成的事件。

默认级别为0.

备份参数

本节讨论的参数定义了与在线备份执行有关的内存缓冲集。

·         [NDBD]BackupDataBufferSize

在创建备份的过程中,为了将数据发送到磁盘,将使用两类缓冲。备份数据缓冲用于填充由扫描节点的表而记录的数据。一旦将该缓冲填充到了指定的水平BackupWriteSize(请参见下面的介绍),就会将页发送至磁盘。在将页写入磁盘的同时,备份进程能够继续填充该缓冲,直至其空间消耗完为止。出现该情况时,备份进程将暂停扫描,直至一些磁盘写入操作完成并释放了内存为止,然后扫描继续。

该参数的默认值为2MB。

·         [NDBD]BackupLogBufferSize

备份日志缓冲扮演的角色类似于备份数据缓冲,不同之处在于,它用于生成备份执行期间进行的所有表写入的日志。相同的原理也适用于备份数据缓冲情形下的页写入,不同之处在于,当备份日志缓冲中没有多余空间时,备份将失败。出于该原因,备份日志缓冲的大小应足以处理执行备份时产生的负载。

该参数的默认值对于大多数应用程序均是适当的。事实上,备份失败的原因更可能是因为磁盘写入速度不够,而不是备份日志缓冲变满。如果没有为应用程序产生的写负载配置磁盘子系统,簇很可能无法执行所需的操作。

最好按恰当的方式配置簇,使得处理器成为瓶颈而不是磁盘或网络连接。

默认值是2MB。

·         [NDBD]BackupMemory

该参数是BackupDataBufferSize和BackupLogBufferSize之和。

默认值是2MB + 2MB = 4MB。

·         [NDBD]BackupWriteSize

该参数指定了由备份日志缓冲和备份数据缓冲写入磁盘的消息大小。

默认值是32KB.
17.4.4.6. 定义MySQL簇内的SQL节点
在config.ini文件的[MYSQLD]部分中,定义了用于访问簇数据的MySQL服务器(SQL节点)的行为。不需要其中所给出的参数。如果未提供计算机或主机名,那么任何主机均能使用该SQL节点。

·         [MYSQLD]Id

该值用作节点的地址,供所有的簇内部消息使用,它必须是介于1和63之间的整数。在簇内,每个簇节点必须有唯一的ID。

·         [MYSQLD]ExecuteOnComputer

它引用的是在配置文件的[COMPUTER]部分定义的主机(计算机)之一。

·         [MYSQLD]ArbitrationRank

该参数用于定义可作为仲裁程序的节点。MGM节点和SQL节点均能成为仲裁程序。如果值为0,表明给定的节点永远不会用作仲裁程序,如果值为1,表明给定的节点在成为仲裁程序方面具有高的优先级,如果值为2,表明给定的节点在成为仲裁程序方面具有低的优先级。对于正常配置,使用管理服务器作为仲裁程序,将它的ArbitrationRank设置为1(默认),并将所有SQL节点的ArbitrationRank设置为0。

·         [MYSQLD]ArbitrationDelay

如果将该参数设置为除0(默认值)以外的其他值,表示仲裁程序对仲裁请求的相应将被延迟设定的毫秒数。通常不需要更改该值。

·         [MYSQLD]BatchByteSize

对于转换为全表扫描或对索引的范围扫描的查询,要想获得最佳性能,重要的是以恰当的大小获取记录。能够以记录数为单位(BatchSize)和字节为单位(BatchByteSize)设置恰当的大小。实际的批大小由两个参数限定。

查询的执行速度可能会出现40%的变化,具体情况取决于该参数的设置。在未来的版本中,MySQL服务器将根据查询类型恰当地设置与批大小相关的参数。

该参数以字节为单位,默认值是32KB。

·         [MYSQLD]BatchSize

该参数以记录数为单位,默认值是64。最大值为992。

·         [MYSQLD]MaxScanBatchSize

批大小指的是从各数据节点发送的每批数据的大小。大多数扫描均是以并行方式执行的,目的是为了防止MySQL服务器收到来自众多节点的过多数据,该参数对所有节点上的总的批大小进行了限制。

该参数的默认值为256KB。其最大大小为16MB。
17.4.4.7. MySQL簇TCP/IP连接

在MySQL簇中,TCP/IP是用于建立连接的默认传输协议。正常情况下不需要定义连接,这是因为,簇能自动建立数据节点间、数据节点与所有MySQL服务器节点、以及数据节点与管理服务器之间的连接(关于该规则的例外,,请参见17.4.4.8节,“使用直接连接的MySQL簇TCP/IP连接”)。

如果打算覆盖默认的连接参数,才需要定义连接。在这种情况下,至少需要定义NodeId1、NodeId2、以及打算更改的参数。

通过在[TCP DEFAULT]部分进行设置,也能更改这些参数的默认值。

·         [TCP]NodeId1 , [TCP]NodeId2

要想确定两个节点之间的连接,需要在配置文件的[TCP]部分中提供每个节点的ID。

·         [TCP]SendBufferMemory

在向操作系统发出调用请求之前,TCP传输器采用缓冲来保存所有的消息。当该缓冲达到64KB时,将发送其内容,执行完一组消息循环后,也将发送这些内容。为了处理临时过载情况,也能定义一个较大的发送缓冲。发送缓冲的默认值是256KB。

·         [TCP]SendSignalId

为了能够回扫分布式消息图,需要确定每条消息。将该参数设置为“Y”时,将通过网络传输消息ID。默认情况下禁止该特性。

·         [TCP]Checksum

该参数也是一个布尔参数(Y/N或1/0),默认情况下是禁止的。启用了该参数时,在将所有消息置于发送缓冲之前,将为所有参数计算校验和。使用该特性,当消息等候在发送缓冲中时,可以确保消息不会损坏,也能确保消息不会被传输机制破坏。

·         [TCP]PortNumber

(已过时)以前,该参数指定了用于监听来自其他节点的连接的端口号。不应再使用嘎参数。

·         [TCP]ReceiveBufferMemory

指定了从TCP/IP套接字接收数据时所使用的缓冲大小。几乎不需要更改该参数的默认值,默认值为64KB,但是如果打算节省内存,也能更改它。
17.4.4.8. 使用直接连接的MySQL簇TCP/IP连接
使用数据节点之间的直接连接建立簇时,需要在簇config.ini文件的[TCP]部分中明确指定如此连接的数据节点的交叉IP地址。

在下面的示例中,假定簇具有至少4台主机,1台用于管理服务器,一台用于SQL节点,两台用于数据节点。作为整体,簇位于LAN的172.23.72.*子网内。除了通常的网络连接外,两个数据节点使用标准的交叉电缆直接相连,并使用范围在1.1.0.*的IP地址彼此间直接通信,如下所示:

# Management Server

[NDB_MGMD]

Id=1

HostName=172.23.72.20

 

# SQL Node

[MYSQLD]

Id=2

HostName=172.23.72.21

 

# Data Nodes

[NDBD]

Id=3

HostName=172.23.72.22

 

[NDBD]

Id=4

HostName=172.23.72.23

 

# TCP/IP Connections

[TCP]

NodeId1=3

NodeId2=4

HostName1=1.1.0.1

HostName2=1.1.0.2

使用数据节点间的直接连接能够改善簇的整体效率,使用该方式,数据节点能绕过以太网设备,如交换器、Hub、路由器等,从而减少了簇的等待时间。注意,对于两个以上的数据节点,要想充分利用这类直接连接的优点,需要为各数据节点建立与相同节点组内的其他数据节点间的直接连接。
17.4.4.9. MySQL簇共享内存连接
MySQL簇将尝试使用共享内存传输器,并在可能的情况下自动配置它,尤其是在相同的簇主机上同时运行着1个以上的节点时。在MySQL簇的早期版本中,仅当使用--with-ndb-shm创建了-max二进制版本时,才支持共享内存段。明确地将共享内存定义为连接方法时,至少需要定义NodeId1、NodeId2和ShmKey。对于所有其他参数,应具有在大多数情况下均良好工作的默认值。

注释:SHM支持仍应被视为试验性的。

·         [SHM]NodeId1, [SHM]NodeId2

要想确定两个节点之间的连接,需要为每个节点提供节点ID,NodeId1和NodeId2。

·         [SHM]ShmKey

设置共享内存段时,节点ID用于唯一地确定通信所用的共享内存段。它以整数表示,没有默认值。

·         [SHM]ShmSize

每个SHM连接均有一个共享内存段,发送方将节点之间的消息置于该处,读取方从该处读取这类消息。gai 共享内存段的大小由ShmSize定义。默认值是1MB。

·         [SHM]SendSignalId

为了回扫分布式消息的路径,需要为每条消息提供唯一性ID。如果将该参数设置为“Y”,也能在网络上传输这类消息ID。默认情况下,该特性是禁止的。

·         [SHM]Checksum

该参数也是一种Y/N参数,默认情况下处于禁止状态。如果允许该参数,在将所有消息置于发送缓冲之前,对为所有消息计算校验和。

使用该特性,当消息等候在发送缓冲中时,能防止消息损坏。此外,它还能用于在传输过程中检查损坏的数据。
17.4.4.10. MySQL簇SCI传输连接
仅当使用--with-ndb-sci=/your/path/to/SCI创建了MySQL-Max二进制版本时,在MySQL簇中才支持使用SCI传输器来连接节点。path应指向包含最低库的目录,并应包括含SISCI库和头文件的目录。

此外,SCI要求专用硬件。

强烈建议,仅应为ndbd进程之间的通信使用SVI传输器。注意,使用SCI传输器意味着ndbd进程永不停止。因此,仅应在具有至少两块专供ndbd进程使用的CPU的机器上使用SCI传输器。每个ndbd进程至少应有1块CPU,至少还应有1块CPU用于处理操作系统的活动。

·         [SCI]NodeId1, [SCI]NodeId2

为了确定两个节点之间的连接,需要为每个节点提供节点ID,NodeId1和NodeId2。

·         [SCI]Host1SciId0

它用于确定第1个簇节点上的SCI节点ID(由NodeId1确定)。

·         [SCI]Host1SciId1

能够为两块SCI卡间的故障切换设置SCI传输器,这两块卡应使用节点之间的不同网络。它用于确定节点ID,以及在第1个节点上使用的第2块SCI卡。

·         [SCI]Host2SciId0

它用于确定第2个簇节点上的SCI节点ID(由NodeId2确定)。

·         [SCI]Host2SciId1

使用两块SCI卡来提供故障切换功能时,该参数用于确定将在第2个节点上使用的第2块SCI卡。

·         [SCI]SharedBufferSize

每个SCI传输器均有1个用于两节点间通信的共享内存段。可将该共享内存段设置为默认的1 MB,这足以应对大多数应用程序。如果使用较小的值,当执行大量并行插入操作时,会出现问题,如共享缓冲过小,还会导致ndbd进程崩溃。

·         [SCI]SendLimit

SCI媒介前面的小缓冲用于保存消息,在通过SCI网络传输这类消息前,会将它们保存在该缓冲内。它的默认值为8kB。按照我们的基准,在64KB时性能最好,但16kB仅有少量提升,即使大于8KB有好处,好处也不大。

·         [SCI]SendSignalId

为了跟踪分布式消息,需要唯一地确定每条消息。将该参数设置为“Y”时,就能在网络上传输消息ID。默认情况下禁止该特性。

·         [SCI]Checksum

T该参数也是一种布尔值,默认情况下,该参数是被禁止的。启用了Checksum(校验和)时,在将所有消息置于发送缓冲之前,将为所有参数计算校验和。使用该特性,当消息等候在发送缓冲中时,可以确保消息不会损坏。此外,它还能用于在传输过程中检查损坏的数据。
17.5. MySQL簇中的进程管理

17.5.1. 用于MySQL簇的MySQL服务器进程使用
17.5.2. ndbd,存储引擎节点进程
17.5.3. ndb_mgmd,“管理服务器”进程
17.5.4. ndb_mgm,“管理客户端”进程
17.5.5. 用于MySQL簇进程的命令选项

要想掌握管理MySQL簇的方法,需要了解4种基本进程。在本章下面的几节内,介绍了这些进程在簇内的作用,它们的使用方法,以及每种进程可用的启动选项。
17.5.1. 用于MySQL簇的MySQL服务器进程使用

mysqld是传统的MySQL服务器进程。要想与MySQL簇一起使用,所创建的mysqld应支持NDB簇存储引擎,就像在预编译的-max二进制版本中那样,http://dev.mysql.com/downloads/。

即使采用该方式创建了mysqld二进制版本,在默认情况下,NDB簇存储引擎仍处于禁止状态。要想启用NDB簇存储引擎,可使用两种可能的选项之一:

·         启动mysqld时,将“—ndbcluster”用作启动选项。

·         在my.cnf文件的[mysqld]部分插入包含ndbcluster的1行内容。

验证运行的服务器是否启用了NDB簇存储引擎的简单方法是,在MySQL监视器(mysql)中发出命令SHOW ENGINES。在列出NDBCLUSTER的行中应能看到值YES,如果在该行上看到NO(或在输出中未显示该行),你所运行的是未启用NDB功能的MySQl版本。如果在该行上看到DISABLED,就需采用上述两种方法之一启用它。

为了读取簇配置数据,MySQL服务器至少需要3种信息:

·         MySQL服务器自己的簇节点ID。

·         管理服务器(MGM节点)的主机名或IP地址。

·         与管理服务器相连的端口。

节点ID可动态分配,因此不一定需要明确指定它们。

mysqld参数ndb-connectstring用于指定连接字符串,或是在启动mysqld时在命令行上指定,或是在my.cnf文件中指定。连接字符串包含主机名或IP地址,以及能够发现管理服务器的端口。

在下面的示例中,ndb_mgmd.mysql.com是管理服务器所在的主机,管理服务器在端口1186上监听簇消息。

shell> mysqld --ndb-connectstring=ndb_mgmd.mysql.com:1186

关于连接字符串的更多信息,请参见17.4.4.2节,“MySQL簇连接字符串”。

给定该信息,MySQL服务器将成为簇中的完全参与者。(有时,我们也将运行在该方式下的mysqld进程称为SQL节点)。它能完全了解所有的簇数据节点以及它们的状态,并能建立与所有数据节点的连接。在这种情形下,它能将任何数据节点用作事务协调器,并能访问数据节点以执行读取和更新操作。
17.5.2. ndbd,存储引擎节点进程

ndbd是使用NDB簇存储引擎处理表中所有数据的进程。通过该进程,存储节点能够实现分布式事务处理,节点恢复,对磁盘的检查点操作,在线备份,以及相关的任务。

在MySQL簇中,一组ndbd进程能够共同处理数据。这些进程可以在相同的计算机(主机)上执行,也能在不同的计算机上执行。数据节点和簇主机之间的通信是完全可配置的。

Ndbd将生成一组日志文件,这些文件位于由配置文件中DataDir指定的目录下。下面列出了这些日志文件。注意,node_id代表节点的唯一ID。例如,ndb_2_error.log是由节点ID为2的存储节点生成的错误日志。

·         ndb_node_id_error.log是包含所引用ndbd进程所遇到的所有崩溃记录的文件。该文件中的每条记录均包含1个简要的错误字符串,以及对该崩溃跟踪文件的引用。该文件的典型条目与下面给出的类似:

·                Date/Time: Saturday 30 July 2004 - 00:20:01

·                Type of error: error

·                Message: Internal program error (failed ndbrequire)

·                Fault ID: 2341

·                Problem data: DbtupFixAlloc.cpp

·                Object of reference: DBTUP (Line: 173)

·                ProgramName: NDB Kernel

·                ProcessID: 14909

·                TraceFile: ndb_2_trace.log.2

·                ***EOM***

注释:请记住,错误日志文件中的最后1个条目并不必然是最新的(也不太可能),这点很重要。错误日志中的条目不是按时间顺序排列的,而是与ndb_node_id_trace.log.next(请参见下面的介绍)中定义的跟踪文件的顺序对应。因此,错误日志条目是按循环方式而不是顺序方式覆盖的。

·         ndb_node_id_trace.log.trace_id是准确描述了错误出现之时所发生情况的跟踪文件。该信息在MySQL簇开发团队进行分析时很有帮助。

能够对覆盖旧文件之前创建的跟踪文件的数目进行配置。trace_id是为每个连续的跟踪文件增加的编号。

·         ndb_node_id_trace.log.next是记录了要指定的下一个跟踪文件编号的文件。

·         ndb_node_id_out.log是包含ndbd进程的任何数据输出的文件。仅当将ndbd启动为端口监督程序时才会创建该文件。

·         ndb_node_id.pid是包含启动时作为端口监督程序的ndbd进程的进程ID的文件。它还能起到锁定文件的作用,以防止启动具有相同ID的节点。

·         ndb_node_id_signal.log是仅在ndbd调试版下使用的文件,它能跟踪ndbd进程中所有的入站、出站和内部消息。以及它们的数据。

建议不要使用通过NFS安装的目录,这是因为在某些情况下,如果pid-file上的锁定依旧有效,即使当进程中止后也会产生问题。

启动ndbd时,或许需要指定管理服务器的主机名以及监听的端口号。作为可选方式,也可以指定进程将使用的节点ID。

shell> ndbd --connect-string="nodeid=2;host=ndb_mgmd.mysql.com:1186"

关于这方面的额外信息,请参见17.4.4.2节,“MySQL簇连接字符串”。

启动ndbd时,它实际上将启动两种进程。第1种进程称为“angel process”(天使进程),它的唯一任务是发现执行进程在何时完成,然后重启ndbd进程(如果作了该配置的话)。因此,如果你打算使用Unix的kill命令杀死ndbd进程,就需要杀死这两个进程。中止ndbd进程的更恰当方法是使用管理客户端,并通过该管理客户端停止进程。

执行进程采用了1个线程,用于读取、写入和扫描数据,以及所有其他任务。该线程是异步实施的,以便能方便地处理数以千计的并发活动。此外,看门狗线程负责监督执行线程,以确保执行线程不会陷入无限循环。线程池负责处理文件I/O,每个线程均能处理一个打开的文件。这些线程也能被ndbd进程中的传输器用作传输器连接。在执行包含更新在内的大量操作的系统中,如果允许,ndbd进程能占用2个CPU。对于拥有多CPU的机器,建议使用属于不同节点组的数个ndbd进程。
17.5.3. ndb_mgmd,“管理服务器”进程

管理服务器是这样一种进程,它读取簇配置文件,并将该信息分配给簇中所有请求该信息的节点。它还负责维护簇活动的日志。管理客户端能够连接到管理服务器,并检查簇的状态。

启动管理服务器时,不是一定需要指定连接字符串。但是,如果使用了1个以上的管理服务器,应提供连接字符串,而且簇中的每个节点应明确指定自己的节点ID。

下述文件是由ndb_mgmd在其启动目录下创建或使用的,并会被置于配置文件中指定的DataDir中。在下面的列表中,node_id是唯一性节点ID。

·         config.ini是作为整体的簇的配置文件。该文件由用户创建并由管理服务器读取。在17.4节,“MySQL簇的配置”中,讨论了设置该文件的方法。

·         ndb_node_id_cluster.log是簇事件日志文件。这类事件的例子包括:检查点操作的启动和完成,节点启动事件,节点故障,以及内存使用水平。关于簇事件的完整列表和描述,请参见17.6节,“MySQL簇的管理”。

当簇日志的大小达到1MB时,文件将被重命名为ndb_node_id_cluster.log.seq_id,其中seq_id是簇日志文件的序列号(例如,如果编号1、2、3已存在,下一个日志文件将用4命名)。

·         ndb_node_id_out.log是将管理服务器用作端口监督程序时用于stdout和stderr的文件。

·         ndb_node_id.pid是将管理服务器用作端口监督程序时所使用的PID文件。
17.5.4. ndb_mgm,“管理客户端”进程

对于簇的运行,实际上不需要管理客户端的进程。其价值在于它提供了一组命令,这组命令可用于检查簇的状态、启动备份、并执行其他管理功能。管理客户端使用C API来访问管理服务器,高级用户也能使用C API来编制专用的管理进程来执行任务,这类任务与ndb_mgm执行的类似。

启动管理客户端时,需要提供管理服务器的主机名和端口号,如下例所示。默认值是localhost和1186。

shell> ndb_mgm localhost 1186

关于使用ndb_mgm的更多信息,请参见17.5.5.4节,“ndb_mgm的命令选项”和17.6.2节,“管理客户端”中的命令。
17.5.5. 用于MySQL簇进程的命令选项

17.5.5.1. 用于mysqld的与MySQL有关的命令选项
17.5.5.2. ndbd命令选项
17.5.5.3. ndb_mgmd的命令选项
17.5.5.4. ndb_mgm的命令选项

所有MySQL簇的可执行文件(除mysqld)均使用下述选项。早期MySQL簇版本的用户应注意,这些选项开关中的一些与MySQL 4.1簇中的相比有所改变,为的是保持它们之间的一致性,以及与mysqld的一致性。可以使用-?开关来查看支持的选项列表。

·         -?, --usage, --help

给出简明清单,以及可用命令选项的描述。

·         -V, --version

给出ndbd进程的版本号。该版本号是MySQL簇的版本号。版本号有一定的作用,这是因为并非所有的版本均能一起使用,而且在启动时,MySQL簇进程将验证二进制文件的版本是否能在同一簇内共存。执行MySQL簇的在线升级时,它也很重要(请参见MySQL簇的软件升级)。

·         -c connect_string, --connect-string

connect_string作为命令选项,用于设置与管理服务器的连接字符串。

shell> ndbd --connect-string="nodeid=2;host=ndb_mgmd.mysql.com:1186"

·         --debug[=options]

该选项仅能用于具有调试功能的版本。使用它,能够以与mysqld进程相同的方式允许来自调试调用的输出。

·         -e, --execute

使用它,能够从系统shell将命令发送至簇执行程序,如:

shell> ndb_mgm -e show



shell> ndb_mgm --execute="SHOW"

等效于

NDB> SHOW;

它类似于“-e”选项与mysql命令行客户端一起工作的方式。请参见4.3.1节,“在命令行上使用选项”。
 
17.5.5.1. 用于mysqld的与MySQL有关的命令选项

·         --ndbcluster

如果二进制版本包含对NDB簇存储引擎的支持,可使用该选项覆盖对NDB簇存储引擎(简称为NDB存储引擎)的默认禁止设置。使用MySQL簇时,NDB簇存储引擎是必要的。

·         --skip-ndbcluster

禁止NDB簇存储引擎。对于包含该功能的二进制版本,在默认情况下,该功能是被禁止的,换句话讲,NDB簇存储引擎处于禁止状态,直至使用“—ndbcluster”选项激活了它为止。仅当所编译的服务器支持NDB簇存储引擎时,才能使用该选项。

·         --ndb-connectstring=connect_string

使用NDB存储引擎时,通过设置该选项,能够指定分配簇配置数据的管理服务器。
17.5.5.2. ndbd命令选项

关于某些常见选项的更多信息,请参见17.5.5节,“用于MySQL簇进程的命令选项”。

·         -d, --daemon

通知ndbd作为daemon(端口监督程序)进程执行(默认行为)。

·         --nodaemon

指明ndbd不得作为daemon(端口监督程序)进程启动。调试ndbd以及希望将输出重定向到屏幕时,它很有用。

·         --initial

通知ndbd执行初始化启动。初始化启动将删除以前ndbd实例为恢复目的创建的任何文件。它还能重新创建恢复用日志文件。注意,在某些操作系统上,该进程可能会占用较长的时间。

仅在首次启动ndbd进程时才应使用—initial启动,这是因为它将删除簇文件系统的所有文件,并再次创建所有的REDO日志文件。该规则的例外如下:

o        执行那些会更改文件内容的软件升级时。

o        用新的ndbd版本重启节点时。

o        出于某种原因,节点重启或系统重启不断失败时的最后手段。在这类情形下,请注意,由于数据文件的损坏,不能使用该节点来恢复数据。

该选项不影响那些已被受影响节点创建的备份文件。

·         --nostart

指示ndbd不自动启动。使用该选项时,ndbd连接到管理服务器,从管理服务器获取配置数据,并初始化通信对象。但是,在管理服务器特别要求之前,它不会实际启动执行引擎。通过向管理客户端发出恰当的命令,可完成该任务。
17.5.5.3. ndb_mgmd的命令选项

关于某些常见选项的更多信息,请参见17.5.5节,“用于MySQL簇进程的命令选项”。

·         -f filename, --config-file=filename, (OBSOLETE): -c filename

通知管理服务器应使用哪个文件作为其配置文件。必须指定该选项。文件名默认为config.ini。注意,“-c”快捷方式已过时,不应在新的安装实例中使用它。

·         -d, --daemon

指示ndb_mgmd作为端口监督程序启动。这是默认行为。

·         --nodaemon

指示管理服务器不作为端口监督程序启动。
17.5.5.4. ndb_mgm的命令选项

关于某些常见选项的更多信息,请参见17.5.5节,“用于MySQL簇进程的命令选项”。

·         [host_name [port_num]]

要想启动管理客户端,需要指定管理服务器所在的位置,即指定主机名和端口。默认的主机名是localhost,默认端口是1186。

·         --try-reconnect=number

如果与管理服务器的连接断开,每隔5秒,节点将尝试再次连接到管理服务器,直至成功。使用该选项,能够将尝试的字数限制在number指定的值,超过该限制后,将放弃尝试并通报错误。
17.6. MySQL簇的管理

17.6.1. MySQL簇的启动阶段
17.6.2. “管理客户端”中的命令
17.6.3. MySQL簇中生成的事件报告
17.6.4. 单用户模式
17.6.5. MySQL簇的联机备份

管理MySQL簇涉及众多任务,首先是配置和启动MySQL簇。详情请参见17.4节,“MySQL簇的配置”和17.5节,“MySQL簇中的进程管理”。

下面介绍了MySQL簇的管理事宜。

有两种积极管理MySQL簇的基本方法。第1种方法是,使用在管理客户端中输入的命令,借此可检查簇的状态,更改日志级别,启动和停止备份,以及启动和停止节点。对于第2种方法,需要研究管理服务器DataDir目录下簇日志文件ndb_node_id_cluster.log的内容。(node_id代表其活动已被记录的节点的唯一ID)。簇日志包含由ndbd生成的事件报告。也能将簇日志条目发送到Unix的系统日志中。
17.6.1. MySQL簇的启动阶段

本节介绍了启动簇时涉及的步骤。

有数种不同的启动类型和模式,如下所述:

·         首次启动:在所有节点上与干净的文件系统一起启动簇。这或是出现在首次启动簇时,或是使用“--initial”选项重启簇时。

·         系统重启:簇启动并读取保存在数据节点中的数据。这出现在下述情况下:使用完后关闭了簇,并希望从簇的停止点恢复簇操作时。

·         节点重启:这是在簇运行的同时簇节点的在线重启。

·         首次节点重启:与节点重启类似,差别在于将再次初始化节点,并与干净的文件系统一起启动。

启动之前,必须对每个节点进行初始化操作(ndbd进程)。这包括下述步骤:

1.    获取节点ID。

2.    获取配置数据。

3.    为节点间的通信分配端口。

4.    根据从配置文件获得的设置分配内存。

一旦完成了对各节点的初始化操作,将进入簇启动进程。在该进程中,簇将经历下述阶段:

·         阶段0

清理簇文件系统。仅当使用“--initial”选项启动簇时,才会出现。

·         阶段1

建立簇连接,建立节点间的通信。启动簇“心跳”机制。

·         阶段2

选举仲裁程序节点。

如果这是系统重启阶段,簇将确定最近的可恢复全局检查点。

·         阶段3

该阶段包括众多内部簇变量的初始化。

·         阶段4

对于初始启动或初始节点重启,将创建redo日志文件。这类文件的数目等于NoOfFragmentLogFiles/

对于系统重启:

o        读取方案。

o        从本地检查点和undo日志读取数据。

o        应用所有的redo信息,直至到达最近的可恢复检查点为止。

对于节点重启,找到redo日志的末尾。

·         阶段5

如果这是首次启动,将创建SYSTAB_0和NDB$EVENTS内部系统表。

对于节点重启或首次节点重启:

o        节点包含在事务处理操作中。

o        将节点的方案与主服务器的方案进行比较,并与其同步。

o        对所收到的、来自本节点所在节点组内其他节点的、INSERT形式的数据进行同步。

o        在任何情形下,等待由仲裁程序判定的本地检查点操作的完成。

·         阶段6

更新内部变量。

·         阶段7

更新内部变量。

·         阶段8

在系统重启中,重建所有的索引。

·         阶段9

更新内部变量。

·         阶段10

在节点重启或首次节点重启的这一阶段,API可能会连接到节点,并接收事件。

·         阶段11

在节点重启或首次节点重启的这一阶段,将事件传递任务提交给加入簇的节点。新加入的节点负责将其主要数据传递给订方。

对于首次启动或系统重启,一旦该进程完成,将启用事务处理功能。对于节点重启或首次节点重启,启动进程的完成意味着节点现在能够成为事务协调程序。
17.6.2. “管理客户端”中的命令
除了中央配置文件外,还能通过管理客户端ndb_mgm提供的命令行界面对簇进行控制。这是运行簇的主要管理界面。

管理客户端提供了下述基本命令。在下面给出的清单中,node_id指的是数据库节点ID或关键字ALL,指明命令将应用到所有的簇数据节点上。

·         HELP

显示关于所有可能命令的信息。

·         SHOW

显示关于簇状态的信息。

注释:在使用多个管理节点的簇中,该命令仅显示与当前管理服务器实际相连的数据节点的信息。

·         node_id START

启动由node_id标识的数据节点(或所有数据节点)。

·         node_id STOP

停止由node_id标识的数据节点(或所有数据节点)。

·         node_id RESTART [-N] [-I]

重启由node_id标识的数据节点(或所有数据节点)。

·         node_id STATUS

显示由node_id标识的数据节点(或所有数据节点)的状态信息。

·         ENTER SINGLE USER MODE node_id

进入单用户模式,仅允许由节点ID“node_id”标识的MySQL服务器访问数据库。

·         EXIT SINGLE USER MODE

退出单用户模式,允许所有的SQL节点(即所有运行的mysqld进程)访问数据库。

·         QUIT

中止管理客户端。

·         SHUTDOWN

关闭除SQL节点之外的所有簇节点,并退出。

在下一节中,介绍了用于事件日志的命令。在这些议题的单独一节中,介绍了用于创建备份以及从备份中恢复的命令。
17.6.3. MySQL簇中生成的事件报告

17.6.3.1. 登记管理命令
17.6.3.2. 日志事件

在本节中,我们讨论了MySQL簇提供的事件日志的类型,以及记录的事件类型。

MySQL簇提供了两种事件日志。它们是cluster log和node logs,cluster log(簇日志)包括由所有簇节点生成的事件,node logs(节点日志)仅记录每个数据节点的本地事件。

由簇事件日志功能生成的输出可以有多个目的地,包括文件、管理服务器控制台窗口、或syslog。由节点事件日志功能生成的输出将被写入数据节点的控制台窗口。

可以对这两类事件日志进行设置,使之记录不同的事件子集。

注释:簇日志是为大多数使用场合推荐的日志,这是因为它在1个文件中提供了关于整个簇的日志信息。节点日志仅应在应用程序的开发过程中使用,或用于调试应用程序代码。

可根据三种不同的判据识别每个值得通报的事件:

·         Category(类别):可以是下述值之一:STARTUP, SHUTDOWN, STATISTICS, CHECKPOINT, NODERESTART, CONNECTION, ERROR,或INFO。

·         Priority(优先级):由从1到15的数字表示,“1”表示“最重要”,“15”表示“最不重要”。

·         Severity Level(严重级别):可以是下述值之一:ALERT, CRITICAL, ERROR, WARNING, INFO, 或DEBUG。

无论是簇日志还是节点日志,都能根据这些属性进行过滤。
17.6.3.1. 登记管理命令

下述管理命令与簇日志有关:

·         CLUSTERLOG ON

打开簇日志。

·         CLUSTERLOG OFF

关闭簇日志。

·         CLUSTERLOG INFO

关于簇日志设置的信息。

·         node_id CLUSTERLOG category=threshold

用小于或等于threshold的优先级将category事件记录到簇日志。

·         CLUSTERLOG FILTER severity_level

将簇事件日志切换为指定的severity_level。

在下表中,介绍了簇日志类别阈值的默认设置(对于所有数据节点)。如果事件的优先级值低于或等于优先级阈值,就会在簇日志中记录。

注意,事件是按数据节点通报的,可在不同的节点上设置不同的阈值。

类别
    

默认阈值(所有数据节点)

STARTUP
    

7

SHUTDOWN
    

7

STATISTICS
    

7

CHECKPOINT
    

7

NODERESTART
    

7

CONNECTION
    

7

ERROR
    

15

INFO
    

7

阈值用于过滤每种类别中的事件。例如,对于优先级为3的STARTUP事件,不会将其记录到日志中,除非将STARTUP的阈值更改为3或更小。如果阈值为3,仅发送优先级等于或小于3的事件。

下面给出了事件的严重级别(注释:它们与Unix的syslog级别对应;但LOG_EMERG和LOG_NOTICE除外,未使用或未映射它们):

1
    

ALERT
    

应立刻更正的状况,如损坏的系统数据库。

2
    

CRITICAL
    

临界状况,如设备错误或资源不足。

3
    

ERROR
    

应予以更正的状况,如配置错误等。

4
    

WARNING
    

不能称其为错误的状况,但仍需要特别处理。

5
    

INFO
    

通报性消息。

6
    

DEBUG
    

调试消息,用于NDB簇开发。

可以打开或关闭事件严重级别。如果打开了事件严重级别,那么优先级等于或低于类别阈值的事件均将被记录。如果关闭了事件严重级别,那么将不记录属于该严重级别的任何事件。
17.6.3.2. 日志事件

事件日志中记录的事件报告采用下述格式:datetime [string] severity – message。例如:

09:19:30 2005-07-24 [NDB] INFO -- Node 4 Start phase 4 completed

本节讨论了所有值得通报的事件,按类别以及每一类别中的严重级别排序。

CONNECTION事件

这类事件与簇节点之间的连接有关。

事件
    

优先级
    

严重级别
    

描述

DB节点已连接
    

8
    

INFO
    

数据节点已连接

DB节点断开连接
    

8
    

INFO
    

数据节点断开连接

通信关闭
    

8
    

INFO
    

SQL节点或数据节点的连接已关闭

通信打开
    

8
    

INFO
    

SQL节点或数据节点的连接已打开

CHECKPOINT事件

下面给出的日志消息与检查点有关。

(注释:GCP =全局检查点,LCP =本地检查点)。

事件
    

优先级
    

严重级别
    

描述

在calc keep GCI中,LCP已停止
    

0
    

ALERT
    

LCP已停止

本地检查点片段完成
    

11
    

INFO
    

片段上的LCP已完成

全局检查点完成
    

10
    

INFO
    

GCP完成

全局检查点启动
    

9
    

INFO
    

启动GCP:将REDO日志写入磁盘

本地检查点完成
    

8
    

INFO
    

LCP已正常完成

本地检查点启动
    

7
    

INFO
    

启动LCP:将数据写入磁盘

报告undo日志已封闭
    

7
    

INFO
    

UNDO日志已封闭:缓冲快要溢出

STARTUP事件

下述事件是在成功或失败的节点启动或簇启动时生成的。它们还提供了与启动进程进展状况有关的信息,包括与日志活动有关的信息。

事件
    

优先级
    

严重级别
    

描述

收到内部启动信号STTORRY
    

15
    

INFO
    

重启完成后收到的信息块

Undo记录已执行
    

15
    

INFO
    

 

新的REDO日志已启动
    

10
    

INFO
    

GCI保持X,最新的可恢复GCI Y

新日志已启动
    

10
    

INFO
    

日志部分X,启动MB Y,停止MB Z

拒绝将节点纳入簇中
    

8
    

INFO
    

由于配置错误、无法建立通信、或其他问题,不能将节点包含在簇中。

DB节点邻居
    

8
    

INFO
    

显示附近的数据节点。

DB节点启动阶段X完成
    

4
    

INFO
    

数据节点启动阶段已完成。

节点已被簇成功接纳
    

3
    

INFO
    

显示节点,管理节点,以及动态ID。

DB节点启动阶段已开始
    

1
    

INFO
    

NDB簇节点正在启动。

DB节点的所有启动阶段已完成
    

1
    

INFO
    

NDB簇节点已启动。

DB节点关闭操作已启动
    

1
    

INFO
    

数据节点的关闭操作已开始

DB节点关闭操作失败
    

1
    

INFO
    

无法正常关闭数据节点。

NODERESTART事件

下述事件是在重启节点时产生的,并与节点重启进程的成功或失败相关。

事件
    

优先级
    

严重级别
    

描述

节点失败阶段完成
    

8
    

ALERT
    

通报节点失败阶段的完成

节点失败,节点状态为X
    

8
    

ALERT
    

通报节点已失败

通报仲裁程序结果
    

2
    

ALERT
    

对于仲裁尝试,有8种不同的可能结果:

·         仲裁检查失败,剩余节点少于1/2。

·         仲裁检查成功,节点组多数

·         仲裁检查失败,丢失节点组

·         网络分区,要求仲裁

·         仲裁成功,来自节点X的正面回应

·         仲裁失败,来自节点X的负面回应

·         网络分区,无可用的仲裁程序

·         网络分区,未配置仲裁程序

完成了片段复制
    

10
    

INFO
    

 

完成了目录信息复制
    

8
    

INFO
    

 

完成了分配信息复制
    

8
    

INFO
    

 

开始复制片段
    

8
    

INFO
    

 

完成了所有片段的复制
    

8
    

INFO
    

 

GCP接收已启动
    

7
    

INFO
    

 

GCP接收已完成
    

7
    

INFO
    

 

LCP接收已启动
    

7
    

INFO
    

 

LCP接收已完成(状态= X)
    

7
    

INFO
    

 

通报是否发现了仲裁程序
    

6
    

INFO
    

搜索仲裁程序时,有7种可能的结果:

·         管理服务器重启仲裁线程[state=X]

·         准备仲裁程序节点X [ticket=Y]

·         接收仲裁程序节点X [ticket=Y]

·         启动仲裁程序节点X [ticket=Y]

·         丢失了仲裁程序节点X – 进程失败 [state=Y]

·         丢失了仲裁程序节点X – 进程退出 [state=Y]

·         丢失了仲裁程序节点X <error msg> [state=Y]

STATISTICS事件

下述事件具有统计特性。它们提供了相应的信息,如事务和其他操作的数目,低浓度节点发送或接收的数据量,以及内存使用率等。

事件
    

优先级
    

严重级别
    

描述

通报作业日程统计
    

9
    

INFO
    

平均的内部作业日程统计

发送的字节数
    

9
    

INFO
    

发送至节点X的平均字节数

接收的自己#
    

9
    

INFO
    

从节点X接收的平均字节数

通报事务统计
    

8
    

INFO
    

事务数目,提交次数,读取次数,简单读取次数,写入次数,并发操作数目。属性信息,以及放弃次数

通报操作
    

8
    

INFO
    

操作数目

通报表创建
    

7
    

INFO
    

 

内存使用
    

5
    

INFO
    

数据内存和索引内存的使用率(80%、90%和100%)

ERROR事件

这些事件与簇错误和告警有关,如果出现1个或多个这类事件,表明出现了重大故障或失败。

事件
    

优先级
    

严重级别
    

描述

因失去心跳而死亡
    

8
    

ALERT
    

因失去心跳而声明节点X死亡。

传输器错误
    

2
    

ERROR
    

 

传输器告警
    

8
    

WARNING
    

 

失去心跳
    

8
    

WARNING
    

节点X失去心跳#Y

一般性告警事件
    

2
    

WARNING
    

 

INFO事件

这些事件给出了关于簇状态和簇维护活动的一般信息,如日志和心跳传输等。

事件
    

优先级
    

严重级别
    

描述

发出心跳
    

12
    

INFO
    

将心跳发送至节点X

创建日志字节
    

11
    

INFO
    

日志部分,日志文件,MB

一般信息事件
    

2
    

INFO
    

 
17.6.4. 单用户模式

采用单用户模式,数据库管理员能够将对数据库系统的访问限制在1个MySQL服务器(SQL节点)。进入单用户模式时,与所有其他MySQL服务器的所有连接均将恰当关闭,而且所有正在运行的事务均将被放弃。不允许启动任何新事务。

一旦簇进入单用户模式,只有指定的SQL节点才有权访问数据库。使用ALL STATUS命令,可查看簇进入单用户模式的时间。

示例:

NDB> ENTER SINGLE USER MODE 5

执行该命令而且簇进入单用户模式后,节点ID为5的SQL节点将成为簇中唯一允许的用户。

上述命令中指定的节点必须是MySQL服务器节点,如果指定任何其他类型的节点,将被拒绝。

注释:执行上述命令时,指定节点上所有正在运行的事务均将被放弃,连接关闭,而且必须重启服务器。

使用EXIT SINGLE USER MODE命令,能够将簇数据节点的状态从单用户模式更改为正常模式。对于等待连接的MySQL服务器(即,对于即将准备就绪并可用的簇),现在允许进行连接。在状态变化期间和变化之后,指定为单用户SQL节点的MySQL服务器将继续运行(如果仍连接的话)。

示例:

NDB> EXIT SINGLE USER MODE

运行在单用户模式下时,如果节点失败,推荐的处理方法是:

    *  

1.    结束所有的单用户模式事务。

2.    发出EXIT SINGLE USER MODE命令。

3.    重启簇的数据节点。



·         进入单用户模式之前重启数据库。
17.6.5. MySQL簇的联机备份

17.6.5.1. 簇备份概念
17.6.5.2. 使用管理服务器创建备份
17.6.5.3. 如何恢复簇备份
17.6.5.4. 簇备份的配置
17.6.5.5. 备份故障诊断与排除

在本节中,介绍了创建备份的方法,以及从备份恢复数据库的方法。
17.6.5.1. 簇备份概念
备份指的是在给定时间对数据库的快照。备份包含三个主要部分:

·         Metadata(元数据):所有数据库表的名称和定义。

·         Table records(表记录):执行备份时实际保存在数据库表中的数据。

·         Transaction log(事务日志):指明如何以及何时将数据保存在数据库中的连续记录。

每一部分均会保存在参与备份的所有节点上。在备份过程中 ,每个节点均会将这三个部分保存在磁盘上的三个文件中:

·         BACKUP-backup_id.node_id.ctl

包含控制信息和元数据的控制文件。每个节点均会将相同的表定义(对于簇中的所有表)保存在自己的该文件中。

·         BACKUP-backup_id-0.node_id.data

包含表记录的数据文件,它是按片段保存的,也就是说,在备份过程中,不同的节点会保存不同的片段。每个节点保存的文件以指明了记录所属表的标题开始。在记录清单后面有一个包含关于所有记录校验和的脚注。

·         BACKUP-backup_id.node_id.log

包含已提交事务的记录的日志文件。在日志中,仅保存已在备份中保存的表上的事务。参与备份的节点将保存不同的记录,这是因为,不同的节点容纳了不同的数据库片段。

在上面所列的内容中,backup_id指的是备份ID,node_id是创建文件的节点的唯一ID。
17.6.5.2. 使用管理服务器创建备份

开始备份前,请确保已为备份操作恰当地配置了簇。(请参见17.6.5.4节,“簇备份的配置”)。

使用管理服务器创建备份包含以下步骤:

1.    启动管理服务器(ndb_mgm)。

2.    执行命令START BACKUP。

3.    管理服务器将用消息“指示备份开始”作出应答。这意味着管理服务器将请求提交给了簇,但尚未收到任何回应。

4.    管理服务器回复“备份backup_id开始”,其中,backup_id是该备份的唯一ID(如果未作其他配置,该ID还将保存在簇日志中)。这意味着簇已收到并开始处理备份请求。它不表示备份已完成。

5.    管理服务器发出消息“备份backup_id完成”,通知备份操作已结束。

要想放弃正在处理的备份:

1.    启动管理服务器。

2.    执行命令ABORT BACKUP backup_id。backup_id是当备份开始时包含在管理服务器应大中的备份ID(在消息“备份backup_id启动”中)。

3.    管理服务器用消息“放弃指示的备份backup_id”确认放弃请求,注意,尚未收到对该请求的实际回应。

4.    一旦放弃了备份,管理服务器将通报“备份backup_id因XYZ而放弃”。这意味着簇中止了备份,并从簇文件系统中删除了与该备份有关的所有文件。

在系统shell中使用下述命令,也能放弃正在执行的备份:

shell> ndb_mgm -e "ABORT BACKUP backup_id"

注释:执行放弃操作时,如果没有ID为backup_id的备份,管理服务器不会给出任何明确回应。但是,所发出的无效放弃命令将在簇日志中给出。
17.6.5.3. 如何恢复簇备份

簇恢复程序是作为单独的命令行实用工具ndb_restore实现的,它将读取由备份创建的文件,并将保存的信息插入数据库。必须为每组备份文件执行恢复程序,也就是说,执行次数与创建备份时运行的数据库节点数相同。

首次执行恢复程序时,还需要恢复元数据。换句话讲,必须重新创建数据库表(注意,开始执行恢复操作时,簇中应有一个空数据库)。恢复程序对于簇来说相当于API,因此,需要一个空闲连接,以便与簇相连。可使用ndb_mgm命令SHOW(在系统shell下使用ndb_mgm -e SHOW即可完成该操作)进行验证。可以使用开关“-c connectstring”来确定MGM节点的位置。(关于连接字符串的更多信息,请参见17.4.4.2节,“MySQL簇连接字符串”。备份文件必须位于恢复程序参量给定的目录下。

能够使用与创建是所用配置不同的配置将备份恢复到数据库。例如,对于备份ID为12的备份,该备份是在具有两个数据库节点(节点ID无恶2和3)的簇中创建的,可以将其恢复到具有4个节点的簇中。这样,ndb_restore必须运行两次,为创建备份时的每个数据库节点运行一次。

注释:对于快速恢复,能够以并行方式恢复数据,但必须有足够的可用簇连接。然而,数据文件必须始终前于日志文件。
17.6.5.4. 簇备份的配置

有4个用于备份的基本配置参数:

·         BackupDataBufferSize

将数据写入磁盘之前用于对数据进行缓冲处理的内存量。

·         BackupLogBufferSize

将日志记录写入磁盘之前用于对其进行缓冲处理的内存量。

·         BackupMemory

在数据库节点中为备份分配的总内存。它应是分配给备份数据缓冲的内存和分配给备份日志缓冲的内存之和。

·         BackupWriteSize

写入磁盘的块大小。它适用于备份数据缓冲和备份日志缓冲

关于这些参数的更多细节,请参见17.4节,“MySQL簇的配置”。
17.6.5.5. 备份故障诊断与排除
如果在发出备份请求时返回了错误代码,最可能的原因是内存不足,或磁盘空间不足。应检查是否为备份分配了足够的内存。此外,还应检查在备份目标的硬盘分区上是否有足够的空间。

NDB不支持重复的读取操作,对于恢复进程,这类操作会导致问题。尽管备份进程“hot”,但从备份恢复MySQL簇并不是100%“hot”的进程。其原因在于,在恢复进程执行期间,正在运行的事务会从已恢复的数据中获取不可重复的读。这意味着,当恢复正在进行时,数据的状态是不一致的。
17.7. 使用与MySQL簇的高速互连

17.7.1. 配置MySQL簇以使用SCI套接字
17.7.2. 理解簇互连的影响

即使在1996年开始NDB簇的设计之前,在创建并行数据库的过程中遇到的一个主要问题显然是网络中节点之间的通信问题。正因为如此,从开始设计时,NDB簇就允许使用多种不同的数据传输机制。在本手册中,我们使用了术语传输器。

目前,MySQL簇代码库包含对四种不同传输器的支持。目前的大多数用户采用的是以太网上的TCP/IP,这是因为它无处不在。它也是迄今为止在MySQL簇中经过最佳测试的传输器。

我们正在努力,以确保与ndbd进程的通信是尽可能大的“组块”,这是因为,它有利于所有的数据传输类型。

对于需要它的用户,也能使用簇互联以进一步增强性能。实现它的方式有两种:或使用能处理该情况的定制传输器,或使用能绕过TCP/IP堆栈的套接字实施方案。我们使用由Dolphin开发的SCI(规模可扩展的计算机连接接口)技术,对这两类技术进行了试验。
17.7.1. 配置MySQL簇以使用SCI套接字

在本节中,我们将介绍如何改编为常规TCP/IP通信配置的簇,以使用SCI套接字。本文档基于2004年10月1日发布的SCI套接字2.3.0版。

前提条件

对于任何打算使用SCI套接字的机器,都必须配备SCI卡。

能够与任何版本的MySQL簇一起使用SCI套接字。不需要特殊创建,这是因为它采用了MySQL簇中已提供的正常套接字调用。但是,目前仅在Linux 2.4和2.6内核上才支持SCI套接字。尽管到目前为止我们仅在Linux 2.4上核实了这点,在一些其他操作系统上也成功测试了SCI传输器。

对于SCI套接字,有四种基本要求:

1.    创建SCI套接字库。

2.    安装SCI套接字内核库。

3.    安装1个或2个配置文件。

4.    必须为整个机器或启动MySQL簇进程的shell启用SCI套接字库。

对于打算将SCI套接字用于节点间的通信的簇,需要为簇中的每台机器重复该进程。

要想使SCI套接字工作,需要获得两个软件包:

1.    包含用于SCI套接字库的DIS支持库的源码软件包。

2.    用于SCI套接字库本身的源码软件包。

目前,仅以源码格式提供了它们。编写本文档时可用的最新版本的软件包分别是DIS_GPL_2_5_0_SEP_10_2004.tar.gz和SCI_SOCKET_2_3_0_OKT_01_2004.tar.gz。在下述站点,可找到它们(也可能找到更新的版本):http://www.dolphinics.no/support/downloads.html。

软件包安装

获得库软件包后,接下来应将它们解包到恰当的目录下,将SCI套接字库解包到DIS代码下的目录中。然后,需要创建库。在下面的示例中,给出了在Linux/x86平台上执行该任务所需的命令:

shell> tar xzf DIS_GPL_2_5_0_SEP_10_2004.tar.gz

shell> cd DIS_GPL_2_5_0_SEP_10_2004/src/

shell> tar xzf ../../SCI_SOCKET_2_3_0_OKT_01_2004.tar.gz

shell> cd ../adm/bin/Linux_pkgs

shell> ./make_PSB_66_release

能够为64位处理器创建这些库。要想为使用64位扩展的Opteron CPU创建这些库,请运行make_PSB_66_X86_64_release而不是make_PSB_66_release。如果创建工作是在Itanium机器上进行的,应使用make_PSB_66_IA64_release。X86-64变体应能与Intel EM64T体系结构一起工作,但这点尚未测试(就我们所知)。

完成创建进程后,在zipped tar文件中可发现已编译好的库,文件的名称符合DIS-<operating-system>-time-date。现在,可将软件包安装到恰当位置。在本例中,我们使用的安装目录是/opt/DIS(注释:你很可能需要以系统根用户的身份运行下述命令):

shell> cp DIS_Linux_2.4.20-8_181004.tar.gz /opt/

shell> cd /opt

shell> tar xzf DIS_Linux_2.4.20-8_181004.tar.gz

shell> mv DIS_Linux_2.4.20-8_181004 DIS

网络配置

至此,所有的库和二进制文件均已安装到了恰当的位置,还需确保SCI卡在SCI的地址空间范围内拥有恰当的节点ID。

进行后面的操作前,还需确定网络结构。对于该情形,有三种可使用的网络结构:

·         一维单环网络。

·         1个或多个SCI交换器,每个交换器端口有1个环形网。

·         2维或3维环面网。

每类拓扑结构均有自己的、用于提供节点ID的方法。下面给出了简要讨论。

单环网采用的节点ID是4的非0倍数,4,8,12…。

下一个可行的选择是SCI交换器。1个SCI交换器有8个端口,每个端口都能支持1个环形网。有必要确保不同的环形网使用了不同的节点ID空间。对于典型的配置,第1个端口使用的节点ID低于64(4~60),接下来的64个节点ID(68~124)指定给下一个端口,依此类推,节点ID 452~508将被指定给第8个端口。

对于2维或3维环面网结构,每个节点位于各维中,在第1维中,各节点的增量为4,在第2维中,增量为64,在第3维中(如果适用的话),增量为1024。关于更详尽的文档,请参见Dolphin的网站。

在我们的测试中,采用了交换器方式,但大多数大型簇安装应用使用的是2维或3维环面网结构。采用交换器方式具有的优点是:使用双SCI卡和双交换器,能够相对容易地创建冗余网络,SCI网络上的平均故障切换时间在100毫秒量级。对于SCI套接字实施,MySQl簇中的SCI传输器支持该结构,而且它也处于发展当中。

对于2D/3D环面网,故障切换也是可能的,但需要向所有节点发送发出新的路由索引。然而,这仅需要100毫秒左右就能完成,对于大多数高可用性情形,这是可接受的。

通过将簇数据节点恰当地置于交换式体系结构中,能够使用2个交换器来构建结构,将16台计算机互联在一起,任何单点故障均不会妨碍1台以上的计算机。对于32台计算机和2台交换器,也能以这样的方式配置簇,任何单点故障均不会导致2个以上的节点丢失,在此情况下,也能知道那一对节点收到影响。因此,通过将两个节点置于不同的节点组,就能创建“安全的”MySQL簇。

要想为SCI卡设置节点ID,可使用/opt/DIS/sbin目录中的下述命令。在本例中,“-c 1”指的是SCI卡的编号(如果在机器上只有1块卡,它总为1),“-a 0”指的是适配器0,“68”是节点ID:

shell> ./sciconfig -c 1 -a 0 -n 68

如果在同一台机器上有多块SCI卡,使用下述命令(假定当前的工作目录是/opt/DIS/sbin),可确定哪块卡使用哪个插槽:

shell> ./sciconfig -c 1 -gsn

它将给出SCI卡的序列号。然后用“-c 2”重复该步骤,依此类推(对机器上的每块卡)。一旦将每块卡与1个插槽匹配后,可为所有卡设置节点ID。

安装了必要的库和二进制文件,并设置了SCI节点ID后,下一步是设置从主机名(或IP地址)到SCI节点ID的映射。该任务可在SCI套接字配置文件中完成,该文件应被保存为/etc/sci/scisock.conf。在该文件中,通过恰当的SCI卡将SCI节点映射到与之通信的主机名或IP地址。这里给出了一个很简单的配置文件示例:

#host           #nodeId

alpha           8

beta            12

192.168.10.20   16

也能对配置进行限制,使其仅应用在这些主机的可用端口子集上。也能使用额外的配置文件/etc/sci/scisock_opt.conf完成该设置,如下所示:

#-key                        -type        -values

EnablePortsByDefault                yes

EnablePort                  tcp           2200

DisablePort                 tcp           2201

EnablePortRange             tcp           2202 2219

DisablePortRange            tcp           2220 2231

驱动程序安装

创建好了配置文件后,可安装驱动程序。

首先应安装底层驱动,然后安装SCI套接字驱动:

shell> cd DIS/sbin/

shell> ./drv-install add PSB66

shell> ./scisocket-install add

如果愿意,可调用脚本来核实SCI套接字配置文件中的所有节点是否均能访问,通过该方式检查安装:

shell> cd /opt/DIS/sbin/

shell> ./status.sh

如果发现错误并需要更改SCI套接字配置,需要使用ksocketconfig来完成该任务:

shell> cd /opt/DIS/util

shell> ./ksocketconfig -f

测试设置

为了确保SCI套接字已实际使用,可使用latency_bench测试程序。使用该实用工具的服务器组件,客户端能够连接服务器以测试连接的等待时间,通过观察等待时间,可方便地判定是否启用了SCI。(注释:使用latency_bench之前,需要设置环境变量LD_PRELOAD,请参见本节后面的介绍)。

要想设置服务器,可使用下述命令:

shell> cd /opt/DIS/bin/socket

shell> ./latency_bench -server

要想运行客户端,再次使用latency_bench,但此时采用“-client”选项:

shell> cd /opt/DIS/bin/socket

shell> ./latency_bench -client server_hostname

到目前为止,应完成了SCI套接字配置,而且MySQL簇已做好了使用SCI套接字和SCI传输器的准备(请参见17.4.4.10节,“MySQL簇SCI传输连接”)。

启动簇

该进程接下来的步骤是启动MySQL簇。要想启用SCI套接字,在启动ndbd、mysqld和ndb_mgmd之前,需要设置环境变量LD_PRELOAD。该变量应指向用于SCI套接字的内核库。

要想在bash shell下启动ndbd,可执行下述命令:

bash-shell> export LD_PRELOAD=/opt/DIS/lib/libkscisock.so

bash-shell> ndbd

在tcsh环境下,可使用下述命令完成相同的任务:

tcsh-shell> setenv LD_PRELOAD=/opt/DIS/lib/libkscisock.so

tcsh-shell> ndbd

注释:MySQL簇可以仅使用SCI套接字的内核变体。
17.7.2. 理解簇互连的影响
ndbd进程有很多简单的结构,它们可用于访问MySQL簇中的数据。我们创建了一个十分简单的基准测试来检查它们的性能,以及各种互联方式对其性能的影响。

有四种访问方法:

·         主键访问

通过其主键简单地访问记录。在最简单的情况下,一次仅访问一条记录,这意味着该单一请求需负担设置众多TCP/IP消息的全部开销以及众多的场景切换开销。对于在一批中发出多个主键访问请求的情况,这些访问请求将分担设置必要TCP/IP消息和场景切换的开销。如果TCP/IP消息是面向不同目标的,还须设置额外的TCP/IP消息。

·         唯一键访问

唯一键访问与主键访问类似,差别在于,唯一键访问是作为在索引表上的读取操作执行的,然后是作用在表上的主键访问。但是,MySQL服务器仅发送一条请求,而且对索引表的读取是由ndbd负责处理的。这类请求也受益于批处理法。

·         全表扫描

对于表查询,当不存在索引时,将执行全表扫描。这将作为单一请求被发送至ndbd进程,该进程随后将表扫描分为在所有簇ndbd进程上进行的一组并行扫描。在未来的MySQL簇版本中,SQL节点能够过滤某些扫描。

·         使用有序索引的范围扫描

使用有序索引时,它将使用与全表扫描相同的方式执行扫描,不同之处在于,它仅扫描位于MySQL服务器(SQL节点)所传输查询使用的范围内的记录。当所有绑定的索引属性包含分区 键中的所有属性时,将以并行方式扫描所有分区。

为了检查这些访问方法的基本性能,我们开发了一组基准。作为这类基准之一,testReadPerf能够测试简单的和批处理式主键和唯一键访问。通过发出返回单一记录的扫描请求,该基准还能测量范围扫描的设置开销。此外,还有1个该基准的变体,它采用了范围扫描来获取批量记录。

这样,我们就能确定单键访问的开销,以及单记录扫描访问的开销,并能根据基本访问方法测量通信媒介的影响。

在我们进行的测试中,为采用TCP/IP套接字的正常传输器和使用SCI套接字的类似设置进行了基本设置。下面给出的数字是关于少量访问的,每次访问20条记录。使用2KB的记录时,串行访问和批式访问之间的差异将降低3~4倍。对于2KB的记录,未测试SCI套接字。测试是在下述簇上进行的,该簇有两个数据节点,这两个数据节点运行在2台双CPU机器上,处理器为AMD MP1900+。

Access type:         TCP/IP sockets           SCI Socket

Serial pk access:    400 microseconds         160 microseconds

Batched pk access:    28 microseconds          22 microseconds

Serial uk access:    500 microseconds         250 microseconds

Batched uk access:    70 microseconds          36 microseconds

Indexed eq-bound:   1250 microseconds         750 microseconds

Index range:          24 microseconds          12 microseconds

我们还执行了另一组测试,以检查SCI套接字的性能以及SCI传输器的性能,并将这两者与TCP/IP传输器的性能进行了比较。所有测试均采用了主键访问方法,或采用串行多线程方式,或采用多线程批方式。

测试结果表明,SCI套接字比TCP/IP约快100%。与SCI套接字相比,在大多数情况下,SCI传输器的速度更快。在具有很多线程的测试程序中出现了1个值得关注的情况,它表明,与mysqld进程一起使用时,SCI传输器的执行性能并不很好。

我们得出的总体结论是,对于大多数基准,与TCP/IP相比,除了罕见的通信性能不成其为关注事宜的情况外,使用SCI套接字能将性能提升约100%。当扫描过滤器占用大多数处理时间,或当执行大量的主键访问,就会出现该情况。在这类情况下,ndbd进程中的CPU处理操作将占用开销的相当大部分。

对于使用SCI传输器来取代SCI套接字,它仅对ndbd进程之间的通信有意义。如果CPU能专门用于ndbd进程,使用SCI传输器也也很有意义,这是因为SCI传输器能确保该进程永不休息。另一个重要特性是,它能确保对ndbd进程的优先级进行特定的设置,即使运行了很长的时间,其优先级也不会丧失,就像在Linux 2.6中通过将进程锁定到CPU而能实现的那样。如果这类配置是可能的,与使用SCI套接字相比,ndbd进程将获得10~70%的性能提升(执行更新以及可能的并行扫描操作时,性能提升较大)。。

对于计算机簇,还有数种其他的优化套接字实施方案,包括Myrinet、吉比特以太网、Infiniband(无限带宽)和VIA接口。迄今为止,我们仅与SCI套接字一起测试了MySQL簇。我们还提供了上述文档,其中,介绍了如何使用针对MySQL簇的常规TCP/IP来设置SCI套接字的方法。
17.8. MySQL簇的已知限制

在本节中,我们列出了5.1.x系列中对MySQL簇的一些已知限制,并与使用MyISAM和InnoDB存储引擎时可用的特性进行了比较。目前,尚不打算在即将推出的MySQL 5.1版本中处理这些问题,但是,我们将在后续的版本系列中,提供这方面的补丁和修正。如果你检查了MySQL缺陷数据库(http://bugs.mysql.com)中的“簇”类别,可发现我们打算在即将推出的MyAQL 5.1版中更正的已知缺陷(如果标记为“5.1”的话)。

(注释:在本节末尾,列出了在当前版本中已解决的MySQL 5.0簇中的一些事宜)。

·         语法中的不兼容性(运行已有的应用程序时导致错误):

o        不支持文本索引。

o        不支持Geometry数据类型(WKT和WKB)。

·         限制或行为中的不兼容性(运行已有的应用程序时可能导致错误):

o        没有事务的部分回滚功能。重复键或类似错误会导致整个事务的回滚。

o        存在很多可配置的硬限制,但能使用簇中的可用主内存设置限制。关于配置参数的完整清单,请参见17.4.4节,“配置文件”。大多数配置参数可在线升级。这些硬限制包括:

§         数据库内存大小和索引内存大小(分别是DataMemory和IndexMemory)。

§         对于能够执行的最大事务数,可使用配置参数MaxNoOfConcurrentOperations进行设置。注意批量加载,TRUNCATE TABLE和ALTER TABLE是通过运行多个事务作为特殊情况进行处理的,因而不受该限制的约束。

§         与表和索引有关的不同限制。例如,每表的最大有序索引数是由MaxNoOfOrderedIndexes确定的。

o        在NDB表中,数据库名称、表名称和属性名称不能与其他表处理程序中的一样长。属性名称将被截短至31个字符,截短后如果不是唯一的,将导致错误。数据库名称和表名的总最大长度为122个字符(也就是说,NDB簇表名的最大长度为122个字符减去该表所属的数据库的名称中的字符数)。

o        所有的簇表行具有固定长度。这意味着(例如),如果表中有仅包含相对较小值的1个或多个VARCHAR字段,与使用MyISAM引擎的相同表和数据相比,使用NDB存储引擎时需要更多的内存和磁盘空间。换句话讲,对于VARCHAR列,它所需的存储空间与具有相同大小的CHAR列所需的相同。

o        簇数据库中的最大表数目限制为1792。

o        每表的最大属性数限制为128。

o        任一行的最大允许大小为8K,不包括保存在BLOB列中的数据。

o        每键的最大属性数为32。

·         不支持的特性(不会导致错误,但不被支持或强制):

o        外键结构将被忽略,就像在MyISAM表中那样。

o        保存点以及对保存点的回滚将被忽略,就像在MyISAM中那样。

·         性能以及与限制有关的事宜

o        由于对NDB存储引擎的连续访问,存在查询性能问题,与MyISAM或InnoDB的情形相比,执行很多范围扫描时,开销相对昂贵。

o        不支持范围统计中的记录,在某些情况下,这会造成非最优查询计划。可采用USE INDEX或FORCE INDEX规避该问题。

o        对于使用USING HASH创建的唯一性哈希索引,如果NULL是键的一部分,不能使用这类索引访问表。

o        MySQL簇不支持磁盘上的持续提交。提交将被复制,但不保证在提交时会将日志写入磁盘。

·         丢失特性:

o        唯一支持的隔离级别是READ_COMMITTED(InnoDB支持READ_COMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE)。关于其如何影响簇数据库备份和恢复的更多信息,请参见17.6.5.5节,“备份故障诊断与排除”。

o        不支持磁盘上的持续提交。提交将被复制,但不保证在提交时会将日志写入磁盘。

·         与多MySQL服务器有关的问题(与MyISAM或InnoDB无关):

o        运行多个MySQL服务器时,ALTER TABLE未完全锁定(无分布式表锁定)。

o        如果在多个MySQL服务器上进行了更新,MySQL复制功能不能正确处理。但是,如果数据库分区方案是在应用级别上完成的,而且在这些分区上非发生事务,那么可使复制功能正确工作。

o        对于访问相同MySQL簇的多MySQL服务器,不支持数据库的自动发现(autodiscovery)功能。但是,在情况下,支持对表的自动发现(autodiscovery)功能。这意味着,创建了名为db_name的数据库后,或使用1个MySQL服务器导入了该数据库后,应在访问相同MySQL簇的每个额外MySQL服务器上发出CREATE DATABASE db_name语句(从MySQL 5.0.2开始,你还能使用CREATE SCHEMA db_name;)。一旦对给定MySQL服务器完成了该操作,服务器应能检测到数据库表,而不产生错误。

·         仅与MySQL簇有关的事宜(与MyISAM或InnoDB无关):

o        簇中使用的所有机器必须具有相同的体系结构,也就是说,所有承载节点的机器必须是big-endian或little-endian,不能混合使用这两者。例如,不能用运行在PPC上的管理节点指挥运行在x86机器上的数据节点。该限制不适用于简单运行mysql或其他客户端(可能会访问簇的SQL节点)的机器。

o        不能像使用ALTER TABLE或CREATE INDEX完成的那样执行在线方案更改,这是因为NDB簇不支持这类变更的自动检测(但是,能够导入或创建使用不同存储引擎的表,然后使用“ALTER TABLE tbl_name ENGINE=NDBCLUSTER;”将其转换为NDB。在这类情况下,需要发出FLUSH TABLES命令,强制簇发现变化)。

o        不能在线增加或舍弃节点(此时必须重启簇)。

o        使用多个管理服务器时:

§         必须在连接字符串中明确为节点指定ID,这是因为,在多个管理服务器上,自动分配的节点ID不能正常工作。

§         必须十分小心,确保所有的管理服务器具有相同的配置。簇对此方面不进行任何特殊检查,

§         要想使管理节点能发现彼此的存在,创建了簇后必须重启所有的数据节点(详细解释请参见Bug #13070)。

o        不支持用于数据节点的多个网络接口。如果使用了这类接口,很容易导致问题,原因在于,在出现某一数据节点失败的情况下,SQL节点将等待以确认该数据节点是否出现问题,但由于该数据节点的另一路径仍保持打开状态,SQL节点永远不会收到该确认信息。这会导致簇无法工作。

o        数据节点的最大数目为48。

o        MySQL簇中总的最大节点数为63。在该数值中包括所有的MySQL服务器(SQL节点),数据节点和管理服务器。

考虑到本节开始时设定的条件,我们力图使上面列出的信息尽可能完全。你可以将任何遇到的差异通报到MySQL缺陷数据库,http://bugs.mysql.com/。如果我们不打算在MySQL 5.1中更正该问题,我们会将其添加到列表中。
17.9. MySQL簇发展的重要历程

17.9.1. MySQL 5.0中的MySQL簇变化
17.9.2. 关于MySQL簇的MySQL 5.1发展历程

在本节,我们讨论了MySQL 5.1在MySQL簇实施中的一些变化,并与MySQL 5.0进行了比较。我们还将讨论对MySQL簇的进一步重大改进,目前是为MySQL 5.2规划的。

在MySQL 5.0和5.1中,NDB簇存储引擎实施方案之间的变化相对较少,因此其升级相对快捷和简单。

为MySQL簇开发的主要新特性均归入了MySQL 5.1树。在本节中,我们还提供了一些提示,介绍了在MySQL 5.1中簇特性可能包含的方面(请参见17.9.2节,“关于MySQL簇的MySQL 5.1发展历程”)。
17.9.1. MySQL 5.0中的MySQL簇变化

MySQL 5.0.3-beta以及更新的版本包含一些有趣的新特性:

·         下推条件:一种查询,如:

·                SELECT * FROM t1 WHERE non_indexed_attribute = 1;

它将使用全表扫描,并会在簇的数据节点内评估条件。因此,不需要在网络中发送用于评估的记录(也就是说,采用函数传输而不是数据传输)。对于这种查询类型,其速度快5~10倍。请注意,模目前该特性是默认禁止的(有待更彻底的测试),但在某些情况下也能良好工作。使用命令“SET engine-condition-pushdown=On; command”可启用该特性。作为可选方式,你也可以用新的“--engine-condition-pushdown”选项标志启动MySQL服务器,运行启用了该特性的mysqld。

可以使用EXPLAIN来判定在何时使用了下推条件。

该变化的一项主要优点在于,能够以并行方式执行查询。这意味着,与以往相比,对非索引列的查询要快5~10倍,乘以数据节点的数目。原因在于,多个CPU能以并行方式执行查询。

·         降低了IndexMemory使用:在MySQL 5.1中,每条记录约消耗25字节的索引内存,每个唯一索引每记录将使用25字节的索引内存(不含某些数据内存,这是因为它们保存在单独表中)。这是因为在索引内存中,不再需要保存主键。

·         为MySQL簇启用了查询高速缓冲:关于配置和使用查询高速缓冲的更多信息,请参见5.13节,“MySQL查询高速缓冲”。

·         新的优化:有一个需要给予特别关注的优化,即,目前在某些查询中使用了批式读取接口。例如,考虑下述查询:

·                SELECT * FROM t1 WHERE primary_key IN (1,2,3,4,5,6,7,8,9,10);

与以往的MySQL簇版本相比,该查询的执行速度快2~3倍,原因在于,全部10个 键查找是在1个批次中发出的,而不是一次发送一个。

·         限制元数据对象的数目:在MySQL 4.1中,每个簇数据库最多可能包含1600个元数据对象,包括数据库表,系统表,索引和BLOB。在MySQL 5.0中,我们希望将该数值增加到20320。我们打算在2005年年中发布的MySQL 5.0.6 beta版中实现该增强特性。
17.9.2. 关于MySQL簇的MySQL 5.1发展历程

这里所介绍的内容是一份状态报告,它基于最近提交到MySQL 5.1源码树的内容。请注意,所有的5.1开发活动均可能随时变化。

目前,有四种为MySQL 5.1开发的主要新特性:

1.    将MySQL簇集成到了MySQL复制中:这样,就能从簇中的任何MySQL服务器执行更新,并由簇中的1个MySQL服务器处理MySQL复制,并保持了从服务器端安装的一致性。

2.    支持基于磁盘的记录:将支持磁盘上的记录。对于包含主键哈希索引的有索引字段,必须仍保存在RAM中,但可以将所有其他字段保存在磁盘上。

3.    大小可变的记录:定义为VARCHAR(255)的列目前使用260字节的存储空间,与任何特定记录中保存的内容无关。在MySQL 5.1簇表中,只保存被记录实际占用的字段部分。这样,在很多情况下,能够将这类列的空间需求量降低5倍。

4.    用户定义的分区功能:用户能够根据主关键的字段部分定义分区。MySQL服务器能够发现是否能将某些分区从WHERE子句中删除。能够根据KEY、HASH、RANGE和LIST处理程序执行分区操作和子分区操作。对于很多其他处理程序,也应能使用该特性。

此外,对于簇表中包含除BLOB或TEXT外的列类型的行,我们正准备增大8K的大小限制值。这是因为,行目前具有固定的大小,页大小为32768字节(减去用于行标题的128字节)。在目前,这意味着,如果允许每记录占用的大小超过8K,剩余的空间将为空(多达14000字节)。在MySQL 5.1中,我们打算更正该限制,从而使得在给定行中使用8K以上的空间不会导致页剩余部分的浪费。
17.10. MySQL簇常见问题解答

·         使用簇与使用复制的区别是什么?

在复制设置中,主MySQL服务器负责更新1个或多个从服务器。事务按顺序提交,较慢的事务会导致从服务器滞后于主服务器。这意味着,如果主服务器失败,从服务器可能无法记录最近的少数事务。如果使用了事务安全引擎,如InnoDB,要末是在从服务器上完成事务,要末是根本就不记录事务,但复制不保证主服务器和从服务器上的所有数据在任何时候都是一致的。在MySQL簇中,所有的数据节点保持同步,任何一个数据节点提交的事务将被提交给所有的数据节点。当某一数据节点失败时,所有剩余的数据节点仍能保持一致的状态。

简而言之,尽管标准的MySQL复制是异步的,但MySQL簇是同步的。

我们计划在MySQL 5.1中为簇实现(异步)复制功能。包括在两个簇之间,以及MySQL簇和非簇类MySQL服务器之间的复制能力。

·         为了运行簇,我是否需要进行特殊的组网呢?(簇中的计算机是如何通信的?)

MySQL簇是为高带宽环境下的使用而设计的,计算机通过TCP/IP相连。其性能直接取决于簇计算机之间的连接速度。对于簇,最低连接要求包括:典型的100MB以太网网络或等效网络。如果可能,建议使用GB以太网。

也支持更快的SCI 协议,但需要特殊硬件。关于SCI的更多信息,请参见17.7节,“使用与MySQL簇的高速互连”。

·         运行簇需要多少台计算机?为什么?

要想运行可行的簇,最少需要3台计算机。但在MySQL簇中,推荐的最低计算机数目为4:1台负责运行管理节点,1台负责运行SQL节点,2台用作存储节点。使用2个数据节点的目的是为了提供冗余性,管理节点必须运行在单独的机器上,这样,当1个数据节点失败时,仍能保证连续的仲裁服务。

·         簇中不同计算机所负责的任务是什么?

MySQL簇包含物理和逻辑结构,计算机是其物理部件。簇的逻辑或功能部件称为节点,容纳簇节点的计算机有时也称为簇主机。在理想情况下,每台簇主机将有1个节点,但在单个簇主机上可运行多个节点。簇中有三种节点,每一种节点均对应特定的角色。它们是:

1.    管理节点(MGM节点):为整个簇提供管理服务,包括启动、停止、备份、以及为其他节点配置数据。管理节点服务器是作为应用程序ndb_mgmd实现的,用于通过MGM节点控制MySQL簇的管理客户端是ndb_mgm。

2.    数据节点:保存和复制数据。数据节点的功能由NDB数据节点进程ndbd的实例负责处理。

3.    SQL节点:这是用“--ndb-cluster”选项启动的MySQL服务器(mysqld)的一个实例。

·         用什么操作系统才能使用簇呢?

在MySQL 5.1中,在Linux、Mac OS X和Solaris平台上均正式支持MySQL簇。我们正在努力为其他平台增加簇支持,包括Windows,我们的目标是,最终在支持MySQL本身的所有平台上实现MySQL簇。

在其他操作系统上运行簇也是可能的。用户告诉我们,他们在FreeBSD平台上超过运行了簇。但是,除了前面介绍的三种操作系统外,在其他平台上运行的簇应被视为alpha软件(最好情况),不保证在生产环境下的可靠性,而且不被MySQL AB支持。

·         运行MySQL簇的硬件要求是什么?

在簇运行的任何平台上,应具有具备NDB功能的二进制文件。显而易见,更快的CPU和更多的内存能够改善性能,64位CPU的效率很可能高于32位处理器。在用于数据节点的机器上必须有足够的内存,以便容纳各节点共享的数据库部分。(更多信息,请参见我需要多少内存?)。节点能通过标准的TCP/IP网络和硬件进行通信。对于SCI支持,需要特殊的组网硬件。

·         由于MySQL簇使用了TCP/IP,这是否意味着我能在Internet上运行1个或多个节点位于远程位置的簇?

请记住,在MySQL簇中,节点间的通信并不安全,这点极其重要,这类通信未加密,也未采用任何防护机制。对于簇,最安全的配置是位于防火墙后的专用网络,不能从外部直接访问任何簇数据或管理节点(对于SQL节点,应采取相同的防护措施,就像在MySQL服务器的其他实例中那样)。

无论是任何情况,在这类条件下簇的可靠运行十分令人怀疑,这是因为设计和实施MySQL簇时,假定它运行在能保证专用高速连通性的条件下,如使用100MB或GB以太网的LAN中(更倾向于后者)。对于低于该要求的任何环境,我们未作任何测试,也不保证其性能。

·         要想使用簇,我是否不得不学习新的编程语言或查询语言?

不。尽管使用了一些专用命令来管理和配置簇本身,但对于下述操作,仅需要标准的(My)SQL查询和命令:

o        创建、更改和撤销表。

o        插入、更新和删除表数据。

o        创建、更改和撤销主索引和唯一索引。

o        配置和管理SQL节点(MySQL服务器)。

·         使用簇时,如何了解错误或警告消息的含义呢?

有两种完成它的方式:

1.    出现错误或告警状况时,在MySQL监视器内,立刻使用SHOW ERRORS或SHOW WARNINGS。也能在MySQL Query Browser(查询浏览器)中显示它们。

2.    在系统shell提示符下,使用perror --ndb error_code。

·         MySQL簇是事务安全的吗?支持什么隔离级别?

是。对于用NDB存储引擎创建的表,支持事务。在MySQL 5.1中,簇仅支持READ_COMMITTED事务隔离级别。

·         簇支持的表类型是什么?

NDB是仅有的支持簇功能的MySQL存储引擎。

(能够使用其他存储引擎在用于簇的MySQL服务器上创建表,如MyISAM或InnoDB,但这类非NDB表不在簇中)。

·         “NDB”的含义是什么?

它是“Network Database”(网络数据库)的缩写。

·         哪些版本的MySQL软件支持簇?我必须从源码编译吗?

在5.1系列的所有MySQL-max二进制版本中均支持簇,但下面介绍的除外。使用命令SHOW VARIABLES LIKE 'have_%';或SHOW ENGINES;,可检查你的服务器是否支持NDB(更多信息,请参见5.1.2节,“mysqld-max扩展MySQL服务器”)。

Linux用户请注意,NDB未包含在标准的MySQL服务器RPM中。对于NDB存储引擎以及所附的管理工具和其他工具,有单独的RPM软件包。请参见MySQL 5.1下载页上的NDB RPM下载部分(以前,你必须使用以.tar.gz档案方式提供的“-max”二进制文件。目前仍能这样,但却不是必须的,如果愿意,可使用Linux分发版的RPM管理器)。通过从源码编译-max二进制文件,也能获得NDB支持,但使用MySQL簇时,不需要这样。要想只下载最新的MySQL 5.1系列的二进制版本、RPM、或源码分发版,请访问http://dev.mysql.com/downloads/mysql/5.1.html。

·         我需要多少RAM?是否能全部使用磁盘内存?

在目前,簇是仅“内存中”的。这意味着所有的表数据(包括索引)均保存在RAM中。因此,如果你的数据占用了1GB的空间,而且打算在簇中将它复制1次,需要2GB的内存。还应加上操作系统以及在簇计算机上运行的应用程序所需的内存。

可以使用下述公司粗略估计簇中每个数据节点所需的RAM量:

(SizeofDatabase * NumberOfReplicas * 1.1 ) / NumberOfDataNodes

要想更准确地计算内存需求量,需要为簇数据库中的每个表确定每行所需的存储空间(详情请参见11.5节,“列类型存储需求”),然后乘以占行数。还必须对列索引进行计算,方式如下:

o        对于为NDB簇表创建的每个主键索引或哈希索引,每记录需要21-25字节。这类索引使用IndexMemory(索引内存)。

o        每个有序索引每记录需要10字节的存储空间,使用DataMemory(数据内存)。

o        创建主键或唯一索引时,还会创建1个有序索引,除非该索引是使用USING HASH创建的。换句话讲,如果未使用USING HASH创建它们,对于簇表上的每个键多因或唯一索引,在MySQL 5.1中,每记录占用31-35字节。

注意,使用USING HASH为所有主键和唯一索引创建MySQL簇表时,通常还能使表的更新速度更快。这是因为,它需要较少的内存(因未创建有序索引),对CPU的占用也较低(仅需读取或更新较少的索引)。

请记住,所有的MySQL簇表必须有主键,这点极其重要。如果未定义,NDB存储引擎将自动创建主键,而且该主键是在未使用USING HASH的条件下创建的。

很难准确确定簇索引在任何给定时间用于存储的内存量,但是,当可用DataMemory和/或IndexMemory的使用率到达80%时,会将警告消息写入簇日志,到达80%、90%等时,也会写入簇日志。

我们经常遇到用户通报的下述问题,当他们视图填充簇数据库时,加载进程提前终止,并显示下述错误消息:

错误1114:表'my_cluster_table'已满。

出现该情况时,很可能是因为在你的设置中未为所有的表数据和索引提供足够的RAM,包括NDB存储引擎所需的主键,以及表定义中不包含主键定义时自动创建的主键。

此外还应注意,所有的数据节点应具有相同的RAM量,这是因为,簇中任何数据节点所能使用的内存均不超过任意单独数据节点的最低可用内存。换句话讲,如果有三台运行簇数据节点的计算机,在其中两台计算机上,有3GB用于保存簇数据的RAM,另一台计算机只有1GB RAM,那么每个数据节点仅能为簇贡献1GB。

·         出现灾难性故障时,例如整个城市断电而且我的UPS也出现故障,我会丢失所有的数据吗?

所有已提交的事务将被记录。因此,在出现灾难的情况下,某些数据可能会丢失,但相当有限。通过减少每事务的操作数,能够进一步减少数据损失(在任何情形下,在每事务上执行大量操作都不是一个好主意)。

·         能够与簇一起使用FULLTEXT索引吗?

FULLTEXT索引功能目前不被NDB存储引擎支持,也不被除MyISAM之外的任何存储引擎支持。我们打算在未来的版本中增加该功能。

·         我能在一台计算机上运行多个节点吗?

可以,但不建议这样。运行簇的主要原因之一是提供冗余,为了获得冗余性的全部优点,每个节点应位于单独的计算机上。如果将多个节点置于同一台机器,当该机器出现故障时,所有节点均将丢失。考虑到MySQL簇能运行在常见的硬件上并利用廉价的操作系统(或不需费用),为了确保任务关键型数据的安全,值得为额外的机器户花费开销。此外还须注意,对运行管理节点的簇主机的要求最低,使用200 MHz Pentium CPU,操作系统所需的足够RAM,以及用于ndb_mgmd和ndb_mgm进程的少量开销,就能完成该任务。

·         我能在不重启的情况下为簇增加节点吗?

目前不行。对于在簇中添加新的MGM或SQL节点来说,简单的重启就是所需的一切。添加数据节点时,进程略微复杂些,需要采取下述步骤:

o        对所有簇数据进行完整备份。

o        彻底关闭簇和所有的簇节点进程。

o        使用“—initial”启动选项重启簇。

o        从备份中恢复所有簇数据。

在未来的MySQL簇版本中,我们希望为MySQL簇实现“热”重配置功能,以便能够将添加新节点时重启簇的要求降至最低(如果不能消除的话)。

·         使用簇时有需要我了解的限制吗?

MySQL中的NDB表服从下述限制:

o        并非所有的字符集和校对均被支持。

o        不支持FULLTEXT索引和前缀索引。只能为完整的列设置索引。不支持第19章:MySQL中的空间扩展中介绍的空间扩展。

o        仅支持对事务的完整回滚。不支持部分回滚以及回滚至保存点。

o        每表允许的最大属性数为128,而且属性名称不得超过31个字符。对于每个表,表和数据库名称的最大组合长度为122个字符。

o        表行的最大大小为8KB,不包括BLOB。对于每表中的行数没有限制,表的大小限制取决于多种因素,尤其是每个数据节点可用的RAM量。

o        NDB引擎不支持外键约束。就像MyISAM表一样,这些约束将被忽略。

o        不支持查询高速缓冲功能。

关于簇限制的更多信息,请参见17.8节,“MySQL簇的已知限制”。

·         怎样将已有的MySQL数据库导入到簇中?

可以将数据库导入到MySQL簇中,就像用其他版本的MySQL那样。除了前一问题所提到的限制外,仅有的特殊要求是,准备包含到簇中的任何表必须使用NDB存储引擎。这意味着,表必须是用ENGINE=NDB或ENGINE=NDBCLUSTER创建的。使用ALTER TABLE,能够将使用其他存储引擎的现有表转换为NDB簇表,但需要采取额外避规措施,详情请参见17.8节,“MySQL簇的已知限制”。

·         簇节点是怎样彼此通信的?

簇节点能够通过下述三种协议中的任何一种进行通信:TCP/IP、SHM(共享内存)和SCI(规模可扩展的计算机连接接口)。在适用的场合,对于位于相同簇主机上的节点间通信,默认协议为SHM。SCI是高速(每秒1GB或更高)、高可用性协议,用于创建可扩展的多处理器系统,它需要特殊硬件和驱动。关于使用SCI作为簇中传输机制的更多信息,请参见17.7节,“使用与MySQL簇的高速互连”。

·         什么是“arbitrator”(仲裁程序)?

如果簇中的1个或多个节点失败,并非所有的簇节点均不能彼此“看到”,这是可能的。事实上,能够在网络分区中将两个节点集合彼此隔离,也称为“分裂大脑”。这类情形并不受欢迎,这是因为,每一个节点集合都试图表现为代表整个簇。

当簇节点出现问题时,有两种可能性。如果剩余节点的50%以上能够彼此通信,那么这就是我们有时称之为的“多数支配”情形,该节点集合将被视为簇。当节点数均等时,仲裁程序将介入:在该情形下,仲裁程序所属的节点集合将被当作簇,不属于该节点集合的节点将被关闭。

上述描述略为简化,更完整的解释需要考虑下面介绍的节点组:

当至少一个节点组中的所有节点均有效时,网络分区不是问题,这是因为,簇中的任一个部分均不能构成1个功能性的簇。当没有一个节点组的所有成员均是有效的时,问题产生,在该情况下,网络分区(“分裂大脑”情形)成为可能。随后就需要仲裁程序。所有的簇节点将相同的节点视为仲裁程序,通常是管理服务器。但是,也能将簇中的任何MySQL服务器配置为仲裁程序。仲裁程序接受第一个与其接触的簇节点集合,并通知剩余的集合关闭。对于MySQL服务器和管理服务器节点,仲裁程序的选择是由ArbitrationRank配置参数控制的(详情请参见17.4.4.4节,“定义MySQL簇管理服务器”)。此外还应注意,仲裁程序不会对指定主机施加过多的要求,因此,仲裁程序主机不需要特别块或拥有用于该目的的额外内存。

·         MySQL簇支持的列类型是什么?

MySQL簇支持所有通常的MySQL列类型,但与MySQL空间扩展有关的例外(请参见第19章:MySQL中的空间扩展)。此外,对于索引,当与NDB表一起使用时存在一些差别。注释:MySQL簇表(即用ENGINE=NDBCLUSTER创建的表)仅有固定宽度列。这意味着(例如)含有VARCHAR(255)列的每一条记录需要256字节来保存列,无论列中保存的数据大小是多少。在未来的MySQL版本中,计划更正它。

关于这些方面的更多信息,请参见17.8节,“MySQL簇的已知限制”。

·         如何启动和停止MySQL簇?

需要按照下述顺序分别启动簇中的每个节点:

1.    用ndb_mgmd命令启动管理节点。

2.    用ndbd命令启动每个数据节点。

3.    使用mysqld_safe --user=mysql &,启动每个MySQL服务器(SQL节点)。

对于这类命令中的每一个,必须在受影响节点所在机器上的系统shell中执行。在容纳MGM节点的机器上启动管理客户端ndb_mgm,可验证簇是否正在运行。

·         当簇关闭时,会对簇数据有什么影响?

由簇数据节点保存在内存中的数据将被写入磁盘,并在下一次启动簇时重新装入内存。

要想关闭簇,请在MGM节点所在的机器上、在shell下输入下述命令:

shell> ndb_mgm -e shutdown

这样就能恰当地中止ndb_mgm、ndb_mgm、以及任何ndbd进程。对于用作簇SQL节点的MySQL服务器,可使用mysqladmin shutdown停止它。

更多信息,请参见17.6.2节,“管理客户端”中的命令和17.3.6节,“安全关闭和重启”。

·         簇中有1个以上的管理节点是否有用?

对于可靠性来说它确有帮助。在任何给定的时间,仅需1个MGM节点来控制簇,但能够将1个MGM配置为主节点,并配置1个或多个额外的管理节点,以便在主MGM节点出现故障时取代它。

·         在簇中能混合使用不同的硬件和操作系统吗?

是。只要所有的机器和操作系统均是相同的endian。也能在不同的节点上使用不同的MySQL簇版本,但是,我们建议仅应将其作为滚动升级的一部分使用。

·         我能在单台主机上运行两个数据节点吗?两个SQL节点?

是,能够这样。在有多个数据节点的情况下,每个节点必须使用不同的数据目录。如果打算在一台机器上运行多个SQL节点,那么每个mysqld实例必须使用不同的TCP/IP端口。

·         我能与MySQL簇一起使用主机名吗?

是,对于簇主机,能够使用DNS和DHCP。但是,如果你的应用程序要求“99.999%”的可用性,建议使用固定的IP地址。这是因为,依赖该服务的簇节点间的通信会引入额外的故障点,故障点越少越好。
17.11. MySQL簇术语表

下面给出的术语对理解MySQL簇有所帮助,而且在与MySQL簇一起使用时有一定的特殊含义。

·         簇:

按照通常的理解,簇是一个计算机集合,其功能相当于1个单位,一起工作以完成单一任务。

NDB簇:

这是MySQL中使用的存储引擎,用于实现数据存储、检索、以及多个计算机间分布式管理。

MySQL簇:

指的是使用NDB存储引擎一起工作的一组计算机,以便在使用“内存中”存储的非共享体系结构中支持分布式MySQL数据库。

·         配置文件:

包含与簇、其主机和节点有关的指令和信息的文本文件。当簇启动时,这类文件由簇的管理节点负责读取。详情请参见17.4.4节,“配置文件”。

·         备份:

所有簇数据、事务和日志的完整拷贝,保存在磁盘上或其他长期存储介质上。

·         恢复:

将簇返回以前的状态,与保存在备份中的相同。

·         检查点:

从广义上讲,将数据保存到磁盘时,即达到了检查点。具体对于簇来说,它是将所有已提交事务保存到磁盘的时间点。对于NDB存储引擎,有两种一起工作的检查点,以确保维护簇数据的一致性:

o        本地检查点(LCP):

这是专门针对单一节点的检查点,但是,LCP也会在簇中的所有节点发生,同步性或强或弱。LCP包括将节点的所有数据保存到磁盘,通常每几分钟出现一次。准确的时间间隔会出现变化,具体情况取决于节点上保存的数据量,簇活动的级别,以及其他因素。

o        全局检查点(GCP):

GCP每数秒出现一次,此时,将对所有节点的事务进行同步化处理,并将redo日志保存到磁盘。

·         簇主机:

构成MySQL簇组成部分的计算机。簇具有物理结构和逻辑结构。从物理意义上讲,簇由多个计算机构成,这些计算机也称为簇主机(或主机)另请参见下面的节点和节点组。

·         节点:

它指的是MySQL簇的逻辑或功能性单元,有时也称为簇节点。对于MySQL簇,我们使用术语“节点”表示进程而不是簇的物理部件。实施有效的MySQL簇需要三种节点。它们是:

o        管理(MGM)节点:

负责管理簇中的其他节点。它为其他节点提供了配置数据,负责启动和停止节点,处理网络分区事宜,创建备份和从备份恢复,等等。

o        SQL(MySQL服务器)节点:

MySQL服务器的实例,用作簇数据节点所保存数据的前端。打算保存、检索或更新数据的客户端可访问SQL节点,就像访问其他MySQL服务器一样,采用通常的鉴定方法和API,节点组之间的基本数据分配对用户和应用程序来说是透明的。SQL节点访问作为整体的簇数据库,而不管数据在不同数据节点或簇主机上的分布情况。

o        数据节点:

这些节点负责保存实际数据。表数据片段保存在节点组集合中,每个节点组保存表数据的不同子集。构成节点组的每个节点均保存了该节点组所负责片段的副本。目前,1个簇能支持总数为48的数据节点。

1个以上的节点能共存于单台机器上(事实上,能够在一台机器上设置完成的簇,但在生产环境下几乎没人会这样做)。请记住,使用MySQL簇时,术语“主机”指的是簇的物理部件,而“节点”指的是逻辑或功能部件(即进程),这很有帮助。

关于过时术语的注释:在MySQL簇文档的早期版本中,数据节点有时也称为“数据库节点”、“DB节点”、或偶尔使用的“存储节点”。此外,SQL节点有时也称为“客户端节点”或“API节点”。为了将混淆降至最低,这类早期术语已被放弃,为此,应避免使用它们。

·         节点组:

数据节点的集合。位于节点组内的所有数据节点包含相同的数据(片段),而且单个组内的所有节点应位于不同的主机上。能够对哪个节点术语哪个节点组进行控制。

·         节点失败:

MySQL簇并不仅取决于构成构成簇的任一节点的功能,如果1个或多个节点失败,簇仍能运行。给定簇能容忍的失败节点的准确数目取决于节点的数目和簇的配置。

·         节点重启:

重启失败簇节点的进程。

·         首次节点重启:

不与其文件系统一起启动簇节点的进程。有时用于软件升级或其他特殊场合。

·         系统崩溃(或系统失败):

当很多簇节点失败并无法保证簇的状态时,就会出现该情况。

·         系统重启:

重启簇并根据磁盘日志和检查点重新初始化簇状态的进程。在簇的计划关闭或非计划关闭后,通常需要执行该进程。

·         片段:

数据库表的一部分,在NDB存储引擎中,将表分为众多片段,并以片段方式保存。片段有时也称为“分区”,但“片段”是更可取的术语。在MySQL簇中,对表进行了片段化处理,从而简化了机器和节点间的负载平衡。

·         副本:

在NDB存储引擎下,每个表片段均有多个副本,这些副本保存在其他数据节点上,以提供冗余性。目前,每片段最多有4个副本。

·         传输器:

提供节点间数据传输的协议。MySQL簇目前提供了四种不同的传输器连接:

o        TCP/IP(本地)

当然,这是广为人知的网络协议,它是Internet上HTTP、FTP等的基础。

o        TCP/IP(远程)

同上,但用于远程通信。

o        SCI

SCI(规模可扩展的计算机连接接口)是一种高速协议,由于构建多处理器系统和并行处理应用程序。与MySQL簇一起使用SCI时,需要专门的硬件,具体讨论请参见17.7.1节,“配置MySQL簇以使用SCI套接字”。关于SCI的基本介绍,请参见dolphinics.com上的文章。

o        SHM

Unix风格共享内存(SHM)段。在任何支持的场合下,将自动使用SHM来连接运行在相同主机上的节点。要想了解这方面的更多信息,请参见关于shmop(2)的Unix手册页。

注释:簇传输器是簇内部的。使用MySQL簇的应用程序与SQL节点进行通信,就像与其他版本的MySQL服务器进行通信一样(通过TCP/IP、或使用Unix套接字、或Windows命名管道)。可以使用标准的MySQL API发出查询并检索结果。

·         NDB:

它是网络数据库(Network Database)的缩写,而且指的是用于启用MySQL簇的存储引擎。NDB存储引擎支持所有通常的MySQL列类型和SQL语句,而且是ACID兼容的。该引擎还提供了对事务的完整支持(提交和回滚)。

·         无共享体系结构:

用于MySQL簇的理想体系结构。在真正的无共享设置下,每个节点运行在单独的主机上。这类安排的优点在于,单个主机或节点不会造成单点故障,也不会成为系统整体的性能瓶颈。

·         内存中存储:

保存在每个数据节点中的数据均保存在节点宿主计算机的内存中。对于簇中的每个数据节点,必须提供足够的RAM,所需的RAM量为:数据库的大小乘以副本数目,然后除以数据节点的数目。因此,如果数据库占用1GB的内存,而且你打算设置具有4个副本和8个数据节点的簇,每节点所需的最小内存为500 MB。注意,还应加上操作系统所需的内存以及运行在主机上的应用程序所需的内存。

·         表:

与关联数据库下的通常情况一样,术语“表”指的是具有等同结构的记录的集合。在MySQL簇中,数据库表以片段集合的方式保存在数据节点中,并将每个片段复制到额外的数据节点上。复制相同片段或片段集合的数据节点集合称为节点组。

·         簇程序:

它们是命令行程序,用于运行、配置和管理MySQL簇。它们包括两种服务器端口监督程序:

o        ndbd:

数据节点端口监督程序(运行数据节点进程)。

o        ndb_mgmd:

管理服务器端口监督程序(运行管理服务器进程)。

以及客户端程序:

o        ndb_mgm:

管理客户端(为执行管理命令提供了1个界面)。

o        ndb_waiter:

用于验证簇中所有节点的状态。

o        ndb_restore:

从备份中恢复簇数据。

关于这些程序及其用法的更多信息,请参见17.5节,“MySQL簇中的进程管理”。

·         事件日志:

MySQL簇按照类别(启动、停止、错误、检查点等)、优先级和严重级别来记录事件。关于所有可通报事件的完整列表,请参见17.6.3节,“MySQL簇中生成的事件报告”。事件日志具有两种类型:

o        簇日志:

为作为整体的簇记录所有所需的、值得记录的事件。

o        节点日志:

为每个单独节点保存的单独日志。

在正常情况下,仅需保存和检查簇日志,这通常已足够。节点日志仅用于应用程序开发和调试目的。
 

这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访问dev.mysql.com。原始参考手册为英文版,与英文版参考手册相比,本翻译版可能不是最新的。
Top / Previous / Next / Up / Table of Contents
自动翻页分割符。  当前页(第    3    页)。  跳到页面顶部  上翻  下翻  跳到页面底部

    * Documentation Library
          o Table of Contents

Search manual:

Additional languages

    * Chinese
    * English
    * German
    * Japanese

MySQL 5.1参考手册 :: 18. 分区
? 17. MySQL簇
19. 中的空间扩展 ?
Section Navigation      [Toggle]

    * 前言
    * 1. 一般信息
    * 2. 安装MySQL
    * 3. 教程
    * 4. MySQL程序概述
    * 5. 数据库管理
    * 6. 中的复制
    * 7. 优化
    * 8. 客户端和实用工具程序
    * 9.语言结构
    * 10. 字符集支持
    * 11. 列类型
    * 12. 函数和操作符
    * 13. SQL语句语法
    * 14. 插件式存储引擎体系结构
    * 15. 存储引擎和表类型
    * 16. 编写自定义存储引擎
    * 17. MySQL簇
    * 18. 分区
    * 19. 中的空间扩展
    * 20. 存储程序和函数
    * 21. 触发程序
    * 22. 视图
    * 23. INFORMATION_SCHEMA信息数据库
    * 24. 精度数学
    * 25. API和库
    * 26. 连接器
    * 27. 扩展MySQL
    * A. 问题和常见错误
    * B. 错误代码和消息
    * C. 感谢
    * D. MySQL变更史
    * E. 移植到其他系统
    * F. 环境变量
    * G. 正则表达式
    * H. Limits in MySQL
    * I. 特性限制
    * J. GNU通用公共许可
    * K. MySQL FLOSS许可例外
    * 索引

第18章:分区

目录

18.1. MySQL中的分区概述
18.2. 分区类型

    18.2.1. RANGE分区
    18.2.2. LIST分区
    18.2.3. HASH分区
    18.2.4. KEY分区
    18.2.5. 子分区
    18.2.6. MySQL分区处理NULL值的方式

18.3. 分区管理

    18.3.1. RANGE和LIST分区的管理
    18.3.2. HASH和KEY分区的管理
    18.3.3. 分区维护
    18.3.4. 获取关于分区的信息

本章讨论MySQL 5.1.中实现的分区。关于分区和分区概念的介绍可以在18.1节,“MySQL中的分区概述”中找到。MySQL 5.1 支持哪几种类型的分区,在18.2节,“分区类型” 中讨论。关于子分区在18.2.5节,“子分区” 中讨论。现有分区表中分区的增加、删除和修改的方法在18.3节,“分区管理” 中介绍。 和分区表一同使用的表维护命令在18.3.3节,“分区维护” 中介绍。

请注意:MySQL 5.1中的分区实现仍然很新(pre-alpha品质),此时还不是可生产的(not production-ready)。 同样,许多也适用于本章:在这里描述的一些功能还没有实际上实现(分区维护和重新分区命令),其他的可能还没有完全如所描述的那样实现(例如, 用于分区的数据目录(DATA DIRECTORY)和索引目录(INDEX DIRECTORY)选项受到Bug #13520) 不利的影响). 我们已经设法在本章中标出这些差异。在提出缺陷报告前,我们鼓励参考下面的一些资源:

    *

      MySQL 分区论坛

      这是一个为对MySQL分区技术感兴趣或用MySQL分区技术做试验提供的官方讨论论坛。来自MySQL 的开发者和其他的人,会在上面发表和更新有关的材料。它由分区开发和文献团队的成员负责监控。
    *

      分区缺陷报告

      已经归档在缺陷系统中的、所有分区缺陷的一个列表,而无论这些缺陷的年限、严重性或当前的状态如何。根据许多规则可以对这些缺陷进行筛选,或者可以从MySQL缺陷系统主页开始,然后查找你特别感兴趣的缺陷。
    *

      Mikael Ronstr?m's Blog

      MySQL分区体系结构和领先的开发者Mikael Ronstr?m 经常在这里贴关于他研究MySQL 分区和MySQL簇的文章。
    *

      PlanetMySQL

      一个MySQL 新闻网站,它以汇集MySQL相关的网誌为特点,那些使用我的MySQL的人应该对此有兴趣。我们鼓励查看那些研究MySQL分区的人的网誌链接,或者把你自己的网誌加到这些新闻报道中。

MySQL 5.1的二进制版本目前还不可用;但是,可以从BitKeeper知识库中获得源码。要激活分区,需要使用--with-分区选项编译服务器。关于建立MySQL 的更多信息,请参见2.8节,“使用源码分发版安装MySQL”。如果在编译一个激活分区的MySQL 5.1创建中碰到问题,可以在MySQL分区论坛中查找解决办法,如果在论坛中已经贴出的文章中没有找到问题的解决办法,可以在上面寻找帮助。
18.1. MySQL中的分区概述

本节提供了关于MySQL 5.1.分区在概念上的概述。

SQL标准在数据存储的物理方面没有提供太多的指南。SQL语言的使用独立于它所使用的任何数据结构或图表、表、行或列下的介质。但是,大部分高级数据库管理系统已经开发了一些根据文件系统、硬件或者这两者来确定将要用于存储特定数据块物理位置的方法。在MySQL中,InnoDB存储引擎长期支持表空间的概念,并且MySQL服务器甚至在分区引入之前,就能配置为存储不同的数据库使用不同的物理路径(关于如何配置的解释,请参见7.6.1节,“使用符号链接”)。

分区又把这个概念推进了一步,它允许根据可以设置为任意大小的规则,跨文件系统分配单个表的多个部分。实际上,表的不同部分在不同的位置被存储为单独的表。用户所选择的、实现数据分割的规则被称为分区函数,这在MySQL中它可以是模数,或者是简单的匹配一个连续的数值区间或数值列表,或者是一个内部HASH函数,或一个线性HASH函数。函数根据用户指定的分区类型来选择,把用户提供的表达式的值作为参数。该表达式可以是一个整数列值,或一个作用在一个或多个列值上并返回一个整数的函数。这个表达式的值传递给分区函数,分区函数返回一个表示那个特定记录应该保存在哪个分区的序号。这个函数不能是常数,也不能是任意数。它不能包含任何查询,但是实际上可以使用MySQL 中任何可用的SQL表达式,只要该表达式返回一个小于MAXVALUE(最大可能的正整数)的正数值。分区函数的例子可以在本章后面关于分区类型的讨论中找到 (请参见18.2节,“分区类型” ),也可在13.1.5节,“CREATE TABLE语法”的分区语法描述中找到。

当二进制码变成可用时(也就是说,5.1 -max 二进制码将通过--with-partition 建立),分区支持就将包含在MySQL 5.1的-max 版本中。如果MySQL二进制码是使用分区支持建立的,那么激活它不需要任何其他的东西 (例如,在my.cnf 文件中,不需要特殊的条目)。可以通过使用SHOW VARIABLES命令来确定MySQL是否支持分区,例如:

mysql> SHOW VARIABLES LIKE '%partition%';

 

+-----------------------+-------+

| Variable_name         | Value |

+-----------------------+-------+

| have_partition_engine | YES   |

+-----------------------+-------+

1 row in set (0.00 sec)

在如上列出的一个正确的SHOW VARIABLES 命令所产生的输出中,如果没有看到变量have_partition_engine的值为YES,那么MySQL的版本就不支持分区。(注意:在显示任何有关分区支持信息的命令SHOW ENGINES的输出中,不会给出任何信息;必须使用SHOW VARIABLES命令来做出这个判断)。

对于创建了分区的表,可以使用你的MySQL 服务器所支持的任何存储引擎;MySQL 分区引擎在一个单独的层中运行,并且可以和任何这样的层进行相互作用。在MySQL 5.1版中,同一个分区表的所有分区必须使用同一个存储引擎;例如,不能对一个分区使用MyISAM,而对另一个使用InnoDB。但是,这并不妨碍在同一个 MySQL 服务器中,甚至在同一个数据库中,对于不同的分区表使用不同的存储引擎。

要为某个分区表配置一个专门的存储引擎,必须且只能使用[STORAGE] ENGINE 选项,这如同为非分区表配置存储引擎一样。但是,必须记住[STORAGE] ENGINE(和其他的表选项)必须列在用在CREATE TABLE语句中的其他任何分区选项之前。下面的例子给出了怎样创建一个通过HASH分成6个分区、使用InnoDB存储引擎的表:

CREATE TABLE ti (id INT, amount DECIMAL(7,2), tr_date DATE)

    ENGINE=INNODB

    PARTITION BY HASH(MONTH(tr_date))

    PARTITIONS 6;

(注释:每个PARTITION 子句可以包含一个 [STORAGE] ENGINE 选项,但是在MySQL 5.1版本中,这没有作用)。

创建分区的临时表也是可能的;但是,这种表的生命周期只有当前MySQL 的会话的时间那么长。对于非分区的临时表,这也是一样的。

注释:分区适用于一个表的所有数据和索引;不能只对数据分区而不对索引分区,反之亦然,同时也不能只对表的一部分进行分区。

可以通过使用用来创建分区表的CREATE TABLE语句的PARTITION子句的DATA DIRECTORY(数据路径)和INDEX DIRECTORY(索引路径)选项,为每个分区的数据和索引指定特定的路径。此外,MAX_ROWS和MIN_ROWS选项可以用来设定最大和最小的行数,它们可以各自保存在每个分区里。关于这些选项的更多信息,请参见18.3节,“分区管理”。注释:这个特殊的功能由于Bug #13250的原因,目前还不能实用。在第一个5.1二进制版本投入使用时,我们应该已经把这个问题解决了。

分区的一些优点包括:

·         与单个磁盘或文件系统分区相比,可以存储更多的数据。

·         对于那些已经失去保存意义的数据,通常可以通过删除与那些数据有关的分区,很容易地删除那些数据。相反地,在某些情况下,添加新数据的过程又可以通过为那些新数据专门增加一个新的分区,来很方便地实现。

通常和分区有关的其他优点包括下面列出的这些。MySQL 分区中的这些功能目前还没有实现,但是在我们的优先级列表中,具有高的优先级;我们希望在5.1的生产版本中,能包括这些功能。

·         一些查询可以得到极大的优化,这主要是借助于满足一个给定WHERE 语句的数据可以只保存在一个或多个分区内,这样在查找时就不用查找其他剩余的分区。因为分区可以在创建了分区表后进行修改,所以在第一次配置分区方案时还不曾这么做时,可以重新组织数据,来提高那些常用查询的效率。

·         涉及到例如SUM() 和 COUNT()这样聚合函数的查询,可以很容易地进行并行处理。这种查询的一个简单例子如 “SELECT salesperson_id, COUNT(orders) as order_total FROM sales GROUP BY salesperson_id;”。通过“并行”, 这意味着该查询可以在每个分区上同时进行,最终结果只需通过总计所有分区得到的结果。

·         通过跨多个磁盘来分散数据查询,来获得更大的查询吞吐量。

要经常检查本页和本章,因为它将随MySQL 5.1后续的分区进展而更新。
18.2. 分区类型

18.2.1. RANGE分区
18.2.2. LIST分区
18.2.3. HASH分区
18.2.4. KEY分区
18.2.5. 子分区
18.2.6. MySQL分区处理NULL值的方式

本节讨论在MySQL 5.1中可用的分区类型。这些类型包括:

·         RANGE 分区:基于属于一个给定连续区间的列值,把多行分配给分区。参见18.2.1节,“RANGE分区”。

·         LIST 分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。参见18.2.2节,“LIST分区”。

·         HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL 中有效的、产生非负整数值的任何表达式。参见18.2.3节,“HASH分区”。

·         KEY 分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值。参见18.2.4节,“KEY分区”。

无论使用何种类型的分区,分区总是在创建时就自动的顺序编号,且从0开始记录,记住这一点非常重要。当有一新行插入到一个分区表中时,就是使用这些分区编号来识别正确的分区。例如,如果你的表使用4个分区,那么这些分区就编号为0, 1, 2, 和3。对于RANGE和LIST分区类型,确认每个分区编号都定义了一个分区,很有必要。对HASH分区,使用的用户函数必须返回一个大于0的整数值。对于KEY分区,这个问题通过MySQL服务器内部使用的 哈希函数自动进行处理。

分区的名字基本上遵循其他MySQL 标识符应当遵循的原则,例如用于表和数据库名字的标识符。但是应当注意,分区的名字是不区分大小写的。例如,下面的CREATE TABLE语句将会产生如下的错误:

mysql> CREATE TABLE t2 (val INT)

    -> PARTITION BY LIST(val)(

    ->     PARTITION mypart VALUES IN (1,3,5),

    ->     PARTITION MyPart VALUES IN (2,4,6)

    -> );

错误1488 (HY000): 表的所有分区必须有唯一的名字。

这是因为MySQL认为分区名字mypart和MyPart没有区别。

注释:在下面的章节中,我们没有必要提供可以用来创建每种分区类型语法的所有可能形式,这些信息可以在13.1.5节,“CREATE TABLE语法” 中找到。
18.2.1. RANGE分区

按照RANGE分区的表是通过如下一种方式进行分区的,每个分区包含那些分区表达式的值位于一个给定的连续区间内的行。这些区间要连续且不能相互重叠,使用VALUES LESS THAN操作符来进行定义。在下面的几个例子中,假定你创建了一个如下的一个表,该表保存有20家音像店的职员记录,这20家音像店的编号从1到20。

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT NOT NULL,

    store_id INT NOT NULL

);

根据你的需要,这个表可以有多种方式来按照区间进行分区。一种方式是使用store_id 列。例如,你可能决定通过添加一个PARTITION BY RANGE子句把这个表分割成4个区间,如下所示:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT NOT NULL,

    store_id INT NOT NULL

)

PARTITION BY RANGE (store_id) (

    PARTITION p0 VALUES LESS THAN (6),

    PARTITION p1 VALUES LESS THAN (11),

    PARTITION p2 VALUES LESS THAN (16),

    PARTITION p3 VALUES LESS THAN (21)

);

按照这种分区方案,在商店1到5工作的雇员相对应的所有行被保存在分区P0中,商店6到10的雇员保存在P1中,依次类推。注意,每个分区都是按顺序进行定义,从最低到最高。这是PARTITION BY RANGE 语法的要求;在这点上,它类似于C或Java中的“switch ... case”语句。

对于包含数据(72, 'Michael', 'Widenius', '1998-06-25', NULL, 13)的一个新行,可以很容易地确定它将插入到p2分区中,但是如果增加了一个编号为第21的商店,将会发生什么呢?在这种方案下,由于没有规则把store_id大于20的商店包含在内,服务器将不知道把该行保存在何处,将会导致错误。 要避免这种错误,可以通过在CREATE TABLE语句中使用一个“catchall” VALUES LESS THAN子句,该子句提供给所有大于明确指定的最高值的值:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT NOT NULL,

    store_id INT NOT NULL

)

PARTITION BY RANGE (store_id) (

    PARTITION p0 VALUES LESS THAN (6),

    PARTITION p1 VALUES LESS THAN (11),

    PARTITION p2 VALUES LESS THAN (16),

    PARTITION p3 VALUES LESS THAN MAXVALUE

);

MAXVALUE 表示最大的可能的整数值。现在,store_id 列值大于或等于16(定义了的最高值)的所有行都将保存在分区p3中。在将来的某个时候,当商店数已经增长到25, 30, 或更多 ,可以使用ALTER TABLE语句为商店21-25, 26-30,等等增加新的分区 (关于如何实现的详细信息参见18.3节,“分区管理” )。

在几乎一样的结构中,你还可以基于雇员的工作代码来分割表,也就是说,基于job_code 列值的连续区间。例如——假定2位数字的工作代码用来表示普通(店内的)工人,三个数字代码表示办公室和支持人员,四个数字代码表示管理层,你可以使用下面的语句创建该分区表:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT NOT NULL,

    store_id INT NOT NULL

)

PARTITION BY RANGE (job_code) (

    PARTITION p0 VALUES LESS THAN (100),

    PARTITION p1 VALUES LESS THAN (1000),

    PARTITION p2 VALUES LESS THAN (10000)

);

在这个例子中, 店内工人相关的所有行将保存在分区p0中,办公室和支持人员相关的所有行保存在分区p1中,管理层相关的所有行保存在分区p2中。

在VALUES LESS THAN 子句中使用一个表达式也是可能的。这里最值得注意的限制是MySQL 必须能够计算表达式的返回值作为LESS THAN (<)比较的一部分;因此,表达式的值不能为NULL 。由于这个原因,雇员表的hired, separated, job_code,和store_id列已经被定义为非空(NOT NULL)。

除了可以根据商店编号分割表数据外,你还可以使用一个基于两个DATE (日期)中的一个的表达式来分割表数据。例如,假定你想基于每个雇员离开公司的年份来分割表,也就是说,YEAR(separated)的值。实现这种分区模式的CREATE TABLE 语句的一个例子如下所示:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY RANGE (YEAR(separated)) (

    PARTITION p0 VALUES LESS THAN (1991),

    PARTITION p1 VALUES LESS THAN (1996),

    PARTITION p2 VALUES LESS THAN (2001),

    PARTITION p3 VALUES LESS THAN MAXVALUE

);

在这个方案中,在1991年前雇佣的所有雇员的记录保存在分区p0中,1991年到1995年期间雇佣的所有雇员的记录保存在分区p1中, 1996年到2000年期间雇佣的所有雇员的记录保存在分区p2中,2000年后雇佣的所有工人的信息保存在p3中。

RANGE分区在如下场合特别有用:

·         当需要删除“旧的”数据时。如果你使用上面最近的那个例子给出的分区方案,你只需简单地使用 “ALTER TABLE employees DROP PARTITION p0;”来删除所有在1991年前就已经停止工作的雇员相对应的所有行。(更多信息请参见13.1.2节,“ALTER TABLE语法” 和 18.3节,“分区管理”)。对于有大量行的表,这比运行一个如“DELETE FROM employees WHERE YEAR(separated) <= 1990;”这样的一个DELETE查询要有效得多。

·         想要使用一个包含有日期或时间值,或包含有从一些其他级数开始增长的值的列。

·         经常运行直接依赖于用于分割表的列的查询。例如,当执行一个如“SELECT COUNT(*) FROM employees WHERE YEAR(separated) = 2000 GROUP BY store_id;”这样的查询时,MySQL可以很迅速地确定只有分区p2需要扫描,这是因为余下的分区不可能包含有符合该WHERE子句的任何记录。注释:这种优化还没有在MySQL 5.1源程序中启用,但是,有关工作正在进行中。
18.2.2. LIST分区

MySQL中的LIST分区在很多方面类似于RANGE分区。和按照RANGE分区一样,每个分区必须明确定义。它们的主要区别在于,LIST分区中每个分区的定义和选择是基于某列的值从属于一个值列表集中的一个值,而RANGE分区是从属于一个连续区间值的集合。LIST分区通过使用“PARTITION BY LIST(expr)”来实现,其中“expr” 是某列值或一个基于某个列值、并返回一个整数值的表达式,然后通过“VALUES IN (value_list)”的方式来定义每个分区,其中“value_list”是一个通过逗号分隔的整数列表。

注释:在MySQL 5.1中,当使用LIST分区时,有可能只能匹配整数列表。

不像按照RANGE定义分区的情形,LIST分区不必声明任何特定的顺序。关于LIST分区更详细的语法信息,请参考13.1.5节,“CREATE TABLE语法” 。

对于下面给出的例子,我们假定将要被分区的表的基本定义是通过下面的“CREATE TABLE”语句提供的:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

);

(这和18.2.1节,“RANGE分区” 中的例子中使用的是同一个表)。

假定有20个音像店,分布在4个有经销权的地区,如下表所示:

地区
    

商店ID 号

北区
    

3, 5, 6, 9, 17

东区
    

1, 2, 10, 11, 19, 20

西区
    

4, 12, 13, 14, 18

中心区
    

7, 8, 15, 16

要按照属于同一个地区商店的行保存在同一个分区中的方式来分割表,可以使用下面的“CREATE TABLE”语句:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY LIST(store_id)

    PARTITION pNorth VALUES IN (3,5,6,9,17),

    PARTITION pEast VALUES IN (1,2,10,11,19,20),

    PARTITION pWest VALUES IN (4,12,13,14,18),

    PARTITION pCentral VALUES IN (7,8,15,16)

);

这使得在表中增加或删除指定地区的雇员记录变得容易起来。例如,假定西区的所有音像店都卖给了其他公司。那么与在西区音像店工作雇员相关的所有记录(行)可以使用查询“ALTER TABLE employees DROP PARTITION pWest;”来进行删除,它与具有同样作用的DELETE (删除)查询“DELETE query DELETE FROM employees WHERE store_id IN (4,12,13,14,18);”比起来,要有效得多。

要点:如果试图插入列值(或分区表达式的返回值)不在分区值列表中的一行时,那么“INSERT”查询将失败并报错。例如,假定LIST分区的采用上面的方案,下面的查询将失败:

INSERT INTO employees VALUES

    (224, 'Linus', 'Torvalds', '2002-05-01', '2004-10-12', 42, 21);

这是因为“store_id”列值21不能在用于定义分区pNorth, pEast, pWest,或pCentral的值列表中找到。要重点注意的是,LIST分区没有类似如“VALUES LESS THAN MAXVALUE”这样的包含其他值在内的定义。将要匹配的任何值都必须在值列表中找到。

LIST分区除了能和RANGE分区结合起来生成一个复合的子分区,与HASH和KEY分区结合起来生成复合的子分区也是可能的。 关于这方面的讨论,请参考18.2.5节,“子分区”。
18.2.3. HASH分区

18.2.3.1. LINEAR HASH分区

HASH分区主要用来确保数据在预先确定数目的分区中平均分布。在RANGE和LIST分区中,必须明确指定一个给定的列值或列值集合应该保存在哪个分区中;而在HASH分区中,MySQL 自动完成这些工作,你所要做的只是基于将要被哈希的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。

要使用HASH分区来分割一个表,要在CREATE TABLE 语句上添加一个“PARTITION BY HASH (expr)”子句,其中“expr”是一个返回一个整数的表达式。它可以仅仅是字段类型为MySQL 整型的一列的名字。此外,你很可能需要在后面再添加一个“PARTITIONS num”子句,其中num 是一个非负的整数,它表示表将要被分割成分区的数量。

例如,下面的语句创建了一个使用基于“store_id”列进行 哈希处理的表,该表被分成了4个分区:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY HASH(store_id)

PARTITIONS 4;

如果没有包括一个PARTITIONS子句,那么分区的数量将默认为1。 例外: 对于NDB Cluster(簇)表,默认的分区数量将与簇数据节点的数量相同,这种修正可能是考虑任何MAX_ROWS 设置,以便确保所有的行都能合适地插入到分区中。(参见第17章:MySQL簇)。

如果在关键字“PARTITIONS”后面没有加上分区的数量,将会出现语法错误。

“expr”还可以是一个返回一个整数的SQL表达式。例如,也许你想基于雇用雇员的年份来进行分区。这可以通过下面的语句来实现:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY HASH(YEAR(hired))

PARTITIONS 4;

“expr”还可以是MySQL 中有效的任何函数或其他表达式,只要它们返回一个既非常数、也非随机数的整数。(换句话说,它既是变化的但又是确定的)。但是应当记住,每当插入或更新(或者可能删除)一行,这个表达式都要计算一次;这意味着非常复杂的表达式可能会引起性能问题,尤其是在执行同时影响大量行的运算(例如批量插入)的时候。

最有效率的哈希函数是只对单个表列进行计算,并且它的值随列值进行一致地增大或减小,因为这考虑了在分区范围上的“修剪”。也就是说,表达式值和它所基于的列的值变化越接近,MySQL就可以越有效地使用该表达式来进行HASH分区。

例如,“date_col” 是一个DATE(日期)类型的列,那么表达式TO_DAYS(date_col)就可以说是随列“date_col”值的变化而发生直接的变化,因为列 “date_col”值的每个变化,表达式的值也将发生与之一致的变化。而表达式YEAR(date_col)的变化就没有表达式 TO_DAYS(date_col)那么直接,因为不是列“date_col”每次可能的改变都能使表达式YEAR(date_col)发生同等的改变。即便如此,表达式YEAR(date_col)也还是一个用于哈希函数的、好的候选表达式,因为它随列date_col的一部分发生直接变化,并且列date_col的变化不可能引起表达式 YEAR(date_col)不成比例的变化。

作为对照,假定有一个类型为整型(INT)的、列名为“int_col”的列。现在考虑表达式“POW(5-int_col,3) + 6”。这对于哈希函数就是一个不好的选择,因为“int_col”值的变化并不能保证表达式产生成比例的变化。列 “int_col”的值发生一个给定数目的变化,可能会引起表达式的值产生一个很大不同的变化。例如,把列“int_col”的值从5变为6,表达式的值将产生“-1”的改变,但是把列“int_col”的值从6变为7时,表达式的值将产生“-7”的变化。

换句话说,如果列值与表达式值之比的曲线图越接近由等式“y=nx(其中n为非零的常数)描绘出的直线,则该表达式越适合于 哈希。这是因为,表达式的非线性越严重,分区中数据产生非均衡分布的趋势也将越严重。

理论上讲,对于涉及到多列的表达式,“修剪(pruning)”也是可能的,但是要确定哪些适于 哈希是非常困难和耗时的。基于这个原因,实际上不推荐使用涉及到多列的哈希表达式。

当使用了“PARTITION BY HASH”时,MySQL将基于用户函数结果的模数来确定使用哪个编号的分区。换句话,对于一个表达式“expr”,将要保存记录的分区编号为N ,其中“N = MOD(expr, num)”。例如,假定表t1 定义如下,它有4个分区:

CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)

    PARTITION BY HASH( YEAR(col3) )

    PARTITIONS 4;

如果插入一个col3列值为'2005-09-15'的记录到表t1中,那么保存该条记录的分区确定如下:

MOD(YEAR('2005-09-01'),4)

=  MOD(2005,4)

=  1

MySQL 5.1 还支持一个被称为“linear hashing(线性哈希功能)”的变量,它使用一个更加复杂的算法来确定新行插入到已经分区了的表中的位置。关于这种算法的描述,请参见18.2.3.1节,“LINEAR HASH分区” 。

每当插入或更新一条记录,用户函数都要计算一次。当删除记录时,用户函数也可能要进行计算,这取决于所处的环境。

注释:如果将要分区的表有一个唯一的键,那么用来作为HASH用户函数的自变数或者主键的column_list的自变数的任意列都必须是那个键的一部分。
18.2.3.1. LINEAR HASH分区

MySQL还支持线性哈希功能,它与常规哈希的区别在于,线性哈希功能使用的一个线性的2的幂(powers-of-two)运算法则,而常规 哈希使用的是求哈希函数值的模数。

线性哈希分区和常规哈希分区在语法上的唯一区别在于,在“PARTITION BY” 子句中添加“LINEAR”关键字,如下面所示:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY LINEAR HASH(YEAR(hired))

PARTITIONS 4;

假设一个表达式expr, 当使用线性哈希功能时,记录将要保存到的分区是num 个分区中的分区N,其中N是根据下面的算法得到:

1.    找到下一个大于num.的、2的幂,我们把这个值称为V ,它可以通过下面的公式得到:

2.           V = POWER(2, CEILING(LOG(2, num)))

(例如,假定num是13。那么LOG(2,13)就是3.7004397181411。 CEILING(3.7004397181411)就是4,则V = POWER(2,4), 即等于16)。

3.    设置 N = F(column_list) & (V - 1).

4.    当 N >= num:

·         设置 V = CEIL(V / 2)

·         设置 N = N & (V - 1)

例如,假设表t1,使用线性哈希分区且有4个分区,是通过下面的语句创建的:

CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)

    PARTITION BY LINEAR HASH( YEAR(col3) )

    PARTITIONS 6;

现在假设要插入两行记录到表t1中,其中一条记录col3列值为'2003-04-14',另一条记录col3列值为'1998-10-19'。第一条记录将要保存到的分区确定如下:

V = POWER(2, CEILING(LOG(2,7))) = 8

N = YEAR('2003-04-14') & (8 - 1)

   = 2003 & 7

   = 3

 

(3 >= 6 为假(FALSE): 记录将被保存到#3号分区中)

第二条记录将要保存到的分区序号计算如下:

V = 8

N = YEAR('1998-10-19') & (8-1)

  = 1998 & 7

  = 6

 

(6 >= 4 为真(TRUE): 还需要附加的步骤)

 

N = 6 & CEILING(5 / 2)

  = 6 & 3

  = 2

 

(2 >= 4 为假(FALSE): 记录将被保存到#2分区中)

按照线性哈希分区的优点在于增加、删除、合并和拆分分区将变得更加快捷,有利于处理含有极其大量(1000吉)数据的表。它的缺点在于,与使用常规HASH分区得到的数据分布相比,各个分区间数据的分布不大可能均衡。
18.2.4. KEY分区

按照KEY进行分区类似于按照HASH分区,除了HASH分区使用的用户定义的表达式,而KEY分区的 哈希函数是由MySQL 服务器提供。MySQL 簇(Cluster)使用函数MD5()来实现KEY分区;对于使用其他存储引擎的表,服务器使用其自己内部的 哈希函数,这些函数是基于与PASSWORD()一样的运算法则。

“CREATE TABLE ... PARTITION BY KEY”的语法规则类似于创建一个通过HASH分区的表的规则。它们唯一的区别在于使用的关键字是KEY而不是HASH,并且KEY分区只采用一个或多个列名的一个列表。

通过线性KEY分割一个表也是可能的。下面是一个简单的例子:

CREATE TABLE tk (

    col1 INT NOT NULL,

    col2 CHAR(5),

    col3 DATE

)

PARTITION BY LINEAR KEY (col1)

PARTITIONS 3;

在KEY分区中使用关键字LINEAR和在HASH分区中使用具有同样的作用,分区的编号是通过2的幂(powers-of-two)算法得到,而不是通过模数算法。关于该算法及其蕴涵式的描述请参考 18.2.3.1节,“LINEAR HASH分区” 。
18.2.5. 子分区

子分区是分区表中每个分区的再次分割。例如,考虑下面的CREATE TABLE 语句:

CREATE TABLE ts (id INT, purchased DATE)

    PARTITION BY RANGE(YEAR(purchased))

    SUBPARTITION BY HASH(TO_DAYS(purchased))

    SUBPARTITIONS 2

    (

        PARTITION p0 VALUES LESS THAN (1990),

        PARTITION p1 VALUES LESS THAN (2000),

        PARTITION p2 VALUES LESS THAN MAXVALUE

    );

表ts 有3个RANGE分区。这3个分区中的每一个分区——p0, p1, 和 p2 ——又被进一步分成了2个子分区。实际上,整个表被分成了3 * 2 = 6个分区。但是,由于PARTITION BY RANGE子句的作用,这些分区的头2个只保存“purchased”列中值小于1990的那些记录。

在MySQL 5.1中,对于已经通过RANGE或LIST分区了的表再进行子分区是可能的。子分区既可以使用HASH希分区,也可以使用KEY分区。这也被称为复合分区(composite partitioning)。

为了对个别的子分区指定选项,使用SUBPARTITION 子句来明确定义子分区也是可能的。例如,创建在前面例子中给出的同一个表的、一个更加详细的方式如下:

CREATE TABLE ts (id INT, purchased DATE)

    PARTITION BY RANGE(YEAR(purchased))

    SUBPARTITION BY HASH(TO_DAYS(purchased))

    (

        PARTITION p0 VALUES LESS THAN (1990)

        (

            SUBPARTITION s0,

            SUBPARTITION s1

        ),

        PARTITION p1 VALUES LESS THAN (2000)

        (

            SUBPARTITION s2,

            SUBPARTITION s3

        ),

        PARTITION p2 VALUES LESS THAN MAXVALUE

        (

            SUBPARTITION s4,

            SUBPARTITION s5

        )

    );

几点要注意的语法项:

·         每个分区必须有相同数量的子分区。

·         如果在一个分区表上的任何分区上使用SUBPARTITION 来明确定义任何子分区,那么就必须定义所有的子分区。换句话说,下面的语句将执行失败:

·                CREATE TABLE ts (id INT, purchased DATE)

·                    PARTITION BY RANGE(YEAR(purchased))

·                    SUBPARTITION BY HASH(TO_DAYS(purchased))

·                    (

·                        PARTITION p0 VALUES LESS THAN (1990)

·                        (

·                            SUBPARTITION s0,

·                            SUBPARTITION s1

·                        ),

·                        PARTITION p1 VALUES LESS THAN (2000),

·                        PARTITION p2 VALUES LESS THAN MAXVALUE

·                        (

·                            SUBPARTITION s2,

·                            SUBPARTITION s3

·                        )

·                    );

即便这个语句包含了一个SUBPARTITIONS 2子句,但是它仍然会执行失败。

·         每个SUBPARTITION 子句必须包括 (至少)子分区的一个名字。否则,你可能要对该子分区设置任何你所需要的选项,或者允许该子分区对那些选项采用其默认的设置。

·         在每个分区内,子分区的名字必须是唯一的,但是在整个表中,没有必要保持唯一。例如,下面的CREATE TABLE 语句是有效的:

·                CREATE TABLE ts (id INT, purchased DATE)

·                    PARTITION BY RANGE(YEAR(purchased))

·                    SUBPARTITION BY HASH(TO_DAYS(purchased))

·                    (

·                        PARTITION p0 VALUES LESS THAN (1990)

·                        (

·                            SUBPARTITION s0,

·                            SUBPARTITION s1

·                        ),

·                        PARTITION p1 VALUES LESS THAN (2000)

·                        (

·                            SUBPARTITION s0,

·                            SUBPARTITION s1

·                        ),

·                        PARTITION p2 VALUES LESS THAN MAXVALUE

·                        (

·                            SUBPARTITION s0,

·                            SUBPARTITION s1

·                        )

·                    );

子分区可以用于特别大的表,在多个磁盘间分配数据和索引。假设有6个磁盘,分别为/disk0, /disk1, /disk2等。现在考虑下面的例子:

CREATE TABLE ts (id INT, purchased DATE)

    PARTITION BY RANGE(YEAR(purchased))

    SUBPARTITION BY HASH(TO_DAYS(purchased))

    (

        PARTITION p0 VALUES LESS THAN (1990)

        (

            SUBPARTITION s0

                DATA DIRECTORY = '/disk0/data'

                INDEX DIRECTORY = '/disk0/idx',

            SUBPARTITION s1

                DATA DIRECTORY = '/disk1/data'

                INDEX DIRECTORY = '/disk1/idx'

        ),

        PARTITION p1 VALUES LESS THAN (2000)

        (

            SUBPARTITION s0

                DATA DIRECTORY = '/disk2/data'

                INDEX DIRECTORY = '/disk2/idx',

            SUBPARTITION s1

                DATA DIRECTORY = '/disk3/data'

                INDEX DIRECTORY = '/disk3/idx'

        ),

        PARTITION p2 VALUES LESS THAN MAXVALUE

        (

            SUBPARTITION s0

                DATA DIRECTORY = '/disk4/data'

                INDEX DIRECTORY = '/disk4/idx',

            SUBPARTITION s1

                DATA DIRECTORY = '/disk5/data'

                INDEX DIRECTORY = '/disk5/idx'

        )

    );

在这个例子中,每个RANGE分区的数据和索引都使用一个单独的磁盘。还可能有许多其他的变化;下面是另外一个可能的例子:

CREATE TABLE ts (id INT, purchased DATE)

    PARTITION BY RANGE(YEAR(purchased))

    SUBPARTITION BY HASH(TO_DAYS(purchased))

    (

        PARTITION p0 VALUES LESS THAN (1990)

        (

            SUBPARTITION s0a

                DATA DIRECTORY = '/disk0'

                INDEX DIRECTORY = '/disk1',

            SUBPARTITION s0b

                DATA DIRECTORY = '/disk2'

                INDEX DIRECTORY = '/disk3'

        ),

        PARTITION p1 VALUES LESS THAN (2000)

        (

            SUBPARTITION s1a

                DATA DIRECTORY = '/disk4/data'

                INDEX DIRECTORY = '/disk4/idx',

            SUBPARTITION s1b

                DATA DIRECTORY = '/disk5/data'

                INDEX DIRECTORY = '/disk5/idx'

        ),

        PARTITION p2 VALUES LESS THAN MAXVALUE

        (

            SUBPARTITION s2a,

            SUBPARTITION s2b

        )

    );

在这个例子中,存储的分配如下:

·         购买日期在1990年前的记录占了大量的存储空间,所以把它分为了四个部分进行存储,组成p0分区的两个子分区(s0a 和s0b)的数据和索引都分别用一个单独的磁盘进行存储。换句话说:

o        子分区s0a 的数据保存在磁盘/disk0中。

o        子分区s0a 的索引保存在磁盘/disk1中。

o        子分区s0b 的数据保存在磁盘/disk2中。

o        子分区s0b 的索引保存在磁盘/disk3中。

·         保存购买日期从1990年到1999年间的记录(分区p1)不需要保存购买日期在1990年之前的记录那么大的存储空间。这些记录分在2个磁盘(/disk4和/disk5)上保存,而不是4个磁盘:

o        属于分区p1的第一个子分区(s1a)的数据和索引保存在磁盘/disk4上 — 其中数据保存在路径/disk4/data下,索引保存在/disk4/idx下。

o        属于分区p1的第二个子分区(s1b)的数据和索引保存在磁盘/disk5上 — 其中数据保存在路径/disk5/data下,索引保存在/disk5/idx下。

·         保存购买日期从2000年到现在的记录(分区p2)不需要前面两个RANGE分区那么大的空间。当前,在默认的位置能够足够保存所有这些记录。

将来,如果从2000年开始后十年购买的数量已经达到了默认的位置不能够提供足够的保存空间时,相应的记录(行)可以通过使用“ALTER TABLE ... REORGANIZE PARTITION”语句移动到其他的位置。关于如何实现的说明,请参见18.3节,“分区管理” 。
18.2.6. MySQL分区处理NULL值的方式

MySQL 中的分区在禁止空值(NULL)上没有进行处理,无论它是一个列值还是一个用户定义表达式的值。一般而言,在这种情况下MySQL 把NULL视为0。如果你希望回避这种做法,你应该在设计表时不允许空值;最可能的方法是,通过声明列“NOT NULL”来实现这一点。

在本节中,我们提供了一些例子,来说明当决定一个行应该保存到哪个分区时,MySQL 是如何处理NULL值的。

如果插入一行到按照RANGE或LIST分区的表,该行用来确定分区的列值为NULL,分区将把该NULL值视为0。例如,考虑下面的两个表,表的创建和插入记录如下:

mysql> CREATE TABLE tnlist (

    ->     id INT,

    ->     name VARCHAR(5)

    -> )

    -> PARTITION BY LIST(id) (

    ->     PARTITION p1 VALUES IN (0),

    ->     PARTITION p2 VALUES IN (1)

    -> );

Query OK, 0 rows affected (0.09 sec)

 

mysql> CREATE TABLE tnrange (

    ->     id INT,

    ->     name VARCHAR(5)

    -> )

    -> PARTITION BY RANGE(id) (

    ->     PARTITION p1 VALUES LESS THAN (1),

    ->     PARTITION p2 VALUES LESS THAN MAXVALUE

    -> );

Query OK, 0 rows affected (0.09 sec)

 

mysql> INSERT INTO tnlist VALUES (NULL, 'bob');

Query OK, 1 row affected (0.00 sec)

 

mysql> INSERT INTO tnrange VALUES (NULL, 'jim');

Query OK, 1 row affected (0.00 sec)

 

mysql> SELECT * FROM tnlist;

+------+------+

| id   | name |

+------+------+

| NULL | bob  |

+------+------+

1 row in set (0.00 sec)

 

mysql> SELECT * FROM tnrange;

+------+------+

| id   | name |

+------+------+

| NULL | jim  |

+------+------+

1 row in set (0.00 sec)

在两个表中,id列没有声明为“NOT NULL”,这意味着它们允许Null值。可以通过删除这些分区,然后重新运行SELECT 语句,来验证这些行被保存在每个表的p1分区中:

mysql> ALTER TABLE tnlist DROP PARTITION p1;

Query OK, 0 rows affected (0.16 sec)

 

mysql> ALTER TABLE tnrange DROP PARTITION p1;

Query OK, 0 rows affected (0.16 sec)

 

mysql> SELECT * FROM tnlist;

Empty set (0.00 sec)

 

mysql> SELECT * FROM tnrange;

Empty set (0.00 sec)

在按HASH和KEY分区的情况下,任何产生NULL值的表达式都视同好像它的返回值为0。我们可以通过先创建一个按HASH分区的表,然后插入一个包含有适当值的记录,再检查对文件系统的作用,来验证这一点。假定有使用下面的语句在测试数据库中创建了一个表tnhash:

CREATE TABLE tnhash (

    id INT,

    name VARCHAR(5)

)

PARTITION BY HASH(id)

PARTITIONS 2;

假如Linux 上的MySQL 的一个RPM安装,这个语句在目录/var/lib/mysql/test下创建了两个.MYD文件,这两个文件可以在bash shell中查看,结果如下:

/var/lib/mysql/test> ls *.MYD -l

-rw-rw----  1 mysql mysql 0 2005-11-04 18:41 tnhash_p0.MYD

-rw-rw----  1 mysql mysql 0 2005-11-04 18:41 tnhash_p1.MYD

注意:每个文件的大小为0字节。现在在表tnhash 中插入一行id列值为NULL的行,然后验证该行已经被插入:

mysql> INSERT INTO tnhash VALUES (NULL, 'sam');

Query OK, 1 row affected (0.00 sec)

 

mysql> SELECT * FROM tnhash;

+------+------+

| id   | name |

+------+------+

| NULL | sam  |

+------+------+

1 row in set (0.01 sec)

回想一下,对于任意的整数N,NULL MOD N 的值总是等于NULL。这个结果在确定正确的分区方面被认为是0。回到系统shell(仍然假定bash用于这个目的) ,通过再次列出数据文件,可以看出值被成功地插入到第一个分区(默认名称为p0)中:

var/lib/mysql/test> ls *.MYD -l

-rw-rw----  1 mysql mysql 20 2005-11-04 18:44 tnhash_p0.MYD

-rw-rw----  1 mysql mysql  0 2005-11-04 18:41 tnhash_p1.MYD

可以看出INSERT语句只修改了文件tnhash_p0.MYD,它在磁盘上的尺寸增加了,而没有影响其他的文件。

假定有下面的一个表:

CREATE TABLE tndate (

    id INT,

    dt DATE

)

PARTITION BY RANGE( YEAR(dt) ) (

    PARTITION p0 VALUES LESS THAN (1990),

    PARTITION p1 VALUES LESS THAN (2000),

    PARTITION p2 VALUES LESS THAN MAXVALUE

);

像其他的MySQL函数一样,YEAR(NULL)返回NULL值。一个dt列值为NULL的行,其分区表达式的计算结果被视为0,该行被插入到分区p0中。
18.3. 分区管理

18.3.1. RANGE和LIST分区的管理
18.3.2. HASH和KEY分区的管理
18.3.3. 分区维护
18.3.4. 获取关于分区的信息

MySQL 5.1 提供了许多修改分区表的方式。添加、删除、重新定义、合并或拆分已经存在的分区是可能的。所有这些操作都可以通过使用ALTER TABLE 命令的分区扩展来实现(关于语法的定义,请参见13.1.2节,“ALTER TABLE语法” )。也有获得分区表和分区信息的方式。在本节,我们讨论下面这些主题:

·         按RANGE或LIST分区的表的分区管理的有关信息,请参见18.3.1节,“RANGE和LIST分区的管理”。

·         关于HASH和KEY分区管理的讨论,请参见18.3.2节,“HASH和KEY分区的管理”。

·         MySQL 5.1中提供的、获得关于分区表和分区信息的机制的讨论,请参见18.3.4节,“获取关于分区的信息” 。

·         关于执行分区维护操作的讨论,请参见18.3.3节,“分区维护”。

注释:在MySQL 5.1中,一个分区表的所有分区都必须有子分区同样的名字,并且一旦表已经创建,再改变子分区是不可能的。

要点:当前,从5.1系列起建立的MySQL 服务器就把“ALTER TABLE ... PARTITION BY ...”作为有效的语法,但是这个语句目前还不起作用。我们期望MySQL 5.1达到生产状态时,能够按照下面的描述实现该语句的功能。

要改变一个表的分区模式,只需要使用带有一个“partition_options”子句的ALTER TABLE 的命令。这个子句和与创建一个分区表的CREATE TABLE命令一同使用的子句有相同的语法,并且总是以关键字PARTITION BY 开头。例如,假设有一个使用下面CREATE TABLE语句建立的按照RANGE分区的表:

CREATE TABLE trb3 (id INT, name VARCHAR(50), purchased DATE)

    PARTITION BY RANGE(YEAR(purchased))

    (

        PARTITION p0 VALUES LESS THAN (1990),

        PARTITION p1 VALUES LESS THAN (1995),

        PARTITION p2 VALUES LESS THAN (2000),

        PARTITION p3 VALUES LESS THAN (2005)

    );

现在,要把这个表按照使用id列值作为键的基础,通过KEY分区把它重新分成两个分区,可以使用下面的语句:

ALTER TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2;

这和先删除这个表、然后使用“CREATE TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2;”重新创建这个表具有同样的效果。
18.3.1. RANGE和LIST分区的管理

关于如何添加和删除分区的处理,RANGE和LIST分区非常相似。基于这个原因,我们在本节讨论这两种分区的管理。关于HASH和KEY分区管理的信息,请参见18.3.2节,“HASH和KEY分区的管理”。删除一个RANGE或LIST分区比增加一个分区要更加简单易懂,所以我们先讨论前者。

从一个按照RANGE或LIST分区的表中删除一个分区,可以使用带一个DROP PARTITION子句的ALTER TABLE命令来实现。这里有一个非常基本的例子,假设已经使用下面的CREATE TABLE和INSERT语句创建了一个按照RANGE分区的表,并且已经插入了10条记录:

mysql> CREATE TABLE tr (id INT, name VARCHAR(50), purchased DATE)

    ->     PARTITION BY RANGE(YEAR(purchased))

    ->     (

    ->         PARTITION p0 VALUES LESS THAN (1990),

    ->         PARTITION p1 VALUES LESS THAN (1995),

    ->         PARTITION p2 VALUES LESS THAN (2000),

    ->         PARTITION p3 VALUES LESS THAN (2005)

    ->     );

Query OK, 0 rows affected (0.01 sec)

 

mysql> INSERT INTO tr VALUES

    ->     (1, 'desk organiser', '2003-10-15'),

    ->     (2, 'CD player', '1993-11-05'),

    ->     (3, 'TV set', '1996-03-10'),

    ->     (4, 'bookcase', '1982-01-10'),

    ->     (5, 'exercise bike', '2004-05-09'),

    ->     (6, 'sofa', '1987-06-05'),

    ->     (7, 'popcorn maker', '2001-11-22'),

    ->     (8, 'aquarium', '1992-08-04'),

    ->     (9, 'study desk', '1984-09-16'),

    ->     (10, 'lava lamp', '1998-12-25');

Query OK, 10 rows affected (0.01 sec)                  

可以通过使用下面的命令查看那些记录已经插入到了分区p2中:

mysql> SELECT * FROM tr

    -> WHERE purchased BETWEEN '1995-01-01' AND '1999-12-31';

+------+-----------+------------+

| id   | name      | purchased  |

+------+-----------+------------+

|    3 | TV set    | 1996-03-10 |

|   10 | lava lamp | 1998-12-25 |

+------+-----------+------------+

2 rows in set (0.00 sec)

要删除名字为p2的分区,执行下面的命令:

mysql> ALTER TABLE tr DROP PARTITION p2;

Query OK, 0 rows affected (0.03 sec)

记住下面一点非常重要:当删除了一个分区,也同时删除了该分区中所有的数据。可以通过重新运行前面的SELECT查询来验证这一点:

mysql> SELECT * FROM tr WHERE purchased

    -> BETWEEN '1995-01-01' AND '1999-12-31';

Empty set (0.00 sec)

如果希望从所有分区删除所有的数据,但是又保留表的定义和表的分区模式,使用TRUNCATE TABLE命令。(请参见13.2.9节,“TRUNCATE语法”)。

如果希望改变表的分区而又不丢失数据,使用“ALTER TABLE ... REORGANIZE PARTITION”语句。参见下面的内容,或者在13.1.2节,“ALTER TABLE语法” 中参考关于REORGANIZE PARTITION的信息。

如果现在执行一个SHOW CREATE TABLE命令,可以观察到表的分区结构是如何被改变的:

mysql> SHOW CREATE TABLE tr/G

*************************** 1. row ***************************

       Table: tr

Create Table: CREATE TABLE `tr` (

  `id` int(11) default NULL,

  `name` varchar(50) default NULL,

  `purchased` date default NULL

) ENGINE=MyISAM DEFAULT CHARSET=latin1

PARTITION BY RANGE (YEAR(purchased)) (

  PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM,

  PARTITION p1 VALUES LESS THAN (1995) ENGINE = MyISAM,

  PARTITION p3 VALUES LESS THAN (2005) ENGINE = MyISAM

)

1 row in set (0.01 sec)

如果插入购买日期列的值在'1995-01-01'和 '2004-12-31'之间(含)的新行到已经修改后的表中时,这些行将被保存在分区p3中。可以通过下面的方式来验证这一点:

mysql> INSERT INTO tr VALUES (11, 'pencil holder', '1995-07-12');

Query OK, 1 row affected (0.00 sec)

 

mysql> SELECT * FROM tr WHERE purchased

    -> BETWEEN '1995-01-01' AND '2004-12-31';

+------+----------------+------------+

| id   | name           | purchased  |

+------+----------------+------------+

|   11 | pencil holder  | 1995-07-12 |

|    1 | desk organiser | 2003-10-15 |

|    5 | exercise bike  | 2004-05-09 |

|    7 | popcorn maker  | 2001-11-22 |

+------+----------------+------------+

4 rows in set (0.00 sec)

 

mysql> ALTER TABLE tr DROP PARTITION p3;

Query OK, 0 rows affected (0.03 sec)

 

mysql> SELECT * FROM tr WHERE purchased

    -> BETWEEN '1995-01-01' AND '2004-12-31';

Empty set (0.00 sec)

注意:由“ALTER TABLE ... DROP PARTITION”语句引起的、从表中删除的行数并没有被服务器报告出来,就好像通过同等的DELETE查询操作一样。

删除LIST分区使用和删除RANGE分区完全相同的“ALTER TABLE ... DROP PARTITION”语法。但是,在对其后使用这个表的影响方面,还是有重大的区别:在这个表中,再也不能插入这么一些行,这些行的列值包含在定义已经删除了的分区的值列表中 (有关示例,请参见18.2.2节,“LIST分区” )。

要增加一个新的RANGE或LIST分区到一个前面已经分区了的表,使用“ALTER TABLE ... ADD PARTITION”语句。对于使用RANGE分区的表,可以用这个语句添加新的区间到已有分区的序列的前面或后面。例如,假设有一个包含你所在组织的全体成员数据的分区表,该表的定义如下:

CREATE TABLE members (

    id INT,

    fname VARCHAR(25),

    lname VARCHAR(25),

    dob DATE

)

PARTITION BY RANGE(YEAR(dob)) (

    PARTITION p0 VALUES LESS THAN (1970),

    PARTITION p1 VALUES LESS THAN (1980),

    PARTITION p2 VALUES LESS THAN (1990)

);

进一步假设成员的最小年纪是16岁。随着日历接近2005年年底,你会认识到不久将要接纳1990年(以及以后年份)出生的成员。可以按照下面的方式,修改成员表来容纳出生在1990-1999年之间的成员:

ALTER TABLE ADD PARTITION (PARTITION p3 VALUES LESS THAN (2000));

要点:对于通过RANGE分区的表,只可以使用ADD PARTITION添加新的分区到分区列表的高端。设法通过这种方式在现有分区的前面或之间增加一个新的分区,将会导致下面的一个错误:

mysql> ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN (1960));

错误1463 (HY000): 对每个分区,VALUES LESS THAN 值必须严格增长

采用一个类似的方式,可以增加新的分区到已经通过LIST分区的表。例如,假定有如下定义的一个表:

CREATE TABLE tt (

    id INT,

    data INT

)

PARTITION BY LIST(data) (

    PARTITION p0 VALUES IN (5, 10, 15),

    PARTITION p1 VALUES IN (6, 12, 18)

);

可以通过下面的方法添加一个新的分区,用来保存拥有数据列值7,14和21的行:

ALTER TABLE tt ADD PARTITION (PARTITION p2 VALUES IN (7, 14, 21));

注意:不能添加这样一个新的LIST分区,该分区包含有已经包含在现有分区值列表中的任意值。如果试图这样做,将会导致错误:

mysql> ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8, 12));

错误1465 (HY000): 在LIST分区中,同一个常数的多次定义

因为带有数据列值12的任何行都已经分配给了分区p1,所以不能在表tt上再创建一个其值列表包括12的新分区。为了实现这一点,可以先删除分区p1,添加分区np,然后使用修正后的定义添加一个新的分区p1。但是,正如我们前面讨论过的,这将导致保存在分区p1中的所有数据丢失——而这往往并不是你所真正想要做的。另外一种解决方法可能是,建立一个带有新分区的表的副本,然后使用“CREATE TABLE ... SELECT ...”把数据拷贝到该新表中,然后删除旧表,重新命名新表,但是,当需要处理大量的数据时,这可能是非常耗时的。在需要高可用性的场合,这也可能是不可行的。

幸运地是,MySQL 的分区实现提供了在不丢失数据的条件下重新定义分区的方式。让我们首先看两个涉及到RANGE分区的简单例子。回想一下现在定义如下的成员表:

mysql> SHOW CREATE TABLE members/G

*************************** 1. row ***************************

       Table: members

Create Table: CREATE TABLE `members` (

  `id` int(11) default NULL,

  `fname` varchar(25) default NULL,

  `lname` varchar(25) default NULL,

  `dob` date default NULL

) ENGINE=MyISAM DEFAULT CHARSET=latin1

PARTITION BY RANGE (YEAR(dob)) (

  PARTITION p0 VALUES LESS THAN (1970) ENGINE = MyISAM,

  PARTITION p1 VALUES LESS THAN (1980) ENGINE = MyISAM,

  PARTITION p2 VALUES LESS THAN (1990) ENGINE = MyISAM.

  PARTITION p3 VALUES LESS THAN (2000) ENGINE = MyISAM

)

假定想要把表示出生在1960年前成员的所有行移入到一个分开的分区中。正如我们前面看到的,不能通过使用“ALTER TABLE ... ADD PARTITION”来实现这一点。但是,要实现这一点,可以使用ALTER TABLE上的另外一个与分区有关的扩展,具体实现如下:

ALTER TABLE members REORGANIZE PARTITION p0 INTO (

    PARTITION s0 VALUES LESS THAN (1960),

    PARTITION s1 VALUES LESS THAN (1970)

);

实际上,这个命令把分区p0分成了两个新的分区s0和s1。同时,它还根据包含在两个“PARTITION ... VALUES ...”子句中的规则,把保存在分区p0中的数据移入到两个新的分区中,所以分区s0中只包含YEAR(dob)小于1960的那些行,s1中包含那些YEAR(dob)大于或等于1960但是小于1970的行。

一个REORGANIZE PARTITION语句也可以用来合并相邻的分区。可以使用如下的语句恢复成员表到它以前的分区:

ALTER TABLE members REORGANIZE PARTITION s0,s1 INTO (

    PARTITION p0 VALUES LESS THAN (1970)

);

使用“REORGANIZE PARTITION”拆分或合并分区,没有数据丢失。在执行上面的语句中,MySQL 把保存在分区s0和s1中的所有数据都移到分区p0中。

“REORGANIZE PARTITION”的基本语法是:

ALTER TABLE tbl_name REORGANIZE PARTITION partition_list INTO (partition_definitions);

其中,tbl_name 是分区表的名称,partition_list 是通过逗号分开的、一个或多个将要被改变的现有分区的列表。partition_definitions 是一个是通过逗号分开的、新分区定义的列表,它遵循与用在“CREATE TABLE”中的partition_definitions 相同的规则 (请参见13.1.5节,“CREATE TABLE语法”)。应当注意到,在把多少个分区合并到一个分区或把一个分区拆分成多少个分区方面,没有限制。例如,可以重新组织成员表的四个分区成两个分区,具体实现如下:

ALTER TABLE members REORGANIZE PARTITION p0,p1,p2,p3 INTO (

    PARTITION m0 VALUES LESS THAN (1980),

    PARTITION m1 VALUES LESS THAN (2000)

);

同样,对于按LIST分区的表,也可以使用REORGANIZE PARTITION。让我们回到那个问题,即增加一个新的分区到已经按照LIST分区的表tt中,但是因为该新分区有一个值已经存在于现有分区的值列表中,添加新的分区失败。我们可以通过先添加只包含非冲突值的分区,然后重新组织该新分区和现有的那个分区,以便保存在现有的那个分区中的值现在移到了新的分区中,来处理这个问题:

ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8));

ALTER TABLE tt REORGANIZE PARTITION p1,np INTO (

    PARTITION p1 VALUES IN (6, 18),

    PARTITION np VALUES in (4, 8, 12)

);

当使用“ALTER TABLE ... REORGANIZE PARTITION”来对已经按照RANGE和LIST分区表进行重新分区时,下面是一些要记住的关键点:

·         用来确定新分区模式的PARTITION子句使用与用在CREATE TABLE中确定分区模式的PARTITION子句相同的规则。

最重要的是,应该记住:新分区模式不能有任何重叠的区间(适用于按照RANGE分区的表)或值集合(适用于重新组织按照LIST分区的表)。

·         partition_definitions 列表中分区的合集应该与在partition_list 中命名分区的合集占有相同的区间或值集合。

例如,在本节中用作例子的成员表中,分区p1和p2总共覆盖了1980到1999的这些年。因此,对这两个分区的重新组织都应该覆盖相同范围的年份。

·         对于按照RANGE分区的表,只能重新组织相邻的分区;不能跳过RANGE分区。

例如,不能使用以“ALTER TABLE members REORGANIZE PARTITION p0,p2 INTO ...”开头的语句,来重新组织本节中用作例子的成员表。因为,p0覆盖了1970年以前的年份,而p2覆盖了从1990到1999(包括1990和1999)之间的年份,因而这两个分区不是相邻的分区。

·         不能使用REORGANIZE PARTITION来改变表的分区类型;也就是说,例如,不能把RANGE分区变为HASH分区,反之亦然。也不能使用该命令来改变分区表达式或列。如果想在不删除和重建表的条件下实现这两个任务,可以使用“ALTER TABLE ... PARTITION BY ....”,例如:

·                ALTER TABLE members

·                    PARTITION BY HASH(YEAR(dob))

·                    PARTITIONS 8;

注释:在MySQL 5.1发布前的版本中,“ALTER TABLE ... PARTITION BY ...”还没有实现。作为替代,要么使用先删除表,然后使用想要的分区重建表,或者——如果需要保留已经存储在表中的数据——可以使用“CREATE TABLE ... SELECT ...”来创建新的表,然后从旧表中把数据拷贝到新表中,再删除旧表,如有必要,最后重新命名新表。
18.3.2. HASH和KEY分区的管理

在改变分区设置方面,按照HASH分区或KEY分区的表彼此非常相似,但是它们又与按照RANGE或LIST分区的表在很多方面有差别。所以,本节只讨论按照HASH或KEY分区表的修改。关于添加和删除按照RANGE或LIST进行分区的表的分区的讨论,参见18.3.1节,“RANGE和LIST分区的管理”。

不能使用与从按照RANGE或LIST分区的表中删除分区相同的方式,来从HASH或KEY分区的表中删除分区。但是,可以使用“ALTER TABLE ... COALESCE PARTITION”命令来合并HASH或KEY分区。例如,假定有一个包含顾客信息数据的表,它被分成了12个分区。该顾客表的定义如下:

CREATE TABLE clients (

    id INT,

    fname VARCHAR(30),

    lname VARCHAR(30),

    signed DATE

)

PARTITION BY HASH( MONTH(signed) )

PARTITIONS 12;

要减少分区的数量从12到6,执行下面的ALTER TABLE命令:

mysql> ALTER TABLE clients COALESCE PARTITION 6;

Query OK, 0 rows affected (0.02 sec)

对于按照HASH,KEY,LINEAR HASH,或LINEAR KEY分区的表, COALESCE能起到同样的作用。下面是一个类似于前面例子的另外一个例子,它们的区别只是在于表是按照LINEAR KEY 进行分区:

mysql> CREATE TABLE clients_lk (

    ->     id INT,

    ->     fname VARCHAR(30),

    ->     lname VARCHAR(30),

    ->     signed DATE

    -> )

    -> PARTITION BY LINEAR KEY(signed)

    -> PARTITIONS 12;

Query OK, 0 rows affected (0.03 sec)

 

mysql> ALTER TABLE clients_lk COALESCE PARTITION 6;

Query OK, 0 rows affected (0.06 sec)

Records: 0  Duplicates: 0  Warnings: 0

COALESCE不能用来增加分区的数量,如果你尝试这么做,结果会出现类似于下面的错误:

mysql> ALTER TABLE clients COALESCE PARTITION 18;

错误1478 (HY000): 不能移动所有分区,使用DROP TABLE代替

要增加顾客表的分区数量从12到18,使用“ALTER TABLE ... ADD PARTITION”,具体如下:

ALTER TABLE clients ADD PARTITION PARTITIONS 18;

注释:“ALTER TABLE ... REORGANIZE PARTITION”不能用于按照HASH或HASH分区的表。
18.3.3. 分区维护

注释:实际上,本节讨论的命令还没有在MySQL 5.1中实现, 在这里提出的目的,是为了在5.1版投产前的开发周期期间,引出来自用户测试该软件的反馈意见。(换句话说,就是“请不要反馈这样的缺陷,说这些命令不起作用”)。随着MySQL5.1版开发的继续,这些信息很有可能发生变化。随着分区功能的实现和提高,我们将更新本节的内容。

MySQL 5.1中可以执行许多分区维护的任务。对于分区表,MySQL不支持命令CHECK TABLE,OPTIMIZE TABLE,ANALYZE TABLE,或REPAIR TABLE。作为替代,可以使用ALTER TABLE 的许多扩展来在一个或多个分区上直接地执行这些操作,如下面列出的那样:

·         重建分区: 这和先删除保存在分区中的所有记录,然后重新插入它们,具有同样的效果。它可用于整理分区碎片。

示例:

ALTER TABLE t1 REBUILD PARTITION (p0, p1);

·         优化分区:如果从分区中删除了大量的行,或者对一个带有可变长度的行(也就是说,有VARCHAR,BLOB,或TEXT类型的列)作了许多修改,可以使用“ALTER TABLE ... OPTIMIZE PARTITION”来收回没有使用的空间,并整理分区数据文件的碎片。

示例:

ALTER TABLE t1 OPTIMIZE PARTITION (p0, p1);

在一个给定的分区表上使用“OPTIMIZE PARTITION”等同于在那个分区上运行CHECK PARTITION,ANALYZE PARTITION,和REPAIR PARTITION。

·         分析分区:读取并保存分区的键分布。

示例:

ALTER TABLE t1 ANALYZE PARTITION (p3);

·         修补分区: 修补被破坏的分区。

示例:

ALTER TABLE t1 REPAIR PARTITION (p0,p1);

·         检查分区: 可以使用几乎与对非分区表使用CHECK TABLE 相同的方式检查分区。

示例:

ALTER TABLE trb3 CHECK PARTITION (p1);

这个命令可以告诉你表t1的分区p1中的数据或索引是否已经被破坏。如果发生了这种情况,使用“ALTER TABLE ... REPAIR PARTITION”来修补该分区。

还可以使用mysqlcheck或myisamchk 应用程序,在对表进行分区时所产生的、单独的MYI文件上进行操作,来完成这些任务。请参见8.7节,“mysqlcheck:表维护和维修程序”。(在pre-alpha编码中,这个功能已经可以使用)。
18.3.4. 获取关于分区的信息

本节讨论获取关于现有分区的信息。这个功能仍然处于计划阶段,所以现阶段在这里描述的,实际上是我们想要在MySQL 5.1中实现的一个概观。

如在本章中别处讨论的一样,在SHOW CREATE TABLE的输出中包含了用于创建分区表的PARTITION BY子句。例如:

mysql> SHOW CREATE TABLE trb3/G

*************************** 1. row ***************************

       Table: trb3

Create Table: CREATE TABLE `trb3` (

  `id` int(11) default NULL,

  `name` varchar(50) default NULL,

  `purchased` date default NULL

) ENGINE=MyISAM DEFAULT CHARSET=latin1

PARTITION BY RANGE (YEAR(purchased)) (

  PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM,

  PARTITION p1 VALUES LESS THAN (1995) ENGINE = MyISAM,

  PARTITION p2 VALUES LESS THAN (2000) ENGINE = MyISAM,

  PARTITION p3 VALUES LESS THAN (2005) ENGINE = MyISAM

)

1 row in set (0.00 sec)

注释:当前,对于按HASH或KEY分区的表,PARTITIONS子句并不显示。 (Bug #14327)

SHOW TABLE STATUS用于分区表,它的输出与用于非分区表的输出相同,除了引擎(Engine)列总是包含'PARTITION'值。(关于这个命令的更多信息,参见13.5.4.18节,“SHOW TABLE STATUS语法”)。要获取单个分区的状态信息,我们计划实现一个SHOW PARTITION STATUS命令(请参见下面)。

计划用于分区表的、两个附加的SHOW命令是:

·         SHOW PARTITIONS

这个命令预期其功能类似于SHOW TABLES和SHOW DATABASES,除了该命令将列出的是分区而不是表或数据库。这个命令的输出可能包含单个称为Partitions_in_tbl_name 的列,其中tbl_name 是分区表的名字。对于SHOW TABLES命令而言,如果一旦选择了一个数据库,随后该数据库将作为SHOW TABLES命令的默认数据库。但是由于SHOW PARTITIONS命令不可能用这样的方式来“选择”一个表,它很可能需要使用FROM子句,以便MySQL知道要显示的是哪个表的分区信息。

·         SHOW PARTITION STATUS

这个命令将提供关于一个或多个分区的详细状态信息。它的输出很可能包含有与SHOW TABLE STATUS 的输出相同或类似的列,此外,还包括显示用于分区的数据和索引路径的附加列。这个命令可能支持LIKE和FROM子句,这样使得通过名字获得关于一个给定分区的信息,或者获得关于属于指定表或数据库的分区的信息,成为可能。

扩展INFORMATION_SCHEMA 数据库的计划也在进行中,以便提供关于分区表和分区的信息。这个计划当前还处一个在非常早的阶段;随着补充的信息变得可用,以及任何新的、与分区有关的INFORMATION_SCHEMA扩展得以实现,我们将更新手册相关部分的内容。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值