【问题标题】:Horrible response time with NHibernate and Oracle11gNHibernate 和 Oracle11g 的可怕响应时间
【发布时间】:2013-11-18 05:49:12
【问题描述】:

我的问题既简单又复杂:

我正在使用带有 ODP 驱动程序的 NHibernate 3.3 和 Oracle 11g。

这段代码就像一个魅力:

var query = Session.CreateSQLQuery("SELECT * FROM wip_event_log WHERE track_id='" + trackId + "'");
query.AddEntity("l", typeof(MotIdenWipEventLog));
var results = query.List<MotIdenWipEventLog>();  

几毫秒我得到了结果集。 (只有 5 条记录来自具有 11.000.000 条记录的表)

另一方面,这段代码:

var results = Session.Query<MotIdenWipEventLog>().Where(m => m.TRACK_ID == trackId).ToList();

大约需要 4 秒才能获得 5 条记录!

我了解到 Oracle 数据库 (http://bit.ly/1bbSlB7) 中的 AnsiString 列存在问题,并在我的流利配置中添加了使用字符串的自定义约定:

Fluently
    .Configure(new Configuration().Configure())
    .Database(OracleClientConfiguration
    .Oracle10
    .ConnectionString(c => c.Is("User ID=XXXX;Password=XXXX;Data Source=(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 1.1.1.1)(PORT = 1521)))(CONNECT_DATA = (SID = iden01)))"))
)
.Mappings(
    cfg => cfg.FluentMappings.LocalAddFromAssemblyOf<MotIdenPackSalesModelsHeaderMap>().Conventions.Add<OracleStringPropertyConvention>()
).BuildConfiguration();

自定义约定MotIdenPackSalesModelsHeaderMap 是:

public class OracleStringPropertyConvention : IPropertyConvention
{
    public void Apply(IPropertyInstance instance)
    {
        if (instance.Property.PropertyType == typeof(string)) instance.CustomType("AnsiString");
    }
}

实体MotIdenWipEventLog定义如下:

[Serializable]
public class MotIdenWipEventLog
{
    public virtual String   TRACK_ID        { get; set; } // VARCHAR2(16 BYTE)  No
    public virtual String   ASSY_PART_NUM   { get; set; } // VARCHAR2(20 BYTE)  Yes
    public virtual String   ASSY_VER_CODE   { get; set; } // VARCHAR2(4 BYTE)   Yes
    public virtual int      PROC_ID         { get; set; } // NUMBER(9,0)        Yes
    public virtual String   WIP_EVENT_CODE  { get; set; } // VARCHAR2(4 BYTE)   Yes
    public virtual DateTime EVENT_DATETIME  { get; set; } // DATE               No
    public virtual int      EVENT_CLKSEQ    { get; set; } // NUMBER(12,0)       Yes
    public virtual String   AREA_ID         { get; set; } // VARCHAR2(8 BYTE)   Yes
    public virtual String   PERSONNEL_ID    { get; set; } // VARCHAR2(11 BYTE)  Yes
    public virtual String   STN_ID          { get; set; } // VARCHAR2(20 BYTE)  Yes
    public virtual int      WIP_COUNT       { get; set; } // NUMBER(3,0)        Yes
    public virtual String   STN_GROUP       { get; set; } // VARCHAR2(8 BYTE)   Yes
}

通过MotIdenWipEventLogMap类映射:

public class MotIdenWipEventLogMap : ClassMap<MotIdenWipEventLog>
{
    public MotIdenWipEventLogMap()
    {
        Table("WIP_EVENT_LOG");

        Id(m => m.TRACK_ID, "TRACK_ID").GeneratedBy.Assigned();

        #region Fields

            Map(m => m.TRACK_ID).Not.Nullable()
                .Length(16).Index("WIP_EVENT_LOG_IDX1");    //  VARCHAR2(16 BYTE)   No

            Map(m=>m.ASSY_PART_NUM).Nullable().Length(20);  //  VARCHAR2(20 BYTE)   Yes
            Map(m=>m.ASSY_VER_CODE).Nullable().Length(4);   //  VARCHAR2(4 BYTE)    Yes
            Map(m=>m.PROC_ID).Nullable();                   //  NUMBER(9,0)         Yes
            Map(m=>m.WIP_EVENT_CODE).Nullable().Length(4);  //  VARCHAR2(4 BYTE)    Yes
            Map(m=>m.EVENT_DATETIME).Not.Nullable();        //  DATE                No
            Map(m=>m.EVENT_CLKSEQ).Nullable();              //  NUMBER(12,0)        Yes
            Map(m=>m.AREA_ID).Nullable().Length(8);         //  VARCHAR2(8 BYTE)    Yes
            Map(m=>m.PERSONNEL_ID).Nullable().Length(11);   //  VARCHAR2(11 BYTE)   Yes
            Map(m=>m.STN_ID).Nullable().Length(20);         //  VARCHAR2(20 BYTE)   Yes
            Map(m=>m.WIP_COUNT).Nullable();                 //  NUMBER(3,0)         Yes
            Map(m=>m.STN_GROUP).Nullable().Length(8);       //  VARCHAR2(8 BYTE)    Yes
        #endregion
    }
}

Log4Net 的调试级别中查看我的 NHibernate 日志文件:

(...)   
2013-11-06 14:24:22,375 DEBUG - Opened IDataReader, open IDataReaders: 1
2013-11-06 14:24:22,376 DEBUG - processing result set
2013-11-06 14:24:26,956 DEBUG - result set row: 0
2013-11-06 14:24:26,959 DEBUG - returning 'F7012B200ZMH' as column: TRACK1_6_
(...)

并在 Loader.cs 类的 NHibernate 源代码中看到:

(...)
try
{
    HandleEmptyCollections(queryParameters.CollectionKeys, rs, session);
    EntityKey[] keys = new EntityKey[entitySpan]; // we can reuse it each time
    if (Log.IsDebugEnabled)
    {
        Log.Debug("processing result set");
    }
    int count;
    for (count = 0; count < maxRows && rs.Read(); count++)
    {
        if (Log.IsDebugEnabled)
        {
            Log.Debug("result set row: " + count);
        }
        object result = GetRowFromResultSet(rs, session, queryParameters, lockModeArray, optionalObjectKey, hydratedObjects, keys, returnProxies);
        results.Add(result);            
(...)

我找不到问题出在哪里...

我做错了什么?

有什么想法吗?

【问题讨论】:

    标签: c# oracle nhibernate oracle11g fluent-nhibernate


    【解决方案1】:

    与其说是一个解决方案,不如说是一个补丁,但您可以创建一个基于函数的索引来匹配您的应用程序请求的类型。

    例如,

    create index patch_index on your_table(cast(your_column as nvarchar2(16)));
    

    在 Oracle 11g 上使用 EXPLAIN PLAN 说明这一点。

    使用

    create table t(x varchar2(10));
    create index idx on t(x);
    insert into t values ('a');
    

    查询

    select * from t where x = 'a';
    

    给你以下计划

    | Id | Operation         | Name | Rows | Bytes  | Cost (%CPU) | Time     |
    --------------------------------------------------------------------------
    | 0  | SELECT STATEMENT  |      | 1     | 7     | 3 (0)       | 00:00:01 |
    |* 1 | TABLE ACCESS FULL | T    | 1     | 7     | 3 (0)       | 00:00:01 |
    --------------------------------------------------------------------------
    

    添加以下索引后

    create index t2 on t(cast(x as nvarchar2(10)))
    

    相同的查询现在为您提供以下计划

    ------------------------------------------------------------------------------------
    | Id | Operation                  | Name | Rows | Bytes | Cost (%CPU)| Time        |
    ------------------------------------------------------------------------------------
    | 0  | SELECT STATEMENT           |      |  1   | 19    | 2 (0)      | 00:00:01    |
    | 1  | TABLE ACCESS BY INDEX ROWID| T    |  1   | 19    | 2 (0)      | 00:00:01    |
    |* 2 | INDEX RANGE SCAN           | T2   |  1   |       | 1 (0)      | 00:00:01    |
    ------------------------------------------------------------------------------------
    

    如果您无法在应用程序端解决问题,您可以应用此技术。

    【讨论】:

    • 嗨法比安!感谢您的回答,并为create index t2 on t(cast(x as nvarchar2(10))) +1。我无法访问 DB...TRACK_ID 的索引存在。
    • “我无法访问数据库...”:那么没有快速修补您的问题 ;) 我希望有人想出一个适当的解决方案来解决您的问题。
    • jeje,我的意思是:“我没有权限修改数据库结构”。
    • 是的,这就是我的理解——你不能创建新的索引。
    猜你喜欢
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-25
    • 2018-08-23
    • 1970-01-01
    • 2021-02-28
    • 2010-12-14
    • 1970-01-01
    相关资源
    最近更新 更多