MySQL备份之【mydumper 学习】
MySQL在备份方面包含了自身的mysqldump工具,但其只支持单线程工作,这就使得它无法迅速的备份数据。而mydumper作为一个实用工具,能够良好支持多线程工作,这使得它在处理速度方面十倍于传统的mysqldump。其特征之一是在处理过程中需要对列表加以锁定,因此如果我们需要在工作时段执行备份工作,那么会引起DML阻塞。但一般现在的MySQL都有主从,备份也大部分在从上进行,所以锁的问题可以不用考虑。这样,mydumper能更好的完成备份任务。
###更新(2016-04-01)###
注意:mydumper的多线程备份是基于表的,所以当只有一张表或99张是小表,1张是超级大表,mydumper不如mysqldump,甚至更慢。其实mydumper是支持对一张表多个线程备份的,参数-r。
Mydumper主要特性:是一个针对MySQL和Drizzle的高性能多线程备份和恢复工具,开发人员主要来自MySQL,Facebook,SkySQL公司。
1:轻量级C语言写的
2:执行速度比mysqldump快10倍
3:事务性和非事务性表一致的快照(适用于0.2.2以上版本)
4:快速的文件压缩
5:支持导出binlog
6:多线程恢复(适用于0.2.1以上版本)
7:以守护进程的工作方式,定时快照和连续二进制日志(适用于0.5.0以上版本)
8:开源 (GNU GPLv3)
下载安装:
环境:Ubuntu 12.04
下载:
wget https://launchpad.net/mydumper/0.5/0.5.2/+download/mydumper-0.5.2.tar.gz
安装:解压后,在README中安装说明
apt-get install cmake make libglib2.0-dev libmysqlclient15-dev zlib1g-dev libpcre3-dev g++
cmake .
make
root@dd:~/mydumper-0.5.2# cmake .
-- The CXX compiler identification is GNU
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Using mysql-config: /usr/bin/mysql_config
-- Found MySQL: /usr/include/mysql, /usr/lib/x86_64-linux-gnu/libmysqlclient_r.so;/usr/lib/x86_64-linux-gnu/libpthread.so;/usr/lib/x86_64-linux-gnu/libz.so;/usr/lib/x86_64-linux-gnu/libm.so;/usr/lib/x86_64-linux-gnu/librt.so;/usr/lib/x86_64-linux-gnu/libdl.so CMake Warning at docs/CMakeLists.txt:9 (message):
Unable to find Sphinx documentation generator -- ------------------------------------------------
-- MYSQL_CONFIG = /usr/bin/mysql_config
-- CMAKE_INSTALL_PREFIX = /usr/local
-- BUILD_DOCS = ON
-- RUN_CPPCHECK = OFF
-- Change a values with: cmake -D<Variable>=<Value>
-- ------------------------------------------------
--
-- Configuring done
-- Generating done
-- Build files have been written to: /root/mydumper-0.5.2
root@dd:~/mydumper-0.5.2# make
Scanning dependencies of target mydumper
[ 20%] Building C object CMakeFiles/mydumper.dir/mydumper.c.o
[ 40%] Building C object CMakeFiles/mydumper.dir/binlog.c.o
[ 60%] Building C object CMakeFiles/mydumper.dir/server_detect.c.o
[ 80%] Building C object CMakeFiles/mydumper.dir/g_unix_signal.c.o
Linking C executable mydumper
[ 80%] Built target mydumper
Scanning dependencies of target myloader
[100%] Building C object CMakeFiles/myloader.dir/myloader.c.o
Linking C executable myloader
[100%] Built target myloader
生成2个工具:mydumper(备份),myloader(导入),放入到bin目录下。
参数:
mydumper(0.5.2):
root@dd:~/mydumper-0.5.2# ./mydumper --help
Usage:
mydumper [OPTION...] multi-threaded MySQL dumping Help Options:
-?, --help Show help options Application Options:
-B, --database 需要备份的库
-T, --tables-list 需要备份的表,用逗号分隔
-o, --outputdir 输出文件的目录
-s, --statement-size 生成插入语句的字节数, 默认 1000000,这个参数不能太小,不然会报 Row bigger than statement_size for tools.t_serverinfo
-r, --rows 分裂成很多行块表
-c, --compress 压缩输出文件
-e, --build-empty-files 即使表没有数据,还是产生一个空文件
-x, --regex 正则表达式: 'db.table'
-i, --ignore-engines 忽略的存储引擎,用逗号分隔
-m, --no-schemas 不导出表结构
-k, --no-locks 不执行共享读锁 警告:这将导致不一致的备份
-l, --long-query-guard 设置长查询时间,默认60秒,超过该时间则会报错:There are queries in PROCESSLIST running longer than 60s, aborting dump
--kill-long-queries kill掉长时间执行的查询
-b, --binlogs 导出binlog
-D, --daemon 启用守护进程模式
-I, --snapshot-interval dump快照间隔时间,默认60s,需要在daemon模式下
-L, --logfile 日志文件
-h, --host The host to connect to
-u, --user Username with privileges to run the dump
-p, --password User password
-P, --port TCP/IP port to connect to
-S, --socket UNIX domain socket file to use for connection
-t, --threads 使用的线程数,默认4
-C, --compress-protocol 在mysql连接上使用压缩协议
-V, --version Show the program version and exit
-v, --verbose 更多输出, 0 = silent, 1 = errors, 2 = warnings, 3 = info, default 2
myloader(0.5.2):
root@dd:~/mydumper-0.5.2# ./myloader --help
Usage:
myloader [OPTION...] multi-threaded MySQL loader Help Options:
-?, --help Show help options Application Options:
-d, --directory 备份文件所在的目录
-q, --queries-per-transaction 每次执行查询数量, 默认1000
-o, --overwrite-tables 如果表存在则先删除。这里注意下,使用该参数,需要备份时候要备份表结构,不然会出问题
-B, --database 指定需要还原的数据库
-e, --enable-binlog 启用二进制恢复数据
-h, --host The host to connect to
-u, --user Username with privileges to run the dump
-p, --password User password
-P, --port TCP/IP port to connect to
-S, --socket UNIX domain socket file to use for connection
-t, --threads 使用的线程数量,默认4
-C, --compress-protocol 连接上使用压缩协议
-V, --version Show the program version and exit
-v, --verbose 更多输出, 0 = silent, 1 = errors, 2 = warnings, 3 = info, default 2
###更新(2016-04-01)###
上面介绍的是0.5.2版本,目前版本已经更新到了0.9.1,新增了一些参数,现在对新参数了解一下:
mydumper(0.9.1):
root@op3:/home/zhoujy# mydumper --help
Usage:
mydumper [OPTION...] multi-threaded MySQL dumping Help Options:
-?, --help Show help options Application Options:
-B, --database 需要备份的数据库,一个数据库一条命令备份,要不就是备份所有数据库,包括mysql。
-T, --tables-list 需要备份的表,用逗号分隔。
-o, --outputdir 备份文件目录
-s, --statement-size 生成插入语句的字节数,默认1000000,这个参数不能太小,不然会报 Row bigger than statement_size for tools.t_serverinfo
-r, --rows 试图用行块来分割表,该参数关闭--chunk-filesize
-F, --chunk-filesize 行块分割表的文件大小,单位是MB
-c, --compress 压缩输出文件
-e, --build-empty-files 即使表没有数据,也产生一个空文件
-x, --regex 正则表达式匹配,如'db.table'
-i, --ignore-engines 忽略的存储引擎,用逗号分隔
-m, --no-schemas 不导出表结构
-d, --no-data 不导出表数据
-G, --triggers 导出触发器
-E, --events 导出事件
-R, --routines 导出存储过程
-k, --no-locks 不执行共享读锁 警告:这将导致不一致的备份
--less-locking 减到最小的锁在innodb表上.
-l, --long-query-guard 设置长查询时间,默认60秒,超过该时间则会报错:There are queries in PROCESSLIST running longer than 60s, aborting dump
-K, --kill-long-queries kill掉长时间执行的查询,备份报错:Lock wait timeout exceeded; try restarting transaction
-D, --daemon 启用守护进程模式
-I, --snapshot-interval dump快照间隔时间,默认60s,需要在daemon模式下
-L, --logfile 使用日志文件,默认标准输出到终端
--tz-utc 备份的时候允许备份Timestamp,这样会导致不同时区的备份还原会出问题,默认关闭,参数:--skip-tz-utc to disable.
--skip-tz-utc
--use-savepoints 使用savepoints来减少采集metadata所造成的锁时间,需要SUPER权限
--success-on- Not increment error count and Warning instead of Critical in case of table doesn't exist
--lock-all-tables 锁全表,代替FLUSH TABLE WITH READ LOCK
-U, --updated-since Use Update_time to dump only tables updated in the last U days
--trx-consistency-only Transactional consistency only
-h, --host The host to connect to
-u, --user Username with privileges to run the dump
-p, --password User password
-P, --port TCP/IP port to connect to
-S, --socket UNIX domain socket file to use for connection
-t, --threads 备份执行的线程数,默认4个线程
-C, --compress-protocol 在mysql连接上使用压缩协议
-V, --version Show the program version and exit
-v, --verbose 更多输出, = silent, = errors, = warnings, = info, default
加粗部分是新增加的参数。
myloader(0.9.1):
root@op:~# myloader --help
Usage:
myloader [OPTION...] multi-threaded MySQL loader Help Options:
-?, --help Show help options Application Options:
-d, --directory 备份文件所在的目录
-q, --queries-per-transaction 每个事务的query数量, 默认1000
-o, --overwrite-tables 如果表存在则先删除,使用该参数,需要备份时候要备份表结构,不然还原会找不到表
-B, --database 指定需要还原的数据库
-s, --source-db 还原的数据库
-e, --enable-binlog 启用二进制日志恢复数据
-h, --host The host to connect to
-u, --user Username with privileges to run the dump
-p, --password User password
-P, --port TCP/IP port to connect to
-S, --socket UNIX domain socket file to use for connection
-t, --threads 使用的线程数量,默认4
-C, --compress-protocol 连接上使用压缩协议
-V, --version Show the program version and exit
-v, --verbose 更多输出, = silent, = errors, = warnings, = info, default
加粗部分是新增加的参数。
测试:测试基本用法
1:备份
./mydumper -u zjy -p ##### -h 192.168.220.245 -P 3306 -B chushihua -o /home/zhoujy/bak/
备份analyzedxy数据库到/home/zhoujy/bak/ 目录中,查看是否多线程:
| 4937639 | zjy | 192.168.200.25:34781 | NULL| Query | 0 | NULL | show processlist |
| 4937677 | zjy | 192.168.200.25:34791 | NULL| Query | 10 | Writing to net | SELECT /*!40001 SQL_NO_CACHE */|
| 4937678 | zjy | 192.168.200.25:34792 | NULL| Query | 5 | Writing to net | SELECT /*!40001 SQL_NO_CACHE */ |
| 4937679 | zjy | 192.168.200.25:34793 | NULL| Query | 10 | Writing to net | SELECT /*!40001 SQL_NO_CACHE */ |
| 4937680 | zjy | 192.168.200.25:34794 | NULL| Query | 10 | Writing to net | SELECT /*!40001 SQL_NO_CACHE */ |
上面显示确实是4个线程(默认)在备份,查看备份文件:
root@zhoujy:/home/zhoujy/bak# ls -lh
-rw-r--r-- 1 root root 322 2013-11-14 17:59 chushihua.dba_hospital_all_name-schema.sql
-rw-r--r-- 1 root root 16M 2013-11-14 17:59 chushihua.dba_hospital_all_name.sql
-rw-r--r-- 1 root root 221 2013-11-14 17:59 chushihua.dba_hospital-schema.sql
-rw-r--r-- 1 root root 658 2013-11-14 17:59 chushihua.dba_hospital.sql
-rw-r--r-- 1 root root 198 2013-11-14 17:59 chushihua.dba_jobTitle-schema.sql
-rw-r--r-- 1 root root 300 2013-11-14 17:59 chushihua.dba_jobTitle.sql
-rw-r--r-- 1 root root 261 2013-11-14 17:59 chushihua.dba_locatedCity-schema.sql
-rw-r--r-- 1 root root 202K 2013-11-14 17:59 chushihua.dba_locatedCity.sql
分析:mydumper把数据和表结构分开备份,并且把二进制日志备份出来单独放到一个文件中。
metadata:元数据 记录备份开始和结束时间,以及binlog日志文件位置。
table data:每个表一个文件
table schemas:表结构文件
binary logs: 启用--binlogs选项后,二进制文件存放在binlog_snapshot目录下
daemon mode:在这个模式下,有五个目录0,1,binlogs,binlog_snapshot,last_dump。
备份目录是0和1,间隔备份,如果mydumper因某种原因失败而仍然有一个好的快照,当快照完成后,last_dump指向该备份。
2:还原:还原到另一台服务器,先建立要还原的数据库(chushihua)
./myloader -u root -p 123456 -h 192.168.200.25 -P 3307 -B chushihua -d /home/zhoujy/bak/
和备份一样查看是否多线程:
| 19 | root | | NULL | Query | 0 | init | show processlist|
| 30 | root | | chushihua | Query | 5 | update| INSERT INTO |
| 31 | root | | chushihua | Query | 5 | update| INSERT INTO |
| 32 | root | | chushihua | Query | 5 | update| INSERT INTO |
| 33 | root | | chushihua | Query | 5 | update| INSERT INTO |
上面显示确实是4个线程(默认)在还原。
进一步测试:测试一些常用的参数
1):备份指定表(-T),并且不要导出表结构(-m)
./mydumper -u root-p 123456 -h 192.168.220.252 -P 3306 -m -B test -T b,a,c,d,e,g,f,h,i -o /home/zhoujy/bak/ zhoujy@zhoujy:~/bak$ ls -lh
-rw-rw-r-- 1 zhoujy zhoujy 3.4K 2013-11-14 20:57 test.a.sql
-rw-rw-r-- 1 zhoujy zhoujy 1.6M 2013-11-14 20:57 test.b.sql
-rw-rw-r-- 1 zhoujy zhoujy 7.8M 2013-11-14 20:57 test.c.sql
-rw-rw-r-- 1 zhoujy zhoujy 1.7M 2013-11-14 20:57 test.d.sql
-rw-rw-r-- 1 zhoujy zhoujy 303K 2013-11-14 20:57 test.e.sql
-rw-rw-r-- 1 zhoujy zhoujy 517K 2013-11-14 20:57 test.f.sql
-rw-rw-r-- 1 zhoujy zhoujy 646K 2013-11-14 20:57 test.g.sql
-rw-rw-r-- 1 zhoujy zhoujy 394K 2013-11-14 20:57 test.h.sql
-rw-rw-r-- 1 zhoujy zhoujy 34K 2013-11-14 20:57 test.i.sql
-rw-rw-r-- 1 zhoujy zhoujy 75 2013-11-14 20:57 metadata
2)压缩备份文件(-c),备份binlog(-b),正则表达式备份表(-x)
./mydumper -u root -p 123456 -h 192.168.200.25 -P 3306 -m -c -b --regex=tmp.* -B test -o /home/zhoujy/bak/ drwx------ 2 zhoujy zhoujy 4.0K 2013-11-14 21:16 binlog_snapshot
-rw-rw-r-- 1 zhoujy zhoujy 133 2013-11-14 21:16 metadata
-rw-rw-r-- 1 zhoujy zhoujy 94K 2013-11-14 21:16 test.tmp_0808.sql.gz
-rw-rw-r-- 1 zhoujy zhoujy 75K 2013-11-14 21:16 test.tmp_0809.sql.gz
-rw-rw-r-- 1 zhoujy zhoujy 25K 2013-11-14 21:16 test.tmp_0813.sql.gz
-rw-rw-r-- 1 zhoujy zhoujy 208K 2013-11-14 21:16 test.tmp_0826.sql.gz
-rw-rw-r-- 1 zhoujy zhoujy 915 2013-11-14 21:16 test.tmp_0827.sql.gz
-rw-rw-r-- 1 zhoujy zhoujy 901 2013-11-14 21:16 test.tmp_0912.sql.gz
-rw-rw-r-- 1 zhoujy zhoujy 2.1K 2013-11-14 21:16 test.tmp_0916.sql.gz
-rw-rw-r-- 1 zhoujy zhoujy 622K 2013-11-14 21:16 test.tmp_0918_a.sql.gz
-rw-rw-r-- 1 zhoujy zhoujy 28M 2013-11-14 21:16 test.tmp_0918_ff.sql.gz
如上所示,备份文件已经是压缩的了(用gzip -d 解压),并且备份出了tmp.*匹配出来的所有表,二进制日志也被备份到了binlog_snapshot文件中,并且也是被压缩的。
###更新(2016-04-01)###
新版本里已经不能备份binlog了,没有-b参数。这里说明下备份指定数据库的方法:--regex 正则匹配
#指定备份数据库:备份abc、bcd、cde
mydumper -u backup -p -h 192.168.180.13 -P -t -c -l -s -e --regex 'abc|bcd|cde' -o bbb/ #指定不备份的数据库:不备份abc、mysql、test,备份其他数据库
mydumper -u backup -p -h 192.168.180.13 -P -t -c -l -s -e --regex '^(?!(abc|mysql|test))' -o bbb/
3)还原,表存在先删除(-o):这里需要注意,使用该参数,备份目录里面需要有表结构的备份文件。
./myloader -u root -p 123456 -h 192.168.200.25 -P 3306 -o -B test -d /home/zhoujy/bak/
更多的参数效果,请自己测试。
最后测试:用mysqldump和mydumper进行对比测试。
#!/usr/bin/env python
#coding=utf-8
import MySQLdb
import os
import sys
import time backup = os.system('') def mysqldump_data():
t1 = time.time()
backup = os.system('mysqldump --no-defaults -uroot -p123456 -h192.168.200.25 --default-character-set=utf8 test > /home/zhoujy/test.bak')
t2 = time.time()
t = round(t2-t1)
print "mysqldump Cost Time %s" %t def mydumper_data():
t1 = time.time()
backup = os.system('mydumper -u root -p 123456 -h 192.168.200.25 -P 3306 -B test -o /home/zhoujy/bak/')
t2 = time.time()
t = round(t2-t1)
print "mydumper Cost Time %s" %t if __name__ =='__main__':
mysqldump_data()
mydumper_data()
测试了2个数据库:
1:
mysqldump Cost Time :162s
mydumper Cost Time :61s
2:
mysqldump Cost Time :483s
mydumper Cost Time :337s
从上面的时间来看,mydumper 确实提升了备份数据,还原也同理。
###### 2016-07-20 更新 #######
mydumper支持一张表多个线程以chunk的方式批量导出,参数-r:试图用行块来分割表,该参数关闭--chunk-filesize参数。如:
mydumper -u zjy -p xxx -h 192.168.123.70 -P -t -c -r 300000 -l -s -B vs -o /home/zhoujy/vs/
表示每个线程用300000行块来分割表,通过show processlist 看到:5个线程备份
| Sending data |
SELECT /*!40001 SQL_NO_CACHE */ * FROM `virtual_station`.`core_event` WHERE `id` IS NULL OR (`id` >= AND `id` < ) |
| Sending to client |
SELECT /*!40001 SQL_NO_CACHE */ * FROM `virtual_station`.`core_error_log` WHERE `id` IS NULL OR (`id` >= AND `id` < ) |
| Sending to client |
SELECT /*!40001 SQL_NO_CACHE */ * FROM `virtual_station`.`core_event` WHERE (`id` >= AND `id` < ) |
| Sending to client |
SELECT /*!40001 SQL_NO_CACHE */ * FROM `virtual_station`.`core_event` WHERE (`id` >= AND `id` < ) |
| Sending data |
SELECT /*!40001 SQL_NO_CACHE */ * FROM `virtual_station`.`core_error_log` WHERE (`id` >= AND `id` < ) |
这个可以更好的备份数据库,不管数据库里是否有大表。
总结:
从上面的测试分析中看出mydumper可以提升备份还原的效率,虽然是多线程操作,但是提升多少受限于磁盘的IO能力,在使用前做好磁盘IO的评估,大家可以尝试使用该工具。
更多信息见:
http://blog.csdn.net/leshami/article/details/46815553
http://www.hoterran.info/mydumper_usage
http://www.ttlsa.com/html/1077.html
http://blog.sina.com.cn/s/blog_534360f5010157rx.html
MySQL备份之【mydumper 学习】的更多相关文章
- MySQL备份恢复之mydumper
Preface In my previous two blogs,we have known about the tool of backing up MySQL db.I'm gon ...
- MySQL备份迁移之mydumper
简介 mydumper 是一款开源的 MySQL 逻辑备份工具,主要由 C 语言编写.与 MySQL 自带的 mysqldump 类似,但是 mydumper 更快更高效. mydumper 的一些优 ...
- mysql备份工具 :mysqldump mydumper Xtrabackup 原理
备份是数据安全的最后一道防线,对于任何数据丢失的场景,备份虽然不一定能恢复百分之百的数据(取决于备份周期),但至少能将损失降到最低.衡量备份恢复有两个重要的指标:恢复点目标(RPO)和恢复时间目标(R ...
- MySQL多线程备份工具:mydumper
MySQL多线程备份工具:mydumper http://www.orczhou.com/index.php/2011/12/how-to-split-mysqldump-file/ Mydumper ...
- MySQL备份还原之一mydumper
1)源码编译安装 1.下载 mydumper源码 2.解压 [mysql@localhost ~]$ tar -xvf mydumper-0.9.1.tar mydumper-0.9.1/CMakeL ...
- Linux学习系列之MySQL备份
MySQL排除表备份 #!/bin/bash #created by 90root #date: 20160809 date_y=$(date +%Y) date_m=$(date +%m) time ...
- MySQL备份可能遇到的坑
MySQL备份工具,支持各种参数选项,使用不同的选项极有可能影响备份处理过程.本文使用我们常规认为合理的备份参数,测试/验证是否存在容易忽视的坑 # 常规备份参数 # mysqldump shell& ...
- MySQL TiDB PingCAP mydumper
MySQL备份之[mydumper 学习] - jyzhou - 博客园https://www.cnblogs.com/zhoujinyi/p/3423641.html maxbube/mydumpe ...
- MySQL备份原理详解
备份是数据安全的最后一道防线,对于任何数据丢失的场景,备份虽然不一定能恢复百分之百的数据(取决于备份周期),但至少能将损失降到最低.衡量备份恢复有两个重要的指标:恢复点目标(RPO)和恢复时间目标(R ...
- MySQL备份还原——AutoMySQLBackup介绍
AutoMySQLBackup是一个开源的MySQL备份脚本.可以说它是一个轻量级的备份方案,AutoMySQLBackup的安装.配置非常简单.方便.AutoMySQLBackup的sourcefo ...
随机推荐
- Swift学习(三):闭包(Closures)
定义 闭包(Closures)是独立的函数代码块,能在代码中传递及使用. 语法 {(parameters) -> return type in statements } 注:闭包表达式语法可以使 ...
- js/jquery的应用
1.JS限制文本框只能输入整数或小数 <script language="JavaScript" type="text/javascript"> f ...
- java collection.sort()根据时间排序list
首先:定义bean 然后:定义比较器 最后:测试使用 一.userBean package com.butterfly.Class; public class user { private Strin ...
- C语言学习 第四次作业总结
本次作业主要为了复习分支语句,同时复习之前学习过的判断语句,printf和scanf函数的使用. 学习到这里,同学们应该已经基本掌握了基本的数据类型,分支结构,循环结构,条件判断语句.应该可以利用这些 ...
- 顺序表java实现
public class SeqList { Object[] data; int maxSize; int length; public SeqList(int maxSize) { this.ma ...
- awk中的system和getline的用法
system只能对命令的输出结果输出到终端. getline在awk中可以使命令的输出结果传到一个变量中保存. # awk 'BEGIN{system("date")|getlin ...
- PyChram中同目录下import引包报错的解决办法?
相信很多同学和我一样在PyChram工具中新建python项目的同目录下import引包会报错提示找不到,这是因为该项目找不到python的环境导致的: 如果文件开始的时候包引包的错误可以,都可以用用 ...
- cxf设置代理访问webservice接口
由于业务上的需要,需要访问第三方提供的webservice接口,但由于公司做了对外访问的限制,不设置代理是不能外网的,如果使用http设置代理访问外网还是比较容易的,但使用cxf有点不知道从哪里入手. ...
- html5图片异步上传/ 表单提交相关
1 form 表单 get/post提交时候. action地址(或者啥ajax的url地址) 会涉及到跨域问题 常见跨域问题http://www.cnblogs.com/rainman/archiv ...
- Python * 和 ** 参数问题
Problem def calcuate(*keys) def calcluate(**keys) Slove *: 用来传递人一个无名字的参数,这些参数会以一个Tuple的形式来访问. **: 用来 ...