最新的項目開始使用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;
二、批量插入
(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();
}
}