您的位置:首页 > 产品设计 > UI/UE

[NHibernate] Guid 作主键速度超慢的背后

2015-07-08 21:50 513 查看
http://blog.csdn.net/educast/article/details/6602353

最近遇到了一个让人抓狂的性能问题。生产环境里有一张表的数据量目前达到了70万条。结果发现无论是匹配主键的查询还是更新,执行一条语句居然需要3.5秒!如果把NHProf中截获的SQL语句拿到PL/SQLDeveloper里执行,就只需几十毫秒。一开始还以为是NH的问题,后来发现其实另有隐情。
  介绍一下环境先。数据库使用Oracle10g,所有字符类型的字段都是varchar2[1]。所有的主键都使用Guid,在数据库里是varchar2(36)类型,相应的,实体的Id属性的类型是string。ORM使用的是NHibernate2.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就行了。

viewsourceprint?

varquery=Session.CreateQuery(
@"selecttfromRegionast
wheret.Id=:Id"
)
.SetAnsiString(
"Id"
,id);
viewsourceprint?

varquery=Session.CreateQuery(
@"selecttfromRegionast
wheret.Idin(:Ids)"
)
.SetParameterList(
"Ids"
,ids.ToList(),NHibernateUtil.AnsiString);
但是如何设置Update和Delete语句的参数类型呢?这里有个小小的秘技,把映射文件里的属性类型指定为“AnsiString”即可。

viewsourceprint?

public
class
RegionMap:TreeNodeMap<Region>
{
public
RegionMap()
{
Table(
"INFRA_REGION"
);
Id(t=>t.Id,
"REGION_ID"
).CustomType(
"AnsiString"
);
...
}
}
注意一定要使用CustomType()而不是CustomSqlType()。
当然了,要是把每一个配置文件都改一遍实在很烦,好像项目使用了FluentNHibernate,只要添加一个IdConvention就行了。

viewsourceprint?

public
class
IdConvention:FluentNHibernate.Conventions.IIdConvention
{
public
void
Apply(FluentNHibernate.Conventions.Instances.IIdentityInstanceinstance)
{
instance.CustomType(
"AnsiString"
);
}
}
想要彻底一点的话,可以再加一个string类型的property的convention。

viewsourceprint?

public
class
StringPropertyConvention:IPropertyConvention,IPropertyConventionAcceptance
{
public
void
Accept(IAcceptanceCriteria<IPropertyInspector>criteria)
{
criteria.Expect(x=>x.Property.PropertyType==
typeof
(
string
));
}
public
void
Apply(IPropertyInstanceinstance)
{
instance.CustomType(
"AnsiString"
);
}
}
把这两个Convention加到配置里面:

viewsourceprint?

Session[
"SessionFactory"
]=Fluently.Configure()
.Database(OracleClientConfiguration.Oracle10
.Dialect<Oracle10gDialect>()
.ConnectionString(
"UserID=iBlast;Password=不可说;DataSource=Moki"
)
.QuerySubstitutions(
"true1,false0,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\”,映射文件应该像这个样子:

viewsourceprint?

<hibernate-mappingxmlns=
"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"
>
<idname=
"Id"
type=
"AnsiString"
>
<columnname=
"REGION_ID"
/>
<generator
class
=
"assigned"
/>
</id>
<versionname=
"Version"
type=
"System.Int32,mscorlib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"
>
<columnname=
"Version"
/>
</version>
<propertyname=
"CreateTime"
type=
"System.DateTime,mscorlib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089"
>
<columnname=
"CREATETIME"
/>
</property>
<propertyname=
"Name"
type=
"AnsiString"
>
<columnname=
"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行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: