最近遇到一个报错:

The MySQL server is running with the --read-only option so it cannot execute this statement

查询报错原因是因为更新操作连接到了数据库的读库造成的。为什么会连到读库呢?追踪代码发现利用了动态数据源

@Service
@DS("abc")
public class XXXServiceImpl extends ServiceImpl<XXXMapper, XXX> implements XXXService {


}

去看数据库配置发现配置为

  datasource:
    dynamic:
      primary: master
      strict: false
      datasource:
        master:
            ---- 数据库配置
        abc_read:
            ---- 数据库配置

这里@DS 配置的是abc,但是数据源里面配置的是abc_read,默认应该是选择master库,但是查看日志却是使用的abc_read。是不是DynamicDataSource 有特殊处理呢。追踪源码,找到DynamicRoutingDataSource类

    private DataSource determinePrimaryDataSource() {
        log.debug("dynamic-datasource switch to the primary datasource");
        DataSource dataSource = dataSourceMap.get(primary);
        if (dataSource != null) {
            return dataSource;
        }
        GroupDataSource groupDataSource = groupDataSources.get(primary);
        if (groupDataSource != null) {
            return groupDataSource.determineDataSource();
        }
        throw new CannotFindDataSourceException("dynamic-datasource can not find primary datasource");
    }

发现

    /**
     * 分组数据库
     */
    private final Map<String, GroupDataSource> groupDataSources = new ConcurrentHashMap<>();

中有key是abc,找到初始化这个map的代码

    /**
     * 新数据源添加到分组
     *
     * @param ds         新数据源的名字
     * @param dataSource 新数据源
     */
    private void addGroupDataSource(String ds, DataSource dataSource) {
        if (ds.contains(UNDERLINE)) {
            String group = ds.split(UNDERLINE)[0];
            GroupDataSource groupDataSource = groupDataSources.get(group);
            if (groupDataSource == null) {
                try {
                    groupDataSource = new GroupDataSource(group, strategy.getDeclaredConstructor().newInstance());
                    groupDataSources.put(group, groupDataSource);
                } catch (Exception e) {
                    throw new RuntimeException("dynamic-datasource - add the datasource named " + ds + " error", e);
                }
            }
            groupDataSource.addDatasource(ds, dataSource);
        }
    }

    private static final String UNDERLINE = "_";

这个逻辑就是数据源中有“_”时,DynamicDataSource会将下划线切分后的第一个词作为key,如果@ds 里面有这个值,就默认会用这个数据源了。所以如果不需要用到数据源分组功能的时候,在配置数据源的时候不要用“_”,否则数据源会切到不确定的数据源。

这个分组功能应该是集群情况下,对多个库做loadbalance用的。


tao不是哭脸
119 声望3 粉丝