net+Oracle开发过程中遇到的小问题
最新的项目开始使用Oracle后,5个月之间遇到一些在SqlServer中没有遇到的问题,这里记录并贴上一些常用的解决办法。
Oracle相关
一、数据库不同版本还原
刚开始我们一直使用Oracle12C进行开发,到上线服务器时说12C与可能不太稳定,有些问题不好定位就转用11g,这里牵扯到12C导出的DMP文件导入到11G上的问题,导出导入语句:
exp 账号/密码@STO_DataBase file=D:\STO_DataBase.dmp full=y imp 账号/密码@STO_DataBase file=D:\DataBackUp\STO_DataBase.dmp full=y
导入报异常版本不一样,无法导入,这里用一款小工具可以解决问题下载地址。
二、数据库创建
数据库文件大小受DB_BLOCK_SIZE决定,默认是8K对应的数据库文件是32G,所以创建数据库时数据库的大小不要设置最大值最好设置成无限大,可以多设置几个数据库文件,以免数据库大小不够用引起数据库异常。创建语句:
//创建表空间 CREATE TABLESPACE STO_Data LOGGING DATAFILE 'D:\DataBase\DbFile\STO_DATA.DBF', 'D:\DataBase\DbFile\STO_DATA1.DBF', 'D:\DataBase\DbFile\STO_DATA2.DBF' SIZE 512M AUTOEXTEND ON NEXT 512M MAXSIZE unlimited EXTENT MANAGEMENT LOCAL; //创建临时表空间 CREATE TEMPORARY TABLESPACE STO_Temp TEMPFILE 'D:\DataBase\DbFile\STO_Temp.DBF' SIZE 512M AUTOEXTEND ON NEXT 512M MAXSIZE unlimite EXTENT MANAGEMENT LOCAL; //创建用户 CREATE USER 用户名 IDENTIFIED BY 用户名密码 DEFAULT TABLESPACE STO_Data TEMPORARY TABLESPACE STO_Temp //用户赋权限 grant connect,resource,dba to 用户名
三、常见问题
(1)存储过程参数
存储过程的参数命名最好可以按照规则来否则会有问题,例如:
create or replace procedure Proc_Test(IN_CompanyName in varchar2, OUT_Table out sys_refcursor) as begin open OUT_Table for select * from Waybill_Pickup t where t.companyname=IN_CompanyName; end Proc_Test;
如果参数使用的和数据库中的表字段一样会引起失效,例如:
create or replace procedure Proc_Test(CompanyName in varchar2, OUT_Table out sys_refcursor) as begin open OUT_Table for select * from Waybill_Pickup t where t.companyname=CompanyName; end Proc_Test;
(2)定期释放Temp文件空间
长时间使用会使Temp文件越来越大,而引起某些操作异常。
alter tablespace STO_TEMP shrink space;
(3)索引失效
Oracle批量插入如果是用Oracle.DataAccess.Client.OracleBulkCopy 类,数据在插入过程中会引起索引失效,如果是业务表就会引起业务查询非常缓慢。但是插入效率很高,如果数据库使用读写分离,或者接口采用读写分离的两个表可以通过此方法进行插入,如果遇到索引失效可用以下语句查看和修复。
//查看失效索引 select * from user_indexes where Status='UNUSABLE' //重建失效索引 begin FOR cur in (select INDEX_NAME from user_indexes where Status='UNUSABLE') loop execute immediate 'alter index '|| cur.INDEX_NAME||' rebuild' ; END LOOP; end;
(4)遇到问题如何排查
当Oracle遇到内部问题出现异常和挂机后的排查步骤(Oracle服务器)
- 查找日志D:\app\Administrator\diag\rdbms\sto_database\stodatabase\alert
- 查询跟踪文件D:\app\Administrator\diag\rdbms\sto_database\stodatabase\trace
- 然后定位问题解决问题
参考文档
当时遇到的问题是:数据库直接Down掉了,连接时报没有监听程序。重启服务器后过一会数据库就挂掉了
检查Oracle日志:
D:\app\Administrator\diag\rdbms\sto_database\stodatabase\alert
查看Log文件,在日志文件中找到错误信息提示:
检查跟踪文件
D:\app\Administrator\diag\rdbms\sto_database\stodatabase\trace
在跟踪文件中查看错误信息引起的原因:
根据错误查找出引起错误的SQL语句,最后定位到是单条语句超过65535个参数引起,由于.net中使用了DbDataAdapter进行保存数据,他类似于拼接的Sql语句,更改批次提交数解决问题。
.net相关
一、处理传参和LONG RAW类型
DbCommand处理Oracle数据库默认参数绑定是按次序且不能查询LONG RAW类型,处理SqlServer数据库不用处理,
所以在初始化DbCommand时需要加入如下代码
(cmd as Oracle.DataAccess.Client.OracleCommand).BindByName=true; (cmd as Oracle.DataAccess.Client.OracleCommand).InitialLONGFetchSize = -;
二、批量插入
(1)OracleBulkCopy
OracleBulkCopy在Oracle.DataAccess.DLL中,速率很快,但不进行主键和唯一键检查,经常会将唯一索引弄坏,并且在插入过程中普通索引也会失效。
/// <summary> /// 批量插入数据库 /// </summary> /// <param name="datatable"></param> /// <returns></returns> public string OracleBulkInsert(DataTable datatable) { OracleBulkCopy bulkCopy = new OracleBulkCopy(_connStr, OracleBulkCopyOptions.UseInternalTransaction); try { bulkCopy.DestinationTableName = datatable.TableName; bulkCopy.BulkCopyTimeout = 600000; bulkCopy.BatchSize = 50000; if (datatable != null && datatable.Rows.Count != 0) bulkCopy.WriteToServer(datatable); return ""; } catch (Exception ex) { throw ex; } finally { if (bulkCopy != null) bulkCopy.Close(); } }
DataTable在插入时应注意字段类型和顺序,可以用以下代码获得表结构
/// <summary> /// 根据表名获取Table结构 /// </summary> /// <param name="tableName">表名</param> /// <returns></returns> public DataTable InitStructureByTable(string tableName) { DataTable dtColums = helper.GetDataSet("select column_name,data_type from user_tab_columns where table_name='" + tableName.ToUpper() + "' order by column_id", null).Tables[0]; DataTable dtNew = new DataTable(); dtNew.TableName = tableName; //匹配列数 for (int j = 0; j < dtColums.Rows.Count; j++) { switch (dtColums.Rows[j][1].ToString().ToUpper()) { case "DATE": dtNew.Columns.Add(new DataColumn(dtColums.Rows[j][0].ToString(), typeof(DateTime))); break; case "NUMBER": dtNew.Columns.Add(new DataColumn(dtColums.Rows[j][0].ToString(), typeof(double))); break; default: dtNew.Columns.Add(new DataColumn(dtColums.Rows[j][0].ToString(), typeof(string))); break; } } return dtNew; }
(2)DbDataAdapter批量插入
这种插入方式要比OracleBulkCopy要慢,相当于拼接了带参数的插入语句插入,注意UpdateBatchSize数值不要过大,否则可能由于单句Sql语句参数超过65535引起数据库崩溃。
/// <summary> /// 批量插入数据库 /// </summary> /// <param name="datatable"></param> /// <returns></returns> public string BulkInsertCopy(DataTable datatable) { DbDataAdapter adapter = _factory.CreateDataAdapter(); DbCommand cmd = _factory.CreateCommand(); DbCommandBuilder cb = _factory.CreateCommandBuilder(); try { adapter.UpdateBatchSize = 200; using (DbConnection conn = _factory.CreateConnection()) { conn.ConnectionString = ConnectionString; conn.Open(); cmd.Connection = conn; //cmd.Transaction = conn.BeginTransaction(); cb.DataAdapter = adapter; adapter.SelectCommand = cmd; adapter.SelectCommand.CommandText = "select * from " + datatable.TableName; adapter.FillSchema(datatable, SchemaType.Source); adapter.Update(datatable); //cmd.Transaction.Commit(); } return ""; } catch (Exception ex) { //cmd.Transaction.Rollback(); return ex.Message; } finally { cmd.Dispose(); cb.Dispose(); adapter.Dispose(); } }