一、需求
1、微服务拆分,服务拆成了多个,mysql数据库也拆成了多个,但是要求新旧服务可以切换,可以并行,所以需要对原有服务实现一个逻辑库来伪装未拆分前的数据库。
2、其中某个配置表在多个库中都存在,在多个库里的表名也相同,但记录里的某个字段取值不会重复,且取值范围大概100多个。
3、通过判断读写sql中的这个字段的取值来选择数据库,也就是维护一个字段值到数据库的map。
4、需要支持Mysql8.x。
Mycat1.6的枚举分片可以支持需求,但是Mycat1.6不支持Mysql8.x,主要卡在用户登录认证方式上,总提示密码错误无法建立连接,且新版Mycat2.0官方缺少维护,连下载页面都打不开了。
apache-shardingsphere-5.5.3可以支持Mysql8.x,但是现有的分片算法不支持需求,需要自己实现一个分片算法类。
二、代码实现
1、代码文件:KeyMappingShardingAlgorithm.java
java
package com.example.sharding;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
public class KeyMappingShardingAlgorithm implements StandardShardingAlgorithm<String> {
private Map<String, String> keyMap;
private String defaultDs = "db0";
@Override
public void init(Properties props) {
String mapFile = props.getProperty("mapFile");
if (mapFile != null && !mapFile.isEmpty()) {
try (InputStream in = new FileInputStream(mapFile)) {
Properties mapProps = new Properties();
mapProps.load(in);
keyMap = mapProps.entrySet().stream()
.collect(Collectors.toMap(
e -> e.getKey().toString(),
e -> e.getValue().toString()
));
} catch (IOException e) {
throw new RuntimeException("Failed to load mapping file: " + mapFile, e);
}
} else {
keyMap = Collections.emptyMap();
}
String defaultFromProps = props.getProperty("default");
if (defaultFromProps != null && !defaultFromProps.isEmpty()) {
defaultDs = defaultFromProps;
}
}
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
String key = shardingValue.getValue();
String target = keyMap.get(key);
if (target == null) {
target = defaultDs;
}
return target;
}
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<String> shardingValue) {
return availableTargetNames;
}
// getProps() 方法已删除,5.5.3 版本中不需要
}
--
2、通过脚本打包成jar
2.1 需要下载jdk,下载地址:Downloads for Amazon Corretto 25 - Amazon Corretto 25
2.2 设置JAVA_HOME、添加PATH等,让java、javac、jar命令可用。
2.3 打包脚本:
比如KeyMappingShardingAlgorithm.java放到KeyMappingShardingAlgorithm目录下:
同目录下存放build.sh脚本:
bash
#!/bin/bash
set -e
# 1. 先编译
javac -cp "/path/to/shardingsphere-proxy/lib/*" KeyMappingShardingAlgorithm.java
# 2. 创建正确的目录结构
mkdir -p com/example/sharding
cp KeyMappingShardingAlgorithm.class com/example/sharding/
# 3. 打包(注意从当前目录开始)
jar cf key-mapping-sharding.jar com/example/sharding/KeyMappingShardingAlgorithm.class
# 4. 验证 JAR 内容
jar tf key-mapping-sharding.jar
# 应该输出:
# com/example/sharding/KeyMappingShardingAlgorithm.class
# 5. 检查 ext-lib 目录是否存在,不存在则创建
mkdir -p /path/to/shardingsphere-proxy/ext-lib/
# 6. 部署到 ext-lib
cp key-mapping-sharding.jar /path/to/shardingsphere-proxy/ext-lib/
# 7. 重启
#/path/to/shardingsphere-proxy/bin/start.sh 3307
执行脚本生成key-mapping-sharding.jar,并放到了/path/to/shardingsphere-proxy/ext-lib/目录下。
三、配置文件
1、在conf/database-sharding.yaml文件中的rules配置如下:
python
databaseName: mydb
dataSources:
db0:
url: jdbc:mysql://10.10.10.16:3306/user_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
db1:
url: jdbc:mysql://10.10.10.17:3306/orders_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
rules:
- !SHARDING
tables:
my_config:
actualDataNodes: db0.my_config, db1.my_config
databaseStrategy:
standard:
shardingColumn: config_key
shardingAlgorithmName: enum-key-algorithm
my_user:
actualDataNodes: db0.my_user
my_role:
actualDataNodes: db0.my_role
orders:
actualDataNodes: db1.orders
books:
actualDataNodes: db1.books
shardingAlgorithms:
enum-key-algorithm:
type: CLASS_BASED
props:
strategy: STANDARD
algorithmClassName: com.example.sharding.KeyMappingShardingAlgorithm
mapFile: ./conf/key-mapping.properties
default: db0
- !SINGLE
defaultDataSource: db0
解释如下:
my_user表、my_role表的请求路由到物理库db0。
orders表、books表的请求路由到物理库db1。
my_config表的请求需要做分片,根据config_key字段使用enum-key-algorithm算法来分片。
enum-key-algorithm分片算法由类com.example.sharding.KeyMappingShardingAlgorithm实现,同时依赖./conf/key-mapping.properties文件作为输入。
在./conf/key-mapping.properties中未涉及的config_key字段取值将默认路由到db0。
2、./conf/key-mapping.properties文件内容如下:
bash
# key 到数据源的映射
server_addr=db0
server_port=db0
orders_max=db1
books_max=db1
当config_key字段的取值为前面的key时,路由到后面的物理库。
3、另外还需要配置conf/global.yaml文件:
python
# conf/global.yaml - 5.5.3 版本正确格式
# 1. 运行模式(必须,取代旧版的 proxy.database-name 和 proxy.port)
mode:
type: Standalone
repository:
type: JDBC
# 2. 系统属性(原先 proxy.props 下的内容,提升到顶层 props)
props:
proxy-frontend-flush-threshold: 128
proxy-hint-enabled: false
sql-show: true # 建议开启,方便调试
# 3. 登录权限(原先 authority 部分,格式基本不变,但注意缩进)
authority:
users:
- user: root
password: 123456
admin: true
privilege:
type: ALL_PERMITTED
# 4. SQL 解析缓存(原先 sqlParser 部分)
sqlParser:
sqlStatementCache:
initialCapacity: 2000
maximumSize: 65535
parseTreeCache:
initialCapacity: 128
maximumSize: 1024
# 5. SQL 联邦查询(原先 sqlFederation 部分)
sqlFederation:
sqlFederationEnabled: true
executionPlanCache:
initialCapacity: 2000
maximumSize: 65535
# 设置默认事务类型为本地事务,禁用 Seata
transaction:
defaultType: LOCAL
--
四、启动并验证
相关命令如下:
bash
# 启动
/path/to/shardingsphere-proxy/bin/start.sh 3307
# 查看启动日志
tail -50 /path/to/shardingsphere-proxy/logs/stdout.log
# 登录mysql
mysql -h127.0.0.1 -P3307 -uroot -p'123456'
# 验证sql
SELECT * FROM my_config WHERE `config_key` = 'server_addr';
SELECT * FROM my_config WHERE `config_key` = 'orders_max';
--
-- end --