mybatis優化(轉)

mybatis優化(轉)

博客分類: 持久層


最近測試發現個myBatis 有個比較嚴重的性能問題, 描述如下:

1. define a bean class
public class Bean {
private int id;
private String desc;
private long price;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public long getPrice() {
return price;
}
public void setPrice(long price) {
this.price = price;
}
}

2. 如果在這個Bean定義中,存在一個屬性沒有 Getter方法 在運行過程中, Mybatis 會表現爲把 class Bean 確定成一個ComplexAccessPlan的對象。那麼, Mybatis對Bean對象填充SQL執行後的返回結果會造成比較嚴重的性能問題。 複雜對象填充Bean的結果在性能上表現比較差一點。 這個性能差異隨着需要設置屬性數量的增加, 性能成正比的下降。 目前我測試的結果是10個屬性情況下影響大約 5-6%的執行時間, 如果屬性增加到 84個(中文站的offer對象),他的性能會導致超過30%的下降。 具體的原因, 我會如下解釋:


com.mybatis.sqlmap.engine.accessplan.AccessPlanFactory Line 60

if (bytecodeEnhancementEnabled) {
try {
plan = new EnhancedPropertyAccessPlan(clazz, propertyNames);
} catch (Throwable t) {
try {
plan = new PropertyAccessPlan(clazz, propertyNames);
} catch (Throwable t2) {
plan = new ComplexAccessPlan(clazz, propertyNames);
}
}
}

com.mybatis.common.beans.ClassInfo Line256
public Method getGetter(String propertyName) {
Method method = (Method) getMethods.get(propertyName);
if (method == null) {
throw new ProbeException("There is no READABLE property named '" + propertyName + "' in class '" + className + "'");
}
return method;
}

class EnhancedPropertyAccessPlan/PropertyAccessPlan call com.mybatis.common.beans.ClassInfo.getGetter(String) that cause an exception when a bean have no Getter method, AccessPlan object choose ComplexAccessPlan.

3. 根據以上的代碼, 我們還可以得出如下結論(這是我給MYBATIS開發團隊的郵件部分, 不翻譯:)):
IBtatis automatic decide a simple bean that property have no Getter method to be Complex type. Mybatis does not prompt any warning enhancementEnable option will be skipped. I think these mybatis exception handling is not smooth. and If user's bean loose some Getter method, a common user does not know why mybatis performance become bad.
就是一個對象由於Getter方法的缺失, Mybatis把這個對象的當做複雜對象, 從而, 導致enhancementEnable=true(bean對象字節增加功能, 有興趣的同學可以看看CGLIB中BulkBean的使用)的定義失去了任何作用, 進一步導致MYBATIS的性能下降。

針對我們發現的問題, 我們建議如下解決問題:
1. 任何被Mybatis 使用的對象屬性必須定義完整的Setter/Getter方法
2. 避免使用自定義類型的對象屬性
3. 如果部分屬性需要被適當處理後才能使用的, 比如表中有一個字段price, 但是我們需要使用的是Money對象, 請按如下方式使用。primitivePrice作爲數據庫使用的屬性, price作爲應用程序使用的屬性。
public class Bean {
private Money price = null;
private long primitivePrice;
public Money getPrice() {
if (price == null) {
this.price = new Money(0, 0);
this.price.setCent(primitivePrice);
}
return price;
}

public void setPrice(Money price) {
if (price == null) {
this.price = new Money(0, 0);
} else {
this.price = price;
}
this.primitivePrice = price.getCent();
}

public void setPrimitivePrice(long price) {
this.primitivePrice = price;
}
public long getPrimitivePrice() {
return this.primitivePrice ;
}
}
======================================================================
Mybatis on Oracle的性能優化
我們先主要看2個參數
1.defaultRowPrefetch of oracle
2.enhancementEnabled of Mybatis
環境
1. Java HotSpot(TM) Server VM (build 1.5.0_12-b04, mixed mode)
Java HotSpot(TM) Server VM (build 1.6.0_05-b13, mixed mode)
2. Intel(R) Core(TM)2 CPU T7400 @ 2.16GHz L2 4M
3. JVM OPTION -Xms512m -Xmx1024m -XX:PermSize=96m
從數據庫中讀取10000行, 5列數據情況, Java Bean對象大約不到100個屬性。循環20次, 外加5次的贓數據。
A. defaultRowPrefetch=default enhancementEnabled=false/true 754ms/743ms
B. defaultRowPrefetch=50 enhancementEnabled=false/true 389ms/382ms
C. defaultRowPrefetch=100 enhancementEnabled=false/true 319ms/319ms
D. defaultRowPrefetch=200 enhancementEnabled=false/true 277ms/274ms
E. defaultRowPrefetch=500 enhancementEnabled=false/true 251ms/250ms
F. defaultRowPrefetch=1000 enhancementEnabled=false/true 242ms/238ms
G. defaultRowPrefetch=1000 enhancementEnabled=true 237ms(JAVA6)
H. defaultRowPrefetch=200 enhancementEnabled=true 271MS(JAVA6)

總結以上情況, 在數據行比較多的情況下, defaultRowPrefetch值的提高, 對於性能的影響是顯著的, 但是, 這個提升是犧牲很多內存爲代價的, 因此, 如果過高的defaultRowPrefetch值會導致內存比較緊張。 另外值得說明的是, 在一樣的參數前提下, JAVA6對於性能還是有一定的提升的。對於比較大的查詢, defaultRowPrefetch經驗值應該是200還是合理的。 另外, 對於enhancementEnabled選項帶來的收益, 相對來說比較少。 但是,對於高壓力的系統, 這是無IO等待下情況的代碼執行提高這些是非常值。

以上的測試數據列數比較少, 因此在JAVA BEAN的建立上是非常的節約時間的, 我們看看在差不多100個屬性的填充下的性能表現, 我們已經知道了defaultRowPrefetch帶來收益的經驗值。 因此, 我們設置defaultRowPrefetch=200.
A. defaultRowPrefetch=200 enhancementEnabled=false 1736ms
B. defaultRowPrefetch=200 enhancementEnabled=true 1721ms
C. defaultRowPrefetch=50 enhancementEnabled=true 1866ms

OK, enhancementEnabled繼續表明對性能的提升作用很小, 但是列的數據大小對性能的影響是非常大的。 但是, 我們無法確定這個時間是消耗在Java Bean 填充上 還是列讀取上。Mybatis沒有具體的辦法測試。 不過, 在減少結果參數說明的情況下, 性能能得到明顯的提升, 我們還是可以斷定, JAVA BEAN的被聲明成結果映射的時候, 儘量減少結果映射的列,可以獲得很高性能的提升。 因此, 使用Mybatis操作大量的數據的表, 建議只映射應該獲取到的數據, 而不是全部的列。 select * from db where... 你可以取需要的列到java bean. 總而言之: select * from db where... 這樣的形式對性能影響比 把所有的列映射到Java Bean 來的小! set bean property + ResultSet.getXXX(int index)的操作消耗了大部分的性能。


一些代碼片段:
數據原的定義
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass">
<value>oracle.jdbc.OracleDriver</value>
</property>
<property name="jdbcUrl">
<value>jdbc:oracle:thin:@10.0.0.1:1521:test</value>
</property>
<property name="properties">
<props>
<prop key="user">test</prop>
<prop key="password">test</prop>
<prop key="defaultRowPrefetch">50</prop>
</props>
</property>
</bean>

爲單個SQL查詢定義defaultRowPrefetch, 在Mybatis的定義中爲fetchSize
<select id="MS-FIND-PublishedOffers-By-MemberId-Paged" resultMap="RM-OfferResult" fetchSize="200">

CGLIB增強定義
<settings cacheModelsEnabled="true" enhancementEnabled="true" lazyLoadingEnabled="false" maxRequests="3000" maxSessions="3000" maxTransactions="3000" useStatementNamespaces="false"/>




根據Ibatis手冊上配置參數說明:
參數名稱
參數說明
cacheModelsEnabled
是否啓動ibatis中的緩存功能。
enhancementEnabled
是否需要POJO啓動Java字節碼增強功能,可以提升getter/setter的調用效能避免Java反射所帶來的性能開銷。
lazyLoadingEnabled
是否同時Lazy Loading帶來極大的性能提升。
useStatementNamespaces
是否使用 domain xml 文件中 namespace 別名配置。
maxRequests
最大併發請求數(Statemetn數)。
maxTransactions
最大併發事務數
maxSessions
最大Session數,當前最大允許的併發 SqlMapCliect數
maxSessions
須界於 maxTransactions和maxRequests之間進行配置
根據自己的多次實踐證明,發現這些配置參數的選擇與數值將對系統的影響是很大的。
經過多次摸索,按照以下配置參數進行配置可以將ibatis的性能進行提升。僅供參數,還需要根據您自己的實 際情況來界定。
具體配置參數如下:
<sqlMapConfig>
<settings cacheModelsEnabled="true"
lazyLoadingEnabled="true"
enhancementEnabled="true"
errorTracingEnabled="true"
maxSessions="1024"
maxTransactions="512"
maxRequests="2048"
useStatementNamespaces="true" />
發佈了152 篇原創文章 · 獲贊 1 · 訪問量 6195
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章