问题记录
17日生产问题记录:
1、移动端时间异常bug(暂未确定是前端还是后端问题~)
有使用者反馈,移动端的查询接口出现返回信息错误情况,错误情况是返回的日期应该是2020-11-15 15:30:00,结果返回信息为2046-11-15 15:30:00,查看后端代码,由于前端要求返回的是时间戳,所以后端的SQL查询进行了格式化,如下:
将此SQL在测试环境的Oracle数据库可以正常查询到结果,且经过时间戳转换工具也是正常时间,生产上一直也是正常的,没有出现过时间戳格式化错误的问题。
解决方案:需要使用者再尝试复现一次,多次切换查询条件,验证问题。
2、开发功能的代码评审意见
(1)代码中使用了几个字符串常量,需要自定义数据字典,避免使用字符串常量。
(2)idea在mvn clean package的时候,指定的settings.xml文件没有生效,需要加-s参数,暂未解决
(3)需要多测试查询Redis服务和原持久层框架切换为jpa后的接口。
3、接口莫名出现503
生产环境上以前一直容易出现504或者是500错误,今天忽然出现503错误,领导让我去排查,在复现过程中,发现接口信息时有时无,且503一般都是网络负载问题,将问题反馈给领导,确定一下是否有在生产环境做压力测试,导致服务器负载出现问题或者是网关是否有问题。
后续反馈:系统间调用采用的服务网格的边车模式,边车相关存在异常,找到相关负责人去排查后解决。
4、生产数据库表恢复
在服务网格问题排查后,对系统进行重启和升级后,发现启动日志中报错,报错信息为表或视图不存在,查看报错SQL,发现是那张最重要的一张表,所以项目组后端成员紧急开会讨论问题所在。
登录生产环境数据库,对该表进行查询后,发现真的删除掉了,而且打电话给运维人员,也得到了确定,是运维人员的误操作导致,所以要尽快恢复。众人拾柴火焰高,开始对数据库表的操作进行分析:
(1)查看Oracle数据库回收站
select object_name,original_name,partition_name,type,ts_name,createtime,droptime from recyclebin;
(2)进行恢复
FLASHBACK TABLE SYS_C008097 TO BEFORE DROP;
在执行第二步操作后,发现表结构已经恢复,但是数据没有恢复,后来运维确定后,是由于使用了truncate 导致数据无法恢复。。
询问原因,是由于近期在做数据库国产化迁移,已将此表的信息迁移到了其他数据库,但是原本想要删除迁移目标库中的此表,由于删除时没有注意,误删了生产上正在使用的表,历史数据也已经迁移到国产化数据库中,所以也还是有历史数据可以恢复的。
恢复方案:
(1)使用迁移工具恢复:由于工具的局限性,仅可以将Oracle数据库迁移至国产化数据库,暂不支持逆向操作。
(2)使用其他数据迁移工具:由于生产环境内网严格,其他工具使用需要提交工单,且需要很多流程,比较复杂。
(3)写个嵌入的程序接口,去迁移目标库查询,再插入回生产数据库表中
经过讨论后,最后采用第三中方式。
数据恢复
恢复方案:
使用最简单的JDBC方式分别连接两个数据库,直接使用main方法的方式实现:
public class DB2DB {
public static void main(String[] args) {
DB2DB test = new DB2DB();
try {
test.deal();
} catch (SQLException e) {
e.printStackTrace();
}
}
private Connection getPolarconn(){
try {
Class.forName("com.aliyun.polardb.Driver");
return DriverManager.getConnection("jdbc:polardb://XXX.XXX.XXX.XXX:XXXX/DB","user","password");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null;
}
private Connection getOracleconn(){
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
return DriverManager.getConnection("jdbc:oracle:thin@//XXX.XXX.XXX.XXX:XXXX/DB","user","password");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null;
}
public void deal() throws SQLException {
//有数据的数据库
Connection polarConn = getPolarconn();
Statement itestmt =polarConn.createStatement();
ResultSet iters = itestmt.executeQuery("select * from test_table_1");
//结果集获取到的长度
int size = iters.getMetaData().getColumnCount();
//直接拼接insert into 语句,为第二个数据库做准备
StringBuffer sbf =new StringBuffer();
sbf.append("insert into test_table_1values (");
String link ="";
for (int i = 0; i <size ; i++) {
sbf.append(link).append("?");
link=",";
}
sbf.append(")");
//目标数据库
Connection oracleConn = getOracleconn();
PreparedStatement dataPstmt = oracleConn.prepareStatement(sbf.toString());
//取出结果集并使用批处理向数据库插入数据
//完成条数
int count =0;
int num=0;
//取消事务(不写入日志)
oracleConn.setAutoCommit(false);
long start = System.currentTimeMillis();
while (iters.next()) {
++count;
for (int i=1;i<= size;i++) {
dataPstmt.setObject(i, iters.getObject(i));
}
//将预先语句存储起来,这里还没有向数据库插入
dataPstmt.addBatch();
//当count 到达 20000条时 向数据库提交
if (count % 20000 ==0 ){
++num;
dataPstmt.executeBatch();
System.out.println("第"+num+"次提交,耗时:"+(System.currentTimeMillis()-start)/1000.0+"s");
}
}
//防止有数据未提交
dataPstmt.executeBatch();
//提交
oracleConn.commit();
System.out.println("完成 "+count+" 条数据,耗时:"+(System.currentTimeMillis()-start)/1000.0+"s");
//恢复事务
// oracleConn.setAutoCommit(true);
//关闭资源
close(oracleConn,dataPstmt,null);
close(polarConn,itestmt,iters);
}
public void close(Connection conn,Statement stmt,ResultSet rs){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
使用此方法,在测试环境上尝试,发现80万条数据大约90s左右,考虑后续优化~