Mycat对于导入和扩容迁移性能压测
机器ip |
OS |
CPU |
CPU Processors |
内存 |
172.16.54.135 |
CentOS Linux release 7.1.1503 |
Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz |
24 |
64G |
172.16.54.136 |
CentOS Linux release 7.1.1503 |
Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz |
24 |
64G |
172.16.54.138 |
CentOS Linux release 7.1.1503 |
Intel(R) Xeon(R) CPU E5-2620 v2 @ 2.10GHz |
24 |
64G |
软件名称 |
版本 |
Jdk |
1.8.0_45 |
Mycat |
1.4.1 |
Mysql |
5.6 |
PostgreSQL |
9.2.14 |
wrapper.conf
# Java AdditionalParameters #wrapper.java.additional.1= wrapper.java.additional.1=-DMYCAT_HOME=. wrapper.java.additional.2=-server wrapper.java.additional.3=-XX:MaxPermSize=64M wrapper.java.additional.4=-XX:+AggressiveOpts wrapper.java.additional.5=-XX:MaxDirectMemorySize=5G wrapper.java.additional.6=-Dcom.sun.management.jmxremote wrapper.java.additional.7=-Dcom.sun.management.jmxremote.port=1984 wrapper.java.additional.8=-Dcom.sun.management.jmxremote.authenticate=false wrapper.java.additional.9=-Dcom.sun.management.jmxremote.ssl=false # Initial JavaHeap Size (in MB) #wrapper.java.initmemory=3 wrapper.java.initmemory=3072 # Maximum JavaHeap Size (in MB) #wrapper.java.maxmemory=64 wrapper.java.maxmemory=3072
schema.xml
<schemaname="testdb" checkSQLschema="false"sqlMaxLimit="100"> <table name="user"dataNode="dn1,dn2" rule="mod-long"/> </schema> <dataNode name="dn1"dataHost="mycat" database="db1"/> <dataNode name="dn2"dataHost="mycat2" database="db1"/> <dataHost name="mycat"maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="native" switchType="1"> <heartbeat>selectuser()</heartbeat> <writeHosthost="host1" url="dev-2:3306" user="test"password="test"/> </dataHost> <dataHost name="mycat2"maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="native" switchType="1"> <heartbeat>selectuser()</heartbeat> <writeHosthost="host2" url="dev-3:3306" user="test"password="test"/> </dataHost>
数据库脚本
create databasedb1; create tabledb1.user(id int, user_name varchar(20), email varchar(20), phone varchar(20));
由于Mycat支持Mysql协议,所以Mycat对于Mysql数据导入方案有三种:
用jdbc直接进行insert操作
根据官网测试结果:在16核上insert性能可以达到12w/s
本轮测试数据量200w,测试工具为testtool.tar.gz。结论:tps约8w
用mysqldump操作
mysqldump操作原理与insert相同,数据量100w,没有预热。结论tps:3w
用load data infile操作
官网说法:是insert性能的几十倍,如下
Load data功能在1.4版本+支持,经过测试发现,如果导入数据量比较大(大于10w),则会出现文件找不到情况,官网群中有人出现同样问题,原因待查。
根据其他人测试结果:loaddata local infile命令64M23万条数据用到500多M内存
从schema db1迁移扩容到db2。由于mycql不支持自动扩容,所以需要手动进行rehash。
目前方案有:
将数据导出本分,清空数据,通过上述导入方案的三种方式进行扩容迁移
性能结果如上测试
手动迁移(注意:此方案只适合单节点迁移):
1) 使用mycat的RehashLauncher重新进行hash算法,得出rehash的id列表
2) 在mysql上使用mysqldump根据rehash的id列表,导出数据
3) 在mycat上mysqldump,导入数据
4) 在mysql上删除迁移的数据
对于1)步骤,有2个bug:
取得id值时错误
此bug已经提交到mycate1.4和mycat master分支,并已合并
hash后的host比较错误
此bug已经提交到1.4分支,并已合并
对于2),3),4)步骤可以采用shell脚本自动运行,官网脚本如下:
# arg1=start, arg2=end, format: %s.%N function getTiming() { start=$1 end=$2 start_s=$(echo$start | cut -d '.' -f 1) start_ns=$(echo$start | cut -d '.' -f 2) end_s=$(echo $end |cut -d '.' -f 1) end_ns=$(echo $end |cut -d '.' -f 2) time=$(( ( 10#$end_s- 10#$start_s ) * 1000 + ( 10#$end_ns / 1000000 - 10#$start_ns / 1000000 ) )) echo "$timems" } #rehash节点 rehashNode=$1 #mycat节点 expanNode=$2 #迁移数据 order_fn="$3" #迁移数据库 rehash_db=db1 #迁移表 rehash_table=user #mysql配置 mysql_port=3306 mysql_user=test mysql_pwd=test #mycat配置 mycat_port=8066 mycat_user=mysql mycat_pwd=123456 mycat_db=testdb start=$(date +%s.%N) if [ "$#" = "0" ]; then echo "Please input parameter, for example:" echo "ReRouter.sh 192.168.84.13 192.168.84.14/home/mycat/T_CMS_ORDER" echo " " exit fi; echo "需要进行迁移的主机总量为:$#, 主机 IP 列表如下:" for i in "$@" do echo "$i" done echo " " #取出 rehash 需要的 SerNum(已经用 in 拼接好) for n in `cat $order_fn` do condOrder=$n done echo "************* 导出 *************" date # 1) 首先调用 mysqldump 进行数据导出 echo "开始导出主机:$ 表:T_CMS_ORDER." mysqldump -h$rehashNode -P$mysql_port -u$mysql_user-p$mysql_pwd $rehash_db $rehash_table --default-characterset=utf8 --extende d-insert=false --no-create-info --add-locks=false--completeinsert --where=" id in $condOrder " >./T_CMS_ORDER_temp.sql echo "导出结束." echo " " echo "************* 导入 *************" date # 2) 调用 mycat 接口进行数据导入 echo "开始导入 T_CMS_ORDER 表数据" mysql -h$expanNode -P$mycat_port -u$mycat_user -p$mycat_pwd$mycat_db --default-character-set=utf8 < ./T_CMS_ORDER_temp.sql echo "导入结束." echo " " echo "************* 删除数据 *************" date # 3) 当前两步都无误的情况下,删除最初的导出数据. echo "开始删除已导出的数据表:." mysql -h$rehashNode -Pmycat_port -u$mysql_user-p$mysql_pwd -e "use $rehash_db;DELETE FROM $rehash_table WHERE id in $condOrder ; commit; " echo "删除结束." echo " " echo "************* 清空临时文件 *************" date # 4) 清空临时文件 rm ./T_CMS_ORDER_temp.sql echo "清空临时文件" echo "#####################主机:$rehashNode 处理完成#####################" date echo " " echo "ReHash 运行完毕." end=$(date +%s.%N) getTiming $start $end
此脚本运行1)执行的文件,会报如下错误:
这是由于1)会一次性根据id列表,采用in语句查询出所有数据,当数据量过大时,报错。本轮测试采用优化方案:
1)步骤每1w为一行
RehashLauncher代码如下:
public class RehashLauncher { private final class RehashRunner implements Runnable { private final File output; private final String table; private RehashRunner(File output, String table) { this.output = output; this.table = table; } public void run(){ int pageSize=500; int page=0; List<Map<String, Object>> list=null; int total=0; int rehashed=0; String hostWithDatabase=args.getHostWithDatabase(); PrintStream ps=null; try { ps=new PrintStream(output); StringBuilder rehashData = new StringBuilder(); list = JdbcUtils.executeQuery(dataSource, "select " + args.getShardingField() + " from " + table + " limit ?,?", page++ * pageSize, pageSize); while (!CollectionUtil.isEmpty(list)) { for(int i=0,l=list.size();i<l;i++){ Map<String, Object> sf=list.get(i); Integer hash=alg.calculate(sf.get(args.getShardingField()).toString()); String host=rehashHosts[hash]; total++; if(!host.equals(hostWithDatabase)){ rehashed++; rehashData.append(sf.get(args.getShardingField())).append(","); if(rehashed % 10000 == 0){ ps.println("(" + rehashData.substring(0, rehashData.length() - 1) + ")"); rehashData.delete(0, rehashData.length()); } } // ps.println(sf+"=>"+host); } list = JdbcUtils.executeQuery(dataSource, "select " + args.getShardingField() + " from " + table + " limit ?,?", page++ * pageSize, pageSize); } if(rehashData.length() >= 1){ ps.println("(" + rehashData.deleteCharAt(rehashData.length() - 1) + ")"); } // ps.println("rehashed ratio:"+(((double)rehashed)/total)); } catch (Exception e) { throw new RehashException(e); }finally{ if(ps!=null){ ps.close(); } latch.countDown(); } } }
program argument:
-jdbcDriver=com.mysql.jdbc.Driver -jdbcUrl=jdbc:mysql://172.16.54.136:3306/db1 -host=172.16.54.136:3306 -user=test -database=db1 -password=test -tablesFile=F:/certusnetSVN/books/mycat/rehash\tables.txt -shardingField=id -rehashHostsFile=F:/certusnetSVN/books/mycat/rehash/hosts.txt -hashType=mod -rehashNodeDir=F:/certusnetSVN/books/mycat/rehash/tmp
hosts.txt配置:
172.16.54.136:3306/db1 172.16.54.138:3306/db1 172.16.54.136:3306/db2 172.16.54.138:3306/db2
tables.txt配置:
user
2),3),4)脚本改为shell多线程,每次处理一行的方案
脚本如下:
# arg1=start, arg2=end, format: %s.%N function getTiming() { start=$1 end=$2 start_s=$(echo $start | cut -d '.' -f 1) start_ns=$(echo $start | cut -d '.' -f 2) end_s=$(echo $end | cut -d '.' -f 1) end_ns=$(echo $end | cut -d '.' -f 2) time=$(( ( 10#$end_s - 10#$start_s ) * 1000 + ( 10#$end_ns / 1000000 - 10#$start_ns / 1000000 ) )) echo "$time ms" } #rehash节点 rehashNode=$1 #mycat节点 expanNode=$2 #迁移数据 order_fn="$3" #迁移数据库 rehash_db=db1 #迁移表 rehash_table=user #mysql配置 mysql_port=3306 mysql_user=test mysql_pwd=test #mycat配置 mycat_port=8066 mycat_user=mysql mycat_pwd=123456 mycat_db=testdb total_start=$(date +%s.%N) if [ "$#" = "0" ]; then echo "Please input parameter, for example:" echo "ReRouter.sh 192.168.84.13 192.168.84.14 /home/mycat/T_CMS_ORDER" echo " " exit fi; echo "需要进行迁移的主机总量为:$#, 主机 IP 列表如下:" for i in "$@" do echo "$i" done echo " " #取出 rehash 需要的 SerNum(已经用 in 拼接好) while read condOrder do { file_id=${condOrder:3:10} echo "************* 导出 *************" date # 1) 首先调用 mysqldump 进行数据导出 echo "开始导出主机:$ 表:T_CMS_ORDER." mysqldump -h$rehashNode -P$mysql_port -u$mysql_user -p$mysql_pwd $rehash_db $rehash_table --no-create-info --skip-add-locks -c --where=" id in $condOrder " > ./T_CMS_ORDER_$file_id.sql echo "导出结束." echo " " echo "************* 导入 *************" date # 2) 调用 mycat 接口进行数据导入 echo "开始导入 T_CMS_ORDER 表数据" mysql -h$expanNode -P$mycat_port -u$mycat_user -p$mycat_pwd -D$mycat_db < ./T_CMS_ORDER_$file_id.sql echo "导入结束." echo " " echo "************* 删除数据 *************" date # 3) 当前两步都无误的情况下,删除最初的导出数据. echo "开始删除已导出的数据表:." mysql -h$rehashNode -Pmycat_port -u$mysql_user -p$mysql_pwd -e "use $rehash_db; DELETE FROM $rehash_table WHERE id in $condOrder ; commit; " echo "删除结束." echo " " echo "************* 清空临时文件 *************" date # 4) 清空临时文件 rm ./T_CMS_ORDER_$file_id.sql echo "清空临时文件" echo "#####################主机:$rehashNode 处理完成#####################" date echo " " echo "ReHash 运行完毕." }& done < $order_fn wait echo "操作总消耗时长:" total_end=$(date +%s.%N) getTiming $total_start $total_end
执行结果100w迁移,耗时约:20s
schema.xml
<schemaname="testdb" checkSQLschema="false"sqlMaxLimit="100"> <tablename="user_test" dataNode="dn1,dn2"rule="mod-long"/> </schema> <dataNode name="dn1"dataHost="mycat" database="db1"/> <dataNode name="dn2"dataHost="mycat2" database="db1"/> <dataHost name="mycat"maxCon="1000" minCon="10" balance="0"writeType="0" dbType="postgresql" dbDriver="jdbc" switchType="1"> <heartbeat>select1</heartbeat> <writeHosthost="host1" url="jdbc:postgresql://dev-2:5432/db1"user="postgres" password="123456"/> </dataHost> <dataHost name="mycat2"maxCon="1000" minCon="10" balance="0"writeType="0" dbType="postgresql" dbDriver="jdbc" switchType="1"> <heartbeat>select1</heartbeat> <writeHosthost="host2" url="jdbc:postgresql://dev-3:5432/db1"user="postgres" password="123456"/> </dataHost>
postgresql.conf
shared_buffers = 128MB max_connections = 1000 fsync = off wal_buffers = -1 commit_delay = 10000 commit_siblings = 500
数据库脚本
create databasedb1; create table user_test(idint, user_name varchar(20), email varchar(20), phone varchar(20));
Mycat对于postgresql的导入方案只有一种,采用jdbc的insert方式。
本轮测试数据量100w,测试工具为testtool.tar.gz。结论:tps约6w
对于postgresql迁移,也只能采用编程程序的方式进行,方案可以参考mysql迁移方案,此轮不再测试。