Guid作主鍵速度超慢的背後

2021-12-29 20:59:44 字數 2482 閱讀 2121

最近遇到了乙個讓人抓狂的效能問題。生產環境裡有一張表的資料量目前達到了 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

}注意 一定要使用 customtype() 而不是 customsqltype()。

當然了,要是把每乙個配置檔案都改一遍實在很煩,好像專案使用了 fluent  nhibernate,只要新增乙個 idconvention 就行了。

public class idconvention : fluentnhibernate.conventions.iidconvention

}想要徹底一點的話,可以再加乙個 string 型別的 property 的 convention。

public class stringpropertyconvention : ipropertyconvention, ipropertyconventionacceptance

public void apply(ipropertyinstance instance)

}把這兩個 convention 加到配置裡面:

session["sessionfactory"] = fluently.configure()

.database(oracleclientconfiguration.oracle10

.dialect()

.connectionstring("user id=iblast;password=不可說;data source=moki")

.querysubstitutions("true 1, false 0, yes y, no n")

.useouterjoin()

.proxyfactoryfactory()

.adonetbatchsize(1000)

.driver())

.mappings(m => )

.buildsessionfactory();

注意倒數第二行的 .exportto(@"f: emp") 是為了測試一下生成的對映檔案對不對而把對映檔案輸出到了 「f: emp」,對映檔案應該像這個樣子:

...[1] 之所以使用 varchar2 而不是 nvarchar2,除了考慮 varchar2 可以節省空間之外,主要是為了避免 nvarchar2 排序時的效能問題。

[2] 見 nhibernate-2.1.0.ga-srcsrcnhibernatetypetypefactory.cs 第 197 行。

[3] 見 nhibernate-2.1.0.ga-srcsrcnhibernatedialectoracle8idialect.cs 第 92 行。

[4] 見 nhibernate-2.1.0.ga-srcsrcnhibernatedialectoracle8idialect.cs 第 88 行。

Mysql操作主鍵

1.查詢主鍵 select t.table name,t.constraint type,c.column name,c.ordinal position from information schema.table constraints as t,information schema.key co...

自動編號主鍵與GUID主鍵詳解

這種方法也是很多朋友在使用的,就是新建乙個id欄位,自動增長,非常方便也滿足主鍵的原則,優點是 資料庫自動編號,速度快,而且是增量增長,聚集型主鍵按順序存放,對於檢索非常有利 數字型的,占用空間小,易排序,在程式中傳遞也方便 如果通過非系統增加記錄 比如手動錄入,或是用其他工具直接在表裡插入新記錄,...

關於使用GUID和Identity做主鍵的一些思考

通常,給資料庫中的表都新增乙個 無意義 的主鍵,能夠大大地簡化程式的開發。這個主鍵用什麼型別呢?其實各種型別,只要大小不超過900位元組都可以,但我們最常面臨的兩種選擇是 guid uniqueidentifity 和identity int。ado.net 2.0高階程式設計 一書的 5.2.2 ...