参考:

http://dev.mysql.com/doc/mysql-utilities/1.5/en/fabric-quick-start-sharding-scenario.html

http://www.percona.com/blog/2014/04/25/managing-farms-of-mysql-servers-with-mysql-fabric/

一、MySQL Fabric主要功能

与MySQL复制功能共同使用实现高可用性时,MySQL Fabric能够提供自动故障检测和故障转移功能,具体包括:

监控主数据库,如果服务器出现故障, MySQL Fabric将选出一个从数据库,并将其升级为新的主数据库。

提供事务处理的自动路径选择以连接到当前主数据库,及从数据库间的查询负载均衡。

拓扑及服务器状态对应用透明。

自动数据分片和再分片功能简化了开发运营团队的流程管理工作。这帮助用户能够:

对表分片,实现读写的横向扩展;

选择对哪些表分片,并指定分片键的字段,包括使用基于哈希映射还是基于区间映射;

将现有数据片转移到新的服务器上,或者将这些数据片进一步分成更小的数据片。

二、MySQL Fabric分片架构图

spacer.gif3A8B8F6563F84018A57DA739A5D0F671

58D885507A5B4AF5869F008337274BCD

三、实验环境

mysql版本:5.6.21,需要支持gtid

数据库备份机:一个mysql实例(mysql版本必须为5.6或以上版本)用于安装backing store,该服务不是Fabric HA组的成员

所有MySQL都运行于日志模式和GTID模式,Fabric特性依赖于MySQL的GTID特性,也就因为这个原因目前仅支持5.6+。

python:python2.7.3

IP地址    功能    
192.168.1.70    master    
192.168.1.71    slave    
192.168.1.76    slave    
192.168.1.227    storage    
192.168.1.230    fabric node

MySQL 5.6 的新特性之一,是加入了全局事务 ID (GTID) 来强化数据库的主备一致性,故障恢复,以及容错能力。

1.什么是GTID?

官方文档:http://dev.mysql.com/doc/refman/5.6/en/replication-gtids.html在这篇文档里,我们可以知道全局事务 ID 的官方定义是:GTID = source_id:transaction_id

MySQL 5.6 中,每一个 GTID 代表一个数据库事务。在上面的定义中,source_id 表示执行事务的主库 uuid(server_uuid),transaction_id 是一个从 1 开始的自增计数,表示在这个主库上执行的第 n 个事务。MySQL 会保证事务与 GTID 之间的 1 : 1 映射。

例如,下面就是一个 GTID:3E11FA47-71CA-11E1-9E33-C80AA9429562:50 表示在以 "3E11FA47-71CA-11E1-9E33-C80AA9429562" 为唯一标示的 MySQL 实例上执行的第 50 个数据库事务。很容易理解,MySQL 只要保证每台数据库的 server_uuid 全局唯一,以及每台数据库生成的 transaction_id 自身唯一,就能保证 GTID 的全局唯一性。

2.什么是server_uuid?

MySQL 5.6 用 128 位的 server_uuid 代替了原本的 32 位 server_id 的大部分功能。原因很简单,server_id 依赖于 my.cnf 的手工配置,有可能产生冲突 —— 而自动产生 128 位 uuid 的算法可以保证所有的 MySQL uuid 都不会冲突。

在首次启动时 MySQL 会调用 generate_server_uuid() 自动生成一个 server_uuid,并且保存到 auto.cnf 文件 —— 这个文件目前存在的唯一目的就是保存 server_uuid。

五、升级mysql

fabric支持的mysql版本必须是5.6以上的,目前系统上的mysql是5.5.18

1. 安装cmake

./bootstrap
gmake
make && make install

2. 安装percona

原先的机器已经有了旧版本的mysql,将数据库和数据库配置文件移走

mv /etc/my.cnf /etc/my.cnf.old
mv /usr/local/mysql/ /usr/local/mysql_old
解压
tar zxvf percona-server-5.6.21-69.0.tar.gz  && cd percona-server-5.6.21-69.0
编译
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
 -DMYSQL_UNIX_ADDR=/tmp/mysql.sock \
 -DDEFAULT_CHARSET=utf8 \
 -DDEFAULT_COLLATION=utf8_general_ci \
 -DWITH_EXTRA_CHARSETS:STRING=utf8,gbk,gb2312 \
 -DWITH_MYISAM_STORAGE_ENGINE=1 \
 -DWITH_INNOBASE_STORAGE_ENGINE=1 \
 -DWITH_MEMORY_STORAGE_ENGINE=1 \
 -DWITH_READLINE=1 \
 -DENABLED_LOCAL_INFILE=1 \
 -DMYSQL_USER=mysql
 安装
