上面配置的规则指的是,当有数据要插入数据库,或者进行查询时,sharding-jdbc通过分片配置的字段id的值,去根据配置的算法 进行运算,得到结果,例如上述分库规则,拿到id值 对2求余加1,那么不管id怎么变化算法返回的值永远是1和2,即app$->{(id % 2)+1} 对应的就是app1和app2库,分表的规则是同样道理。
说明:这里只是配置了简单的分片规则来演示sharding-jdbc如何完成分库分表,我们也可以使用代码重写
方法来实现更复杂的分片策略。最后,这里的$->{(id % 2)+1} 的{}中实际上是一个Groovy语法的表达式,sharding-jdbc是通过Groovy语法糖来解析分片策略的。所以想要配置更为复杂的策略,建议学一下Groovy语法。
4.3、接下来我们介绍配置的读写分离规则,如何实现读写分离
master-slave-rules:
app1: #分区 app1
master-data-source-name: master0 #分区 app1的主数据源
slave-data-source-names: master0slave0 #分区 app1的从数据源
app2: #分区 app2
master-data-source-name: master1 #分区 app2的主数据源
slave-data-source-names: master1slave0 #分区 app2的从数据源
上面读写分离的规则指的是,分区app1的主从数据源,分区app2的主从数据源。至于这里的分区为什么是app1和app2?这里说明一下,我自己配置的时候,配置了几次都没有成功,一开始参照官网手册配置,以为分区名称可以自定义,于是配置的是ds0和ds1,但是项目启动报错了。报错信息是:
Cannot find data source in sharding rule, invalid actual data node is: 'app1.user1'
开始以为是使用的sharding-jdbc版本问题,但是换了版本还是有问题,于是开始调试了一下源码:
从上面的截图中很明显就能发现,这里是要判断我们配置的分区集合也就是ds0和ds1是否包含 实际节点的数据源名称,也就是数据库名称。所以这里的分区名称是和我们上面配置的分片策略的数据库名称有关系的。
4.4、验证
接下来我们验证实际的效果。这里贴一下单元测试的代码。
/**
* @author cgg
* @version 1.0.0
* @date 2021/10/25
*/
@SpringBootTest(classes = ShardingJdbcApp.class)
@RunWith(SpringRunner.class)
public class AppTest {
@Resource
private IUserService userService;
/**
* 测试sharding-jdbc添加数据
*/
@Test
public void testShardingJdbcInsert() {
userService.InsertUser();
}
/**
* 测试sharding-jdbc查询数据
*/
@Test
public void testShardingJdbcQuery() {
//全部查询
userService.queryUserList();
//根据指定条件查询
userService.queryUserById(1452619866473324545L);
}
}
/**
* @author cgg
* @version 1.0.0
* @date 2021/10/25
*/
@Service
@Slf4j
public class UserServiceImpl implements IUserService {
@Resource
private UserMapper userMapper;
@Resource
private DataSource dataSource;
@Override
public List<User> queryUserList() {
List<User> userList = userMapper.queryUserList();
userList.forEach(user -> System.out.println(user.toString()));
return userList;
}
@Override
public User queryUserById(Long id) {
User user = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getId, id));
System.out.println(user.toString());
return user;
}
@Override
public void InsertUser() {
for (int i = 20; i < 40; i++) {
User user = new User();
user.setName("XX-" + i);
int count = userMapper.insert(user);
System.out.println(count);
}
}
}
4.4.1、首先看全部查询的结果
4.4.2、再看下单条查询的结果
4.4.3、再看下新增结果(实际插入到了主数据源的app1库user1表,并且后续每条插入都是走的主数据源,没有slave的操作)