免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1736 | 回复: 0

mydumper0.23版本的使用和源代码分析 [复制链接]

论坛徽章:
2
2015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之上海
日期:2016-05-05 09:45:14
发表于 2011-12-23 01:34 |显示全部楼层

mydumper是一个多线程、高性能的数据逻辑备份、恢复的工具,相比MySQL自带的mysqldump提速不少。我下载了0.23的稳定版本,阅读了源码并总结了一些使用的心得。

mysqldump是个单线程的逻辑备份工具,依次一个个导出多个表,没有一个并行的机制。mydumper弥补了这方面的缺陷可以并行的多线程的从表中读入数据并同时写到不同的文件里。项目的作者是由一群在sun、fb、skysql的工程师完成的。类似的工具还有mk-parallel-dump

编译安装cmake . make sudo make install 源码分析

根据流程图解释一下mydumper的工作步骤。

解析参数
使用glib的g_option_context_parse,比libc里的getopt_long简单多了。

连接目标数据库。

通过show processlist来判断是否有长查询,如果有长查询则退出dump,可以通过–long-query-guard加长时间,或者使用–kill-long-queries杀掉长查询。

锁定myisam表。

针对innodb table开启事务。

产生3个消息队列(线程ready队列、任务队列、myisam表处理完毕队列)。

conf.queue = g_async_queue_new(); conf.ready = g_async_queue_new(); conf.unlock_tables= g_async_queue_new();

产生指定的线程个数,–threads可以指定,默认是4个。

GThread **threads = g_new(GThread*,num_threads); struct thread_data *td= g_new(struct thread_data, num_threads); for (n=0; n<num_threads; n++) { td[n].conf= &conf; td[n].thread_id= n+1; threads[n] = g_thread_create((GThreadFunc)process_queue,&td[n],TRUE,NULL); g_async_queue_pop(conf.ready); }

dump_database,从DATA_DICTIONARY.TABLES读取所有表,通过–ignore, –tables-list, regex等过滤条件,产生需要dump的目标表列表,分别插入innodb_tables、non_innodb_table、 table_schemas三个链表。

query= g_strdup_printf("SELECT TABLE_NAME, ENGINE, TABLE_TYPE as COMMENT FROM DATA_DICTIONARY.TABLES WHERE TABLE_SCHEMA='%s'", database); .... innodb_tables= g_list_append(innodb_tables, dbt); .... non_innodb_table= g_list_append(non_innodb_table, dbt); .... table_schemas= g_list_append(table_schemas, dbt);

dump non-innodb table 把需要导出myisam表加入到任务队列。

for (non_innodb_table= g_list_first(non_innodb_table); non_innodb_table; non_innodb_table= g_list_next(non_innodb_table)) { dbt= (struct db_table*) non_innodb_table->data; dump_table(conn, dbt->database, dbt->table, &conf, FALSE); g_atomic_int_inc(&non_innodb_table_counter); }

dump innodb table把需要导出innodb表加入任务队列。

for (innodb_tables= g_list_first(innodb_tables); innodb_tables; innodb_tables= g_list_next(innodb_tables)) { dbt= (struct db_table*) innodb_tables->data; dump_table(conn, dbt->database, dbt->table, &conf, TRUE); }

dump schema 把需要导出表结构任务加入到任务队列。

for (table_schemas= g_list_first(table_schemas); table_schemas; table_schemas= g_list_next(table_schemas)) { dbt= (struct db_table*) table_schemas->data; dump_schema(dbt->database, dbt->table, &conf); g_free(dbt->table); g_free(dbt->database); g_free(dbt); }

典型的生产者(主线程)消费者(子线程)模式,子线程会从任务队列里读取需要处理的表名字和表类型,再通过select * from table_name 读入数据各自写入到各自的文件。