make -j 9
make install

3.编辑配置文件

设置配置文件
cp support-files/my-default.cnf /etc/my.cnf
初始化数据库
/usr/local/mysql/scripts/mysql_install_db --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --user=mysql
设置mysql启动
cp support-files/mysql.server /etc/init.d/mysqld 
chmod 777 /etc/init.d/mysqld 
启动mysql
 /usr/local/mysql/bin/mysqld_safe &
查看mysql版本
mysql -V

六、安装mysql fabric

下载

wget http://cdn.mysql.com/archives/mysql-utilities/mysql-utilities-1.5.0.tar.gz

解压、安装

tar zxvf mysql-utilities-1.5.0.tar.gz && cd mysql-utilities-1.5.0
python setup.py install

七、建立backing store账号

Backing Store用于存储整个HA集群的服务器等相关配置,它需要一个MySQL实例来存储这些信息,这个实例的版本需要跟其它在HA中的MySQL实例版本保持一致,而且必须是5.6.10及更高的版本,采用了机器:192.168.1.227

首先,你需要一个帐号来连接Backing Store的MySQL实例,这个帐号需要有对fabric数据库的管理员级权限,建议不要使用root,为fabric创建单独的账号,如下:

grant all on fabric.* to fabric@'192.168.1.230';
SELECT `Host`,`User` FROM `user`;
flush privileges;


八、修改每一台机器的fabric配置文件/etc/mysql/fabric.cfg

在[storage]段设置backing store的用户账号和密码,或者为空;

详细配置文件如下:

cat /etc/mysql/fabric.cfg
1. [DEFAULT] #如果请求的选项没有在命令行指定,或没有在配置文件找到,Fabric将查看该段的信息
2. prefix =
3. sysconfdir = /etc #配置文件存放目录
4. logdir = /var/log #日志文件存储位置,绝对路径,由守护进程创建
5. [storage] #配置backing store相关的选项
6. address = 172.17.0.49:3306 #指定state store的mysql实例地址和端口
7. user = fabric #连接到mysql实例的用户名
8. password = fabric@123 #认证密码,也能设置空密码
9. database = fabric #存储Fabric表的数据库
10. auth_plugin = mysql_native_password #设置使用的认证插件
11. connection_timeout = 6 #中断请求之前等待的最大时间,单位秒
12. connection_attempts = 6 #创建连接的最大尝试次数
13. connection_delay = 1 #连续尝试创建连接之间的延时时间,默认1s
14. [servers]
15. user = fabric
16. password =
17. [protocol.xmlrpc] #该段定义Fabric接收通过XML-RPC协议的请求
18. address = 0.0.0.0:32274 #标识Fabric使用的主机和端口,接收XML-RPC请求
19. threads = 5 #XML-RPC会话线程的并发创建数,决定多少并发请求Fabric能接受
20. user = admin #用户名,认证命令行请求
21. password = #用户密码,认证命令行请求
22. disable_authentication = no #是否启用命令行请求需要认证,默认要认证
23. realm = MySQL Fabric
24. ssl_ca = #使用ssl认证方式,指定PEM格式文件,包含信任SSL证书的列表
25. ssl_cert = #SSL认证文件,用于创建安全的连接
26. ssl_key = #SSL key文件
27. [executor] #通过XML-RPC接收到的请求,映射到程序能立即执行或通过队列执行者,保证冲突的请求处理按序执行。通常读操作立即执行通过XML-RPC会话线程,写操作通过执行者
28. executors = 5 #多少线程用于执行者
29. [logging] #设置Fabric日志信息记录到哪里,如果不是开启为后台进程,将打印日志到标准输出
30. level = INFO #日志级别,支持DEBUG,INFO,WARNING,ERROR,CRITICAL
31. url = file:///var/log/fabric.log #存储日志的文件,能为绝对或相对路径(如是相对路径,将参照default段logdir参数指定的日志目录)
32. [sharding] #Fabric使用mysqldump和mysql客户端程序,执行移动和分离shards,指定程序的路径
33. mysqldump_program = /usr/bin/mysqldump
34. mysqlclient_program = /usr/bin/mysql
35. [statistics]
36. prune_time = 3600 #删除大于1h的条目
37.
38. [failure_tracking] #连接器和其他外部实体能报告错误,fabric保持跟踪服务器健康状态和采取相应的行为,如提升一个新的master,如果一个服务器时不稳定的,但不是master,将简单的标记为错误。
39. notifications = 300 #多少次报告错误后,将标志服务器不可用
40. notification_clients = 50 #多少不同源报告错误
41. notification_interval = 60 #评估错误数的统计时间
42. failover_interval = 0 #为了避免整个系统不可用,自上次提升间隔多少秒后,新master才能选取
43. detections = 3 #为了缓解fabric,提供内建的错误检查,如果错误检查启动监控一个组,需要连续尝试3(默认)次访问当前master都错误后,才能提升新master,
44. detection_interval = 6 #连续检查之间的间隔时间
45. detection_timeout = 1 #错误检查程序尝试连接到一个组中服务器的超时时间
46. prune_time = 3600 #在错误日志中保留多久的错误信息
47.
48. [connector] #Fabric-aware连接器连接到Fabric,获取组、shards、服务器的信息,缓存结果到本地的时长,以提高性能。
49. ttl = 1 #缓存生存时间,单位s,决定多长时间,连接器考虑一个信息从Fabric获取是有效的
50. [client]
51. password =

