使用copy函数完成数据库迁移
最近在该一个迁移工具的迁移方式,从ora8迁移到postgresql使用原来的插入迁移速度太慢了,老板说让使用缓存迁移,即使用postgresql的copy函数,因此去pg官网查阅了相关资料,我们需要迁移的数据量大约有3000万条,需要时间在半个小时之内,这个迁移第一步先把相关的表结构迁移过去,然后开始导入数据,下面是我缓存迁移写的一段代码,核心都在里面:
1 private boolean toPostgresql(int tableIndex) throws SQLException, IOException
2 {
3 int success_flag = 0; // 设置成功标志
4
5 boolean isHasError = false;
6
7 TableConfig tableConfig = (TableConfig) tableConfigList.get(tableIndex);
8 ITable destTable = tableConfig.getDestTable(); // 可以通过这个获得目的表的表名字
9 ITable srITable = tableConfig.getDestTable();
10 // srITable.
11 String dest_tablename = destTable.getName().toString();
12 String dest_schema = destTable.getSchemaName();
13
14 Statement stmt = null;
15 ResultSet rs = null;
16
17 Setfetchsize s1=new Setfetchsize();
18 try
19 {
20 s1.getxml_setfetchsize();
21 } catch (ParserConfigurationException e)
22 {
23 // TODO Auto-generated catch block
24 e.printStackTrace();
25 } catch (SAXException e)
26 {
27 // TODO Auto-generated catch block
28 e.printStackTrace();
29 }
30 int stmt_setfetchsize = s1.getSetfetchsize_num();// 设置setfetchsize
31 // 取值大小
32 stmt = srcConn.createStatement();
33 stmt.setFetchSize(stmt_setfetchsize);
34 rs = getSrcResultSet(tableConfig, stmt);
35 ResultSetMetaData rsmd = rs.getMetaData();// 为了获得字段用的
36 // System.out.println("缓存setfetchsize设置为:"+stmt_setfetchsize);
37 int columnCount = rsmd.getColumnCount(); // 获得字段的个数
38 StringBuilder sbuild = new StringBuilder();
39 //System.out.println("stmt_setfetchsize实际取值为:"+stmt.getFetchSize());
40 /**
41 * 把结果集的数据拼接成字符串语句,保存到sbulid中 大小受缓存大小影响
42 */
43 KBCopyOutputStream kb_output = new KBCopyOutputStream((BaseConnection) destConn, "COPY " + dest_schema + "." + dest_tablename + " FROM STDIN");
44
45 while (rs.next())
46 {
47
48 row_totalnum += 1; // 记录总数据的总行数
49 for (int i = 1; i <= columnCount; i++)
50 {
51 String val = rs.getString(i);
52 int coltype = rsmd.getColumnType(i);
53 // 对字符字段的转义字串进行处理,使其转义效果失效
54 if (coltype == Types.CHAR || coltype == Types.VARCHAR || coltype == Types.NCHAR || coltype == Types.NVARCHAR || coltype == Types.LONGVARCHAR)
55 {
56 int valen = val.length();
57 sbuild.ensureCapacity(valen + 4);
58 for (int j = 0; j < valen; j++)
59 {
60 char ch = val.charAt(j);
61 switch (ch)
62 {
63 case '\t' :
64 sbuild.append("\\t");
65 break;
66 case '\n' :
67 sbuild.append("\\n");
68 break;
69 case '\r' :
70 sbuild.append("\\r");
71 break;
72 case '\\' :
73 sbuild.append("\\\\");
74 break;
75 default :
76 sbuild.append(ch);
77 }
78 }
79 } else
80 sbuild.append(val);
81
82 if (i < columnCount)
83 sbuild.append('\t');
84 }
85 sbuild.append('\n');
86
87 String s = sbuild.toString();
88 byte[] bytes = s.getBytes("UTF-8");
89 kb_output.write(bytes);
90 if (row_totalnum % 10 == 0)
91 {
92 successRowNum = 0;
93 addToSuccessNum(row_totalnum);
94 }
95 // System.out.print(s);
96 sbuild.setLength(0);
97 }
98 kb_output.close();
99 success_flag = 1;
100 destConn.commit();
101
102 if (success_flag == 1)
103 {
104 writeFinishResult(srITable.getFullName(), destTable.getFullName(), row_totalnum, row_totalnum, 0);
105 successRowNum = 0;
106 addToSuccessNum(row_totalnum);
107 } else
108 {
109 writeFinishResult(srITable.getFullName(), destTable.getFullName(), row_totalnum, 0, row_totalnum);
110 addToErrorNum(row_totalnum); // copyin执行失败返回错误数
111 }
112 writeSteps(" ");
113 return !isHasError;
114 }
我们的setfetchsize可以自己设置一下,这个目的是减少来回访问oracle拿取数据的开销,具体用法可以查看相关资料,主要是访问的数据量大的时候用的。上面的代码有个小缺陷,就是我把处理过的数据都放到一个stringbuilder里了,当数据量很大超过内存时候,估计就跑不动了,你可以对stringbuilder里的数据进行大小判断一下,当满足的时候commit一下啊就行,这个数据迁移是在一个事务中进行的,所以最后有个commit提交!
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· SQL Server如何跟踪自动统计信息更新?
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· 分享一个我遇到过的“量子力学”级别的BUG。
· AI Agent爆火后,MCP协议为什么如此重要!
· Draw.io:你可能不知道的「白嫖级」图表绘制神器
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· Java使用多线程处理未知任务数方案