for(;;) { .... job=(struct job *)g_async_queue_pop(conf->queue); .... switch (job->type) { case JOB_DUMP: .... dump_table_data_file(thrconn, tj->database, tj->table, tj->where, tj->filename); .... case JOB_DUMP_NON_INNODB: .... dump_table_data_file(thrconn, tj->database, tj->table, tj->where, tj->filename); case JOB_SCHEMA: .... dump_schema_data(thrconn, sj->database, sj->table, sj->filename); }

所有导数据的任务加入任务队列之后,会再加入让线程退出的任务,让线程自然退出。

case JOB_SHUTDOWN: g_message("Thread %d shutting down", td->thread_id); if (thrconn) mysql_close(thrconn); g_free(job); mysql_thread_end(); return NULL; break;

解除myisam表锁。

等待子线程退出。

使用

导出test database的数据

mydumper -h 127.0.0.1 -u root --database test

指定某个目录

mydumper -h 127.0.0.1 -u root --outputdir=.

不导出表结构

mydumper -h 127.0.0.1 -u root --no-schema

如果表数据是空,还是产生一个空文件(默认无数据则只有表结构文件)

mydumper -h 127.0.0.1 -u root --build-empty-files

设置长查询的上限,如果存在比这个还长的查询则退出mydumper,也可以设置杀掉这个长查询

mydumper -h 127.0.0.1 -u root --long-query-guard 200 --kill-long-queries

设置要dump的列表–tables-list,不需要设置db名字,逗号分割

mydumper -h 127.0.0.1 -u root --tables-list=ddd,zzz

通过regex也设置正则表达,需要设置db名字

mydumper -h 127.0.0.1 -u root --regex=test.z

把单表分成多个chunks,这个后面会讲分割的原理

mydumper -h 127.0.0.1 -u root --rows 10000

过滤某个引擎的表

mydumper -h 127.0.0.1 -u root -B test --ignore-engines=innodb

详细日志

mydumper -h 127.0.0.1 -u root -B test -v 3 几个注意点

各自线程都要自己连接到数据库,因为libmysql是线程不安全的。
因为对myisam表有有表锁,所有先处理myisam表,记录myisam表个数,每处理一个myisam都原子操作数量减一。并在myisam表都处理完毕后,立即解锁,尽量减少锁定的时间,而不是在导出innodb表数据的时候还在lock myisam表。

main_thread

for (non_innodb_table= g_list_first(non_innodb_table); non_innodb_table; non_innodb_table= g_list_next(non_innodb_table)) { dbt= (struct db_table*) non_innodb_table->data; dump_table(conn, dbt->database, dbt->table, &conf, FALSE); g_atomic_int_inc(&non_innodb_table_counter); }

child_thread

if (g_atomic_int_dec_and_test(&non_innodb_table_counter) && g_atomic_int_get(&non_innodb_done)) { g_async_queue_push(conf->unlock_tables, GINT_TO_POINTER(1)); }

main_thread

g_async_queue_pop(conf.unlock_tables); g_message("Non-InnoDB dump complete, unlocking tables"); mysql_query(conn, "UNLOCK TABLES");

–regex的处理在–tables-list后, 先满足–tables-list再满足–regex,如下只会dump表z1

mydumper -h 127.0.0.1 -u root --regex=test.z1 --outputdir=. --rows=10000 -v 3 -e --tables-list=z2,z1 ** Message: Thread 1 dumping data for `test`.`z1` ** Message: Thread 2 dumping schema for `test`.`z1`

–rows的使用,设置–rows可以把一个表分成多个文件。分块的原则并不是根据–rows设定的行数来决定生成文件里包含的函数,而是通过rows和表的总行数计算出要生成的文件个数,尽量保证每个文件的大小一致。

表的总行数是如何获得的?
首先mydumper会选择一个索引,顺序是pk、uk或者show index from table里Cardinality最高的一个索引,再通过explain select index from table的rows字段获得总行数total_nums(可能不准确),于是第一个文件就是从select * from table where index >=1 and index < total_nums/ (int(total_nums/ rows) – 1) + 1。每个分块可以分到不同的线程,所以即便同一个表dump都可以很快加速。

ps:这个项目大量使用glib(gnome)比较少见,看了一下glib doc觉得glib设计的挺好的用起来很方便,否则实现一个消息队列加多线程还是要几百行代码的。接下来要看mydumper0.50的代码。

作者: hoterran

原文链接地址:http://www.hoterran.info/mydumper_usage

myumper源码下载地址:http://www.mydumper.org/?p=23

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP