惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

SecWiki News
SecWiki News
量子位
The Cloudflare Blog
美团技术团队
T
The Exploit Database - CXSecurity.com
博客园 - 【当耐特】
Spread Privacy
Spread Privacy
P
Proofpoint News Feed
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 三生石上(FineUI控件)
T
Tor Project blog
博客园 - 司徒正美
宝玉的分享
宝玉的分享
T
Threatpost
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Secure Thoughts
T
Threat Research - Cisco Blogs
Hacker News: Ask HN
Hacker News: Ask HN
Jina AI
Jina AI
博客园 - 聂微东
A
Arctic Wolf
I
Intezer
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Know Your Adversary
Know Your Adversary
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
爱范儿
爱范儿
Hugging Face - Blog
Hugging Face - Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
小众软件
小众软件
T
Tailwind CSS Blog
The Hacker News
The Hacker News
L
LINUX DO - 最新话题
Hacker News - Newest:
Hacker News - Newest: "LLM"
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
TaoSecurity Blog
TaoSecurity Blog
Project Zero
Project Zero
博客园 - 叶小钗
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cloudbric
Cloudbric
雷峰网
雷峰网
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
大猫的无限游戏
大猫的无限游戏
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Troy Hunt's Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
V2EX - 技术
V2EX - 技术
The GitHub Blog
The GitHub Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog

博客园 - 1-2-3

[日记]超解压的几句话 配置NHibernate将枚举保存为Oracle数据库中的字符串 【效率】专为Win7系统设计的极简番茄计时器 - MiniPomodoro (附源码) AutoMapper 最佳实践 三国杀的10个人生感悟 [Oracle](不会的是三炮)把状态列表作为存储过程参数这件小事 [激励机制]浅谈内部竞争——如何让你的员工玩命干活? PC使用网线上网的条件下,通过PC的Wifi共享提供手机上网教程 [非技术]滑雪恐怖故事 Oracle 数据库的逻辑迁移 VMware Linux 下安装 Oracle 11gR2 RAC [javascript]图解+注释版 Ext.extend() Oracle 数据库隔离级别,特性,问题和解决方法 [双板滑雪]教学视频和技术贴精选 [日记]夜滑 [推荐]番茄工作法——专治拖延症、精神涣散、再要五分钟综合症 建模心法(3) 模型的演进——保持简单和弹性的3个建议 [日记]游长白遇梅花,植物大战僵尸 ExtJs4 与 SASS 的亲密接触——使用 SASS 自定义主题
[NHibernate] Guid 作主键速度超慢的背后
1-2-3 · 2011-07-12 · via 博客园 - 1-2-3

  最近遇到了一个让人抓狂的性能问题。生产环境里有一张表的数据量目前达到了 70 万条。结果发现无论是匹配主键的查询还是更新,执行一条语句居然需要 3.5 秒!如果把 NH Prof 中截获的 SQL 语句拿到 PL/SQL Developer 里执行,就只需几十毫秒。一开始还以为是NH的问题,后来发现其实另有隐情。
  介绍一下环境先。数据库使用 Oracle10g,所有字符类型的字段都是 varchar2 [1]。所有的主键都使用 Guid,在数据库里是 varchar2(36) 类型,相应的,实体的 Id 属性的类型是 string。ORM 使用的是 NHibernate 2.1.0 和 FluentNHibernate1.1。
  经过一番排查之后发现,问题的根源是 NH 将 SQL 语句传递给 Oracle 时,所有字符型的参数都是 nvarchar2 类型,而数据库里对应的字段却是 varchar2 类型,这将导致 Oracle 无法使用索引,终于造成全表扫描,所以数据量稍大就慢得不行。
  第一种解决方法是,把数据库中所有的字符型字段的类型由 varchar2 更改为 nvarchar2,出于种种原因我们不希望这么做。
  第二种解决方法是,让 NH 把 varchar2 作为参数类型传递给 Oracle。
  事实上,NH 默认把 .net 的 string 映射为 DbType.String [2],把 DbType.String 映射为 nvarchar2 [3]。把 DbType.AnsiString 映射为 varchar2 [4]
所以对于查询比较简单,只要把 HQL 的参数类型指定为 AnsiString 就行了。

var query = Session.CreateQuery(@"select t from Region as t
                                   where t.Id = :Id")
                     .SetAnsiString("Id", id);
var query = Session.CreateQuery(@"select t from Region as t
                                   where t.Id in (:Ids)")
                     .SetParameterList("Ids", ids.ToList(), NHibernateUtil.AnsiString);

但是如何设置 Update 和 Delete 语句的参数类型呢?这里有个小小的秘技,把映射文件里的属性类型指定为“AnsiString”即可。

public class RegionMap : TreeNodeMap<Region>
{
    public RegionMap()
    {
        Table("INFRA_REGION");
        Id(t => t.Id, "REGION_ID").CustomType("AnsiString");
        ...
    }
}

注意 一定要使用 CustomType() 而不是 CustomSqlType()。
当然了,要是把每一个配置文件都改一遍实在很烦,好像项目使用了 Fluent  NHibernate,只要添加一个 IdConvention 就行了。

public class IdConvention : FluentNHibernate.Conventions.IIdConvention
{
    public void Apply(FluentNHibernate.Conventions.Instances.IIdentityInstance instance)
    {
        instance.CustomType("AnsiString");
    }
}

想要彻底一点的话,可以再加一个 string 类型的 property 的 convention。

public class StringPropertyConvention : IPropertyConvention, IPropertyConventionAcceptance
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        criteria.Expect(x => x.Property.PropertyType == typeof(string));
    }

    public void Apply(IPropertyInstance instance)
    {
        instance.CustomType("AnsiString");
    }
}

把这两个 Convention 加到配置里面:

Session["SessionFactory"] = Fluently.Configure()
          .Database(OracleClientConfiguration.Oracle10
              .Dialect<Oracle10gDialect>()
              .ConnectionString("User ID=iBlast;Password=不可说;Data Source=Moki")
              .QuerySubstitutions("true 1, false 0, yes 'Y', no 'N'")
              .UseOuterJoin()
              .ProxyFactoryFactory<ProxyFactoryFactory>()
              .AdoNetBatchSize(1000)
              .Driver<OracleClientDriver>())
          .Mappings(m => { m.HbmMappings.AddFromAssembly(Assembly.Load("Infrastructure.Repositories")); 
                           m.FluentMappings.AddFromAssembly(Assembly.Load("Infrastructure.Repositories"))
                                           .Conventions.Add<EnumConvention>()
                                           .Conventions.Add<HasManyConvention>()
                                           .Conventions.Add<HasManyToManyConvention>()
                                           .Conventions.Add<StringPropertyConvention>()
                                           .Conventions.Add<IdConvention>()
                                           .ExportTo(@"F:\temp\"); })
          .BuildSessionFactory();

注意倒数第二行的 .ExportTo(@"F:\temp\") 是为了测试一下生成的映射文件对不对而把映射文件输出到了 “F:\temp\”,映射文件应该像这个样子: 

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
  <class xmlns="urn:nhibernate-mapping-2.2" dynamic-insert="true" dynamic-update="true" mutable="true" where="IsDelete=0" name="Dawn.HIS.Infrastructure.Core.Data.Region, Infrastructure.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="INFRA_REGION">
    <id name="Id" type="AnsiString">
      <column name="REGION_ID" />
      <generator class="assigned" />
    </id>
    <version name="Version" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="Version" />
    </version>
    <property name="CreateTime" type="System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="CREATETIME" />
    </property>
    <property name="Name" type="AnsiString">
      <column name="NAME" />
    </property>
    ...
  </class>
</hibernate-mapping>


[1] 之所以使用 varchar2 而不是 nvarchar2,除了考虑 varchar2 可以节省空间之外,主要是为了避免 nvarchar2 排序时的性能问题
[2] 见 NHibernate-2.1.0.GA-src\src\NHibernate\Type\TypeFactory.cs 第 197 行。
[3] 见 NHibernate-2.1.0.GA-src\src\NHibernate\Dialect\Oracle8iDialect.cs 第 92 行。
[4] 见 NHibernate-2.1.0.GA-src\src\NHibernate\Dialect\Oracle8iDialect.cs 第 88 行。