省流助手
项目使用的Mysql主从,并且做了读写分离,而在设置同步binlog文件的时候输入错了文件名,导致主库的更新内容没有同步到从库上,又因为期间修改了表结构,所以当接口去向从库发起请求的时候会因为从库的表结构与实例无法对应起来而导致请求失败(Unknown column ‘xxx’ in ‘field list’)。之前的接口都涉及到写操作,都是对主库进行操作,因此没有出现以上的问题。
解决方案: 设置好主从关系并且再手动同步一下主从库即可。
问题分析与定位
问题代码如下:
@DS(DataSourceConstant.SLAVE)
@Override
public CouponEntity getById(Serializable id) {
ValueOperations<String,CouponEntity> couponOps = redisTemplate.opsForValue();
String key = COUPON_TEMPLATE_PREFIX+id;
CouponEntity couponEntity = couponOps.get(key);
if(couponEntity!=null){
return couponEntity;
}else{ // 将模版查询好放入到redis缓存中
System.out.println("查询");
CouponEntity entity = this.baseMapper.selectById(id);
System.out.println(entity);
couponOps.set(key,entity);
return entity;
}
}
函数内容就是一般的查询语句,但是在测试中却出现了问题,仔细核对数据库字段,并没有发现问题,并且之前的其他接口没有出现问题。也就是直接调用couponDao并不会出现问题,而通过这个方法去调用就有问题,示意测试代码如下:
@RunWith(SpringRunner.class)
@SpringBootTest
public class CouponApplicationTests {
@Autowired
CouponService couponService;
@Autowired
CouponDao couponDao;
// 这个测试会报错Unknown column 'xxx' in 'field list'
@Test
public void TestEhCache() {
couponService.getById(9);
couponService.getById(9);
}
// 下面这个测试正常,没有报错
@Test
public void Testmysql() {
couponService.getBaseMapper().selectById(9);
couponDao.selectById(9);
}
}
可以分析到,实例与数据库表字段映射是没有问题的,而其中请求的sql语句都是一样的,那么可能出问题的就是在两个方法他们的数据库环境不同而导致的错误。
问题代码中有注解@DS,其作用是切换数据源,切换到从库,而若不声明,就默认是主库。使用Navicat查看发现从库和主库的表结构不一致。
使用show slave status;查看从库状态
使用show master status;查看主库状态
发现两者的binglog(mysql-bin.xxxx)不一致,即主从同步设置失败。
此时只要设置正确的主从同步关系,并且手动同步一下主库和从库即可
手动同步主库和从库
同步方式就是通过备份sql文件重放完成同步
分为一下几步:
1.获取需要同步的表的备份sql文件,在主库机器上执行
# mysqldump -u<用户名> -p -h<主机地址> <数据库名> <表名> > <备份文件名>
mysqldump -uroot -p -hlocalhost greensource_sms sms_coupon_history > sms_coupon_history.bak.sql
2.将备份传输到从库的机器上
3.连接从库mysql,使用source重放sql
source /sms_coupon_history.bak.sql
注意文件路径
4.设置正确的主从关系
change master to master_host = '主库主机', master_user = '同步用户名', master_port=端口, master_password='密码', master_log_file = '主库的mysql-bin.xxxxxxxx', master_log_pos=主库的position;
start slave;
show slave status;
等待sql执行完毕即可。