自身系统结合mycat,根据多字段进行分片插入的调整思路

前言 :

     因为新增需求,需要增加分库插入数据的功能。由于原先使用了mycat进行分库查询,所以优先考虑使用mycat进行相应的改造。

    同时,该文档仅在个人系统架构前提下进行的分析考虑,因此对于不同架构或情况的小伙伴来说仅供参考。欢迎一起探讨更多方案。


MYCAT分片规则配置说明:

    在查看mycat分片(数据片,独立的物理数据库节点)规则时,发现只需要改动少数配置就能支持通过mycat进行分片插入,具体改动文件及内容如下:

rule.xml


    在rule.xml增加对应的分片规则和引用函数,若函数需要自定义,则需要在Mycat-Server-1.6.5\src\main\java\io\mycat\route\function的路径下增加新类用于引用,具体实现自行参照mycat其它相关类。

    之后在schema.xml的table标签中增加刚刚新增的rule的引用即可,具体可参照mycat的说明文档。

<table name="travelrecord" dataNode="multipleDn$0-99,multipleDn2$100-199" rule="auto-shardinglong" ></table>

    同时写sql时需要注意的是必须有分片的关键字段,如上图的id,否则会报错。若insert into A values(),或者insert into A(test_column) values()。


主要问题:

现在遇到这样一个问题:

    mycat的确支持按照相应字段进行分片操作,但这个相应字段只允许为单个字段(具体相关源码见底部),但是在业务上又需要支持按照2个字段进行分片操作。那该如何通过mycat实现这一需求?


思路历程:

思路一:

    当然,遇到这种问题的话,有时间有精力当然是优先考虑改造源码,但是由于个人时间精力有限,且对mycat的源码了解程度仅限于自己曾经接触使用的部分,所以这一方案只能留到最后。


思路二:

    再次结合自身系统来思考时发现,虽然是需要根据字段A和字段B来进行数据分片,但是字段A存于session中,单次登录后该字段的值会保持不变,因此可以考虑在操作mycat之前先对字段A进行相应处理。

    同时,好在自身系统使用了多数据源,所以这个问题相对来说就更方便解决了。

    因此考虑在对字段A在代码层进行分流,即字段A值为1时,使用数据源①,字段值为2时,使用数据源②。然后数据源①和②配置不同的mycat scheme或服务,再在相应的table上对字段B进行相应的分片规则处理。

    这种方案缺点很多。首先,在字段A增加不同值时,需要增加新的数据源配置(当然也可以考虑用通配的方式去匹配,理论上来说应该是可以实现的),同时mycat也要增加相应的配置。且前提要求较为严格,对于超过2个字段或同一sql操作内两个字段的值均有不同时,该方案就不适用。因此需要再考虑其他方案(如果后续还有其他思路)。


思路三:

     若需要进行相应分片的表允许改动,或系统刚起步允许改动表结构和更新历史数据的话,可以考虑以下做法。

    若需要进行分片判断的表字段为A,B,C,D多个字段的话 ,可以考虑增加字段E,其字段值为CONCAT(A,B,C,D)(oracle:A||B||C||D),然后分片规则定义在该字段上。

    这种方案较为简单,且可操作性高,但是对于表结构调整较大,或者sql改动较大的项目,请谨慎!


附录:

批量插入时,mycat的分片处理代码

Mycat-Server-1.6.5\src\main\java\io\mycat\route\parser\druid\impl\DruidInsertParser.java

	/**
	 * insert into .... select .... 或insert into table() values (),(),....
	 * @param schema
	 * @param rrs
	 * @param insertStmt
	 * @throws SQLNonTransientException
	 */
	private void parserBatchInsert(SchemaConfig schema, RouteResultset rrs, String partitionColumn, 
			String tableName, MySqlInsertStatement insertStmt) throws SQLNonTransientException {
		//insert into table() values (),(),....
		if(insertStmt.getValuesList().size() > 1) {
			//字段列数
			int columnNum = insertStmt.getColumns().size();
			int shardingColIndex = getShardingColIndex(insertStmt, partitionColumn);
			if(shardingColIndex == -1) {
				String msg = "bad insert sql (sharding column:"+ partitionColumn + " not provided," + insertStmt;
				LOGGER.warn(msg);
				throw new SQLNonTransientException(msg);
			} else {
				List<ValuesClause> valueClauseList = insertStmt.getValuesList();
				
				Map<Integer,List<ValuesClause>> nodeValuesMap = new HashMap<Integer,List<ValuesClause>>();
				Map<Integer,Integer> slotsMap = new HashMap<>();
				TableConfig tableConfig = schema.getTables().get(tableName);
				AbstractPartitionAlgorithm algorithm = tableConfig.getRule().getRuleAlgorithm();
				for(ValuesClause valueClause : valueClauseList) {
					if(valueClause.getValues().size() != columnNum) {
						String msg = "bad insert sql columnSize != valueSize:"
					             + columnNum + " != " + valueClause.getValues().size() 
					             + "values:" + valueClause;
						LOGGER.warn(msg);
						throw new SQLNonTransientException(msg);
					}
					SQLExpr expr = valueClause.getValues().get(shardingColIndex);
					String shardingValue = null;
					if(expr instanceof SQLIntegerExpr) {
						SQLIntegerExpr intExpr = (SQLIntegerExpr)expr;
						shardingValue = intExpr.getNumber() + "";
					} else if (expr instanceof SQLCharExpr) {
						SQLCharExpr charExpr = (SQLCharExpr)expr;
						shardingValue = charExpr.getText();
					}
					
					Integer nodeIndex = algorithm.calculate(shardingValue);
					if(algorithm instanceof SlotFunction){
						slotsMap.put(nodeIndex,((SlotFunction) algorithm).slotValue()) ;
					}
					//没找到插入的分片
					if(nodeIndex == null) {
						String msg = "can't find any valid datanode :" + tableName 
								+ " -> " + partitionColumn + " -> " + shardingValue;
						LOGGER.warn(msg);
						throw new SQLNonTransientException(msg);
					}
					if(nodeValuesMap.get(nodeIndex) == null) {
						nodeValuesMap.put(nodeIndex, new ArrayList<ValuesClause>());
					}
					nodeValuesMap.get(nodeIndex).add(valueClause);
				}
				
				RouteResultsetNode[] nodes = new RouteResultsetNode[nodeValuesMap.size()];
				int count = 0;
				for(Map.Entry<Integer,List<ValuesClause>> node : nodeValuesMap.entrySet()) {
					Integer nodeIndex = node.getKey();
					List<ValuesClause> valuesList = node.getValue();
					insertStmt.setValuesList(valuesList);
					nodes[count] = new RouteResultsetNode(tableConfig.getDataNodes().get(nodeIndex),
							rrs.getSqlType(),insertStmt.toString());
					if(algorithm instanceof SlotFunction) {
						nodes[count].setSlot(slotsMap.get(nodeIndex));
						nodes[count].setStatement(ParseUtil.changeInsertAddSlot(nodes[count].getStatement(),nodes[count].getSlot()));
					}
					nodes[count++].setSource(rrs);

				}
				rrs.setNodes(nodes);
				rrs.setFinishedRoute(true);
			}
		} else if(insertStmt.getQuery() != null) { // insert into .... select ....
			String msg = "TODO:insert into .... select .... not supported!";
			LOGGER.warn(msg);
			throw new SQLNonTransientException(msg);
		}
	}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值