对于docker部署sharding-proxy感兴趣的读者,本文将会是一篇不错的选择,我们将详细介绍docker部署ragflow,并为您提供关于AmazonAurora的读写能力扩展之Shardi
对于docker 部署 sharding-proxy感兴趣的读者,本文将会是一篇不错的选择,我们将详细介绍docker 部署ragflow,并为您提供关于Amazon Aurora 的读写能力扩展之 ShardingSphere-Proxy 篇、Apache ShardingSphere Proxy 分库分表小练习、Apache ShardingSphere Proxy 负载均衡小练习、docker - nginx - proxy_pass + proxy_redirect的有用信息。
本文目录一览:- docker 部署 sharding-proxy(docker 部署ragflow)
- Amazon Aurora 的读写能力扩展之 ShardingSphere-Proxy 篇
- Apache ShardingSphere Proxy 分库分表小练习
- Apache ShardingSphere Proxy 负载均衡小练习
- docker - nginx - proxy_pass + proxy_redirect
docker 部署 sharding-proxy(docker 部署ragflow)
1、背景
Sharding-proxy 定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前先提供 PostgreSQL 版本,它可以使用任何兼容 PostgreSQL 协议的访问客户端 (如:PostgreSQL Command Client, Navicat 等) 操作数据,对 DBA 更加友好。
- 向应用程序完全透明,可直接当做 PostgreSQL 使用。
- 适用于任何兼容 MySQL/PostgreSQL 协议的的客户端。
2、安装 docker,这一步没什么可讲的
3、制作 sharding proxy 镜像
先制作配置文件:config-sharding.yaml 主要分片所用,文末
schemaName: sharding_db
dataSources:
ds0:
url: jdbc:postgresql://127.0.0.1:5432/ds0
username: postgres
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 65
ds1:
url: jdbc:postgresql://127.0.0.1:5432/ds1
username: postgres
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 65
shardingRule:
tables:
t_order:
actualDataNodes: ds${0..1}.t_order${0..1}
databaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds${user_id % 2}
tableStrategy:
inline:
shardingColumn: order_id
algorithmExpression: t_order${order_id % 2}
keyGenerator:
type: SNOWFLAKE
column: order_id
t_order_item:
actualDataNodes: ds${0..1}.t_order_item${0..1}
databaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds${user_id % 2}
tableStrategy:
inline:
shardingColumn: order_id
algorithmExpression: t_order_item${order_id % 2}
keyGenerator:
type: SNOWFLAKE
column: order_item_id
bindingTables:
- t_order,t_order_item
defaultTableStrategy:
none:
encryptRule:
encryptors:
encryptor_aes:
type: aes
props:
aes.key.value: 123456abc
tables:
t_order:
columns:
order_id:
plainColumn: order_plain
cipherColumn: order_cipher
encryptor: encryptor_aes
以上配置文件主要分三块:schemaName、dataSources、shardingRule
schemaName 定义 sharding proxy 数据库名;
DataSource 定义数据源,可以添加多个,每个数据源;
shardingRule 定义分片规则。
由示例得知待分片的库为 ds0 和 ds1, 每个库分片的逻辑表为 t_order 和 t_order_item 俩个表,表分片规则都是 order_id % 2,所以每个库有 4 张真实表,需要提前在 postgres 建好库和表:t_order_0, t_order_1, t_order_item_0, t_order_item_1,库分片规则为 user_id % 2。
制作 server.yaml 文件,改文件是通用配置,登录 sharding peoxy 的一些信息
authentication:
users:
root:
password: root
sharding:
password: sharding
authorizedSchemas: sharding_db
props:
max.connections.size.per.query: 1
acceptor.size: 16
executor.size: 16
proxy.transaction.enabled: false
proxy.opentracing.enabled: false
sql.show: true
编写 dockerfile
FROM sharding-proxy
ADD config-sharding.yaml /opt/sharding-proxy/conf/config-sharding.yaml
ADD server.yaml /opt/sharding-proxy/conf/server.yaml
制作镜像
docker build -t sharding-proxy:v1 .
4、制作 postgresql 镜像,并初始化数据库
由上面配置文件 config-sharding.yaml 可知,pgsql 需要先建 2 个库 ds0 ds1, 且每个库有 4 张真实表,需要提前在 postgres 建好库和表:t_order_0, t_order_1, t_order_item_0, t_order_item_1
编写初始化数据库的 shell 脚本
#!/bin/bash
dbname=ds0
# sql to check whether given database exist
sql1="select count(1) from pg_catalog.pg_database where datname = ''$dbname''"
# depending on how PATH is set psql may require a fully qualified path
cmd="psql -t -c \"$sql1\""
db_exists=`eval $cmd`
if [ $db_exists -eq 0 ] ; then
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE DATABASE ds0;
CREATE DATABASE ds1;
\c ds0;
CREATE TABLE t_order_0( order_id INT NOT NULL, user_id INT NOT NULL, status CHAR(10));
CREATE TABLE t_order_1( order_id INT NOT NULL, user_id INT NOT NULL, status CHAR(10));
CREATE TABLE t_order_item_0( order_id INT NOT NULL, user_id INT NOT NULL, status CHAR(10));
CREATE TABLE t_order_item_1( order_id INT NOT NULL, user_id INT NOT NULL, status CHAR(10));
\c ds1;
CREATE TABLE t_order_0( order_id INT NOT NULL, user_id INT NOT NULL, status CHAR(10));
CREATE TABLE t_order_1( order_id INT NOT NULL, user_id INT NOT NULL, status CHAR(10));
CREATE TABLE t_order_item_0( order_id INT NOT NULL, user_id INT NOT NULL, status CHAR(10));
CREATE TABLE t_order_item_1( order_id INT NOT NULL, user_id INT NOT NULL, status CHAR(10));
EOSQL
fi
编写 dockerfile
FROM 10.1.11.71/onlineshop/postgres:9.4
ADD init-user-db.sh /docker-entrypoint-initdb.d/init-user-db.sh
制作镜像
docker build -t postfres:9.4.1
5、启动镜像
docker run -id -d -e POSTGRES_PASSWORD=123456 -p 5432:5432 postgres:9.4.2
docker run -it -d -p 3307:3307 -v /root/sharding-proxy/config-sharding.yaml:/opt/sharding-proxy/conf/config-sharding.yaml sharding-proxy:v1
6、连接测试
连接 pgsql 查看默认库和表是否创建完成
连接 sharding proxy,nacicat 暂时连不上 不知道为什么,用 pgsql cli 连是可以的
psql -U sharding_db -h postgres -p 3307
插入数据测试
在 sharding proxy 容器日志里查看到数据具体插入到哪个库哪个表
分别到 ds0 ds1 查数据看看
在 sharding proxy 里直接查询
Amazon Aurora 的读写能力扩展之 ShardingSphere-Proxy 篇
文章来源于亚马逊 AWS 官方博客
马丽丽
亚马逊云科技数据库解决方案架构师,十余年数据库行业经验,先后涉猎 NoSQL 数据库 Hadoop/Hive、企业级数据库 DB2、分布式数仓 Greenplum/Apache HAWQ 以及亚马逊云原生数据库的开发和研究。
1. 前言
Amazon Aurora 是亚马逊云科技自研的一项关系数据库服务,它在提供和开源数据库 MySQL、PostgreSQL 的完好兼容性同时,也能够提供和商业数据库媲美的性能和可用性。性能方面,Aurora MySQL 能够支持到与开源标准 MySQL 同等配置下五倍的吞吐量,Aurora PostgreSQL 能够支持与开源标准 PostgreSQL 同等配置下三倍的吞吐量的提升。在扩展性的角度,Aurora 在存储与计算、横向与纵向方面都进行了功能的增强和创新。Aurora 支持多达 128TB 的存储容量,而且支持 10GB 为单位的存储层动态收缩。计算方面,Aurora 提供多个读副本的可扩展性配置支持一个区域内多达 15 个读副本的扩展,提供多主的架构来支持同一个区域内 4 个写节点的扩展,提供 Serverless 无服务器化的架构实例级别的秒级纵向扩展,提供全球数据库来实现数据库的低延迟跨区域扩展。
随着用户数据量的增长,Aurora 已经提供了很好的扩展性,那是否可以进一步处理更多的数据量、支持更多的并发访问呢?您可以考虑利用分库分表的方式,来支持底层多个 Aurora 集群的配置。基于此,包含这篇博客在内的系列博客会进行相应的介绍,旨在为您进行分库分表时选择使用代理或者 JDBC 提供参考。
本篇博客会聚焦如何使用 ShardingSphere-Proxy,一个开源的分库分表中间件工具,来进行数据库集群的构建,会涵盖分库分表、读写分离、动态配置等方面。
2. ShardingSphere-Proxy 介绍
作为中间件,ShardingSphere-Proxy 的定位是透明化的数据库代理端。它采用 Apache2.0 协议,持续迭代版本,最新版本为 5.1.0,目前支持 MySQL 和 PostgreSQL 版本。它对应用程序透明,兼容 MySQL/PostgreSQL 协议的客户端。MySQL 命令行 mysql,MySQL workbench 等都可以直接访问 ShardingSphere-Proxy。
ShardingSphere-Proxy 下层可以连接不同的数据库,这些数据库可以是同构也可以是异构的。用户可以有两种方式指定底层数据库的分库分表或者读写分离规则:1)根据 yaml 配置文件静态指定;2)利用 ShardingSphere 提供的增强性的 DistSQL 语言来指定。因为 DistSQL 支持动态创建规则不需要重启 Proxy 本身,它成为 ShardingSphere-Proxy 未来发展的重点。
作为数据库代理,是否能够提供连接池增强用户并发访问的连接处理是需要考量的一方面,ShardingSphere-Proxy 在添加数据源并进行初始化时,会支持为每个数据库配置一个 Hikari 连接池。Hikari 是业界广泛使用的连接池,对性能损耗较小,而且被 SpringBoot 采用为缺省连接池。ShardingSphere-Proxy 的连接池可以支持用户配置最大连接数、最大空闲时间以及缓存相关的信息等。除 Hikari 连接池外,ShardingSphere-Proxy 也支持其它连接池的配置。
和现有 SQL 的语法兼容性也是用户衡量数据库代理的关键因素,因为这涉及到是否更改应用代码。以 MySQL 为例,ShardingSphere 支持大部分的 MySQL 语法,但也有少量不支持的语法,比如 optimize 表、资源组的管理、用户的创建和 GRANT 权限管理等。具体可以查阅 ShardingSphere 的最新文档
(https://shardingsphere.apache...)。
下面会分享我对 ShardingSphere-Proxy 连接 Aurora 的几个维度的实验测试:1)分库分表;2)动态扩展;3)读写分离;4)多表 join;5)故障恢复。
3.环境构建
3.1 Aurora 集群搭建
首先根据 Aurora 集群创建指南(https://docs.aws.amazon.com/z...)创建三套 Aurora MySQL 集群,机型为 db.r5.2xlarge,每套集群有一个写节点一个读节点。
3.2 ShardingSphere-Proxy 搭建
在与 Aurora 相同的可用区下启动一台 EC2 节点,机型为 r5.8xlarge,然后在上面安装 ShardingSphere-Proxy。
3.2.1 下载安装包
直接下载二进制安装包,进行解压。下载最新版本 5.1.0,它对 DistSQL 支持较好。
wget https://dlcdn.apache.org/shardingsphere/5.1.0/apache-shardingsphere-5.1.0-shardingsphere-proxy-bin.tar.gz
tar -xvf apache-shardingsphere-5.1.0-shardingsphere-proxy-bin.tar.gz
ShardingSphere-Proxy 自带的库里包含对 PostgreSQL 的 JDBC driver,但不包含 MySQL 的 driver。因为创建的集群是 MySQL,需要将 MySQL 的 JDBC driver 拷贝到 lib 目录。
wget https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.47/mysql-connector-java-5.1.47.jar
cp mysql-connector-java-5.1.47.jar apache-shardingsphere-5.1.0-shardingsphere-proxy-bin/lib/
3.2.2 配置 Proxy 的服务端
在 ShardingSphere-Proxy 的根目录下,有个配置文件目录为 conf,里面有一个文件是 server.yaml,用来配置 ShardingSphere-Proxy 自己作为代理对外提供服务的信息以及元信息存放等。下面是一个配置示例,里面配置了用户权限信息,特定属性信息,以及元信息以集群模式存放在 zookeeper 里。
rules:
- !AUTHORITY
users: //访问Proxy的用户名和密码信息
- root@%:root
- sharding@:sharding
provider: //控制用户对schema的登陆权限
type: ALL_PRIVILEGES_PERMITTED
- !TRANSACTION //事务类型配置,支持本地事务、XA两阶段事务、BASE柔性事务
defaultType: XA
providerType: Atomikos
props: //特定属性配置
max-connections-size-per-query: 1
proxy-hint-enabled: true //为强制路由使用,默认值为false
mode: //元信息存放的配置,shardingsphereProxy支持三种模式:内存、单机和集群
type: Cluster
repository:
type: ZooKeeper //可以设置为zookeeper、etcd等
props:
namespace: shardingproxy
server-lists: localhost:2181
retryIntervalMilliseconds: 500
timeToLiveSeconds: 60
maxRetries: 3
operationTimeoutMilliseconds: 500
overwrite: false
3.3启动 Proxy
直接在 ShardingSphere-Proxy 根目录下的 bin 对应着启动和停止脚本。运行时的日志在目录 logs 下。启动 Proxy:
`bin/start.sh
bin/stop.sh`
3.4验证连接
如无特殊配置,ShardingSphere-Proxy 默认使用 3307 端口。使用 3.2.2 中配置的用户名和密码登录 Proxy。在 EC2 上运行 MySQL 命令行工具进行连接,连接成功。注意这里没有任何数据库,因为我们没有使用 YAML 配置文件预先配置数据源。
[ec2-user@ip-111-22-3-123 bin]$ mysql -h 127.0.0.1 -uroot --port 3307 -proot
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.7.22-ShardingSphere-Proxy 5.1.0
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type ''help;'' or ''\h'' for help. Type ''\c'' to clear the current input statement.
MySQL [(none)]> show databases;
Empty set (0.01 sec)
4.功能测试
4.1
DistSQL 创建分片规则和数据分片测试
本节来验证 ShardingSphere 的基本的分库分表能力。ShardingSphere-Proxy 支持两种方式创建分片规则和读写分离规则,YAML 和 DistSQL。DistSQL 扩展了 SQL 语法,可以支持在线创建数据源、创建和更改建表规则,较为灵活,本文只介绍 DistSQL 的用例。
4.1.1 创建数据库
连接到 ShardingSphere-Proxy,去创建数据库,作为逻辑的分布式数据库。
MySQL [(none)]> create database distsql_sharding_db;
Query OK, 0 rows affected (0.90 sec)
在各个 Aurora 集群上创建数据库,作为数据库源进行连接。其中,rshard1,rshard2,rshard3 是我自己定义的连接 Aurora 数据库的 alias。
alias rshard1=’mysql -h $dbname -u$username -p$password’
[ec2-user@ ip-111-22-3-123 bin]$ rshard1 -e "create database dist_ds";
[ec2-user@ ip-111-22-3-123 bin]$ rshard2 -e "create database dist_ds;"
[ec2-user@ ip-111-22-3-123 bin]$ rshard3 -e "create database dist_ds;"
4.1.2 创建数据源
在 ShadingSphere-Proxy 中运行下面 DistSQL 语句创建 3 个数据源,分别指向 3 个不同 Aurora 集群。
MySQL [distsql_sharding_db]> add resource ds_0(url="jdbc:mysql://aurora-2-07-7-shard1.cluster-12345678.us-east-1.rds.amazonaws.com:3306/dist_ds?serverTimezone=UTC&useSSL=false",user=admin,password=12345678);
Query OK, 0 rows affected (0.03 sec)
MySQL [distsql_sharding_db]> add resource ds_1(url="jdbc:mysql://aurora-2-07-7-shard2.cluster-12345678.us-east-1.rds.amazonaws.com:3306/dist_ds?serverTimezone=UTC&useSSL=false",user=admin,password=12345678);
Query OK, 0 rows affected (0.06 sec)
MySQL [distsql_sharding_db]> add resource ds_2(url="jdbc:mysql://aurora-2-07-7-shard3.cluster-12345678.us-east-1.rds.amazonaws.com:3306/dist_ds?serverTimezone=UTC&useSSL=false",user=admin,password=12345678);
Query OK, 0 rows affected (0.05 sec)
4.1.3 创建分片规则
这里指明 t_order 表的分片规则,注意分片规则的表名和后续要创建的表表名一致。具体规则为:对底层的 3 个数据源(Aurora 集群)按照 order_id 对表进行 hash 分片,分成 6 份。另外,对 order_id 采用值自动生成的策略,采用策略为 snowflake 算法。ShardingSphere 支持两种分布式主键生成策略:UUID 和雪花算法 SNOWFLAKE。使用雪花算法生成的主键,二进制表示形式包含 4 部分,从高位到低位分表为:1bit 符号位、41bit 时间戳位、10bit 工作进程位以及 12bit 序列号位。在 ShardingSphere-Proxy 中运行下面 DistSQL 语句建立分片规则:
MySQL [distsql_sharding_db]> CREATE SHARDING TABLE RULE t_order(
→ RESOURCES(ds_0,ds_1, ds_2),
→ SHARDING_COLUMN=order_id,
→ TYPE(NAME=hash_mod,PROPERTIES("sharding-count"=6)),
→ KEY_GENERATE_STRATEGY(COLUMN=order_id,TYPE(NAME=snowflake))
→ );
Query OK, 0 rows affected (0.02 sec)
4.1.4 建表
建表语句和普通 MySQL 建表语句一致。在 ShardingSphere-Proxy 中运行下面语句建表:
MySQL [distsql_sharding_db]> CREATE TABLE `t_order` ( `order_id` bigint NOT NULL, `user_id` int NOT NULL, `status` varchar(45) DEFAULT NULL, PRIMARY KEY (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
-> ;
Query OK, 0 rows affected (0.22 sec)
在 ShardingSphere-Proxy 上查看表的状态。
MySQL [distsql_sharding_db]> show tables;
+-------------------------------+------------+
| Tables_in_distsql_sharding_db | Table_type |
+-------------------------------+------------+
| t_order | BASE TABLE |
+-------------------------------+------------+
1 row in set (0.00 sec)
分别连接到 3 个 Aurora 集群上查看表是否自动创建。可以看到每个底层数据库集群上都创建了两张表,一共是 6 张表。而且表名是以“t_oder_”数字排序的。
[ec2-user@ ip-111-22-3-123 bin]$ rshard1 -Ddist_ds -e "show tables;"
+-------------------+
| Tables_in_dist_ds |
+-------------------+
| t_order_0 |
| t_order_3 |
+-------------------+
[ec2-user@ ip-111-22-3-123 bin ]$ rshard2 -Ddist_ds -e "show tables;"
+-------------------+
| Tables_in_dist_ds |
+-------------------+
| t_order_1 |
| t_order_4 |
+-------------------+
[ec2-user@ ip-111-22-3-123 bin]$ rshard3 -Ddist_ds -e "show tables;"
+-------------------+
| Tables_in_dist_ds |
+-------------------+
| t_order_2 |
| t_order_5 |
+-------------------+
4.1.5 插入和查找数据
在 ShardingSphere-Proxy 中插入并查找数据,数据可以正常插入和查找成功。在 ShardingSphere-Proxy 中运行:
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (1, ''ok'');
insert into t_order(user_id, status) values (2, ''abc'');
Query OK, 1 row affected (0.01 sec)
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (2, ''abc'');
insert into t_order(user_id, status) values (3, ''abc'');
Query OK, 1 row affected (0.00 sec)
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (3, ''abc'');
insert into t_order(user_id, status) values (4, ''abc'');
Query OK, 1 row affected (0.01 sec)
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (4, ''abc'');
insert into t_order(user_id, status) values (5, ''abc'');
Query OK, 1 row affected (0.00 sec)
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (5, ''abc'');
insert into t_order(user_id, status) values (6, ''abc'');
Query OK, 1 row affected (0.01 sec)
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (6, ''abc'');
insert into t_order(user_id, status) values (7, ''abc'');
Query OK, 1 row affected (0.00 sec)
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (7, ''abc'');
insert into t_order(user_id, status) values (8, ''abc'');
Query OK, 1 row affected (0.01 sec)
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (8, ''abc'');
Query OK, 1 row affected (0.00 sec)
MySQL [distsql_sharding_db]> insert into t_order(user_id, status) values (9, ''abc'');
Query OK, 1 row affected (0.00 sec)
MySQL [distsql_sharding_db]> select * from t_order;
+--------------------+---------+--------+
| order_id | user_id | status |
+--------------------+---------+--------+
| 708700161915748353 | 2 | abc |
| 708700161995440128 | 5 | abc |
| 708700169725542400 | 9 | abc |
| 708700161877999616 | 1 | ok |
| 708700161936719872 | 3 | abc |
| 708700162041577472 | 7 | abc |
| 708700161970274305 | 4 | abc |
| 708700162016411649 | 6 | abc |
| 708700162058354689 | 8 | abc |
+--------------------+---------+--------+
9 rows in set (0.01 sec)
去各个 Aurora 集群中查找子表插入的数据,可以看到在 Proxy 插入的 9 条记录被打散到底层的 6 张表中。因为 order_id 为 snowflake 算法生成而数据量比较小,这里的数据并不均匀。
[ec2-user@ip-111-22-3-123 bin]$ rshard1 -Ddist_ds -e "select * from t_order_0;"
[ec2-user@ip-111-22-3-123 bin]$ rshard1 -Ddist_ds -e "select * from t_order_3;"
+--------------------+---------+--------+
| order_id | user_id | status |
+--------------------+---------+--------+
| 708700161915748353 | 2 | abc |
+--------------------+---------+--------+
[ec2-user@ip-111-22-3-123 bin]$ rshard2 -Ddist_ds -e "select * from t_order_1;"
[ec2-user@ip-111-22-3-123 bin]$ rshard2 -Ddist_ds -e "select * from t_order_4;"
+--------------------+---------+--------+
| order_id | user_id | status |
+--------------------+---------+--------+
| 708700161995440128 | 5 | abc |
| 708700169725542400 | 9 | abc |
+--------------------+---------+--------+
[ec2-user@111-22-3-123 bin]$ rshard3 -Ddist_ds -e "select * from t_order_2;"
+--------------------+---------+--------+
| order_id | user_id | status |
+--------------------+---------+--------+
| 708700161877999616 | 1 | ok |
| 708700161936719872 | 3 | abc |
| 708700162041577472 | 7 | abc |
+--------------------+---------+--------+
[ec2-user@ip-111-22-3-123 bin]$ rshard3 -Ddist_ds -e "select * from t_order_5;"
+--------------------+---------+--------+
| order_id | user_id | status |
+--------------------+---------+--------+
| 708700161970274305 | 4 | abc |
| 708700162016411649 | 6 | abc |
| 708700162058354689 | 8 | abc |
+--------------------+---------+--------+</code></pre></div>
上述实验验证了 ShardingSphere-Proxy 具有创建逻辑库、连接数据源、创建分片规则、创建逻辑表时会自动在底层数据库上创建子表、能够执行查询的分发以及聚合能力。
4.2动态伸缩验证(在线扩展分片)
本节来验证 ShardingSphere-Proxy 是否具有动态更改表的分片规则的能力。
ShardingSphere-Proxy 提供在线更改分片规则的能力,但是如果子表已经按照之前的规则创建成功,则不会有新的子表随着分片数目的增多被创建出来,也不会有原来的子表随着分片数目的减少而被删除。所以需要手动在底层分片数据库上创建表名和迁移数据。
将 4.1 节里的表的分片数从 6 调高到 9,修改分片规则本身能够成功,但是后续查找会出错,因为没有新的子表创建出来。在 ShardingSphere-Proxy 上运行下面 DistSQL:
MySQL [distsql_sharding_db]> alter SHARDING TABLE RULE t_order(
-> RESOURCES(ds_0,ds_1, ds_2),
-> SHARDING_COLUMN=order_id,
-> TYPE(NAME=hash_mod,PROPERTIES("sharding-count"=9)),
-> KEY_GENERATE_STRATEGY(COLUMN=order_id,TYPE(NAME=snowflake))
-> );
Query OK, 0 rows affected (0.01 sec)
MySQL [distsql_sharding_db]> select * from t_order;
ERROR 1146 (42S02): Table ''dist_ds.t_order_6'' doesn''t exist
如果此时在子集群上分别创建好对应的子表,再在 ShardingSphere-Proxy 上查找就不会再出错。连接到 3 个 Aurora 集群,手动创建子表。
[ec2-user@ip-111-22-3-123 bin]$ rshard1 -Ddist_ds -e "create table t_order_6(order_id bigint not null, user_id int not null, status varchar(45) default null, primary key(order_id)) engine=innodb default charset=utf8mb4; "
[ec2-user@ ip-111-22-3-123 bin]$ rshard2 -Ddist_ds -e "create table t_order_7(order_id bigint not null, user_id int not null, status varchar(45) default null, primary key(order_id)) engine=innodb default charset=utf8mb4; "
[ec2-user@ip-111-22-3-123 bin]$ rshard3 -Ddist_ds -e "create table t_order_8(order_id bigint not null, user_id int not null, status varchar(45) default null, primary key(order_id)) engine=innodb default charset=utf8mb4; "
Proxy 查找整个逻辑表不再报错。在 ShardingSphere-Proxy 上运行下面 SQL:
MySQL [distsql_sharding_db]> select * from t_order;
+--------------------+---------+--------+
| order_id | user_id | status |
+--------------------+---------+--------+
| 708700161915748353 | 2 | abc |
| 708700161995440128 | 5 | abc |
| 708700169725542400 | 9 | abc |
| 708700161877999616 | 1 | ok |
| 708700161936719872 | 3 | abc |
| 708700162041577472 | 7 | abc |
| 708700161970274305 | 4 | abc |
| 708700162016411649 | 6 | abc |
| 708700162058354689 | 8 | abc |
+--------------------+---------+--------+
9 rows in set (0.01 sec)
如果有新的数据插入,会按照新的分片规则进行到子表的映射。在 ShardingSphere-Proxy 上查看 SQL 语句的查询计划:
MySQL [distsql_sharding_db]> preview insert into t_order values(7, 100, ''new'');
+------------------+---------------------------------------------+
| data_source_name | sql |
+------------------+---------------------------------------------+
| ds_1 | insert into t_order_7 values(7, 100, ''new'') |
+------------------+---------------------------------------------+
1 row in set (0.00 sec)
MySQL [distsql_sharding_db]> insert into t_order values(7, 100, ''new'');
Query OK, 1 row affected (0.00 sec)
登录到 Aurora 子集群上查看子表,可以看到数据已经成功插入。
[ec2-user@ip-111-22-3-123 bin]$ rshard2 -Ddist_ds -e "select * from t_order_7;"
+----------+---------+--------+
| order_id | user_id | status |
+----------+---------+--------+
| 7 | 100 | new |
+----------+---------+--------+
再来看下在线减少分片的情况。如果将分片数目调小,比如调到 3,表里的已有数据不会被迁移,查找整张表时只能拿到部分数据。在 ShardingSphere-Proxy 上运行下面 DistSQL 和 SQL 语句:
MySQL [distsql_sharding_db]> alter SHARDING TABLE RULE t_order(
-> RESOURCES(ds_0,ds_1, ds_2),
-> SHARDING_COLUMN=order_id,
-> TYPE(NAME=hash_mod,PROPERTIES("sharding-count"=3)),
-> KEY_GENERATE_STRATEGY(COLUMN=order_id,TYPE(NAME=snowflake))
-> );
Query OK, 0 rows affected (0.02 sec)
MySQL [distsql_sharding_db]> select * from t_order;
+--------------------+---------+--------+
| order_id | user_id | status |
+--------------------+---------+--------+
| 708700161877999616 | 1 | ok |
| 708700161936719872 | 3 | abc |
| 708700162041577472 | 7 | abc |
+--------------------+---------+--------+
3 rows in set (0.00 sec)
经过上面验证,我们的结论是 ShardingSphere-Proxy 的分片规则是可以在线更改的,但子表的创建和数据的重新分布需要手动去完成。
4.3绑定表和广播表的测试
本节来验证 ShardingSphere-Proxy 对于多表 join 的支持。尽管 OLTP 的数据库中的操作通常较为简单,但也有可能会涉及到多表 join 的情况。ShardingSphere-Proxy 针对多表 join 的优化有支持绑定表和广播表。如果两张表是绑定表而且 join 时采用的是 shard key,可以进行两张表的 join。广播表通过把小表复制到各个节点,可以实现大表和小表的快速 join。
4.3.1 绑定表
ShardingSphere-Proxy 的绑定表可以通过 DistSQL 里的 CREATE SHARDING BINDING TABLE RULES 来绑定两张表。这里以 4.1 节中提到的 t_order 表和新创建的一张表 t_order_item 为例进行展开。
连接到 ShardingSphere-Proxy 上运行下面 DistSQL 和 SQL 语句。
MySQL [distsql_sharding_db]> CREATE SHARDING TABLE RULE t_order_item(
-> RESOURCES(ds_0,ds_1, ds_2),
-> SHARDING_COLUMN=order_id,
-> TYPE(NAME=hash_mod,PROPERTIES("sharding-count"=6)));
Query OK, 0 rows affected (0.04 sec)
MySQL [distsql_sharding_db]> CREATE TABLE `t_order_item` ( `order_id` bigint NOT NULL, `item_id` int NOT NULL, `name` varchar(45) DEFAULT NULL, PRIMARY KEY (`item_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
Query OK, 0 rows affected (0.08 sec)
创建了 binding rule 以后,查看 join 计划,我们看到 join 下推到对应子表和子表的 join 上。在 ShardingSphere-Proxy 上运行:
MySQL [distsql_sharding_db]> CREATE SHARDING BINDING TABLE RULES (t_order,t_order_item);
Query OK, 0 rows affected (0.04 sec)
MySQL [distsql_sharding_db]> preview select * from t_order, t_order_item where t_order.order_id=t_order_item.order_id;
+------------------+------------------------------------------------------------------------------------------+
| data_source_name | sql |
+------------------+------------------------------------------------------------------------------------------+
| ds_0 | select * from t_order_0, t_order_item_0 where t_order_0.order_id=t_order_item_0.order_id |
| ds_0 | select * from t_order_3, t_order_item_3 where t_order_3.order_id=t_order_item_3.order_id |
| ds_1 | select * from t_order_1, t_order_item_1 where t_order_1.order_id=t_order_item_1.order_id |
| ds_1 | select * from t_order_4, t_order_item_4 where t_order_4.order_id=t_order_item_4.order_id |
| ds_2 | select * from t_order_2, t_order_item_2 where t_order_2.order_id=t_order_item_2.order_id |
| ds_2 | select * from t_order_5, t_order_item_5 where t_order_5.order_id=t_order_item_5.order_id |
+------------------+------------------------------------------------------------------------------------------+
6 rows in set (0.01 sec)
4.3.2 广播表
广播表是指每张表在每个库里都有一个完整的备份,可以通过 CREATE SHARDING BROADCAST TABLE RULES 来指定。
MySQL [distsql_sharding_db]> CREATE SHARDING BROADCAST TABLE RULES (t_user);
Query OK, 0 rows affected (0.03 sec)
MySQL [distsql_sharding_db]> create table t_user (user_id int, name varchar(100));
Query OK, 0 rows affected (0.04 sec)
登录到各个 shard Aurora 集群查看创建的表。可以看到与分片表的子表名末尾有数字序号不同的是,广播表对应的每个库上的名字是相同的,就是逻辑表名本身。
[ec2-user@ip-111-22-3-123 bin]$ rshard1 -D dist_ds -e "show tables like ''%user%'';"
+----------------------------+
| Tables_in_dist_ds (%user%) |
+----------------------------+
| t_user |
+----------------------------+
[ec2-user@ip-111-22-3-123 bin]$ rshard2 -D dist_ds -e "show tables like ''%user%'';"
+----------------------------+
| Tables_in_dist_ds (%user%) |
+----------------------------+
| t_user |
+----------------------------+
[ec2-user@ip-111-22-3-123 bin]$ rshard3 -D dist_ds -e "show tables like ''%user%'';"
+----------------------------+
| Tables_in_dist_ds (%user%) |
+----------------------------+
| t_user |
+----------------------------+
在 ShardingSphere-Proxy 中运行广播表和其它表的 join,采用的是本地 join 的方式。
MySQL [distsql_sharding_db]> preview select * from t_order, t_user where t_order.user_id=t_user.user_id;
+------------------+------------------------------------------------------------------------+
| data_source_name | sql |
+------------------+------------------------------------------------------------------------+
| ds_0 | select * from t_order_0, t_user where t_order_0.user_id=t_user.user_id |
| ds_0 | select * from t_order_3, t_user where t_order_3.user_id=t_user.user_id |
| ds_1 | select * from t_order_1, t_user where t_order_1.user_id=t_user.user_id |
| ds_1 | select * from t_order_4, t_user where t_order_4.user_id=t_user.user_id |
| ds_2 | select * from t_order_2, t_user where t_order_2.user_id=t_user.user_id |
| ds_2 | select * from t_order_5, t_user where t_order_5.user_id=t_user.user_id |
+------------------+--------
上面实验验证了 ShardingSphere-Proxy 是可以支持两张绑定表的 join,以及广播表和分片表的 join 的。对于非绑定的两张分片表的 join,ShardingSphere-Proxy 有一个 Federation 的功能是在支持的,但还不是很成熟,建议后续持续关注。
4.4读写分离功能验证
本节来验证 ShardingSphere-Proxy 对于读写分离的支持。随着业务增长,写和读的负载分别在不同的数据库节点上能够有效提供整个数据库集群的处理能力。Aurora 通过读/写的 endpoint 可以满足用户写和强一致性读的需求,只读的 endpoint 可以满足用户非强一致性读的需求。Aurora 的读写延迟在毫秒级别,比 MySQL 基于 binlog 的逻辑复制要低得多,所以有很多负载是可以直接打到只读 endpoint 的。
ShardingSphere-Proxy 提供的读写分离的特性可以进一步可以封装 Aurora 的读/写端点和只读端点。用户可以直接连接到 Proxy 的端点,即可进行自动的读写分离。ShardingSphere-Proxy 对特殊情况的处理逻辑是:1)同一线程且同一数据库连接内,如果有写入操作,则后续的读操作均从主库读取;2)可以通过 Hint 的机制强制把读请求发到写节点(主库)。下面会以 Aurora3 个集群中的第一个集群来验证 ShardingSphere-Proxy 读写分离的能力。
4.4.1 查看 Aurora 集群读/写端点和只读端点
Aurora 集群有两个端点,写的端点和读的端点。
4.4.2 在 Aurora 集群中创建数据库
连接到 Aurora 集群中运行:
[ec2-user@ip-111-22-3-123 ~]$ rdbw -e "create database wr_ds;"
4.4.3 数据源配置
在 ShardingSphere-Proxy 上创建数据源,写的数据源指向 Aurora 的读写 endpoint,读的数据源指向 Aurora 的只读 endpoint。注意:对域名的情况,ShardingSphere-Proxy 只支持通过 url 的方式创建数据源,尚未支持通过 HOST、Port 的方式。连接到 ShardingSphere-Proxy 上创建逻辑数据库 distsql_rwsplit_db 并在改数据库中添加数据源:
MySQL [(none)]> create database distsql_rwsplit_db;
Query OK, 0 rows affected (0.02 sec)
MySQL [(none)]> use distsql_rwsplit_db;
Database changed
MySQL [distsql_rwsplit_db]> add resource write_ds(url="jdbc:mysql://aurora-2-07-7-shard1.cluster-12345678.us-east-1.rds.amazonaws.com:3306/wr_ds?serverTimezone=UTC&useSSL=false",user=admin,password=12345678), read_ds(url="jdbc:mysql://aurora-2-07-7-shard1.cluster-ro-12345678.us-east-1.rds.amazonaws.com:3306/wr_ds?serverTimezone=UTC&useSSL=false",user=admin,password=12345678);
Query OK, 0 rows affected (0.08 sec)
4.4.4 读写分离规则配置
创建读写分离规则,写请求发到写的数据源,读请求发到读的数据源。与分库分表规则要求 RULE 后面必须是表名不同的是,这里的 RULE 后面跟的是数据源的名字,适用于在这个数据库里创建的所有的表。在 ShardingSphere-Proxy 上运行下面 DistSQL 语句:
MySQL [distsql_ rwsplit_db]> CREATE READWRITE_SPLITTING RULE wr_ds (
-> WRITE_RESOURCE=write_ds,
-> READ_RESOURCES(read_ds),
-> TYPE(NAME=random)
-> );
Query OK, 0 rows affected (0.36 sec)
4.4.5 建表
创建一张普通表,建表语句和 MySQL 建表语句一致。在 ShardingSphere-Proxy 上运行下面 SQL 语句:
MySQL [distsql_ rwsplit_db]> create table wr_table (a int, b int, c varchar(20));
Query OK, 0 rows affected (0.17 sec)
4.4.6 检查读写分离是否实现
在 ShardingSphere-Proxy 上运行下面语句查看查询计划,查看语句是发送到底层哪个数据源。可以看到:写请求发送到写节点,读请求会发送到读写点。
MySQL [distsql_rwsplit_db]> preview insert into wr_table values(1,1,''ab'');
+------------------+---------------------------------------+
| data_source_name | sql |
+------------------+---------------------------------------+
| write_ds | insert into wr_table values(1,1,''ab'') |
+------------------+---------------------------------------+
1 row in set (0.10 sec)
MySQL [distsql_rwsplit_db]> preview select * from wr_table;
+------------------+------------------------+
| data_source_name | sql |
+------------------+------------------------+
| read_ds | select * from wr_table |
+------------------+------------------------+
1 row in set (0.02 sec)
运行一个脚本来多次操作,再去 Aurora 集群指标监控中去验证。该脚本是一个循环,运行 1000 次,每次会插入一条记录,并查找表的记录总条数。
[ec2-user@ip-111-22-3-123 shardingproxy]$ cat testReadWrite.sh
#!/bin/bash
n=1
while [ $n -le 1000 ]
do
mysql -h 127.0.0.1 -uroot --port 3307 -proot -Ddistsql_rwsplit_db -e "insert into wr_table values($n,$n,''ok'');"
mysql -h 127.0.0.1 -uroot --port 3307 -proot -Ddistsql_rwsplit_db -e "select count(*) from wr_table;"
let n++
done
查看 Aurora 集群的写节点和读节点的读写延迟,可以看到写延迟只在写节点上发生,读延迟只在读节点上发生。说明读写分离规则生效。
尽管 Aurora 的写和读节点之间的复制延迟很低在毫秒级别,但某些应用还是会有强一致性的需求,即要求写后立刻可以读。这时候,可以采用强制将读请求发送到写节点的方式。ShardingSphere-Proxy 通过 hint 的方式来支持。首先需要在前面提到的 conf/server.yaml 里添加一个属性 proxy-hint-enabled: true。然后在连接中显式设置 readwrite_splitting hint source 值为 write 来开启强制路由到写节点通过设置值为 auto 或者 clear hint 可以采用默认的规则。readwrite_splitting hint source 可以在 session 级别生效。
在 ShardingSphere-Proxy 上依次运行下面语句。可以看到默认的读请求是发送到读节点,将 readwrite_splitting hint source 设置为 write 以后,会发送到写节点,再设成 auto,可以发回至读写点。
MySQL [distsql_rwsplit_db]> preview select count(*) from wr_table;
+------------------+-------------------------------+
| data_source_name | sql |
+------------------+-------------------------------+
| read_ds | select count(*) from wr_table |
+------------------+-------------------------------+
1 row in set (0.01 sec)
MySQL [distsql_rwsplit_db]> set readwrite_splitting hint source = write;
Query OK, 0 rows affected (0.00 sec)
MySQL [distsql_rwsplit_db]> preview select count(*) from wr_table;
+------------------+-------------------------------+
| data_source_name | sql |
+------------------+-------------------------------+
| write_ds | select count(*) from wr_table |
+------------------+-------------------------------+
1 row in set (0.01 sec)
MySQL [distsql_rwsplit_db]> set readwrite_splitting hint source = auto;
Query OK, 0 rows affected (0.00 sec)
MySQL [distsql_rwsplit_db]> preview select count(*) from wr_table;
+------------------+-------------------------------+
| data_source_name | sql |
+------------------+-------------------------------+
| read_ds | select count(*) from wr_table |
+------------------+-------------------------------+
1 row in set (0.00 sec)
另外不使用 YAML 文件更改的方式是直接在 DistSQL 里先后设置两个变量 proxy_hint_enabled 和 readwrite_splitting hint source。
MySQL [distsql_rwsplit_db]> set variable proxy_hint_enabled=true;
Query OK, 0 rows affected (0.01 sec)
MySQL [distsql_rwsplit_db]> set readwrite_splitting hint source = write;
Query OK, 0 rows affected (0.01 sec)
MySQL [distsql_rwsplit_db]> preview select * from wr_table;
+------------------+------------------------+
| data_source_name | sql |
+------------------+------------------------+
| write_ds | select * from wr_table |
+------------------+------------------------+
1 row in set (0.00 sec)
以上实验验证了 ShardingSphere-Proxy 有良好的读写分离的能力。它验证了底下连接单个 Aurora 集群进行读写分离的场景。如果既需要分库分表又需要读写分离,ShardingSphere-Proxy 也是支持的。比如先分到 3 个 Aurora 集群,然后每个集群需要提供读写分离的能力,我们可以直接将读写分离规则后面定义的数据源名称(4.4.4 里的 wr_ds)放在分库分表规则对每张表指定的数据源里(4.1.3 里的 ds_0,ds_1,ds_2)。
4.5故障恢复验证
本节来验证 ShardingSphere-Proxy 对于 Aurora 集群故障切换的感知能力。在 Aurora 集群发生主备切换时,如果 Proxy 能够动态检测到主备切换并连接到新的主数据库是比较理想的。本节实验仍然是验证第一个 Aurora 集群。
测试脚本如下,它会持续连接到写节点并发送 update 请求,每次请求间隔 1 秒钟。
[ec2-user@ip-111-22-3-123 shardingproxy]$ cat testFailover.sh
#!/bin/bash
while true
do
mysql -h 127.0.0.1 -uroot --port 3307 -proot -Ddistsql_rwsplit_db -e "update wr_table set c=''failover'' where a = 1;"
now=$(date +"%T")
echo "update done: $now"
sleep 1
done
运行脚本,然后在 Aurora 集群的写节点上点击 Action->Failover。会启动 Aurora 写节点和读节点的自动切换。在切换过程中,整个集群的读/写 endpoint 和只读 endpoint 维持不变,只是底层映射的节点发生变化。
通过观测 Aurora 的 Event(事件),可以看到整个故障切换在 30 秒左右完成。
遗憾的是,应用程序直接连接 ShardingSphere-Proxy 也就是前面的运行脚本不能自动监测到底层的 IP 变化。运行脚本一直抛错:
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --read-only option so it cannot execute this statement
update done: 15:04:04
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --read-only option so it cannot execute this statement
update done: 15:04:05
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --read-only option so it cannot execute this statement
update done: 15:04:06
直接在 MySQL 命令行连接到 Proxy 也是会有一样的错误。
MySQL [distsql_rwsplit_db]> update wr_table set c="failover" where a =2;
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
分析原因在于 Aurora 发生故障切换的时候,读写 endpoint 和 IP 的映射会发生变化,而 ShardingSphere 的连接池在连接 Aurora 的时候,没有更新到新的 IP 上。我们可以采用下面的 workaround 可以使 ShardingSphere-Proxy 指向新的写节点,即重新创建数据源。尽管数据源本身定义没有发生变化,但是通过重建数据源 alter resource 的操作, ShardingSphere-Proxy 会重新拿取一遍 endpoint 到 IP 的映射,所以能够成功运行。
MySQL [distsql_rwsplit_db]> alter resource write_ds(url="jdbc:mysql://aurora-2-07-7-shard1.cluster-12345678.us-east-1.rds.amazonaws.com:3306/wr_ds?serverTimezone=UTC&useSSL=false",user=admin,password=12345678), read_ds(url="jdbc:mysql://aurora-2-07-7-shard1.cluster-ro-12345678.us-east-1.rds.amazonaws.com:3306/wr_ds?serverTimezone=UTC&useSSL=false",user=admin,password=12345678);
Query OK, 0 rows affected (0.05 sec)
MySQL [distsql_rwsplit_db]> update wr_table set c="failover" where a =2;
Query OK, 1 row affected (0.01 sec)
每次 Aurora 故障切换时,我们可以检测故障切换的 event,或者是在应用收到 read-only 报错时显式调用上面语句。为了降低对应用的影响,我们可以采用 Lambda 的方式将 failover 重置数据源的操作自动化。因为 Aurora 的 failover 的事件是可以被监测到的,我们可以写一个 Lambda 函数,在监测到 failover 成功以后,显示调用更改 resource 的操作。
总体的思路是:RDS 通过 Event Subscription 将 Event 的通知信息传递给 SNS topic,再由 SNS topic 传递给 Lambda 方法,然后在 Lambda 方法里显式连接 ShardingSphere-Proxy 调用 alter resource 的 DistSQL 语句。
具体步骤如下:
4.5.1 创建 SNS
按照 SNS 创建指南创建 SNS。打开 SNS 的 dashboard,点击创建 SNS topic,选择 Standard 标准类型。其它选择默认或者根据需要调整。
4.5.2 对要进行的 Aurora 集群创建 Event Subscription
在 RDS 的 Event Subscriptions 上,点击“Create Event Subscription”,在弹出的选项卡中选择 Target 为上一步骤创建的 SNS,Source type 选择为 Cluster,Cluster 里面选中我们需要关注的 Aurora 的集群,事件选择 Failover 事件。
4.5.3 创建 Lamdba 方法
因为 Lambda 要调用 VPC 里的 EC2 上部署的 ShardingProxy,应该给它绑定一个专门的 Role,这个 Role 有权限在 VPC 里执行 Lambda 方法: AWSLambdaVPCAccessExecutionRole 按照 IAM Role(https://docs.aws.amazon.com/z...)创建文档创建 Role 和 Policy,使 failoverlambda 的 role 有 AWSLambdaVPCAccessExecutionRole 的权限。
接下来按照 Lambda(https://docs.aws.amazon.com/z...)文档创建 Lambda 方法。
创建好 Lambda 方法以后,点击 Trigger,指明为 SNS,并指明在 4.5.1 里创建的 SNS topic。
4.5.4 编写 Lambda 方法
import os
import json
import pymysql
# connect to ShardingProxy to reset the data source
def resetDataSource():
db = pymysql.connect(host=''111.22.3.123'', user=''root'', password=''root'', port=3307, database=''distsql_rwsplit_db'')
cur = db.cursor()
SQL = "alter resource write_ds(url=\"jdbc:mysql://aurora-2-07-7-shard1.cluster-12345678.us-east-1.rds.amazonaws.com:3306/wr_ds?serverTimezone=UTC&useSSL=false\",user=admin,password=12345678), read_ds(url=\"jdbc:mysql://aurora-2-07-7-shard1.cluster-ro-12345678.us-east-1.rds.amazonaws.com:3306/wr_ds?serverTimezone=UTC&useSSL=false\",user=admin,password=12345678);"
print (SQL)
cur.execute(SQL)
result = cur.fetchall()
for x in result:
print(x)
db.close()
def lambda_handler(event, context):
wholeMessage = event[''Records''][0][''Sns''][''Message'']
print ("whole message" + wholeMessage)
wholeMessageInJson = json.loads(wholeMessage)
eventMessage = wholeMessageInJson[''Event Message'']
print ("event message: " + eventMessage)
isFailover = eventMessage.startswith(''Completed failover to DB instance'')
if isFailover == True:
print ("Failover completed! " + eventMessage)
resetDataSource()
return {
''statusCode'': 200,
''body'': Lambda Invocation Successful!''
}
Lambda 方法是 Python 语言书写的,在访问 ShardingSphere-Proxy 时以 MySQL 方式访问,所以需要引入 pymysql 的 lib 库。具体方法为:
1)在 Linux 上安装 pymysql,以 Amazon-Linux 虚拟机为例,会默认安装到目录 ./home/ec2-user/.local/lib/python3.7/site-packages/pymysql 下
2)将 pymysql 目录拷贝到临时目录 /tmp
3)写 Lambda 方法,存储到 lambda_function.py 文件中
4)打包 zip -r lambda.zip pymysql lambda_function.py
5) 在控制台通过 S3 或者本地上传。
4.5.5 设置 ShardingSphere-Proxy 所在 EC2 的 security group
因为 Lambda 要在 VPC 里访问 ShardingSphere-Proxy,而 ShardingSphere-Proxy 以 3307 端口运行,应该配置相应 secuity group,打开 3307 端口给同一个 VPC 内部的访问。依据安全组配置文档配置成的 security group 如下:
4.5.6 验证 failover
重复本小节开始的操作,运行 testFailover.sh,然后手动在 RDS console 页 failover Aurora 节点,会发现 testFailover.sh 持续稳定输出, 不会再出现 read-only 的错误。
update done: 13:44:44
…
update done: 13:44:47
update done: 13:45:00
update done: 13:45:01
…
update done: 13:45:17
去 cloudwatch 里查看 Lambda function 的日志,会发现 Lambda 被成功调用。
以上实验验证了 ShardingSphere-Proxy 对于 Aurora 集群故障切换的感知能力。尽管 ShardingSphere-Proxy 自己没有提供良好的匹配性,通过监测 Aurora 集群事件触发 Lamdba 方法来显式重置 ShardingSphere-Proxy 数据源的方式,我们可以实现 ShardingSphere-Proxy 与 Aurora 结合的故障切换能力。
5.结语
本篇文章通过数据库中间件 ShardingSphere-Proxy 拓展了 Aurora 的分库分表能力和读写分离的能力。
ShardingSphere-Proxy 内置连接池,对 MySQL 语法支持较强,在分库分表和读写分离上表现出色。它对多表 join 上,可以支持分片规则相同的表的 join,以及小表和大表的 join,基本能满足 OLTP 场景的需求。在动态分片上,ShardingSphere-Proxy 提供在线更改分片规则的能力,但需要用户在底层 Aurora 集群手动操作子表创建及数据迁移,需要一定工作量。故障切换维度,ShardingSphere-Proxy 与 Aurora 的融合不是很好,但是可以通过本文提供的 Aurora 故障切换 Event 调用 Lambda 方法来显式重置数据源的方式,实现 ShardingSphere-Proxy 对 Aurora 集群故障切换对感知。
总体而言,ShardingSphere-Proxy 这个中间件产品还是能与 Aurora 集群进行一个良好匹配,进一步提升 Aurora 集群的读写能力的。它有良好的文档,也是比较受关注的开源产品,建议读者在考虑 Aurora 分库分表实现时,评估下这个产品。后续我们会继续推出对其他中间件以及 JDBC 方面的拓展和研究系列博客。
如果大家对 Apache ShardingSphere 有任何疑问或建议,欢迎在 GitHub issue 列表提出,或可前往中文社区交流讨论。
GitHub issue:https://github.com/apache/shardingsphere/issues
贡献指南:https://shardingsphere.apache.org/community/cn/contribute/
中文社区:https://community.sphere-ex.com/
Apache ShardingSphere Proxy 分库分表小练习
因为我们之前在《Apache ShardingSphere Proxy 负载均衡小练习》里面已经对 1、2、3做了主从复制作为负载均衡练习,为了不影响之前的数据,我们增加4、5、6 库用来演示分表的功能,下面是 docker-compose.xml 的内容
version: ''3.7''
services:
mysql8_1:
image: "mysql:8.0.19"
container_name: mysql8_1
ports:
- "33080:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
mysql8_2:
image: "mysql:8.0.19"
container_name: mysql8_2
ports:
- "33081:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
mysql8_3:
image: "mysql:8.0.19"
container_name: mysql8_3
ports:
- "33082:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
mysql8_4:
image: "mysql:8.0.19"
container_name: mysql8_4
ports:
- "33083:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
mysql8_5:
image: "mysql:8.0.19"
container_name: mysql8_5
ports:
- "33084:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
mysql8_6:
image: "mysql:8.0.19"
container_name: mysql8_6
ports:
- "33085:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
我们先看一个纯分片,但不读写分离的配置
schemaName: master_slave_db
dataSources:
ds1:
url: jdbc:mysql://mysql8_1:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 12345678
connectionTimeoutMilliseconds: 2000
ds2:
url: jdbc:mysql://mysql8_2:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 12345678
readOnly: true
connectionTimeoutMilliseconds: 2000
ds3:
url: jdbc:mysql://mysql8_3:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 12345678
connectionTimeoutMilliseconds: 2000
ds4:
url: jdbc:mysql://mysql8_4:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 12345678
connectionTimeoutMilliseconds: 2000
ds5:
url: jdbc:mysql://mysql8_5:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 12345678
connectionTimeoutMilliseconds: 2000
ds6:
url: jdbc:mysql://mysql8_6:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 12345678
connectionTimeoutMilliseconds: 2000
masterSlaveRule:
name: ds_ms
masterDataSourceName: ds1
slaveDataSourceNames:
- ds2
- ds3
loadBalanceAlgorithmType: ROUND_ROBIN
shardingRule:
tables:
category:
actualDataNodes: ds${4..6}.category
user:
actualDataNodes: ds${4..6}.user
databaseStrategy:
inline:
shardingColumn: id
algorithmExpression: ds${id%3+4}
order:
actualDataNodes: ds${4..6}.order${0..1}
databaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds${user_id%3+4}
tableStrategy:
inline:
shardingColumn: id
algorithmExpression: order${id%2}
keyGenerator:
type: SNOWFLAKE
column: id
order_item:
actualDataNodes: ds${4..6}.order_item${0..1}
databaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds${user_id%3+4}
tableStrategy:
inline:
shardingColumn: id
algorithmExpression: order_item${order_id%2}
bindingTables:
- user,order,order_item
broadcastTables:
- category
defaultDataSourceName: ds4
defaultDatabaseStrategy:
none:
defaultTableStrategy:
none:
defaultKeyGenerator:
type: SNOWFLAKE
column: id
在我们配置好分片后,需要通过shardingProxy来执行下面的建表语句
create table category
(
id bigint not null,
name varchar(50) null,
constraint category_id_uindex
unique (id)
);
alter table category
add primary key (id);
create table user
(
id bigint null,
name varchar(60) null,
create_date timestamp default now() null
);
create unique index user_id_uindex
on user (id);
alter table user
add constraint user_pk
primary key (id);
create table `order`
(
id bigint null,
user_id bigint null,
fee decimal(8,2) null
);
create unique index order_id_uindex
on `order` (id);
alter table `order`
add constraint order_pk
primary key (id);
create table order_item
(
id bigint null,
order_id bigint null,
item_name varchar(50) null,
buy_num int null
);
create unique index order_item_id_uindex
on order_item (id);
alter table order_item
add constraint order_item_pk
primary key (id);
注意,我这里的主键用的是 bigint,是因为雪花算法算出来的数字比较大,int 存不下。我们插入几条user信息,因为我们默认已经指定了id是用 snowflake自动生成,所以insert 语句中就不用给id了,proxy会自动为id赋值。
通过上面的建表语句,mysql中实际建立的表有这些:
Tables_in_test |
---|
category |
order0 |
order1 |
order_item0 |
order_item1 |
user |
插入三个用户,看看分片的效果
insert into user(name) values (''user1'');
insert into user(name) values (''user2'');
insert into user(name) values (''user3'');
控制台输出的内容,显示出了有两条进入了ds4,一条进入了ds5,因为我们的id是用雪花算法生成的,数字并不是连续的,所以按照取模的方式,并不能完全保证均匀的分到不同的shard上:
[INFO ] 06:31:41.364 [ShardingSphere-Command-0] ShardingSphere-SQL - Rule Type: sharding
[INFO ] 06:31:41.365 [ShardingSphere-Command-0] ShardingSphere-SQL - Logic SQL: insert into user(name) values (''user1'')
[INFO ] 06:31:41.365 [ShardingSphere-Command-0] ShardingSphere-SQL - SQLStatement: InsertSQLStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.InsertStatement@27b96d8f, tablesContext=TablesContext(tables=[Table(name=user, alias=Optional.absent())], schema=Optional.absent())), columnNames=[name], insertValueContexts=[InsertValueContext(parametersCount=0, valueExpressions=[LiteralExpressionSegment(startIndex=31, stopIndex=37, literals=user1), DerivedLiteralExpressionSegment(super=LiteralExpressionSegment(startIndex=0, stopIndex=0, literals=458519221849030657))], parameters=[])])
[INFO ] 06:31:41.365 [ShardingSphere-Command-0] ShardingSphere-SQL - Actual SQL: ds4 ::: insert into user(name, id) values (''user1'', 458519221849030657)
[INFO ] 06:33:52.327 [ShardingSphere-Command-2] ShardingSphere-SQL - Rule Type: sharding
[INFO ] 06:33:52.327 [ShardingSphere-Command-2] ShardingSphere-SQL - Logic SQL: insert into user(name) values (''user2'')
[INFO ] 06:33:52.327 [ShardingSphere-Command-2] ShardingSphere-SQL - SQLStatement: InsertSQLStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.InsertStatement@28723307, tablesContext=TablesContext(tables=[Table(name=user, alias=Optional.absent())], schema=Optional.absent())), columnNames=[name], insertValueContexts=[InsertValueContext(parametersCount=0, valueExpressions=[LiteralExpressionSegment(startIndex=31, stopIndex=37, literals=user2), DerivedLiteralExpressionSegment(super=LiteralExpressionSegment(startIndex=0, stopIndex=0, literals=458519771147665408))], parameters=[])])
[INFO ] 06:33:52.327 [ShardingSphere-Command-2] ShardingSphere-SQL - Actual SQL: ds4 ::: insert into user(name, id) values (''user2'', 458519771147665408)
[INFO ] 06:34:30.208 [ShardingSphere-Command-3] ShardingSphere-SQL - Rule Type: sharding
[INFO ] 06:34:30.208 [ShardingSphere-Command-3] ShardingSphere-SQL - Logic SQL: insert into user(name) values (''user3'')
[INFO ] 06:34:30.208 [ShardingSphere-Command-3] ShardingSphere-SQL - SQLStatement: InsertSQLStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.InsertStatement@6bdfe200, tablesContext=TablesContext(tables=[Table(name=user, alias=Optional.absent())], schema=Optional.absent())), columnNames=[name], insertValueContexts=[InsertValueContext(parametersCount=0, valueExpressions=[LiteralExpressionSegment(startIndex=31, stopIndex=37, literals=user3), DerivedLiteralExpressionSegment(super=LiteralExpressionSegment(startIndex=0, stopIndex=0, literals=458519930032095233))], parameters=[])])
[INFO ] 06:34:30.209 [ShardingSphere-Command-3] ShardingSphere-SQL - Actual SQL: ds5 ::: insert into user(name, id) values (''user3'', 458519930032095233)
当我们用查询语句查询的时候会分别的查询三个库里面的user表:
mysql root@localhost:master_slave_db> select * from user;
+--------------------+--------+---------------------+
| id | name | create_date |
|--------------------+--------+---------------------|
| 458519221849030657 | user1 | 2020-04-19 06:31:41 |
| 458519771147665408 | user2 | 2020-04-19 06:33:52 |
| 458519930032095233 | user3 | 2020-04-19 06:34:30 |
+--------------------+--------+---------------------+
3 rows in set
Time: 0.019s
控制台日志:
[INFO ] 06:38:44.308 [ShardingSphere-Command-4] ShardingSphere-SQL - Rule Type: sharding
[INFO ] 06:38:44.308 [ShardingSphere-Command-4] ShardingSphere-SQL - Logic SQL: select * from user
[INFO ] 06:38:44.308 [ShardingSphere-Command-4] ShardingSphere-SQL - SQLStatement: SelectSQLStatementContext(super=CommonSQLStatementContext(sqlStatement=org.apache.shardingsphere.sql.parser.sql.statement.dml.SelectStatement@56721809, tablesContext=TablesContext(tables=[Table(name=user, alias=Optional.absent())], schema=Optional.absent())), projectionsContext=ProjectionsContext(startIndex=7, stopIndex=7, distinctRow=false, projections=[ShorthandProjection(owner=Optional.absent())], columnLabels=[id, name, create_date]), groupByContext=org.apache.shardingsphere.sql.parser.relation.segment.select.groupby.GroupByContext@761320d4, orderByContext=org.apache.shardingsphere.sql.parser.relation.segment.select.orderby.OrderByContext@7e256b1e, paginationContext=org.apache.shardingsphere.sql.parser.relation.segment.select.pagination.PaginationContext@14c8421a, containsSubquery=false)
[INFO ] 06:38:44.308 [ShardingSphere-Command-4] ShardingSphere-SQL - Actual SQL: ds4 ::: select * from user
[INFO ] 06:38:44.309 [ShardingSphere-Command-4] ShardingSphere-SQL - Actual SQL: ds5 ::: select * from user
[INFO ] 06:38:44.309 [ShardingSphere-Command-4] ShardingSphere-SQL - Actual SQL: ds6 ::: select * from user
常见问题
1、数据库分库分表的结构ShardingSphere会自动创建吗?
因为配置文件中并不定义数据表的结构,所以无法自动创建,创建符合分片规则的表是通过proxy来执行创建原始表的语句来实现的,proxy会解析创建表的语句给各个分片的ds上都创建对应的表。
Apache ShardingSphere Proxy 负载均衡小练习
我们在容器启动4个mysql实例,docker-compose.xml 内容如下:
version: ''3.7''
services:
mysql8_1:
image: "mysql:8.0.19"
container_name: mysql8_1
ports:
- "33080:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
mysql8_2:
image: "mysql:8.0.19"
container_name: mysql8_2
ports:
- "33081:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
mysql8_3:
image: "mysql:8.0.19"
container_name: mysql8_3
ports:
- "33082:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
mysql8_4:
image: "mysql:8.0.19"
container_name: mysql8_4
ports:
- "33083:3306"
environment:
MYSQL_ROOT_PASSWORD: 12345678
然后参照我《mysql5-主从同步配置》和 《mysql8多主一从配置》配置一下数据库的主从同步,完成后继续向下执行,如果你已经有了数据库集群切有主从同步,就不需要做上面的操作。
使用 docker 来启动一个 ShardingProxy
1、获取sharding-proxy的docker镜像
docker pull apache/sharding-proxy:4.0.1
2、配置文件在容器中的路径是/opt/sharding-proxy/conf
,所以启动时可以在将此路径映射到容器外部,便于修改配置。
docker run -d -v /${your_work_dir}/conf:/opt/sharding-proxy/conf -e PORT=3308 -p13308:3308 --name shardingproxy apache/sharding-proxy:latest
可以通过-e JVM_OPTS=""
环境变量指定JVM配置,通过映射/opt/sharding-proxy/ext-lib
地址到宿主机来方便添加扩展jar包
本机举例,我执行的命令就是
docker run --name shardingProxy -d -v /home/yangyan/conf/sharding-proxy:/opt/sharding-proxy/conf -v /home/yangyan/conf/sharding-proxy/ext-lib:/opt/sharding-proxy/ext-lib -e PORT=3308 -p13308:3308 apache/sharding-proxy:latest
容器启动后,查看日志,发现没有成功启动,缺少mysql的jdbc包,然后我们需要将jar复制到/opt/sharding-proxy/lib/
目录下,因为我看了一下这个服务的start.sh脚本,加入到 classpath的路径有lib,但没有ext-lib这个目录,我就放到了lib下,而且我专门试了移动到ext-lib下面是会找不到驱动的。
然后我们增加配置文件 server.yaml 到 shardingProxy 的 conf 目录下
authentication:
users:
root:
password: root
props:
executor.size: 16
sql.show: true
从简单开始,配置一个主从集群
增加 config-test.xml 到shardingProxy 的 conf 目录下
schemaName: master_slave_db
dataSources:
ds_master1:
url: jdbc:mysql://mysql8_1:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
username: root
password: 12345678
ds_slave1:
url: jdbc:mysql://mysql8_2:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
username: root
password: 12345678
ds_slave2:
url: jdbc:mysql://mysql8_3:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
username: root
password: 12345678
masterSlaveRule:
name: ds_ms
masterDataSourceName: ds_master1
slaveDataSourceNames:
- ds_slave1
- ds_slave2
loadBalanceAlgorithmType: ROUND_ROBIN
容器启动失败,我遇到了两个问题:
问题1:因为我用的 mysql jdbc jar 包是 java8 编译的,shardingProxy 容器里面java环境是1.7,所以无法成功加载mysql jdbc 驱动,所以需要在容器中替换为 java8 的环境。
这里可以讲本地下载的java8目录通过 docker cp 命令复制到容器中:
docker cp ~/Downloads/java-1.8.0-openjdk-amd64 08343b40ab39:/usr/lib/jvm/
对于 java 环境路径的处理,按正常来说应该是使用 update-java-alternatives命令比较方便,但是我这里一直无法运行成功,所以我就一条条的执行了创建链接
update-alternatives --install /usr/bin/idlj idlj /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/idlj 999
update-alternatives --install /usr/bin/wsimport wsimport /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/wsimport 999
update-alternatives --install /usr/bin/rmic rmic /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/rmic 999
update-alternatives --install /usr/bin/jinfo jinfo /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jinfo 999
update-alternatives --install /usr/bin/jsadebugd jsadebugd /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jsadebugd 999
update-alternatives --install /usr/bin/native2ascii native2ascii /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/native2ascii 999
update-alternatives --install /usr/bin/jstat jstat /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jstat 999
update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/javac 999
update-alternatives --install /usr/bin/javah javah /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/javah 999
update-alternatives --install /usr/bin/jps jps /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jps 999
update-alternatives --install /usr/bin/jstack jstack /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jstack 999
update-alternatives --install /usr/bin/jrunscript jrunscript /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jrunscript 999
update-alternatives --install /usr/bin/javadoc javadoc /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/javadoc 999
update-alternatives --install /usr/bin/jhat jhat /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jhat 999
update-alternatives --install /usr/bin/javap javap /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/javap 999
update-alternatives --install /usr/bin/jconsole jconsole /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jconsole 999
update-alternatives --install /usr/bin/jar jar /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jar 999
update-alternatives --install /usr/bin/xjc xjc /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/xjc 999
update-alternatives --install /usr/bin/schemagen schemagen /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/schemagen 999
update-alternatives --install /usr/bin/extcheck extcheck /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/extcheck 999
update-alternatives --install /usr/bin/jmap jmap /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jmap 999
update-alternatives --install /usr/bin/appletviewer appletviewer /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/appletviewer 999
update-alternatives --install /usr/bin/jstatd jstatd /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jstatd 999
update-alternatives --install /usr/bin/jdb jdb /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jdb 999
update-alternatives --install /usr/bin/serialver serialver /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/serialver 999
update-alternatives --install /usr/bin/wsgen wsgen /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/wsgen 999
update-alternatives --install /usr/bin/jcmd jcmd /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jcmd 999
update-alternatives --install /usr/bin/jarsigne jarsignerr /usr/lib/jvm/java-1.8.0-openjdk-amd64/bin/jarsigner 999
我没有找到JAVA_HOME这个环境变量在哪个文件里配置的,不过这会执行java和javac已经是运行的jdk8的bin下的文件了,这一块不是本次关注的重点,生产环境时候的时候,确保不要出现java版本太低这种问题。你可以选择其他的方式安装java8到你的环境中。
问题2: 因为的创建mysql集群的时候使用的是单独的一个docker-compose文件,启动shardingProxy容器的时候是单独起的,所以mysql集群和shardingProxy的容器之间的网络是不通的,所以需要讲shardingProxy这个容器加入到myslq集群的网络中。
docker network connect mysql-cluster_default shardingProxy
执行后,进入shardingProxy去ping mysql 集群机器已经可以ping通了,上面一切正常后,我们持续输出日志,方便我们观察集群的执行情况。
docker logs -f shardingProxy
然后我们使用mysql客户端连接 shardingProxy
mycli -uroot -h 127.0.0.1 -P 13308 --database=master_slave_db
然后我们查询数据库,再插入一条数据
mysql root@127.0.0.1:master_slave_db> show tables;
+------------------+
| Tables_in_test |
|------------------|
| user |
+------------------+
1 row in set
Time: 0.009s
mysql root@127.0.0.1:master_slave_db> select * from user;
+------+-----------+
| id | name |
|------+-----------|
| 1 | xiaoming |
| 2 | xiaohong |
| 3 | xiaoling |
| 4 | xiaolizi2 |
| 5 | xiaowang |
+------+-----------+
5 rows in set
Time: 0.029s
mysql root@127.0.0.1:master_slave_db> insert into `user`(name) VALUES(''dawang'')
Query OK, 1 row affected
Time: 0.031s
docker的日志输出如下:
[INFO ] 13:42:53.324 [main] c.a.icatch.provider.imp.AssemblerImp - USING: com.atomikos.icatch.oltp_retry_interval = 10000
[INFO ] 13:42:53.324 [main] c.a.icatch.provider.imp.AssemblerImp - USING: java.naming.provider.url = rmi://localhost:1099
[INFO ] 13:42:53.324 [main] c.a.icatch.provider.imp.AssemblerImp - USING: com.atomikos.icatch.force_shutdown_on_vm_exit = false
[INFO ] 13:42:53.324 [main] c.a.icatch.provider.imp.AssemblerImp - USING: com.atomikos.icatch.default_jta_timeout = 300000
[INFO ] 13:42:53.325 [main] c.a.icatch.provider.imp.AssemblerImp - Using default (local) logging and recovery...
[INFO ] 13:42:53.347 [main] c.a.d.xa.XATransactionalResource - resource-1-ds_master1: refreshed XAResource
[INFO ] 13:42:53.366 [main] c.a.d.xa.XATransactionalResource - resource-2-ds_slave1: refreshed XAResource
[INFO ] 13:42:53.536 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872] REGISTERED
[INFO ] 13:42:53.537 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872] BIND: 0.0.0.0/0.0.0.0:3308
[INFO ] 13:42:53.539 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] ACTIVE
tail: unrecognized file system type 0x794c7630 for ‘/opt/sharding-proxy/logs/stdout.log’. please report this to bug-coreutils@gnu.org. reverting to polling
[INFO ] 13:44:22.871 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ: [id: 0xa355d100, L:/172.17.0.5:3308 - R:/172.17.0.1:58402]
[INFO ] 13:44:22.873 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ COMPLETE
[INFO ] 13:44:26.566 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ: [id: 0xf8e8f9e6, L:/172.17.0.5:3308 - R:/172.17.0.1:58442]
[INFO ] 13:44:26.566 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ COMPLETE
[INFO ] 13:44:26.580 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ: [id: 0x820865ab, L:/172.17.0.5:3308 - R:/172.17.0.1:58446]
[INFO ] 13:44:26.580 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ COMPLETE
[INFO ] 13:44:27.209 [ShardingSphere-Command-2] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:44:27.209 [ShardingSphere-Command-2] ShardingSphere-SQL - SQL: SHOW TABLES ::: DataSources: ds_master1
[INFO ] 13:44:27.325 [ShardingSphere-Command-4] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:44:27.325 [ShardingSphere-Command-4] ShardingSphere-SQL - SQL: select TABLE_NAME, COLUMN_NAME from information_schema.columns
where table_schema = ''None''
order by table_name,ordinal_position ::: DataSources: ds_slave1
[INFO ] 13:48:26.249 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ: [id: 0xea03ccca, L:/172.17.0.5:3308 - R:/172.17.0.1:60580]
[INFO ] 13:48:26.250 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ COMPLETE
[INFO ] 13:48:29.239 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ: [id: 0x8c998abc, L:/172.17.0.5:3308 - R:/172.17.0.1:60610]
[INFO ] 13:48:29.239 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ COMPLETE
[INFO ] 13:48:30.602 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ: [id: 0xc666fe46, L:/172.17.0.5:3308 - R:/172.17.0.1:60622]
[INFO ] 13:48:30.603 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ COMPLETE
[INFO ] 13:48:31.742 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ: [id: 0x2f66e703, L:/172.17.0.5:3308 - R:/172.17.0.1:60636]
[INFO ] 13:48:31.743 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ COMPLETE
[INFO ] 13:48:31.759 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ: [id: 0x62df3a93, L:/172.17.0.5:3308 - R:/172.17.0.1:60640]
[INFO ] 13:48:31.759 [epollEventLoopGroup-2-1] i.n.handler.logging.LoggingHandler - [id: 0x0a369872, L:/0.0.0.0:3308] READ COMPLETE
[INFO ] 13:48:31.782 [ShardingSphere-Command-6] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:31.782 [ShardingSphere-Command-6] ShardingSphere-SQL - SQL: SHOW TABLES ::: DataSources: ds_master1
[INFO ] 13:48:31.800 [ShardingSphere-Command-7] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:31.800 [ShardingSphere-Command-7] ShardingSphere-SQL - SQL: SELECT @@VERSION ::: DataSources: ds_slave1
[INFO ] 13:48:31.816 [ShardingSphere-Command-9] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:31.816 [ShardingSphere-Command-9] ShardingSphere-SQL - SQL: SELECT @@VERSION_COMMENT ::: DataSources: ds_slave1
[INFO ] 13:48:31.818 [ShardingSphere-Command-8] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:31.818 [ShardingSphere-Command-8] ShardingSphere-SQL - SQL: select TABLE_NAME, COLUMN_NAME from information_schema.columns
where table_schema = ''master_slave_db''
order by table_name,ordinal_position ::: DataSources: ds_slave1
[INFO ] 13:48:31.940 [ShardingSphere-Command-10] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:31.940 [ShardingSphere-Command-10] ShardingSphere-SQL - SQL: SELECT CONCAT("''", user, "''@''",host,"''") FROM mysql.user ::: DataSources: ds_slave1
[INFO ] 13:48:31.979 [ShardingSphere-Command-11] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:31.979 [ShardingSphere-Command-11] ShardingSphere-SQL - SQL: SELECT ROUTINE_NAME FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE="FUNCTION" AND ROUTINE_SCHEMA = "master_slave_db" ::: DataSources: ds_slave1
[INFO ] 13:48:32.010 [ShardingSphere-Command-12] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:32.010 [ShardingSphere-Command-12] ShardingSphere-SQL - SQL: SELECT name from mysql.help_topic WHERE name like "SHOW %" ::: DataSources: ds_slave1
[INFO ] 13:48:34.702 [ShardingSphere-Command-13] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:34.702 [ShardingSphere-Command-13] ShardingSphere-SQL - SQL: show tables ::: DataSources: ds_master1
[INFO ] 13:48:54.065 [ShardingSphere-Command-14] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:48:54.065 [ShardingSphere-Command-14] ShardingSphere-SQL - SQL: select * from user ::: DataSources: ds_slave1
[INFO ] 13:50:54.826 [ShardingSphere-Command-15] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 13:50:54.826 [ShardingSphere-Command-15] ShardingSphere-SQL - SQL: insert into `user`(name) VALUES(''dawang'') ::: DataSources: ds_master1
[INFO ] 14:04:00.439 [ShardingSphere-Command-0] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 14:04:00.439 [ShardingSphere-Command-0] ShardingSphere-SQL - SQL: select * from user ::: DataSources: ds_slave1
[INFO ] 14:04:08.809 [ShardingSphere-Command-1] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 14:04:08.809 [ShardingSphere-Command-1] ShardingSphere-SQL - SQL: select * from user ::: DataSources: ds_slave2
[INFO ] 14:05:15.457 [ShardingSphere-Command-2] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 14:05:15.457 [ShardingSphere-Command-2] ShardingSphere-SQL - SQL: select * from user ::: DataSources: ds_slave1
[INFO ] 14:05:15.833 [ShardingSphere-Command-3] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 14:05:15.833 [ShardingSphere-Command-3] ShardingSphere-SQL - SQL: select * from user ::: DataSources: ds_slave2
[INFO ] 14:05:16.270 [ShardingSphere-Command-4] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 14:05:16.270 [ShardingSphere-Command-4] ShardingSphere-SQL - SQL: select * from user ::: DataSources: ds_slave1
[INFO ] 14:05:16.851 [ShardingSphere-Command-5] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 14:05:16.851 [ShardingSphere-Command-5] ShardingSphere-SQL - SQL: select * from user ::: DataSources: ds_slave2
[INFO ] 14:05:17.151 [ShardingSphere-Command-6] ShardingSphere-SQL - Rule Type: master-slave
[INFO ] 14:05:17.151 [ShardingSphere-Command-6] ShardingSphere-SQL - SQL: select * from user ::: DataSources: ds_slave1
可以看出我们查询数据的时候走的是slave,插入数据的时候走的是 master,然后我执行了很多次的查询,走的都是 slave 节点,并且是在多个slave之间轮训着去查询。
当我们停掉启动一台slave的时候再执行查询,就会发现一次成功,一次失败。因为目前这个架构只是负载均衡,当一个slave出现问题的时候,目前是不会自动移除在这个有问题的slave的,当在轮训飘到这个已经无法连接的slave去执行的时候,会导致查询失败,飘到活着的那个slave的时候就会成功,所以我们下一步要增加故障转移,防止让请求飘到已经出问题的mysql实例上。
配置注册中心
首先我们启动一个 zookpper 实例,为了方便,此处还是使用 docker 来启动一个 zookeeper
docker run -p 2181:2181 --name zk --restart unless-stopped -d zookeeper
因为 zookeeper 比较常用,占用的资源也很小,所以我用了 –restart unless-stopped,表示除非人工stop这个容器,否则这个容器每次都自动重启。
然后我们在 server.yaml
中增加以下配置:
orchestration:
name: orchestration_ds
overwrite: true
registry:
type: zookeeper
serverLists: 172.17.0.2:2181
namespace: orchestration
这里的 serverLists 后面的就是 zookeeper 的地址,启动服务,就可以看到,我们的proxy实例已经注册到了zookeeper了。
sharding-ui(可选)
sharding-ui 是 shardingSphere官方辅助的一个用来数据治理的界面,github仓库地址是https://github.com/apache/incubator-shardingsphere
,下载代码后,切换到4.0.1的分支(tag),使用maven 编译前后端(sharding-ui-backend 和 sharding-ui-frontend)项目,然后启动,默认密码是 admin/admin,登录后需要添加注册中心。切换分支是因为之前我们通过docker安装的shardingproxy是4.0.1版本的,因为这个过程比较简单,我这里就不详细说明了。

zkui(可选)
为了方便的查看zookeeper中的信息,可以使用 zkui 这个工具来查看,github仓库:https://github.com/DeemOpen/zkui.git
下载代码,进入项目目录,执行mvn clean package
生成 target/zkui-2.0-SNAPSHOT-jar-with-dependencies.jar
,然后复制项目中的 config.cfg 与这个jar同一目录下,执行java -jar zkui-2.0-SNAPSHOT-jar-with-dependencies.jar
,打开页面9090,就可以看到zkui的界面了,用户名和密码默认是
用户名: admin
密码: manager
也可以直接使用zkClient.sh 查看:
[zk: localhost:2181(CONNECTED) 9] ls /orchestration/orchestration_ds
[config, state]
当我们通过界面启用或者禁用某个DataSource的时候,zookeeper 会触发WatchedEvent

[zk: localhost:2181(CONNECTED) 18] ls /orchestration/orchestration_ds/state/datasources
[master_slave_db.ds_slave1, master_slave_db.ds_slave2]
WatchedEvent state:SyncConnected type:NodeDataChanged path:/orchestration/orchestration_ds/state/datasources/xxx
在应用内会被org.apache.shardingsphere.orchestration.internal.registry.state.listener.DataSourceStateChangedListener
这个监听器监听到,然后会抛出一个应用内部的事件对象DisabledStateChangedEvent
,用的是Guava的一个简单的事件订阅和发布的组件,然后应用内监听到这个事件后,会被带有注解com.google.common.eventbus.Subscribe
的方法处理,会将禁用的dataSource名称加入到MasterSlaveRule对象的禁用DataSource列表中,以后再执行查询时,就会排除掉这些已经被禁用的 SlaveNode。
master_slave_db.ds_slave1 这个名字其实分两部分组成,点前面的是 逻辑schema的名字,点后面的是dataSource的名字。
ShardingSphere Proxy Master Slave 这块有一些不太完善的地方,比如说,所有的slave都已经被DISABLED的情况下,会有除0异常(位置org/apache/shardingsphere/core/strategy/masterslave/RoundRobinMasterSlaveLoadBalanceAlgorithm.java:52),还有一个问题就是获取到某个DataSource之后,很有可能这个DataSource已经连不上了,但是状态并不是DISABLED,目前没有看到有自动DISABLED的逻辑。
我的想法是可以考虑获取一定频率的自动检测每个DataSource获取一个连接执行下isValid方法,如果无效就可以加入到DISABLED里面
docker - nginx - proxy_pass + proxy_redirect
目的:使项目域名 www.foo.test(/index.php)/controller/action
变为 www.foo.test/project/controller/action
,仍然可以正常访问。(非index.php二级目录的URL重写)
背景:
Name Command State Ports
------------------------------------------------------------------------------------------------------------
laradock_docker-in-docker_1 dockerd-entrypoint.sh Up 2375/tcp
laradock_mysql_1 docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
laradock_nginx_1 nginx Up 0.0.0.0:443->443/tcp,
0.0.0.0:80->80/tcp
laradock_php-fpm_1 docker-php-entrypoint php-fpm Up 9000/tcp
laradock_workspace_1 /sbin/my_init Up 0.0.0.0:2222->22/tcp
foo.conf
配置为
server {
listen 80;
listen [::]:80;
server_name www.foo.test;
root /var/www/foo/web;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
#try_files $uri /index.php =404;
fastcgi_pass php-upstream;
fastcgi_index index.php;
#fastcgi_buffers 16 16k;
#fastcgi_buffer_size 32k;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#fixes timeouts
#fastcgi_read_timeout 600;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt/;
log_not_found off;
}
error_log /var/log/nginx/laravel_error.log;
access_log /var/log/nginx/laravel_access.log;
}
解决:
在 foo.conf
配置里添加
location /project {
proxy_pass http://127.0.0.1/;
proxy_redirect http://127.0.0.1/ /project;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
访问 www.foo.test/project/controller/action
,成功。
总结:
一开始,同事给我发了一份测试服务器上的配置,
location /project {
proxy_pass http://127.0.0.1:9000/;
proxy_redirect http://127.0.0.1:9000/ /project;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
可我按这个配置写入我本的 nginx
的配置时,死活不行。后还想到,我自己用的docker
环境(测试环境上不是),那么 127.0.0.1
根本不是我的 php-fpm
容器地址。如果要连接,需要用到php-fpm
的容器名称。
所以实际上,nginx
容器本地地址 127.0.0.1:80
其实就是 php-fpm
的 9000
端口。
最后,删除了 proxy_pass http://127.0.0.1:9000/;
中的 9000
后配置成功了。
今天的关于docker 部署 sharding-proxy和docker 部署ragflow的分享已经结束,谢谢您的关注,如果想了解更多关于Amazon Aurora 的读写能力扩展之 ShardingSphere-Proxy 篇、Apache ShardingSphere Proxy 分库分表小练习、Apache ShardingSphere Proxy 负载均衡小练习、docker - nginx - proxy_pass + proxy_redirect的相关知识,请在本站进行查询。
本文标签: