ShardingSphere5.0 Proxy通过自定义类实现分库分表

ShardingSphere5.0跟之前的4.0在配置文件上有了区别,并且自定义规则类的定义引用跟4.0也发生了变化。这里介绍一下5.0的自定义规则的定义和调用。

一、为什么用自定义规则。

 通过取余无法实现的规则,需要通过自定义类根据自己定义的分库分表逻辑实现分库分表规则,返回数据对应的分库名称和分表名称

     官网和网上大部分介绍的都是通过行表达式 对ID字段取余或者取整的方式,但是实际分库分表可能会需要按日期范围,按年,月,周,季度分库分表这时候就无法通过行表达式实现,需要自己定义规则类,然后在配置文件中指定字段,引用自定义规则类,实现字段根据自定义规则分库分表。

二、自定义规则有什么作用?

       官网介绍的分库分表都是通过在配置文件中行表达式,计算字段值对应的分库名称和分表名称,自定义规则也是根据字段值计算并返回对应的分库和分表名称

三、自定义类需要继承的接口

      在自定义类中继承shardingsphere5.0接口StandardShardingAlgorithm和接口函数。同时需要      引入对应的shardingsphare定义的jar包,我偷懒直接将shardingsphere5.0工程中lib目录下的jar包都引用了,没有具体区分。

四、.自定规则分类

     自定规则可以有自定义分库规则类,分表规则类。 不同的分库分表规则定义不同的类,分别在shardingsphere配置文件中引用。分库类和分表类都需要实现StandardShardingAlgorithm接口

比如表Test 自定义分库分表规则:

1.根据创建日期字段Create_Date中年分表,一年一个表 。

    比如 2021-01-01,2021-05-01,2021-06-01,年份是2021的都存储在Test_2021中。 

    2015-02-03,2015-05-03,2015-06-03 年份是2015的都存储在 Test_2015中

2.1995年年之前的数据都存储在Test_1995标中
3.分库规则:分四个库,创建日期字段Create_Date中年分对4取余,余数就是分库名称。比如ds_0,ds_1,ds_2,ds_3

分库规则如下:

package com.standard;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import com.google.common.collect.Range;

public class ModuloShardingDatabaseDateYearAlgorithm implements StandardShardingAlgorithm<String>{
	private int ModedID=5; 
	private static final String DataBase_NAME = "ds_";

    //获得日期字段中年份数字,如果年份<1995,返回1995
	 private int getYearFromString(String value) throws ParseException {
		   int nYear=-1;
		   try {
			   if(null==value||""==value||value.length()<4) {
				   Date date = new Date(); 
					return date.getYear();
			   }
			   else {
				   try {
				   nYear=Integer.valueOf(value.substring(0, 4));
				   }
				   catch(Exception e) {
						Date date = new Date(); 
						return date.getYear();
					}
			   }
				if(nYear<1995)
					nYear=1995;
		   }
			catch(Exception e) {
				Date date = new Date(); 
				return date.getYear();
			}
			return nYear;
		}


  /*
*databaseNamescollection:配置文件中定义的数据库名称集合
*shardingValue:insert,select,update,delate时,Create_Date字段值
 *函数返回值:返回函数值shardingValue经过取余计算后对于的数据库名称。比如 1995%4 =3,返回数据库名称是ds_3
*/
	@Override
	public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
		 if (shardingValue != null) {
        	 String value = shardingValue.getValue();//获取传递的字段值
        	  
       int nYear=-1;
        	 try {
				nYear = getYearFromString(value);//获取年份
				 
			} catch (ParseException e) {
				
				e.printStackTrace();
			}
//对年份取余,将余数跟数据库前缀组合成数据库名称,
//检查这个名称是否在配置文件数据库集合中,如果存在返回数据库名称
        	 for (String each : availableTargetNames) {
 	            if (each.equals(DataBase_NAME + String.valueOf(nYear%4))) {
 	                return each;
 	            }
 	        }
 	        throw new  UnsupportedOperationException("content_channel没有匹配到可用数据库节点");
        } else {
            throw new UnsupportedOperationException("分片列为空");
        }
	}

  /*
*databaseNamescollection:配置文件中定义的数据库名称集合
*rangeShardingValue:insert,select,update,delate时,Create_Date 范围查询字段值。比如 between 2015-01-01 and 2021-01-01,存储的就是 2015-01-01和2021-01-01
 *函数返回值:返回函数值rangeShardingValue经过取余计算后对于的数据库名称。比如 between 2015-01-01 and 2021-01-01 ,根据这时间段年份,得到返回的集合是ds_0,ds_1,ds_2,ds_3
*/
   
	@Override
	public Collection<String> doSharding(Collection<String> databaseNamescollection, RangeShardingValue<String> rangeShardingValue) {

		Collection<String> collect = new ArrayList<>();
		 if (rangeShardingValue != null) {		
        Range<String> valueRange = rangeShardingValue.getValueRange();//获得范围区间值
		String slowerEndpointDate = String.valueOf(valueRange.hasLowerBound()?valueRange.lowerEndpoint():"");       //获得返回区间值下限       
        String supperEndpointDate = String.valueOf(valueRange.hasUpperBound()?valueRange.upperEndpoint():"");//获得范围区间值上限
         
          
        int nStartYear =-1;
        int nEndYear=-1;
		try {
			 
			  nStartYear = getYearFromString(slowerEndpointDate) ;//获得下限年份
			  nEndYear = getYearFromString(supperEndpointDate) ;//获得上限年份
               
			  if(nStartYear==-1&&nEndYear!=-1) {//下限年份为空时,上限不为空时,下限=上限-5
				  nStartYear=nEndYear-ModedID;
			  } else if(nStartYear!=-1&&nEndYear==-1) {//下限不为空,上限为空,上限=下限+5
				  nEndYear=nStartYear+ModedID;
			  }
		} catch (ParseException e) {
 
			e.printStackTrace();
		}
 
        //根据上下限范围,循环取值判断对应的数据库名称,返回数据库名称集合
		for (String each : databaseNamescollection) {
			for(int i=nStartYear;i<=nEndYear;i++) {
				if (each.equals(DataBase_NAME + String.valueOf(i%4))) {
					if(!collect.contains(each)) {
						collect.add(each);
					}	
	            }
			}  
        }
		return collect;
   
		
		 }else {
	            throw new UnsupportedOperationException("分片列为空");
	        }
		 
	}
}