九、storage机创建fabric数据库和相关表

创建表之前,在其他机器上给账号fabric全部的权限

use mysql
GRANT ALL PRIVILEGES ON *.* TO fabric@192.168.1.227;
flush privileges;


创建fabric数据库和相关表

mysqlfabric manage setup --param=storage.user=fabric

wKiom1SXnhHwkO2QAAHUL8U3MyA770.jpg 

如果xmlrpc密码没有设置,将提示进行设置。密码设置为admin


十、开启和关闭mysql fabric

mysqlfabric manage start
或者 mysqlfabric manage start &
[INFO] 1413775302.891194 - MainThread - Initializing persister: user (fabric), server (115.236.73.227:3306), database (fabric).
[INFO] 1413775302.898067 - MainThread - Loading Services.
[INFO] 1413775302.922307 - MainThread - Fabric node starting.
[INFO] 1413775302.948540 - MainThread - Starting Executor.
[INFO] 1413775302.948644 - MainThread - Setting 5 executor(s).
[INFO] 1413775302.949223 - Executor-0 - Started.
[INFO] 1413775302.949702 - Executor-1 - Started.
[INFO] 1413775302.950757 - Executor-2 - Started.
[INFO] 1413775302.951271 - Executor-3 - Started.
[INFO] 1413775302.952056 - Executor-4 - Started.
[INFO] 1413775302.952148 - MainThread - Executor started.
[INFO] 1413775302.963377 - MainThread - Starting failure detector.
[INFO] 1413775302.964364 - XML-RPC-Server - XML-RPC protocol server ('127.0.0.1', 32274) started.
[INFO] 1413775302.965491 - XML-RPC-Server - Setting 5 XML-RPC session(s).
[INFO] 1413775302.966021 - XML-RPC-Session-0 - Started XML-RPC-Session.
[INFO] 1413775302.966650 - XML-RPC-Session-1 - Started XML-RPC-Session.
[INFO] 1413775302.967432 - XML-RPC-Session-2 - Started XML-RPC-Session.
[INFO] 1413775302.967533 - XML-RPC-Session-3 - Started XML-RPC-Session.
[INFO] 1413775302.968104 - XML-RPC-Session-4 - Started XML-RPC-Session.
mysqlfabric manage stop
[INFO] 1413775374.662738 - XML-RPC-Session-2 - Stopping failure detector.
[INFO] 1413775374.662936 - XML-RPC-Session-2 - Shutting down Executor.
[INFO] 1413775374.664397 - XML-RPC-Session-2 - Executor has stopped.
[INFO] 1413775374.664975 - XML-RPC-Session-2 - Fabric node stopped.


十一、在节点机(即安装mysql fabric服务的机器:192.168.1.230)上创建组,并添加MySQL服务

运用mysql fabric的connector进行连接操作

36E2579A86704D5E951E8DC86A52D366




1.初始化完环境,设置权限,分配好角色,使用mysqlfabric创建组

mysqlfabric groupcreate group_id-1
Procedure :
{ uuid = 01952f88-ec87-4006-aca5-8628ac726a9a,
finished = True,
success = True,
return = True,
activities =
}

2.为组添加实例

mysqlfabric group add group_id-1 192.168.1.70:3306
mysqlfabric group add group_id-1 192.168.1.71:3306
mysqlfabric group add group_id-1 192.168.1.76:3306
Procedure :
{ uuid = e04b87d0-db05-4871-bbad-2e5ea4a3a34f,
finished = True,
success = False,
return = ServerError: Error accessing server (192.168.1.70:3306).,
activities =
}

####################

添加实例错误,如果权限没有错误 ,需要重新安装mysqlfabric


3.查看组的信息

mysqlfabric group lookup_servers group_id-1
Command :
{ success = True
return = [{'status': 'SECONDARY', 'server_uuid': '7a45f71d-7934-11e4-9e8c-782bcb74823a', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.71:3306'}, {'status': 'SECONDARY', 'server_uuid': '9cf162ca-7934-11e4-9e8d-782bcb1b6b98', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.76:3306'}, {'status': 'SECONDARY', 'server_uuid': 'ae94200b-7932-11e4-9e81-a4badb30e16b', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.70:3306'}]
activities =
}


4.选举master角色

mysqlfabric group promote group_id-1 --slave_id 7a45f71d-7934-11e4-9e8c-782bcb74823a
Procedure :
{ uuid = be774572-d97c-4570-b486-8569d2813114,
finished = True,
success = True,
return = True,
activities =
}

查看选举结果

mysqlfabric group lookup_servers group_id-1
Command :
{ success = True
return = [{'status': 'PRIMARY', 'server_uuid': '7a45f71d-7934-11e4-9e8c-782bcb74823a', 'mode': 'READ_WRITE', 'weight': 1.0, 'address': '192.168.1.71:3306'}, {'status': 'SECONDARY', 'server_uuid': '9cf162ca-7934-11e4-9e8d-782bcb1b6b98', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.76:3306'}, {'status': 'SECONDARY', 'server_uuid': 'ae94200b-7932-11e4-9e81-a4badb30e16b', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.70:3306'}]
activities =
}


5.激活HA组(group_id-1 )

mysqlfabric group activate group_id-1


十二、创建数据库,验证mysql fabric高可用集群已经启动

1.创建表,在节点机上运行,数据表会自动在master上创建,同步到slave机

python setup_table_ha.py
#!/usr/bin/python
import mysql.connector
from mysql.connector import fabric
conn = mysql.connector.connect(
fabric={"host" : "localhost", "port" : 32274, "username": "admin", \
"password" : "123456"},
user="root", database="test", password="",
autocommit=True
)
conn.set_property(mode=fabric.MODE_READWRITE, group="group_id-1")
cur = conn.cursor()
cur.execute(
"CREATE TABLE IF NOT EXISTS subscribers ("
" sub_no INT, "
" first_name CHAR(40), "
" last_name CHAR(40)"
")"
)


2.验证数据表是否创建

mysql -h 192.168.1.70 -P3306 -u root -e "use test;show tables"
mysql -h 192.168.1.71 -P3306 -u root -e "use test;show tables"
mysql -h 192.168.1.76 -P3306 -u root -e "use test;show tables"

返回结果:

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

| Tables_in_test |

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

| subscribers |

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


3.为创建的数据表添加数据

pyhon add_subs_ha.py
#!usr/bin/python
import mysql.connector
from mysql.connector import fabric
def add_subscriber(conn, sub_no, first_name, last_name):
conn.set_property(group="group_id-1", mode=fabric.MODE_READWRITE)
cur = conn.cursor()
cur.execute(
"INSERT INTO subscribers VALUES (%s, %s, %s)",
(sub_no, first_name, last_name)
)
conn = mysql.connector.connect(
fabric={"host" : "localhost", "port" : 32274, "username": "admin", \
"password" : "123456"},
user="root", database="test", password="",
autocommit=True
)
conn.set_property(group="group_id-1", mode=fabric.MODE_READWRITE)
add_subscriber(conn, 72, "Billy", "Fish")
add_subscriber(conn, 500, "Billy", "Joel")
add_subscriber(conn, 1500, "Arthur", "Askey")
add_subscriber(conn, 5000, "Billy", "Fish")
add_subscriber(conn, 15000, "Jimmy", "White")
add_subscriber(conn, 17542, "Bobby", "Ball")


4.验证数据表是否插入数据

mysql -h 192.168.1.70 -P3306 -u root -e "use test;select * from subscribers"
mysql -h 192.168.1.71 -P3306 -u root -e "use test;select * from subscribers"
mysql -h 192.168.1.76 -P3306 -u root -e "use test;select * from subscribers"

返回结果:

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

| sub_no | first_name | last_name |

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

| 72 | Billy | Fish |

| 500 | Billy | Joel |

| 1500 | Arthur | Askey |

| 5000 | Billy | Fish |

| 15000 | Jimmy | White |

| 17542 | Bobby | Ball |

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


5.查询已经创建好的数据表

python read_table_ha.py
#!/usr/bin/python
import mysql.connector
from mysql.connector import fabric
def find_subscriber(conn, sub_no):
conn.set_property(group="group_id-1", mode=fabric.MODE_READONLY)
cur = conn.cursor()
cur.execute(
"SELECT first_name, last_name FROM subscribers "
"WHERE sub_no = %s", (sub_no, )
)
for row in cur:
print row
conn = mysql.connector.connect(
fabric={"host" : "localhost", "port" : 32274, "username": "admin", \
"password" : "123456"},
user="root", database="test", password="",
autocommit=True
)
find_subscriber(conn, 72)
find_subscriber(conn, 500)
find_subscriber(conn, 1500)
find_subscriber(conn, 5000)
find_subscriber(conn, 15000)
find_subscriber(conn, 17542)


返回结果:

(u'Billy', u'Fish')

(u'Billy', u'Joel')

(u'Arthur', u'Askey')

(u'Billy', u'Fish')

(u'Jimmy', u'White')

(u'Bobby', u'Ball')


十三、创建全局组,在机器:192.168.1.70起三个mysql实例

1.配置文件如下:

cat /etc/my10.cnf
basedir = /usr/local/mysql/
datadir = /data/database/data10
socket = /data/database/socket/mysqlfab10.socket
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
tmpdir=/data/database/fabric_tmp
explicit_defaults_for_timestamp=true
binlog-format=ROW
log-slave-updates=true
log-bin=binlog
gtid-mode=on
enforce-gtid-consistency=true
master-info-repository=TABLE
relay-log-info-repository=TABLE
sync-master-info=1
slave-parallel-workers=2
binlog-checksum=CRC32
master-verify-checksum=1
slave-sql-verify-checksum=1
binlog-rows-query-log_events=1
report-port=3307
port=3307
report-host=192.168.1.70
server_id = 10


2.初始化数据库

scripts/mysql_install_db --basedir=/usr/local/mysql --datadir=/data/database/data10 --defaults-file=/etc/my10.cnf


3.赋以存放数据目录的权限

chown mysql:mysql -R /data/database/data10/
chown mysql:mysql -R /data/database/socket/


4.启动数据库

/usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my10.cnf &


5.登陆数据库

mysql -h 127.0.0.1 -P3307

同理,其他的实例照此操作即可


6.为mysql赋以权限

#!/bin/bash
mysql -h 127.0.0.1 -P3306 -u root -e "GRANT ALL ON *.* TO root@'%'"
mysql -h 127.0.0.1 -P3307 -u root -e "GRANT ALL ON *.* TO root@'%'"
mysql -h 127.0.0.1 -P3308 -u root -e "GRANT ALL ON *.* TO root@'%'"
mysql -h 127.0.0.1 -P3309 -u root -e "GRANT ALL ON *.* TO root@'%'"
mysql -h 127.0.0.1 -P3307 -u root -e "GRANT ALL ON *.* TO fabric@'%'"
mysql -h 127.0.0.1 -P3308 -u root -e "GRANT ALL ON *.* TO fabric@'%'"
mysql -h 127.0.0.1 -P3309 -u root -e "GRANT ALL ON *.* TO fabric@'%'"
mysql -h 127.0.0.1 -P3306 -u root -e "GRANT ALL ON *.* TO fabric@'%'"

注意:防火墙要开放3306-3309端口


7.创建全局组(global-group)

全局组的两个特点:1.全局组任何数据架构的更改,都会复制到其他HA组中 2.如果数据表包含的数据应该复制到HA组中(不是分片),那么将对全局组进行插入、更新、删除操作,然后复制到HA组的数据表,这个表称为全局表

a)创建全局组

mysqlfabric group create global-group

b)为全局组添加实例

mysqlfabric group add global-group 192.168.1.70:3307
mysqlfabric group add global-group 192.168.1.70:3308
mysqlfabric group add global-group 192.168.1.70:3309

返回结果:

Procedure :
{ uuid = 605c295c-f797-44cb-bafe-e85e99bdfa56,
finished = True,
success = True,
return = True,
activities =
}

c)在全局组中选举master

mysqlfabric group promote global-group

d)查看全局组的详细信息

mysqlfabric group lookup_servers global-group

返回结果:

Command :
{ success = True
return = [{'status': 'PRIMARY', 'server_uuid': '03bab725-79d3-11e4-a296-a4badb30e16b', 'mode': 'READ_WRITE', 'weight': 1.0, 'address': '192.168.1.70:3308'}, {'status': 'SECONDARY', 'server_uuid': '83843c77-79d8-11e4-a2ba-a4badb30e16b', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.70:3309'}, {'status': 'SECONDARY', 'server_uuid': 'cd008b47-79d1-11e4-a28e-a4badb30e16b', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.70:3307'}]
activities =
}

e)将在HA组中创建的数据表导入全局表中

因为全局组还没有数据,要从HA组(group_id-1)导出数据库,并将数据库导入全局组中

mysqldump -d -u root --single-transaction --set-gtid-purged=OFF -h 192.168.1.70 -P3306 --all-databases> my-schema.sql

将数据库导入全局组

mysql -h 192.168.1.70 -P 3307 -u root <my-schema.sql

验证导数据入全局组是否成功

mysql -h 192.168.1.70 -P 3307 -u root -e 'show create table test.subscribers\G'

返回结果:

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

Table: subscribers

Create Table: CREATE TABLE `subscribers` (

`sub_no` int(11) DEFAULT NULL,

`first_name` char(40) DEFAULT NULL,

`last_name` char(40) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8


十四、对HA组(group_id-1)进行分片

1.定义碎片映射

碎片映射是用于定义某些表应如何分片HA组之间的实体,有可能具有多个碎片映射。当定义碎片映射时,有两个参数:

映射的类型——hash或者range

将要使用的全局组

2.创建分片的映射类型

mysqlfabric sharding create_definition HASH global-group

返回结果:

Procedure :
{ uuid = b05a6e49-fb1a-498e-9d49-ca476a61a608,
finished = True,
success = True,
return = 1,
activities =
}

3.列出被定义的碎片映射

mysqlfabric sharding list_definitions

返回结果:

Command :
{ success = True
return = [[1, 'HASH', 'global-group']]
activities =
}

4.查看全局组的映射(全局组的映射应该在storage机上)

mysql -h 192.168.1.227 -P3306 -u root -e "SELECT * FROM fabric.shard_maps"

返回结果:

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

| shard_mapping_id | type_name | global_group |

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

| 1 | HASH | global-group |

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

5.定义该数据表中哪些列应该作为分片键(其上hash函数被执行的值,或者是采用range映射,限定范围进行比较的值)

在此例中,只有一个表被分片,采用了sub_no列为分片键

mysqlfabric sharding add_table 1 test.subscribers sub_no

返回结果:

Procedure :
{ uuid = 0cb9db2a-1c0f-4f98-814e-59a713ee3b0f,
finished = True,
success = True,
return = True,
activities =
}

6.此时,表test的碎片映射已经创建,但是还没有创建碎片

创建碎片,该碎片存储在HA组中(group_id-1)

mysqlfabric sharding add_shard 1 group_id-1 --state=enabled

返回结果:

Procedure :
{ uuid = 42666bdc-0ede-4444-b718-1e45d15f5551,
finished = True,
success = True,
return = True,
activities =
}

7.验证HA组(group_id-1)已经存储了碎片

mysql -h 192.168.1.227 -P3306 -u root -e "SELECT * FROM fabric.shards"

返回结果:

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

| shard_id | group_id | state |

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

| 1 | group_id-1 | ENABLED |

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


十五、扩展分片

其上,数据库在技术上已经实现了分片功能,当还不提供可扩展性,因为这里仅定义了一个碎片

创建多一个HA组( group_id-2),使分片功能可扩展,如图:

wKioL1SXnw6h0BwIAAFR7OfwI8k770.jpg

创建HA组( group_id-2)的过程与HA组(group_id-1)一致,如下:

1.创建HA组(group_id-2)

mysqlfabric group create group_id-2

2.为HA组(group_id-2)添加实例

mysqlfabric group add group_id-2 192.168.1.70:3310
mysqlfabric group add group_id-2 192.168.1.71:3310
mysqlfabric group add group_id-2 192.168.1.76:3309

3.HA组(group_id-2)自动选举master

mysqlfabric group promote group_id-2

4.查看HA组(group_id-2)选举结果

mysqlfabric group lookup_servers group_id-2

返回结果:

Command :
{ success = True
return = [{'status': 'PRIMARY', 'server_uuid': '2ed06823-79d9-11e4-a2be-782bcb1b6b98', 'mode': 'READ_WRITE', 'weight': 1.0, 'address': '192.168.1.76:3309'}, {'status': 'SECONDARY', 'server_uuid': '60f9b1a4-7a00-11e4-a3be-a4badb30e16b', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.70:3310'}, {'status': 'SECONDARY', 'server_uuid': 'd52ea4dd-79d8-11e4-a2bc-782bcb74823a', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.1.71:3309'}]
activities =
}

5.开始分裂碎片

分片,即分裂现有碎片,分裂指定碎片(shard_id=1),将分裂的碎片存储到新HA组(group_id-2)

a)分裂碎片

mysqlfabric sharding split_shard 1 group_id-2

出现错误1,如下:

Procedure :
{ uuid = b4f6c783-a2c1-4d9b-9206-b7e54f9d7850,
finished = True,
success = False,
return = BackupError: ('Error while taking backup using MySQLDump\n, %s', "mysqldump: Got error: 1045: Access denied for user 'fabric'@'192.168.1.230' (using password: NO) when trying to connect\n"),
activities =
}

参考:http://bugs.mysql.com/bug.php?id=73210

解决方法:

需要在mysqlfabric的配置文件中client模块添加密码

[client]

password = 123456


b)重新执行分片命令,出现错误2,如下:

Procedure :
{ uuid = 6984a49f-8206-4655-ad71-b7bb813d39bb,
finished = True,
success = False,
return = BackupError: ('Error while restoring the backup using the mysql client\n, %s', "ERROR 1840 (HY000) at line 24 in file: 'MySQL_192.168.1.70_3306.sql': @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.\n"),
activities =
}

查看slave状态,显示以下错误:

mysql -h 192.168.1.70 -u root -e "show slave status\G"

Last_IO_Errno: 1236

Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'could not find next log; the first event '' at 4, the last event

参考:http://www.fromdual.com/replication-troubleshooting-classic-vs-gtid

解决方法:

备份master角色的数据库

mysqldump -h 192.168.1.71 -u root -P3306 --all-databases --flush-privileges --single-transaction --flush-logs --triggers --routines --events --hex-blob >/data/database/full_backup_master.sql


检查备份机(即slave角色)GTID值

head -n 60 /data/database/full_backup_master.sql | grep PURGED


返回结果:

SET @@GLOBAL.GTID_PURGED='03bab725-79d3-11e4-a296-a4badb30e16b:1-6,


重置slave机的GTID_EXECUTED和GTID_PURGED

mysql -h 192.168.1.70 -P3306 -u root -e "stop slave"
mysql -h 192.168.1.70 -P3306 -u root -e "reset master"


将从master机备份出来的数据还原到slave机

mysql -h 192.168.1.70 -P3306 -u root </data/database/full_backup_master.sql


确保GTID_EXECUTED和GTID_PURGED值不变

mysql -h 192.168.1.70 -P 3306 -u root -e "SHOW GLOBAL VARIABLES LIKE 'gtid_executed'"
mysql -h 192.168.1.70 -P 3306 -u root -e "SHOW GLOBAL VARIABLES LIKE 'gtid_purged'"


开启slave

mysql -h 192.168.1.70 -P 3306 -u root -e "start slave"


分片问题依旧没有解决

参考:http://bugs.mysql.com/bug.php?id=73212

解决:

mysql fabric用户不能有密码,设置fabric用户密码为空

#!/bin/bash
mysql -h 127.0.0.1 -P3306 -u root -e "use mysql;update user set password=password('') where user='fabric';flush privileges"
mysql -h 127.0.0.1 -P3307 -u root -e "use mysql;update user set password=password('') where user='fabric';flush privileges"
mysql -h 127.0.0.1 -P3308 -u root -e "use mysql;update user set password=password('') where user='fabric';flush privileges"
mysql -h 127.0.0.1 -P3309 -u root -e "use mysql;update user set password=password('') where user='fabric';flush privileges"
mysql -h 127.0.0.1 -P3310 -u root -e "use mysql;update user set password=password('') where user='fabric';flush privileges"

重新安装mysqlfabric,重建storage机上fabric数据库

cd mysql-utilities-1.5.0 && python setup.py install
mysql -h 192.168.1.227 -P 3306 -u root -e "drop database fabric"
mysqlfabric manage setup

重新设置HA组,HA组角色

重建碎片映射


c)重置环境后,执行分片命令

出现错误3,如下:

mysqlfabric sharding split_shard 1 group_id-2

wKiom1SXnpbiaMEPAACkEGG_9BA104.jpg


解决:

去到HA组(groud_id-2)的机器,在全部的mysql实例中执行“reset master”


d)继续执行分片命令

出现问题4,如下:

mysqlfabric sharding split_shard 1 group_id-2

wKioL1SXn2LxzjUzAACrJ81qSh4249.jpg

解决:

修改数据库fabric的数据表shard_ranges的lower_bound的字段长度

登陆存放fabric数据库的机器

use fabric
select shard_mapping_id, hex(lower_bound), shard_id from shard_ranges;

wKiom1SXnszwbWR6AABgy8MYHuk270.jpg

