Oracle數(shù)據(jù)庫Guid作主鍵時執(zhí)行速度超慢的原因在哪里
Oracle數(shù)據(jù)庫GUID作為主鍵執(zhí)行速度過慢的原因到底在哪里呢?接下來我們開始介紹。首先介紹一下環(huán)境。數(shù)據(jù)庫使用Oracle10g,所有字符類型的字段都是varchar2[1]。所有的主鍵都使用Guid,在數(shù)據(jù)庫里是varchar2(36) 類型,相應(yīng)的,實體的Id 屬性的類型是string。ORM 使用的是NHibernate 2.1.0 和 FluentNHibernate1.1。
經(jīng)過一番排查之后發(fā)現(xiàn),問題的根源是NH將SQL語句傳遞給Oracle 時,所有字符型的參數(shù)都是nvarchar2 類型,而數(shù)據(jù)庫里對應(yīng)的字段卻是varchar2 類型,這將導(dǎo)致Oracle 無法使用索引,終于造成全表掃描,所以數(shù)據(jù)量稍大就慢得不行。
***種解決方法是,把數(shù)據(jù)庫中所有的字符型字段的類型由varchar2 更改為nvarchar2,出于種種原因我們不希望這么做。
第二種解決方法是,讓NH 把varchar2 作為參數(shù)類型傳遞給Oracle。
事實上,NH 默認(rèn)把.net 的string 映射為DbType.String[2],把DbType.String映射為nvarchar2[3]。把DbType.AnsiString 映射為varchar2[4]。
所以對于查詢比較簡單,只要把HQL的參數(shù)類型指定為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);
但是如何設(shè)置Update 和Delete語句的參數(shù)類型呢?這里有個小小的秘技,把映射文件里的屬性類型指定為“AnsiString”即可。
- public class RegionMap : TreeNodeMap<Region>
- {
- public RegionMap()
- {
- Table("INFRA_REGION");
- Id(t => t.Id, "REGION_ID").CustomType("AnsiString");
- ...
- }
- }
注意:一定要使用CustomType()而不是CustomSqlType()。
當(dāng)然了,要是把每一個配置文件都改一遍實在很煩,好像項目使用了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();
注意倒數(shù)第二行的.ExportTo(@"F:\temp\") 是為了測試一下生成的映射文件對不對而把映射文件輸出到了 “F:\temp\”,映射文件應(yīng)該像這個樣子:
- <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 可以節(jié)省空間之外,主要是為了避免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 行。
本文就介紹到這里,如果想了解更多Oracle數(shù)據(jù)庫的知識,可以看一下這里的文章:http://database.51cto.com/oracle/。
【編輯推薦】