首页 文章详情

动态数据源运行中切换数据源的数据库(dynamic-datasource)

小尘哥 | 78 2024-04-29 11:40 0 0 0
UniSMS (合一短信)

1.缘起

源自一个匪夷所思的需求:系统需要频繁采集大量(单表可能超过1亿)外部数据做审核,由于单机mysql自身限制,数据量大了之后查询一次都是问题,因此准备改造成处理一批,归档一批,也分析过分库分表分区方案,这里不在对比,有时间的话再开一篇讨论。同时,可以随时切换到归档库查看历史数据,并且还可以在归档库继续办业务,还可以对归档库归档。

2.基础框架

  1. springboot 2.6.2

  2. dynamic-datasource-spring-boot-starter 4.2.0

  3. mybatis 2.2.0

  4. spring-cloud-alibaba 2021.1

  5. druid 1.2.20

其他的就不再啰嗦,需要主要springcloud-alibaba和springboot的版本对应,可以去官网查看。

3.设计思路

  1. 用户输入归档使用的新库名(推荐默认值:原库_{年月日})

  2. 备份整个库

  3. 生成备份记录表(主要记录:原库名、新库名、时间等)

  4. 归档库仍归档到原服务器上(至关重要),当然,也可以完整的实现数据源配置入库,略复杂,考虑安全性

  5. 提供【切换数据库】操作

4.目标

系统运行过程中,本身master数据源查的是mos库,需要通过点击【切换数据库】按钮,将master数据源切换为查询mos_20240421库。即仅修改数据库名,其他配置信息依然使用原来的。

5.实现原理

由于我们本来就使用了多数据源,多数据源的原理是系统启动的时候已将数据源初始化完成,将多个数据源存放到DynamicRoutingDataSource中,可以跟进断点,看到最终使用的数据源是DruidDataSource,因此,只需修改DynamicRoutingDataSource中master对应的jdbcurl中的dbname就可以了。

示例

    
  1. @RequestMapping ( "change/{db}" )

  2. public void changeDataSource ( @PathVariable String db ) {

  3. String newDatabase = "1" . equals ( db ) ? "mos" : "mos_bak" ;

  4. Map < String , DynamicRoutingDataSource > dataSourceMap = beanFactory . getBeansOfType ( DynamicRoutingDataSource . class );

  5. for ( Map . Entry < String , DynamicRoutingDataSource > entry : dataSourceMap . entrySet ()) {

  6. DynamicRoutingDataSource dynamicRoutingDataSource = entry . getValue ();

  7. Map < String , DataSource > stringDataSourceMap = dynamicRoutingDataSource . getDataSources ();

  8. for ( Map . Entry < String , DataSource > dataSourceEntry : stringDataSourceMap . entrySet ()) {

  9. String datasourceKey = dataSourceEntry . getKey ();

  10. ItemDataSource itemDataSource = ( ItemDataSource ) dataSourceEntry . getValue ();

  11. DruidDataSource druidDataSource = ( DruidDataSource ) itemDataSource . getDataSource ();

  12. if ( "master" . equals ( datasourceKey )) {

  13. DruidDataSource newDatasource = druidDataSource . cloneDruidDataSource ();

  14. String jdbcUrl = newDatasource . getRawJdbcUrl ();

  15. newDatasource . setUrl ( replaceDatabase ( jdbcUrl , newDatabase ));

  16. itemDataSource . setDataSource ( newDatasource );

  17. stringDataSourceMap . put ( "master" , itemDataSource );

  18. }

  19. }

  20. }

  21. }

  22. private static String replaceDatabase ( String jdbcUrlString , String newDatabase ){

  23. String oldDatabaseName = currentDatabaseName ( jdbcUrlString );

  24. return jdbcUrlString . replace ( oldDatabaseName , newDatabase );

  25. }

  26. private static String currentDatabaseName ( String jdbcUrlString ){

  27. String [] a = jdbcUrlString . split ( "\\?" );

  28. return a [ 0 ]. substring ( a [ 0 ]. lastIndexOf ( "/" )+ 1 );

  29. }

  30. @RequestMapping ( "get" )

  31. public AjaxResult currentDataSource (){

  32. Map < String , String > result = new HashMap <>();

  33. Map < String , DynamicRoutingDataSource > dataSourceMap = beanFactory . getBeansOfType ( DynamicRoutingDataSource . class );

  34. for ( Map . Entry < String , DynamicRoutingDataSource > entry : dataSourceMap . entrySet ()) {

  35. DynamicRoutingDataSource dynamicRoutingDataSource = entry . getValue ();

  36. Map < String , DataSource > stringDataSourceMap = dynamicRoutingDataSource . getDataSources ();

  37. for ( Map . Entry < String , DataSource > dataSourceEntry : stringDataSourceMap . entrySet ()) {

  38. String datasourceKey = dataSourceEntry . getKey ();

  39. ItemDataSource itemDataSource = ( ItemDataSource ) dataSourceEntry . getValue ();

  40. DruidDataSource druidDataSource = ( DruidDataSource ) itemDataSource . getDataSource ();

  41. result . put ( datasourceKey , currentDatabaseName ( druidDataSource . getRawJdbcUrl ()));

  42. }

  43. }

  44. return AjaxResult . success ( result );

  45. }

  46. @Override

  47. public void setBeanFactory ( BeanFactory beanFactory ) throws BeansException {

  48. this . beanFactory = ( DefaultListableBeanFactory ) beanFactory ;

  49. }




good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter