TokuDB 使用简单说明

按照官方的介绍, TokuDB 引擎是可扩展的,支持事务 ACID 特性, 支持多版本控制(MVCC), 这几点等同 InnoDB 的特性, 不过对基于索引的查询做了很好的改进, 还提供了支持在线表更改的支持(不是所有字段都支持, 后面再说明), 在磁盘和缓存方面也做了很好的改进. TokuDB 结合 松散树索引(Fractal Tree indexing) 可以应用于高负载的大量写(write-intensive)的场景里. 现在的限制还比较多, Percona 从 5.6.19-67.0 开始支持 tokudb, 现在还只能运行到 64 位的 Linux 版本中, Debian 6.0 和 Ubuntu 10.0.4 因为 gcc 版本冲突的原因也不支持, 同时也不支持外键(foreign key)功能,如果表有外键,切换到TokuDB引擎后,此约束将被忽略. 其它版本(mariadb, mongodb)的 TokuDB 引擎支持可以从 Tokutek 的github 页面查找: Tokutek

先从 tokudb-index-using-tokudb 概览下 TokuDB 的特性, 需要注意的是不要移动或修改任何 TokuDB 文件, 这可能会引起 MySQL Server 端的崩溃.

1. 丰富的索引类型以及索引的快速创建

TokuDB 除了支持现有的索引类型外, 还增加了(第二)集合索引, 以满足多样性的覆盖索引的查询, 在快速创建索引方面提高了查询的效率;

2. (第二)集合索引

也可以称作非主键的集合索引, 这类索引也包含了表中的所有列, 可以用于覆盖索引的查询需要, 比如以下示例, 在where 条件中直接命中 index_b 索引, 避免了从主键中再查找一次.

CREATE TABLE table (
column_a INT,
column_b INT,
column_c INT,
PRIMARY KEY index_a (column_a),
CLUSTERING KEY index_b (column_b)) ENGINE = TokuDB;
SELECT column_c
FROM table
WHERE column_b BETWEEN 10 AND 100;

见: introducing_multiple_clustering_indexes

3. 索引在线创建(Hot Index Creation)

> SET tokudb_create_index_online=ON;
Query OK, 0 rows affected (0.00 sec)
> CREATE INDEX index ON table (field_name);

TokuDB 允许直接给表增加索引而不影响更新语句(insert, update 等)的执行. 可以通过变量 tokudb_create_index_online 来控制是否开启该特性, 不过遗憾的是目前还只能通过 CREATE INDEX 语法实现在线创建, 不能通过 ALTER TABLE 实现. 这种方式比通常的创建方式慢了许多, 创建的过程可以通过 show processlist 查看. 如下所示:

Adding of indexes to ./test/text_data#P#p20150528 about 92.2% done

不过 tokudb 不支持在线删除索引, 删除索引的时候会对表加全局锁.

4. 在线更改列(Add, Delete, Expand, Rename)

TokuDB 可以在轻微阻塞更新或查询语句的情况下, 允许实现以下操作:

增加或删除表中的列;
扩充字段: char, varchar, varbinary 和 int 类型的列;
重命名列, 不支持字段类型: TIME, ENUM, BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB;

这些操作通常是以表锁级别阻塞(几秒钟时间)其他查询的执行, 当表记录下次从磁盘加载到内存的时候, 系统就会随之对记录进行修改操作(add, delete 或 expand), 如果是 rename 操作, 则会在几秒钟的停机时间内完成所有操作;

TokuDB的这些操作不同于 InnoDB, 对表进行更新后可以看到 rows affected 为 0, 即更改操作会放到后台执行, 比较快速的原因可能是由于 Fractal-tree 索引的特性, 将随机的 IO 操作替换为顺序 IO 操作, Fractal-tree的特性中, 会将这些操作广播到所有行, 不像 InnoDB, 需要 open table 并创建临时表来完成.

看看官方对该特性的一些指导说明:

1. 所有的这些操作不是立即执行, 而是放到后台中由 Fractal Tree 完成, 操作包括主键和非主键索引. 也可以手工强制执行这些操作, 使用 OPTIMIZE TABLE X 命令即可, TokuDB 从 7.1.0 开始OPTIMIZE TABLE命令也支持在线完成, 但是不会重建索引;
2. 不要一次更新多列, 分开对每列进行操作;
3. 避免同时对一列进行 add, delete, expand 或 drop 操作;
4. 表锁的时间主要由缓存中的脏页(dirty page)决定, 脏页越多 flush 的时间就越长. 每做一次更新, MySQL 都会关闭一次表的连接以释放之前的资源;
5. 避免删除的列是索引的一部分, 这类操作会特别慢, 非要删除的话可以去掉索引和该列的关联再进行删除操作;
6. 扩充类的操作只支持 char, varchar, varbinary 和 int 类型的字段, 不支持字段收缩;
7. 一次只 rename 一列, 操作多列会降级为标准的 MySQL 行为, 语法中列的属性必须要指定上, 如下:
    ALTER TABLE table
    CHANGE column_old column_new
    DATA_TYPE REQUIRED_NESS DEFAULT
8. rename 操作还不支持字段: TIME, ENUM, BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB.
9. 不支持更新临时表;

更多特性见: Hot-schema-change-with-TokuDB

5. 数据压缩

TokuDB中所有的压缩操作都在后台执行, 高级别的压缩会降低系统的性能, 有些场景下会需要高级别的压缩. 按照官方的建议: 6核数以下的机器建议标准压缩, 反之可以使用高级别的压缩。 每个表在 create table 或 alter table 的时候通过 ROW_FORMAT 来指定压缩的算法:

CREATE TABLE table (
  column_a INT NOT NULL PRIMARY KEY,
  column_b INT NOT NULL) ENGINE=TokuDB
ROW_FORMAT=row_format;

ROW_FORMAT默认由变量 tokudb_row_format 控制, 默认为 tokudb_zlib, 可以的值包括:

tokudb_zlib: Compress using the zlib library, which provides mid-range compression and CPU utilization.
tokudb_quicklz: Compress using the quicklz library, which provides light compression and low CPU utilization.
tokudb_lzma: Compress using the lzma library, which provides the highest compression and high CPU utilization.
tokudb_uncompressed: This setting turns off compression and is useful for tables with uncompressible data.

6. Read free 复制特性

得益于 Fracal Tree 索引的特性, TokuDB 的 slave 端能够以低于读IO的消耗来应用 master 端的变化, 其主要依赖 Fractal Tree 索引的特性,可以在配置里启用特性:

1. insert/delete/update操作部分可以直接插入到合适的 Fractal Tree 索引中, 避免 read-modify-write 行为的开销;
2. delete/update 操作可以忽略唯一性检查带来的 IO 方面的开销;

不好的是, 如果启用了 Read Free Replication 功能, Server 端需要做如下设置:

master: 复制格式必须为 ROW, 因为 tokudb 还没有实现对 auto-increment函数进行加锁处理, 所以多个并发的插入语句可能会引起不确定的 auto-increment值, 由此造成主从两边的数据不一致.
slave:  开启 read-only; 关闭唯一性检查(set tokudb_rpl_unique_checks=0);关闭查找(read-modify-write)功能(set tokudb_rpl_lookup_rows=0); 

slave 端的设置可以在一台或多台 slave 中设置; MySQL5.5 和 MariaDB5.5中只有定义了主键的表才能使用该功能, MySQL 5.6, Percona 5.6 和 MariaDB 10.X 没有此限制;

7. 事务, ACID 和恢复

  1. 默认情况下, TokuDB 定期检查所有打开的表, 并记录 checkpoint 期间所有的更新, 所以在系统崩溃的时候, 可以恢复表到之前的状态(ACID-compliant), 所有的已提交的事务会更新到表里,未提交的事务则进行回滚. 默认的检查周期每60s一次, 是从当前检查点的开始时间到下次检查点的开始时间, 如果 checkpoint 需要更多的信息, 下次的checkpoint 检查会立即开始, 不过这和 log 文件的频繁刷新有关. 用户也可以在任何时候手工执行 flush logs 命令来引起一次 checkpoint 检查; 在数据库正常关闭的时候, 所有开启的事务都会被忽略.

  2. 管理日志的大小: TokuDB 一直保存最近的checkpoing到日志文件中, 当日志达到100M的时候, 会起一个新的日志文件; 每次checkpoint的时候, 日志中旧于当前检查点的都会被忽略, 如果检查的周期设置非常大, 日志的清理频率也会减少。 TokuDB也会为每个打开的事务维护回滚日志, 日志的大小和事务量有关, 被压缩保存到磁盘中, 当事务结束后,回滚日志会被相应清理.
  3. 恢复: TokuDB自动进行恢复操作, 在崩溃后使用日志和回滚日志进行恢复, 恢复时间由日志大小(包括未压缩的回滚日志)决定.

  4. 禁用写缓存: 如果要保证事务安全, 就得考虑到硬件方面的写缓存. TokuDB 在 MySQL 里也支持事务安全特性(transaction safe), 对系统而言, 数据库更新的数据不一样真的写到磁盘里, 而是缓存起来, 在系统崩溃的时候还是会出现丢数据的现象, 比如TokuDB不能保证挂载的NFS卷可以正常恢复, 所以如果要保证安全,最好关闭写缓存, 但是可能会造成性能的降低.通常情况下需要关闭磁盘的写缓存, 不过考虑到性能原因, XFS文件系统的缓存可以开启, 不过穿线错误”Disabling barriers”后,就需要关闭缓存. 一些场景下需要关闭文件系统(ext3)缓存, LVM, 软RAID 和带有 BBU(battery-backed-up) 特性的RAID卡.

8. 过程追踪

TokuDB 提供了追踪长时间运行语句的机制. 对 LOAD DATA 命令来说,SHOW PROCESSLIST 可以显示过程信息, 第一个是类似 “Inserted about 1000000 rows” 的状态信息, 下一个是完成百分比的信息, 比如 “Loading of data about 45% done”; 增加索引的时候, SHOW PROCESSLIST 可以显示 CREATE INDEX 和 ALTER TABLE 的过程信息, 其会显示行数的估算值, 也会显示完成的百分比; SHOW PROCESSLIST 也会显示事务的执行情况, 比如 committing 或 aborting 状态.

9. 迁移到 TokuDB

可以使用传统的方式更改表的存储引擎, 比如 “ALTER TABLE … ENGINE = TokuDB” 或 mysqldump 导出再倒入, INTO OUTFILE 和 LOAD DATA INFILE 的方式也可以.

10. 热备

Percona Xtrabackup 还未支持 TokuDB 的热备功能, percona 也为表示有支持的打算 tokudb-tips-mysql-backups ;对于大表可以使用 LVM 特性进行备份, mylvmbackup , 或 mysdumper 进行备份. TokuDB 官方提供了一个热备插件 tokudb_backup.so, 可以进行在线备份, 详见 tokudb-backup-plugin, 不过其依赖 backup-enterprise, 无法编译出 so 动态库, 是个商业的收费版本, 见 tokudb_installation.html

TokuDB Hot Backup is a proprietary, commercial product available for download only from Tokutek.com. It is not available for download from Percona.

二. 安装说明

tokudb 依赖库 libjemalloc (> 3.3.0) 进行内存分配, Percona Server 可以在配置中的 mysqld_safe 中指定 malloc-lib 参数显示调用 libjemalloc, 比如以下:

# malloc 
malloc-lib=/opt/Percona-Server-5.6.23-rel72.1-Linux.x86_64/lib/mysql/libjemalloc.so

同时还需要禁掉大页的支持:

echo never > /sys/kernel/mm/redhat_transparent_hugepage/defrag
echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled

较新的版本从 5.6.22-72.0 开始增加了 ps_tokudb_admin 命令来简化安装 TokuDB 存储引擎的安装, 比如以下:

[root@cz node3321]# /opt/Percona-Server-5.6.23-rel72.1-Linux.x86_64/bin/ps_tokudb_admin --enable -uroot -S data/s3321 --host=localhost -p
Enter password:
Checking if Percona server is running with jemalloc enabled...
>> Percona server is running with jemalloc enabled.
Checking transparent huge pages status on the system...
>> Transparent huge pages are currently disabled on the system.
Checking if thp-setting=never option is already set in config file...
>> Option thp-setting=never is set in the config file.
Checking TokuDB plugin status...
>> TokuDB plugin is installed.

手动安装相对麻烦些, 和其它插件的安装类似:

INSTALL PLUGIN tokudb SONAME 'ha_tokudb.so';
INSTALL PLUGIN tokudb_file_map SONAME 'ha_tokudb.so';
INSTALL PLUGIN tokudb_fractal_tree_info SONAME 'ha_tokudb.so';
INSTALL PLUGIN tokudb_fractal_tree_block_map SONAME 'ha_tokudb.so';
INSTALL PLUGIN tokudb_trx SONAME 'ha_tokudb.so';
INSTALL PLUGIN tokudb_locks SONAME 'ha_tokudb.so';
INSTALL PLUGIN tokudb_lock_waits SONAME 'ha_tokudb.so';

表数据压缩方式简单做下说明,每个表在 create table 或 alter table 的时候通过 ROW_FORMAT 来指定压缩的算法:

TOKUDB_ZLIB         - This compression is using zlib library and provides mid-range compression with medium CPU utilization.
TOKUDB_QUICKLZ      - This compression is using quicklz library and provides light compression with low CPU utilization.
TOKUDB_LZMA         - This compression is using lzma library and provides the highest compression with high CPU utilization.
TOKUDB_UNCOMPRESSED - This option disables the compression.

zlib也是默认的压缩方式.

三. 维护说明

多数情况下, TokuDB 的参数保留默认值即可, 不过还需要注意一些参数的配置:

1. 内存分配

默认情况下 TokuDB 分配 50% 的系统内存(由参数 toukudb_cache_size 控制), 也可能超过分配的内存, 如果系统内存不够, 仍然会引起 swap 交换操作. 可以通过修改 toukudb_cache_size 参数来限制内存的使用, 类似 InnoDB 的 buffer pool size, 该参数也是只读变量, 修改后重启生效, 比如参数配置中修改:

tokudb_cache_size = 4G

如果同时使用了 InnoDB 和 ToukuDB 引擎, 需要分别设置, 根据机器的配置和表的引擎情况适当调整:

innodb_buffer_pool_size = 2G
tokudb_cache_size = 8G

如果 ToukuDB 和 Federated 引擎混用, 对于 Federated 引擎而言, 访问远程主机表的时候会将表内存拉到本地再执行查询,见手册页说明:

Querying a local FEDERATED table automatically pulls the data from the remote (federated) tables. No data is stored on the local tables.

这种情况下需要保证本地有足够的内存处理从远程主机获取到的数据, 内存不够用也可能引起大量的 swap 交换, 更甚引起 Server 的崩溃.

2. 指定 TokuDB 文件的位置

有3个参数可以控制 TokuDB 产生的文件、日志、临时表等目录位置:

tokudb_data_dir: 指定 TokuDB 表数据存储的位置, 默认为 MySQL data 目录;
tokudb_log_dir: 指定 TokuDB 日志文件存放的位置, 默认为 MySQL data 目录, 官方建议 表数据和日志文件放到一个目录里;
tokudb_tmp_dir: 指定生成临时表的目录; TokuDB 在使用 LOAD DATA 导入数据的时候会通过临时表(可能会很大)来完成;

