既有项目修改shardingshpere分库分表踩坑记(三)

背景

基于既有项目修改shardingshpere分库分表踩坑记(二),在测试过程中又发现一种情况。在同一个事务中,对config表(非分库分表可以编辑,分库分表可以编辑,业务关联查询)进行保存和查询,会大概率出现报错。查找报错原因是查不到记录。
伪代码如下:

@Transactional
	public void doSomething(){
		Config config = new Config();
		saveConfig();
		
		config = getConfig();
		
		//doingOthers
	
	}

正常情况下,同一事务中,保存之后是可以查到记录的。
但这里有点特殊,踩坑记(二)中,将config表配置成只更新主库。而保存之后查询走的是单播路由,查的不一定是主库,也可能查其他库,这时由于事务还没提交。所以其他库是查不到这条记录的。所以这里只能特殊处理下。

方案

使用LocalThread缓存下。判断若当前线程有对config表进行编辑的话,单播路由只从主库查。

修改

  • 增加单表修改线程副本类
public class ThreadLocalUtils {
    public static TransmittableThreadLocal<Boolean> singleTableAlterThreadLocal = new TransmittableThreadLocal();

}
  • 修改ShardingJDBCAop类,将单表编辑状态初始为false。
 @Around("round()")
    public Object round(ProceedingJoinPoint point) throws Throwable {
        HintManager hintManager = HintManager.getInstance();
        //单表编辑状态设为false
        ThreadLocalUtils.singleTableAlterThreadLocal.set(false);

        try {
        
            String region = getRegion();
            shardingRuleConfigurationProperties.getBindingTables().stream().map(s -> s.split(",")).flatMap(Arrays::stream).forEach(
                    table -> {
                hintManager.addDatabaseShardingValue(table, region );
                hintManager.addTableShardingValue(table, region );
            });
            return point.proceed();
        }  finally {
            hintManager.close();
        }
    }
  • 修改数据库路由类DatabaseBroadcastRoutingEngine,若为单表修改的话,线程副本状态设为true。
public RoutingResult route() {
        RoutingResult result = new RoutingResult();

        if(CollectionUtils.isNotEmpty(logicTables) && shardingShpereExtConfig != null && CollectionUtils.isNotEmpty(shardingShpereExtConfig.getSingleTables())){
            List<String> singleTables = shardingShpereExtConfig.getSingleTables();
            for (String table : logicTables){
                if(singleTables.contains(table.toLowerCase())){
                	//单表编辑状态设为true
                    ThreadLocalUtils.singleTableAlterThreadLocal.set(true);
                    result.getRoutingUnits().add(new RoutingUnit(this.shardingRule.getShardingDataSourceNames().getDataSourceNames().iterator().next()));
                    return result;
                }
            }
        }

        Iterator var2 = this.shardingRule.getShardingDataSourceNames().getDataSourceNames().iterator();

        while(var2.hasNext()) {
            String each = (String)var2.next();
            result.getRoutingUnits().add(new RoutingUnit(each));
        }

        return result;
    }
  • 重写单表路由类UnicastRoutingEngine

package org.apache.shardingsphere.core.route.type.unicast;

import com.augurit.farmhouse.common.config.ShardingShpereExtConfig;
import com.augurit.farmhouse.common.sharding.util.CollectionUtils;
import com.augurit.farmhouse.common.sharding.util.SpringContextUtils;
import com.augurit.farmhouse.common.sharding.util.ThreadLocalUtils;
import com.google.common.collect.Sets;
import java.beans.ConstructorProperties;
import java.util.*;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.shardingsphere.core.config.ShardingConfigurationException;
import org.apache.shardingsphere.core.route.type.RoutingEngine;
import org.apache.shardingsphere.core.route.type.RoutingResult;
import org.apache.shardingsphere.core.route.type.RoutingUnit;
import org.apache.shardingsphere.core.route.type.TableUnit;
import org.apache.shardingsphere.core.rule.DataNode;
import org.apache.shardingsphere.core.rule.ShardingRule;
import org.apache.shardingsphere.core.rule.TableRule;

public final class UnicastRoutingEngine implements RoutingEngine {
    private final ShardingRule shardingRule;
    private final Collection<String> logicTables;
    private final ShardingShpereExtConfig shardingShpereExtConfig;

    public RoutingResult route() {
        RoutingResult result = new RoutingResult();
        ArrayList tableUnits;

        //是否单表查询
        boolean singleTable =false;
        if(CollectionUtils.isNotEmpty(logicTables) && shardingShpereExtConfig != null && CollectionUtils.isNotEmpty(shardingShpereExtConfig.getSingleTables())){
            List<String> singleTables = shardingShpereExtConfig.getSingleTables();
            singleTable = logicTables.stream().anyMatch(i->singleTables.contains(i.toLowerCase()));
        }
        //若是的话,判断当前线程的单表编辑状态,是否编辑过,若是的话则直接查主库
        if(singleTable && BooleanUtils.isTrue(ThreadLocalUtils.singleTableAlterThreadLocal.get())){
            result.getRoutingUnits().add(new RoutingUnit(this.shardingRule.getShardingDataSourceNames().getDataSourceNames().iterator().next()));
            return result;
            //若不是的话,走原逻辑
        }else if (this.shardingRule.isAllBroadcastTables(this.logicTables)) {
            tableUnits = new ArrayList(this.logicTables.size());
            Iterator var3 = this.logicTables.iterator();

            while(var3.hasNext()) {
                String each = (String)var3.next();
                tableUnits.add(new TableUnit(each, each));
            }

            RoutingUnit routingUnit = new RoutingUnit(this.shardingRule.getShardingDataSourceNames().getRandomDataSourceName());
            routingUnit.getTableUnits().addAll(tableUnits);
            result.getRoutingUnits().add(routingUnit);
        } else if (this.logicTables.isEmpty()) {
            result.getRoutingUnits().add(new RoutingUnit(this.shardingRule.getShardingDataSourceNames().getRandomDataSourceName()));
        } else if (1 == this.logicTables.size()) {
            String logicTableName = (String)this.logicTables.iterator().next();
            if (!this.shardingRule.findTableRule(logicTableName).isPresent()) {
                result.getRoutingUnits().add(new RoutingUnit(this.shardingRule.getShardingDataSourceNames().getRandomDataSourceName()));
                return result;
            }

            DataNode dataNode = this.shardingRule.getDataNode(logicTableName);
            RoutingUnit routingUnit = new RoutingUnit(dataNode.getDataSourceName());
            routingUnit.getTableUnits().add(new TableUnit(logicTableName, dataNode.getTableName()));
            result.getRoutingUnits().add(routingUnit);
        } else {
            tableUnits = new ArrayList(this.logicTables.size());
            Set<String> availableDatasourceNames = null;
            boolean first = true;
            Iterator var5 = this.logicTables.iterator();

            while(var5.hasNext()) {
                String each = (String)var5.next();
                TableRule tableRule = this.shardingRule.getTableRule(each);
                DataNode dataNode = (DataNode)tableRule.getActualDataNodes().get(0);
                tableUnits.add(new TableUnit(each, dataNode.getTableName()));
                Set<String> currentDataSourceNames = new HashSet(tableRule.getActualDatasourceNames().size());
                Iterator var10 = tableRule.getActualDataNodes().iterator();

                while(var10.hasNext()) {
                    DataNode eachDataNode = (DataNode)var10.next();
                    currentDataSourceNames.add(eachDataNode.getDataSourceName());
                }

                if (first) {
                    availableDatasourceNames = currentDataSourceNames;
                    first = false;
                } else {
                    availableDatasourceNames = Sets.intersection((Set)availableDatasourceNames, currentDataSourceNames);
                }
            }

            if (((Set)availableDatasourceNames).isEmpty()) {
                throw new ShardingConfigurationException("Cannot find actual datasource intersection for logic tables: %s", new Object[]{this.logicTables});
            }

            RoutingUnit routingUnit = new RoutingUnit(this.shardingRule.getShardingDataSourceNames().getRandomDataSourceName((Collection)availableDatasourceNames));
            routingUnit.getTableUnits().addAll(tableUnits);
            result.getRoutingUnits().add(routingUnit);
        }

        return result;
    }

    @ConstructorProperties({"shardingRule", "logicTables","shardingShpereExtConfig"})
    public UnicastRoutingEngine(ShardingRule shardingRule, Collection<String> logicTables,ShardingShpereExtConfig shardingShpereExtConfig) {
        this.shardingRule = shardingRule;
        this.logicTables = logicTables;
        this.shardingShpereExtConfig = shardingShpereExtConfig;
    }
    @ConstructorProperties({"shardingRule", "logicTables"})
    public UnicastRoutingEngine(ShardingRule shardingRule, Collection<String> logicTables) {
        this.shardingRule = shardingRule;
        this.logicTables = logicTables;
        this.shardingShpereExtConfig = (ShardingShpereExtConfig) SpringContextUtils.getBean("shardingShpereExtConfig");
    }
    @ConstructorProperties({"shardingRule"})
    public UnicastRoutingEngine(ShardingRule shardingRule) {
        this.shardingRule = shardingRule;
        this.logicTables = null;
        this.shardingShpereExtConfig = (ShardingShpereExtConfig) SpringContextUtils.getBean("shardingShpereExtConfig");
    }
}

结论

1、线程入口将单表编辑状态设为false
2、在数据源广播表判断若是单表编辑的话,将单表编辑状态设为true
3、单播路由判断单表编辑状态。若修改过(true)则查主库,若未修改过则采用原单播路由逻辑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值