http://blog.csdn.net/educast/article/details/6602353
最近遇到了一个让人抓狂的性能问题。生产环境里有一张表的数据量目前达到了 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 |
.SetAnsiString( "Id" , id); |
var query = Session.CreateQuery( @"select t from Region as t |
.SetParameterList( "Ids" , ids.ToList(), NHibernateUtil.AnsiString); |
但是如何设置 Update 和 Delete 语句的参数类型呢?这里有个小小的秘技,把映射文件里的属性类型指定为“AnsiString”即可。
public class RegionMap : TreeNodeMap<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'" ) |
.ProxyFactoryFactory<ProxyFactoryFactory>() |
.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\" ); }) |
注意倒数第二行的 .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" /> |
<version name= "Version" type= "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > |
<column name= "Version" /> |
<property name= "CreateTime" type= "System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > |
<column name= "CREATETIME" /> |
<property name= "Name" type= "AnsiString" > |
[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 行。
- 使用Guid做主键和int做主键性能比较
使用Guid做主键和int做主键性能比较 在数据库的设计中我们常常用Guid或int来做主键,根据所学的知识一直感觉int做主键效率要高,但没有做仔细的测试无法 说明道理.碰巧今天在数据库的优化过程中 ...
- mysql批量insert速度超慢
在进行大批量数据insert的时候,我使用的是hibernate的进行save,而数据库采用mysql.但是在save的时候,速度很慢. 刚开始以为是MYSQL进行DNS解析的问题,于 ...
- GUID做主键真的合适吗
在一个分布式环境中,我们习惯使用GUID做主键,来保证全局唯一,然后,GUID做主键真的合适吗? 其实GUID做主键本身没有问题,微软的很多项目自带DB都是使用GUID做主键的,显然,这样做是没有问题 ...
- 基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理
我们在设计数据库表的时候,往往为了方便,主键ID一般采用字符串类型或者GUID类型,这样对于数据库表记录的迁移非常方便,而且有时候可以在处理关联记录的时候,提前对应的ID值.但有时候进行数据记录插入的 ...
- SQLSERVER如何使用递增排序的GUID做主键
场景: 产品表数据量较大想用Guid做表的主键,并在此字段上建立聚簇索引. 因为Guid是随机生成的,生成的值大小是不确定的,每次生成的数可能很大,也可能很小.这样会影响插入的效率 1.NEWSEQU ...
- 开发反模式(GUID) - 伪键洁癖
一.目标:整理数据 有的人有强迫症,他们会为一系列数据的断档而抓狂. 一方面,Id为3这一行确实发生过一些事情,为什么这个查询不返回Id为3的这一行?这条记录数据丢失了吗?那个Column到底是什么? ...
- 主键、外键、超键、候选键的区别【Written By KillerLegend】
先说一下属性的定义: 表的每一行对应一个元组,表的每一列对应一个域.由于域可以相同,为了加以区分,必须对每列起一个唯一的名字,称为属性(Attribute). 再来看看几个键的定义: 超键:在关系模式 ...
- NHibernate联合主键详细示例
使用NHibernate实现一对多,多对一的关联很是简单,可如果要用复合主键实现确实让人有些淡淡的疼.虽然很淡疼但还是要去抹平这个坑,在下不才,愿意尝试. 以示例进入正文,源码下载地址: 一.数据表关 ...
- MyBatis 返回insert操作主键
应用场景 在向数据库插入数据时,需要保留插入数据的id,以便进行后续的update操作或者将id存入其他表作为外键.但是,在默认情况下,insert操作返回的是一个int值,它并非表示主键id ...
随机推荐
- greenplum的用法
gp建表的实例 gp 创建外部表的实例:(外部表不能建立分布键) CREATE EXTERNAL TABLE user_app_tag ( ...
- git statsh命令报错解决
git stash命令主要用于当在一个分支的开发工作未完成,却又要切换到另外一个分支进行开发的时候,除了commit原分支的代码改动的方法外,提供暂存代码的方式. git stash命令参考这篇:ht ...
- 最新Java校招面试题及答案
本文作者在一年之内参加过多场面试,应聘岗位均为 Java 开发方向.在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点. 主要包括以下几个部分: Java 基础知识点 Java 常见集合 ...
- linux proc目录介绍
https://www.cnblogs.com/DswCnblog/p/5780389.html
- jquery ajax 请求中 中文汉字的 转码问题
1.汉字参数直接跟在请求连接的后面,这样需要使用encodeURIComponent(fileName)或者encodeURI(fileName)转码两次 后台使用URLDecoder.decode( ...
- 【Access2007】将Excel表导入至Access2007的当中一张已存在的表之中
将Excel表导入至Access2007,你会发现万恶的Access2007会帮你自己主动创建一张表.全然没有问你是否要插入一张已存在的表之中. 那么,我们须要这样解决: 一.依照正常的步骤先将Exc ...
- 5 -- Hibernate的基本用法 --1 ORM和Hibernate
目前流行的编程语言,如Java.C#等,它们都是面向对象的编程语言,而目前铸就的数据库产品,例如Oracle.DB2等,依然是关系数据库等.编程语言和底层数据库的发展不协调,催生出了ORM框架.ORM ...
- PostgreSQL存储过程(5)-异常错误处理
1. 异常错误处理 在PL/pgSQL函数中,如果没有异常捕获,函数会在发生错误时直接退出,与其相关的事物也会随之回滚.我们可以通过使用带有EXCEPTION子句的BEGIN块来捕获异常并使其从中恢复 ...
- WAF Bypass数据库特性(MSsql探索篇)
0x01 背景 探索玩了Mysql特性,继续来探索一下MSsql特性. 0x02 测试 常见有5个位置即:select * from admin where id=1[位置一]union[位置二]se ...
- urllib 基础模块
(1) urllib.request:最基本的HTTP请求模块,用来模拟发送请求,就像在浏览器里输入网址然后回车一样(2) urllib.error:异常处理模块,如果出现请求错误,我们可以捕获这些异 ...