分表规则代码:

package com.standard;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;

import org.apache.shardingsphere.sharding.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.RangeShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.standard.StandardShardingAlgorithm;

import com.google.common.collect.Range;

public class ModuloShardingTableDateYearAlgorithm implements StandardShardingAlgorithm<String>{
	private static final String TABLE_NAME = "content_channel_";
	private int ModedID=5;
	 
   //获得日期字段中年份数字,如果年份<1995,返回1995
	 private int getYearFromString(String value) throws ParseException {
		   int nYear=-1;
		   try {
			   if(null==value||""==value||value.length()<4) {
				   Date date = new Date(); 
					return date.getYear();
			   }
			   else {
				   try {
				   nYear=Integer.valueOf(value.substring(0, 4));
				   }
				   catch(Exception e) {
						Date date = new Date(); 
						return date.getYear();
					}
			   }
				if(nYear<1995)
					nYear=1995;
		   }
			catch(Exception e) {
				Date date = new Date(); 
				return date.getYear();
			}
			return nYear;
		}
	 
  /*
*availableTargetNames:配置文件中定义的表名称集合
*shardingValue:insert,select,update,delate时,Create_Date字段值
 *函数返回值:返回函数值shardingValue年份对应的表名。比如 1995 =3,返回表名Test_1995
*/
	@Override
	public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
		if (shardingValue != null) {
       	 String value = shardingValue.getValue();//获取传递的字段值
         int nYear=-1;
       	 try {
				 
				nYear = getYearFromString(value) ;//获取年份
				 
			} catch (ParseException e) {
				
				e.printStackTrace();
			}
        //根据年份判断表名集合是否存在对应表表名集合,存在时返回表名
       	 for (String each : availableTargetNames) {
       		 new  UnsupportedOperationException(each+"--"+TABLE_NAME + String.valueOf(nYear));
	            if (each.endsWith(String.valueOf(nYear))) { 	            	  
	                return each;
	            }
	        }
	        throw new  UnsupportedOperationException("content_channel没有匹配到可用表");
       } else {
           throw new UnsupportedOperationException("分片列为空");
       }
	}

  /*
*availableTargetNames:配置文件中定义的表名称集合
*rangeShardingValue:insert,select,update,delate时,Create_Date 范围查询字段值。比如 between 2015-01-01 and 2021-01-01,存储的就是 2015-01-01和2021-01-01
 *函数返回值:返回函数值rangeShardingValue 年份对应的表名集合比如 between 2020-01-01 and 2021-01-01 ,根据这时间段年份,得到返回的集合是Test_2020,Test_2021
*/
	@Override
	public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<String> rangeShardingValue) {
		// TODO Auto-generated method stub
				Collection<String> collect = new ArrayList<>();
				 if (rangeShardingValue != null) {
				
		       Range<String> valueRange = rangeShardingValue.getValueRange();//获得范围区间值
			   String slowerEndpointDate = String.valueOf(valueRange.hasLowerBound()?valueRange.lowerEndpoint():"");    //获得返回区间值下限             
		       String supperEndpointDate = String.valueOf(valueRange.hasUpperBound()?valueRange.upperEndpoint():"");//获得范围区间值上限
		       
		       
		       int nStartYear =-1;
		       int nEndYear=-1;
				try {
					  nStartYear = getYearFromString(slowerEndpointDate) ;//获得下限年份
					  nEndYear = getYearFromString(supperEndpointDate) ;//获得上限年份
					  if(nStartYear==-1&&nEndYear!=-1) {
						  nStartYear=nEndYear-ModedID;//下限年份为空时,上限不为空时,下限=上限-5
					  } else if(nStartYear!=-1&&nEndYear==-1) {
						  nEndYear=nStartYear+ModedID;//下限不为空,上限为空,上限=下限+5
					  }
					  
						  
				} catch (ParseException e) {

					e.printStackTrace();
				}

                 //根据上下限范围,循环取值判断对应的表名称,返回符合条件的表名称集合
			        for (int i = nStartYear; i <= nEndYear; i++) {
			            for (String each : availableTargetNames) {
			                if (each.endsWith(String.valueOf(i))) {
			                	if(!collect.contains(each))
			                	{ 
			                		collect.add(each);
			                	}
			                }
			            }
			        }
			        return collect;
				 }
				 return null;
	}

}

五、配置文件引用

 将第四部定义的类生成jar包,拷贝到ShardingSphere目录中lib子目录下

 rules:
 - !SHARDING
   tables:
     Test:
       actualDataNodes: ds_${0..3}.Test_${1995..2042} #分4个库,分表从1995-2042
       tableStrategy:#分表设置
         standard:
           shardingColumn: Create_date #分表字段 
           shardingAlgorithmName: test_shard #分表规则名称
       databaseStrategy:#分库设置(如果多个表分库字段和规则相同,可以不用设置每个表分库规则,设置一个默认分库规则defaultDatabaseStrategy就可以)
         standard:
           shardingColumn: Create_date #分库字段
           shardingAlgorithmName: test_DB_shard #分库规则名称
       keyGenerateStrategy:
         column: id
         keyGeneratorName: snowflake

    
   bindingTables:
     - Test
   defaultDatabaseStrategy:
     standard: #分表
       shardingColumn: Create_Date #默认分库字段
       shardingAlgorithmName: test_DB_shard #默认分库规则名称
 
   shardingAlgorithms:
     test_DB_shard:
       type: CLASS_BASED # 声明类引用
       props:
         strategy: STANDARD #设置属性
         algorithmClassName: com.standard.ModuloShardingDatabaseDateYearAlgorithm  #引用分库类

     test_shard:
       type: CLASS_BASED  # 声明类引用
       props:
         strategy: STANDARD #设置属性
         algorithmClassName: com.standard.ModuloShardingTableDateYearAlgorithm #引用分表类
  
   keyGenerators:
     snowflake:
       type: SNOWFLAKE
       props:
         worker-id: 123

根据上面的代码和配置文件设置,就完成了自定义规则类在shardingspehre5.0中的引用,实现了自定义规则定义和引用。

六、感觉ShardingSphere相关文档写的不是很详细,并且4.0和5.0在自定义分库分表上没有形成一个完整的继承体系,

七、上面内容本人经过实际验证可以正确执行,达到分库分表目的,数据库可以按规则插入对应的库和表。由于水平有限,有错误地方请多谅解。

  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
以下是一个基于shardingsphere-sharding-boot-starter 5.1.2版本实现分库分表的例子: 1. 添加依赖 在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-proxy-backend-common</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-proxy-frontend-common</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-proxy-bootstrap</artifactId> <version>5.1.2</version> </dependency> ``` 2. 配置数据源 在application.yml文件中配置数据源信息,例如: ```yaml spring: datasource: sharding: datasource: ds_0: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai username: root password: root ds_1: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai username: root password: root # 配置分库规则 shardingRule: # 分库策略 defaultDatabaseStrategy: inline: shardingColumn: user_id algorithmExpression: ds_${user_id % 2} # 分表策略 defaultTableStrategy: inline: shardingColumn: order_id algorithmExpression: t_order_${order_id % 2} # 绑定表 bindingTables: - t_order # 分库分表算法 shardingAlgorithms: inline: type: INLINE props: algorithm-expression: ds_${user_id % 2} algorithm-expression: t_order_${order_id % 2} # 配置默认数据源 url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai username: root password: root driver-class-name: com.mysql.jdbc.Driver ``` 3. 创建实体 创建一个简单的实体,例如: ```java public class Order { private Long orderId; private Long userId; private String status; // getter and setter } ``` 4. 创建DAO 创建一个简单的DAO,通过注解来指定表名和主键,例如: ```java @Repository @Mapper public interface OrderDao { @Insert("insert into t_order (order_id, user_id, status) values (#{orderId}, #{userId}, #{status})") @Options(useGeneratedKeys = true, keyProperty = "orderId") int insert(Order order); @Select("select * from t_order where order_id = #{orderId}") @Results({ @Result(column = "order_id", property = "orderId"), @Result(column = "user_id", property = "userId"), @Result(column = "status", property = "status") }) Order selectByPrimaryKey(Long orderId); } ``` 5. 编写测试用例 编写一个简单的测试用例,例如: ```java @RunWith(SpringRunner.class) @SpringBootTest public class OrderDaoTest { @Autowired private OrderDao orderDao; @Test public void testInsert() { Order order = new Order(); order.setUserId(1L); order.setStatus("INIT"); orderDao.insert(order); System.out.println(order.getOrderId()); } @Test public void testSelectByPrimaryKey() { Order order = orderDao.selectByPrimaryKey(1L); System.out.println(order); } } ``` 6. 运行测试用例 通过运行测试用例来验证分库分表是否正常工作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值