show tables

wKioL1SXn4XCPnVlAABtURTGRRs416.jpg

desc shard_ranges;

wKiom1SXnvDhGxV0AABmUzfwUzA870.jpg

alter table shard_ranges modify column lower_bound varbinary(32);

wKioL1SXn6nTW0DGAABwsdNIo7M131.jpg




e)在节点机执行分片

mysqlfabric sharding split_shard 1 group_id-2

返回结果:

wKiom1SXnxOAN69KAAA-4b8RpF8837.jpg


###分片成功###


f)查看分片效果

查看HA组(group_id-1)的机器上数据库test的数据

mysql -h 192.168.1.71 -P 3306 -u root -e 'select * from test.subscribers'

wKioL1SXn8vzlL7gAAB2Dj9bufI982.jpg

###slave机和master机显示如上


g)查看group_id-2的机器

mysql -h 192.168.1.76 -P 3309 -u root -e 'select * from test.subscribers'

wKioL1SXn9uTN8qnAABWfaNlLZs617.jpg



十六、运用mysql fabric的python接口来操作数据库

为数据库test的表添加新行,用于连接的表属性设置为test.subscribers和给予该表的sub_no列的值作为key来分片

1.添加新行的python脚本

python add_subs_shards2.py
import mysql.connectorfrom mysql.connector 
import fabric
def add_subscriber(conn, sub_no, first_name, last_name):
    conn.set_property(tables=["test.subscribers"], key=sub_no, \
    mode=fabric.MODE_READWRITE)
    cur = conn.cursor()
    cur.execute( "INSERT INTO subscribers VALUES (%s, %s, %s)",
    (sub_no, first_name, last_name) )
conn = mysql.connector.connect( fabric={"host" : "localhost", "port" : 32274, "username": "admin", \ "password" : "admin"}, user="root", database="test", password="", autocommit=True)
conn.set_property(tables=["test.subscribers"], scope=fabric.SCOPE_LOCAL)
add_subscriber(conn, 22, "Billy", "Bob")
add_subscriber(conn, 8372, "Banana", "Man")
add_subscriber(conn, 93846, "Bill", "Ben")
add_subscriber(conn, 5006, "Andy", "Pandy")
add_subscriber(conn, 15050, "John", "Smith")
add_subscriber(conn, 83467, "Tommy", "Cannon")

###此时,group_id-1的表添加了新行,实时将表分片到group_id-2,如下:

2.在节点机查看group_id-1的表是否添加了新行

mysql -P 3306 -h 192.168.1.70 -u root -e 'select * from test.subscribers'

wKiom1SXn0ezdmlxAACiT8kj5AY314.jpg


3.在节点机查看group_id-2的表是否添加了新行

mysql -h 192.168.1.76 -P 3309 -u root -e 'select * from test.subscribers'

wKioL1SXoASg5X7FAABwsBClP6s946.jpg


4.用python脚本读取每个已添加的记录,连接模式设置为fabric.MODE_READONLY,python接口可知HA组内在查询数据时是均衡的,不是把所有的数据都丢给primary角色

python read_table_shards2.py
import mysql.connectorfrom
mysql.connector import fabric
def find_subscriber(conn, sub_no):
conn.set_property(tables=["test.subscribers"], key=sub_no, mode=fabric.MODE_READONLY)
cur = conn.cursor()
cur.execute( "SELECT first_name, last_name FROM subscribers " "WHERE sub_no = %s",
(sub_no, ) )
for row in cur:
print row conn = mysql.connector.connect( fabric={"host" : "localhost", "port" : 32274, "username": "admin", "password" : "admin"}, user="root", database="test", password="", autocommit=True )
find_subscriber(conn, 22)
find_subscriber(conn, 72)
find_subscriber(conn, 500)
find_subscriber(conn, 1500)
find_subscriber(conn, 8372)
find_subscriber(conn, 5000)
find_subscriber(conn, 5006)
find_subscriber(conn, 93846)
find_subscriber(conn, 15000)
find_subscriber(conn, 15050)
find_subscriber(conn, 17542)
find_subscriber(conn, 83467)


十七、mysql fabric的缺点

1.sharding对应用还不是完全透明的,应用不需要知道数据在哪个mysql server上,也不需要知道这些数据是否被转移,只需要提供sharding key去访问数据库

2.自增长ID不能被使用作为sharding key

3.所有的事务和查询必须被限制在一个single sharding中。

4.mysql fabric进程本身不提供故障容错,当出现故障时,需要进行重启。当mysql fabric不可用时,the connectors能够通过本地的cache继续路由操作