3. 表维护

TokuDB基于 Fractal tree 的特性, 可以快速的将较小的数据放到 buffer 里, 而不是像 btree 索引那样需要额外的 io 操作才能对表的记录进行更新, 这点意味着对 TokuDB 表的维护操作会大大快于其它引擎的表, 比如一个大表做了很多的 delete 操作, 对该表做一次 OPTIMIZE(可以释放表空间) 的维护操作比起以往会方便很多. TokuDB提供下面几种维护方式,以提升 OPTIMIZE 操作 :

  1. 在线优化限制 默认情况下, 只要系统资源可用,TokuDB 会一直对表做优化操作, 没有上线设置, tokudb_optimize_throttle 变量用来限制一秒内优化 Fractal tree 叶子节点的数量. 该参数 默认为 0, 有效值的范围为 [0, 1000000], 比如以下命令限制一秒最多优化一个叶子节点.
    set tokudb_optimize_throttle=1
    optimize table t;
    
  2. 优化表的单个索引 可以通过 tokudb_optimize_index_name 变量按照索引名字指定优化的索引, 比如以下:
    set tokudb_optimize_index_name='primary'; 
    optimize table t;
    
  3. 优化 Fractal tree 索引的子集 如果 Fractal tree 索引树的左边做了很多 delete 操作,那么只优化左边这部分是个很好的方式, 变量 tokudb_optimize_index_fraction 可以用来控制索引树的子树的大小, 值范围为 [0.0 1.0], 默认为 1.0, 表示优化整个 Fractal tree 索引, 下面的示例只优化索引树最左边的10%的部分:
    set tokudb_optimize_index_name='primary'; 
    set tokudb_optimize_index_fraction=0.1;
    optimize table t;
    

4. 安装注意事项

  1. Tokudb 运行依赖 jemalloc 库, 需安装 jemalloc.x86_64;
  2. 增加 [mysqld_safe] 启用设置:
      [mysqld_safe]
      thp-setting=never
      malloc-lib = /usr/lib64/libjemalloc.so.1
    
  3. 重新启动实例;
  4. 增加 [mysqld] 配置, 将 tokudb datatokudb log 目录与默认的实例目录分开, 设置为相对目录, 方便 tokudb 表数据的管理, 这里可以设置为隐藏目录, 在 show databases 查看的时候不会将此目录当作库名显示:
     tokudb_cache_size = 12G
     tokudb_data_dir = .tokudb_data
     tokudb_log_dir = .tokudb_data
     tokudb_tmp_dir = /dev/shm   # 单主机多实例情况下不设置该参数, 避免所文件的冲突.
     tokudb_dir_per_db = 1       # 开启该参数后, 每个 tokudb 表数据在其所在的 db 下存放, 若开启可以忽略 tokudb_data_dir 和 tokudb_log_dir 参数的设置. 不过设置此参数后, AliSQLBackup 不能对 tokudb 表进行备份.
     tokudb_create_index_online = 1
     tokudb_commit_sync = 1
     tokudb_directio = 0
    
  5. 创建目录, tokudb 数据的目录需要创建到 MySQL datadir 路径下面, 以便 xtrabackup 的备份. 详见 AliSQLBackup, tokudb 表备份需要 tokudb 数据目录在 datadir 目录下面.
    mkdir  /export/mysql/nodexxxx/data/tokudb_data
    
  6. 启用 TokuDB, 提供 defaults-file 文件, 初始化的时候按照 tokudb 参数进行设置:
     /opt/Percona-Server-5.6.38-rel83.0-Linux.x86_64.ssl101/bin/ps_tokudb_admin --enable --defaults-file my.node.cnf -S data/s3316 -u root -pXXXXXXX