阿里云rds数据库迁移实战
由于某几个业务表数据量太大,数据由业务写,数据部门读。 写压力不大,读却很容易导致长时间等待问题(读由单独系统进行读),导致连接被占用,从而容易并发稍稍增长导致全库卡死! 于是,就拆库呗。 业务系统拆分就不要做了(微服务化),没那工夫。 直接原系统拆两个数据源出来,对某几个高压力表的写就单独用这个数据源,从而减轻压力。 所以,分库工作就变为了两个步骤:
再由于方便性,数据库也是使用阿里的rds数据库,一个变为两个! 代码上做两个数据源很简单,尤其是在原有代码就写得比较清晰的情况下; 如下是使用springboot和mybatis做的多数据源配置:
= MainDataSourceConfig.SCAN_BASE_PACKAGE,sqlSessionFactoryRef = "sqlSessionFactory" </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> String SCAN_BASE_PACKAGE = "com.xxx.dao.mapper.main"<span style="color: #000000">;
</span><span style="color: #008000">/**</span><span style="color: #008000">
* xml 配置文件扫描路径
</span><span style="color: #008000">*/</span>
<span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> String SCAN_XML_MAPPER_LOCATION = "classpath:mybatis/mappers/mysql/main/**/*Mapper.xml"<span style="color: #000000">;
</span><span style="color: #008000">//</span><span style="color: #008000">jdbcConfig</span>
@Value("${jdbc.main.url}"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String jdbcUrl;
@Value(</span>"${jdbc.main.driver}"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String driverName;
@Value(</span>"${pool.main.maxPoolSize}"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">int</span><span style="color: #000000"> maxPoolSize;
@Value(</span>"${jdbc.main.username}"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String jdbcUserName;
@Value(</span>"${jdbc.main.password}"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String jdbcPwd;
@Value(</span>"${pool.main.maxWait}"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span> <span style="color: #0000ff">int</span><span style="color: #000000"> jdbcMaxWait;
@Value(</span>"${pool.main.validationQuery}"<span style="color: #000000">)
</span><span style="color: #0000ff">private</span><span style="color: #000000"> String validationQuery;
@Bean(name </span>= "druidDataSource"<span style="color: #000000">)
@Primary
</span><span style="color: #0000ff">public</span><span style="color: #000000"> DruidDataSource druidDataSource(){
DruidDataSource ds </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> DruidDataSource();
ds.setUrl(jdbcUrl);
ds.setDriverClassName(driverName);
ds.setMaxActive(maxPoolSize);
ds.setUsername(jdbcUserName);
ds.setPassword(jdbcPwd);
ds.setRemoveAbandoned(</span><span style="color: #0000ff">true</span><span style="color: #000000">);
ds.setMaxWait(jdbcMaxWait);
ds.setValidationQuery(validationQuery);
</span><span style="color: #0000ff">return</span><span style="color: #000000"> ds;
}
@Bean(name </span>= "dataSourceTransactionManager"<span style="color: #000000">)
@Primary
</span><span style="color: #0000ff">public</span><span style="color: #000000"> DataSourceTransactionManager dataSourceTransactionManager(){
DataSourceTransactionManager dm </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> DataSourceTransactionManager();
dm.setDataSource(druidDataSource());
</span><span style="color: #0000ff">return</span><span style="color: #000000"> dm;
}
@Bean(name</span>="sqlSessionFactory"<span style="color: #000000">)
@Primary
</span><span style="color: #0000ff">public</span> SqlSessionFactory sqlSessionFactory() <span style="color: #0000ff">throws</span><span style="color: #000000"> Exception {
SqlSessionFactoryBean sqlSessionFactory </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> SqlSessionFactoryBean();
ResourcePatternResolver resolver </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> PathMatchingResourcePatternResolver();
Resource[] mapperXmlResource </span>=<span style="color: #000000"> resolver.getResources(SCAN_XML_MAPPER_LOCATION);
sqlSessionFactory.setDataSource(druidDataSource());
sqlSessionFactory.setMapperLocations(mapperXmlResource);
</span><span style="color: #0000ff">return</span><span style="color: #000000"> sqlSessionFactory.getObject();
}
} <span style="color: #008000">//<span style="color: #008000"> 新数据源配置,仅仅改了下配置名,但是还不得不另一个配置类<span style="color: #000000">@Configuration @MapperScan(basePackages = ExtraDataSourceConfig.SCAN_BASE_PACKAGE,sqlSessionFactoryRef = "sqlSessionFactoryExt"<span style="color: #000000">) <span style="color: #0000ff">public <span style="color: #0000ff">class<span style="color: #000000"> ExtraDataSourceConfig {
} 然后,将需要分离的表操作转移到相应的包路径下,即可实现多数据源操作了! classpath:mybatis/mappers/mysql/main/**/*Mapper.xml
所以,还是那句话:不是所有听起来好的东西就一定是好,在这里转换为不是所有听起来方便的东西用起来就一定方便!
如果是自行搭建的mysql服务,我们很自然地考虑使用binlog同步(主从)来做!具体配置方法也不复杂,自行查找资料即可! 如果使用阿里云服务,则不是binlog那样的配置了(但其实质仍然是对binlog的订阅写)。不过倒也都是页面操作!(网上不一定好找资料,但是官网上一定是最全的) 要进行数据库迁移,大体步骤分为:
DTS服务(数据传输服务),专门用于做数据迁移和数据同步!其打开方式为: 1. 自然是花钱买服务了,买好后才能进入操作页面;这里服务要分两个:一是新实例rds数据库,二是新实例同步回原实例的同步服务; 2. 设置ip白名单,以使mysql可访问; 3. 创建高权限用户,如root,以使后续可高权限操作mysql,同步时可使用该账号或者另用一个普通读写账号; 4. 将全量数据刷入新实例,这里可以选择阿里云免费的数据库迁移服务,也可以自己将数据dump下来,然后自行导致新库;(不过服务既然是免费的,那为啥不用呢!) 5. 设置单向同步,从新实例到原实例,此时是不会有数据同步的,因为没有新写入; 6. 数据刷入,同步设置完成后,就可以发布新代码了,此时最好将前端入口停止,否则可能出现数据错乱问题; 7. 发布代码后,需要自行验证。此时,先选择一台机器进行验证,可以选择两种方式验证:一是自行调用关键接口进行验证;二是将该机器绑定eip外网,使用该外网进行页面访问验证(更完整的验证);验证的方向主要有两个:1. 接口正常响应,没有错误发生(此处应该要有监控设施,否则只能凭感觉);2. 数据有没有正常同步(一般同步都是秒级的); 8. 将代码发布到集群中,观察各机器运行情况!此处主要查看数据库连接情况,是否存在连接失败情况,应用监控是主要手段,也可以通过mysql的show full processlist; 进行查看应用连接db情况; 9. 观察正常后,此时可以将前端应用入口打开,此时如有条件,应限制ip访问,使变更进行充分测试无误; 10. 一切无误后,完全开放访问服务;监控用户数据,迁移完成; 至此,整个迁移就完成了,其实思路是很简单的,关键是要小心操作。一个不小心的操作,就可能带来很大的隐患,毕竟,数据无小事!请保持对数据和代码的敬畏! 临了临了,附几个操作的贴心小技巧,避免入坑!1. 买rds数据库时,尽量买与原实例相同的区域(大区和可用区都相同),否则后期在做同步的时候会花更多的钱,因为跨区的网络通信会让你支付更多; 2. 新实例数据库容量可以稍微降配以节省钱,因为毕竟你是将原来的部分功能拆分出来的,没必要一开始就为全部将来买单; 3. 买同步服务时,注意几点:1. 按量计算(按小时)比预付费(包月)更贵,但是也更容易订制化,如果仅仅操作两个rds间同步,且短时间内不会下线服务,则建议选择预付费包月形式;2. 将区域选择正确,比如同区域同步将更便宜;3. 能单向同步就不要双向同步了,便宜的同时,也减小了误操作带来的影响;4. 同步性能一般小流量选择small即可,高配的同步用不上关键是贵; 4. 同步服务尽早开启,但是后期对于账号密码的变更,一定要及时更改同步配置,否则将带来数据一致性问题;(人工发现往往较晚,尽量设置监控报警) 5. 数据库白名单中,需要加入阿里云数据传输服务的白名单,否则无法检查数据库响应性及同步作业; 6. 选择同步对象时,尽量以库作为单位!如果选择以表为同步单位,将存在后续新增表时,不会同步回原实例情况。如果实在不能以库作为单位,在后续迭代时,一定记得添加此处同步表;(关注点太多,麻烦) 7. 后期做数据变更时,注意操作对象所属实例,别一顿操作猛如虎,然后没什么卵用,因为我们只是选择了单向同步; 8. 自己可以不定期地做checksum检查,以确认同步功能正常工作;(checksum table test) 9. 代码上分库一定要做准确了,因为这里可能是一定时间内的唯一可信参考资料;(简单但是关键) 最后,我还想说下使用别人服务和自己动手的一些个人感觉:1. 使用自己搭建的服务,最大的好处在于可以做任意的改变不受限,而且不需要付出额外的可见费用; 2. 使用自己的服务的可能坏处是:如果你不是这方面的专家,往往会被自己埋下的各种坑难住;遇到问题没能力处理;考虑方面不周全,容易引发安全问题;对未来的因素没办法考虑,使后期运作困难;如果你是专家,那多半这些都不是事儿; 3. 使用别人的服务,最大的好处就是简单易用,且有人维护;这些服务往往都是一路填坑过来的,时间越久往往越可靠(百年老字号最佳,哈哈);安全性、扩展性、性能调优、高可用等等; 4. 使用别人的服务,其坏处主要是钱的问题,这个自不必说。还有个不是钱的问题的坏处,那就是你不能随意订制你想要的功能了,你的能力被别人限制住了,这个可能促使你转场到自己提供服务;另外,各家提供的服务都不一样,不像自己搭建的服务,网上会有各种资料可查,所以有一定的学习成本,具体取决于官方设计与官方文档的完整性(当然一般都会很简单);其实还有一个,就不说了,懂的都懂; ? 好了,借着数据库迁移的小事,扯了这些淡。只当是抛砖引玉了!欢迎指教! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- MYSQL教程Mysql数据库之主从分离实例代码
- mysql exists与not exists的简单示例
- MYSQL教程Mysql查看连接数、连接状态的方法
- Mysql实例mysql 一个较特殊的问题:You can't specify
- MYSQL数据库MySQL OOM 系统二 OOM Killer
- Mysql实例mysql 导入导出数据库、数据表的方法
- java – 如何对OK的JOptionPane.showMessageDialog执行操作
- mysql – 最大SQL连接表数限制是否适用于整个查询,还是单独
- Mysql入门mysql中SELECT语句的执行顺序
- Mysql入门mysql中engine=innodb和engine=myisam的区别介绍