天天看點

新發現:對比mybatis,delicacy代碼生成器

測試背景

最近公司接到一個新客戶關系管理項目,為了能夠提高開發效率,研發經理嘗試了很多種方法,終于在一個Spdycoding(https://www.spdycoding.com/)的網站上找到了可以快速自動生成代碼的delicacy生成器,讓我研究研究帶領小團隊學習使用。

delicacy架構是一個基礎MVC架構的背景架構,提供标準的接口通路,裡面封裝了DelicacyDao(資料庫持久層),無需再使用第三的資料庫持久層架構,官網提供delicacy代碼生成器,結合delicacy架構,可以通過資料庫定義的表結構、自定義寫的複雜SQL語句生成相應資料庫操作代碼(增、删、改、查),同時可以根據使用者的需求生成相對應的前端頁面代碼。

拿到生成器和jar包,我先看了反編出來的代碼,了解開發者的邏輯思維。這是一個與常用spring項目不同的思維,delicacy生成後的代碼與jar包中的基礎類契合,構成了以servlet為基礎的web項目。萬變不離其宗,以servlet為基礎則離不開接口業務分發,delicay使用了單一接口位址,按約定參數分發業務的方式。同時使用了内置按線程綁定的資料庫連接配接池,更令人驚訝的是,統一了對象的tojson方法,各對象重寫此方法實作自己的行為多态,着實驚豔!更多細節有品味之處便不在這裡一一講述。

驚豔過後,我開始探究此結構的便利性與執行性能,并試圖與springmvc+mybatis項目作對比,那麼第一步是建構項目。最典型的servlet項目pom、ant等打包,war部署,delicacy的項目同樣如此,隻要三個簡單的配置——web.xml、資料源與log4j日志,項目就可以啟動了(此處有坑,先賣關子)。當然由于此次是測試性質,我們在項目裡寫了很多程式起點的main()用來測試單獨的一個方面,也把war放入tomcat測試了應用性能。好了,閑言到此為此,請看以下測試資料。

測試項目及簡析

單表插入

delicacy第一次

單次循環插入

2019-12-07 01:14:32 [TestMain]-[WARN] 插入100條資料使用時間是:206

2019-12-07 01:14:33 [TestMain]-[WARN] 插入1000條資料使用時間是:1588

2019-12-07 01:14:47 [TestMain]-[WARN] 插入10000條資料使用時間是:14073

批量插入

2019-12-07 01:14:48 [TestMain]-[WARN] 插入100條資料使用時間是:168

2019-12-07 01:14:49 [TestMain]-[WARN] 插入1000條資料使用時間是:1389

2019-12-07 01:15:03 [TestMain]-[WARN] 插入10000條資料使用時間是:13736

delicacy第二次

單次循環插入

2019-12-07 01:19:16 [TestMain]-[WARN] 插入100條資料使用時間是:201

2019-12-07 01:19:18 [TestMain]-[WARN] 插入1000條資料使用時間是:1508

2019-12-07 01:19:32 [TestMain]-[WARN] 插入10000條資料使用時間是:14254

批量插入

2019-12-07 01:19:32 [TestMain]-[WARN] 插入100條資料使用時間是:169

2019-12-07 01:19:33 [TestMain]-[WARN] 插入1000條資料使用時間是:1394

2019-12-07 01:19:47 [TestMain]-[WARN] 插入10000條資料使用時間是:13840

delicacy第三次

單次循環插入

2019-12-07 01:22:00 [TestMain]-[WARN] 插入100條資料使用時間是:196

2019-12-07 01:22:02 [TestMain]-[WARN] 插入1000條資料使用時間是:1509

2019-12-07 01:22:16 [TestMain]-[WARN] 插入10000條資料使用時間是:14261

批量插入

2019-12-07 01:22:16 [TestMain]-[WARN] 插入100條資料使用時間是:168

2019-12-07 01:22:17 [TestMain]-[WARN] 插入1000條資料使用時間是:1390

2019-12-07 01:22:31 [TestMain]-[WARN] 插入10000條資料使用時間是:13723

mybatis第一次

單次循環插入

2019-12-07 01:16:24 [TestMain]-[WARN] 插入100條資料使用時間是:488

2019-12-07 01:16:26 [TestMain]-[WARN] 插入1000條資料使用時間是:1602

2019-12-07 01:16:41 [TestMain]-[WARN] 插入10000條資料使用時間是:15536

批量插入

2019-12-07 01:16:42 [TestMain]-[WARN] 插入100條資料使用時間是:87

2019-12-07 01:16:42 [TestMain]-[WARN] 插入1000條資料使用時間是:121

2019-12-07 01:16:42 [TestMain]-[WARN] 插入10000條資料使用時間是:507

mybatis第二次

單次循環插入

2019-12-07 01:20:33 [TestMain]-[WARN] 插入100條資料使用時間是:480

2019-12-07 01:20:34 [TestMain]-[WARN] 插入1000條資料使用時間是:1618

2019-12-07 01:20:50 [TestMain]-[WARN] 插入10000條資料使用時間是:15252

批量插入

2019-12-07 01:20:50 [TestMain]-[WARN] 插入100條資料使用時間是:89

2019-12-07 01:20:50 [TestMain]-[WARN] 插入1000條資料使用時間是:116

2019-12-07 01:20:50 [TestMain]-[WARN] 插入10000條資料使用時間是:505

mybatis第三次

單次循環

2019-12-07 01:23:51 [TestMain]-[WARN] 插入100條資料使用時間是:469

2019-12-07 01:23:53 [TestMain]-[WARN] 插入1000條資料使用時間是:1620

2019-12-07 01:24:08 [TestMain]-[WARN] 插入10000條資料使用時間是:15043

批量

2019-12-07 01:24:08 [TestMain]-[WARN] 插入100條資料使用時間是:71

2019-12-07 01:24:08 [TestMain]-[WARN] 插入1000條資料使用時間是:120

2019-12-07 01:24:08 [TestMain]-[WARN] 插入10000條資料使用時間是:511

測試操作步驟及設定情況

Log4j設定日志為warn,事務自動送出,每次測試後,都清空資料庫,再進行下一次測試。delicacy與mybatis的測試交替進行。

簡析

delicacy的平均單條插入時候在比mybatis短,連續100條時delicacy的單條平均時間為2ms,mybatis的單條平均為4.8ms.随着連續條數的增長到10000條,delicacy的單條平均時間為1.4ms,此時mybatis的單條平均為1.5ms.在測試樣本内,mybatis始終未能超越delicacy的單條插入能力。delicacy的批量插入時間顯著落後于mybatis。小樣本數量時delicacy與mybatis的批量插入時間較單條循環都有顯著提升,大樣本數量時,delicacy的批量插入時間趨近于單條循環時間。

父子表插入

Delicacy第一次

2019-12-07 23:09:41 [UnionTableTest]-[WARN] 插入100條資料使用時間是:1090

2019-12-07 23:09:52 [UnionTableTest]-[WARN] 插入1000條資料使用時間是:11048

2019-12-07 23:14:40 [UnionTableTest]-[WARN] 插入10000條資料使用時間是:287114

Delicacy第二次

2019-12-07 23:34:37 [UnionTableTest]-[WARN] 插入100條資料使用時間是:1366

2019-12-07 23:34:48 [UnionTableTest]-[WARN] 插入1000條資料使用時間是:11131

2019-12-07 23:39:34 [UnionTableTest]-[WARN] 插入10000條資料使用時間是:286707

Delicacy第三次

2019-12-07 23:47:15 [UnionTableTest]-[WARN] 插入100條資料使用時間是:1085

2019-12-07 23:47:26 [UnionTableTest]-[WARN] 插入1000條資料使用時間是:11096

2019-12-07 23:52:25 [UnionTableTest]-[WARN] 插入10000條資料使用時間是:299705

Mybatis第一次

2019-12-09 23:44:45 [UnionTest]-[WARN] 插入100條資料使用時間是:828

2019-12-09 23:44:49 [UnionTest]-[WARN] 插入1000條資料使用時間是:3456

2019-12-09 23:45:22 [UnionTest]-[WARN] 插入10000條資料使用時間是:32876

Mybatis第二次

2019-12-09 23:47:05 [UnionTest]-[WARN] 插入100條資料使用時間是:812

2019-12-09 23:47:08 [UnionTest]-[WARN] 插入1000條資料使用時間是:3524

2019-12-09 23:47:41 [UnionTest]-[WARN] 插入10000條資料使用時間是:32764

Mybatis第三次

2019-12-09 23:48:27 [UnionTest]-[WARN] 插入100條資料使用時間是:809

2019-12-09 23:48:31 [UnionTest]-[WARN] 插入1000條資料使用時間是:3504

2019-12-09 23:49:03 [UnionTest]-[WARN] 插入10000條資料使用時間是:32259

簡析

父子表插入時,mybatis的時間稍大于delicacy的時間,單就持久層來看mybatis更強。其主要原因有兩點:第一,mybatis沒有關心插入時的子表記錄的清洗,是純的持久層,而delicacy在插入時,根據入參不同,清理的原來的明細資訊。第二,從之前的記錄看,mybatis在單表批量插入的時候性能遠超delicacy,也以在此次測試時,我們使用1條記錄5條明細的插入對mybatis批量插入有優勢。

測試後我及時向此架構的開發者回報了單表批量插入情況,開發者也作出了及時的針對mysql資料庫的優化,我們在最後完成測試計劃後再做驗證。

多線程單表插入

Delicacy第一次

2019-12-11 22:00:10 [MultiThreadInsert]-[WARN] 10個線程并發完成100000條插入的時間是26933

Delicacy第二次

2019-12-11 22:00:58 [MultiThreadInsert]-[WARN] 10個線程并發完成100000條插入的時間是26624

Delicacy第三次

2019-12-11 22:06:34 [MultiThreadInsert]-[WARN] 10個線程并發完成100000條插入的時間是27119

Mybatis第一次

2019-12-11 21:49:08 [MultiThreadInsert]-[WARN] 10個線程并發完成100000條插入的時間是28075

Mybatis第二次

2019-12-11 21:50:49 [MultiThreadInsert]-[WARN] 10個線程并發完成100000條插入的時間是27891

Mybatis第三次

2019-12-11 21:51:30 [MultiThreadInsert]-[WARN] 10個線程并發完成100000條插入的時間是27884

簡析

多線程插入時,delicacy的速度比mybatis快。測試時使用了10線程的固定線程池,都使用了連接配接池,delicacy是線程綁定的連接配接池,mybatis是預設連接配接池,預設連接配接數是10。此結果從側面驗證了第一項測試的結果:單條插入delicacy比Mybatis快。

在測試時觀查到“Wed Dec 11 22:06:07 CST 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.”這并不是第一次出現,因為我的資料庫預設提示使用賬号密碼的連接配接,固jdbc驅動在WARN級别作出提示,但更奇怪的時,在給mybatis做測試的時候,此文本列印次數超過10次,為348次,但從mysql workbench中監測到确實隻有10個連接配接在工作,而delicacy的列印次數隻有10次,workbench中也隻有10個連接配接工作。說明Mybatis連接配接資料庫的次數超過了10次,總連接配接數正常,其預設連接配接池工作原理還有待深入檢查。

單表查詢

Delicacy第一次

2019-12-11 23:48:11 [SingleTableSelect]-[WARN] 執行10000次查詢的時間是:3534

Delicacy第二次

2019-12-11 23:48:47 [SingleTableSelect]-[WARN] 執行10000次查詢的時間是:3343

Delicacy第三次

2019-12-11 23:49:01 [SingleTableSelect]-[WARN] 執行10000次查詢的時間是:3367

Mybatis第一次

2019-12-11 23:28:41 [SingleSelectTest]-[WARN] 執行10000次查詢的時間是:4043

Mybatis第二次

2019-12-11 23:29:03 [SingleSelectTest]-[WARN] 執行10000次查詢的時間是:4099

Mybatis第三次

2019-12-11 23:29:19 [SingleSelectTest]-[WARN] 執行10000次查詢的時間是:4027

簡析

在被邀請做此測試的時候已經預見到此結果,delicay的單表查詢比mybatis快。Delicay是生成器的目标代碼,在sql結果轉換成java Bean的時候使用了直接創造對象new,但是mybatis是通用持久層,隻能用反射建立對象。Java反射一直比直接創造對象要慢。

父子表查詢

Delicacy第一次

2019-12-12 22:32:31 [UnionTableSelect]-[WARN] 執行10000次父子表查詢的時間是:5219

Delicacy第二次

2019-12-12 23:43:59 [UnionTableSelect]-[WARN] 執行10000次父子表查詢的時間是:4979

Delicacy第三次

2019-12-12 23:44:25 [UnionTableSelect]-[WARN] 執行10000次父子表查詢的時間是:4950

Mybatis第一次

2019-12-12 01:26:29 [UnionSelectTest]-[WARN] 執行10000次父子表查詢的時間是:2840

Mybatis第二次

2019-12-12 01:33:57 [UnionSelectTest]-[WARN] 執行10000次父子表查詢的時間是:2834

Mybatis第三次

2019-12-12 01:34:12 [UnionSelectTest]-[WARN] 執行10000次父子表查詢的時間是:2866

簡析

首先,當我看到父子表查詢消耗的時間少于單表查詢時,立即讓我以為測試出了問題。檢查後發現是資料庫中的資料量變少了,單表查詢時User表中資料是60萬,現在user表中資料是5.5萬,Customer表中資料是1.1萬.并且user表中的customer_id字段做了索引(沒做索引時很慢,explan後發現user表被全表掃描)。

其次,Delicacy在做父子表查詢時對子表的查詢是獨立的查詢,然後把子表資料裝進傳回對象的list中,是以在本次測試中10000次查詢,則delicacy實際進行了20000次查詢資料庫的操作,因而速度變慢。從具體的時間消息上也可以證明此過程。

Web項目應用感受

delicacy程式搭建:

Ssm 這個結構在國内網上有大把的樣闆代碼,配置可整套複制然後修改即可,maven依賴各大倉庫都有。但值得說的是:ssm本人也很久沒有用過了,加上idea 社群版不能直接調試,在這個項目搭建上也折騰了好一會。再說delicacy,除資料連接配接和日志外沒有配置,這個很爽。但是在啟動項目過程中遇到兩次運作時異常,一次為缺少commons-fileupload,一次為缺少表column_domains。特意把這兩個提出來說的原因是,我并沒有用到檔案上傳,也并沒有用到column_domains表,這是delicacy的兩個隐藏依賴,成功滿足後,項目順利運作。

運作:

1.在程式中寫了一個用随機id查詢使用者并傳回json的接口,兩者的簡易程式相當。Jmeter配置10線程測試得ssm的qps為13945.1,而delicacy的為7846.2。分析發現delicacy架構在warn級别打出了日志

2019-12-16 23:01:44 [delicacy.servlet.DelicacyServlet]-[WARN]

2019-12-16 23:01:44 [delicacy.servlet.DelicacyServlet]-[WARN] "id":13780,"account":"n","password":"h","userName":"z","customerId":2756,"a":"f","b":"f","c":"f","d":"i","e":"e","f":"z","g":"m"

此日志為詳細輸入參數與輸出結果,ssm沒有日志。我認為輸入輸出日志是調試日志,架構應用在warn級别列印此日志略有不妥,其它開源架構一般列印在debug,或trace級别。

盡最大努力排除日志因素影響,把delicacy的日志級别調到error級别後測得QPS為12810.7。這個結果讓我驚訝,前面的測試,單表查詢,與結果集解析,都是delicacy以10-20%的優勢勝出 ,為什麼輸出到用戶端的吞吐量要比ssh的小呢?

2.delicay生成一個父子關系的表查詢,很容易就能查詢并傳回父子結構的資料,這個是很不錯的功能。也是在開發中常用到的。但是在前期我們知道delicacy的子表資訊是多次查詢得到的,為非最佳效率的方法。就此我考慮專門寫一個sql語句給生成器,專門執行這個查詢任務。

3.寫了一個查詢,使用到四張表,客戶,聯系人,客戶聯系人關系,使用者(各表中都有Create_user_id,連上使用者表以把id轉換為名字),一次性獲得該客戶的所有詳細的可顯示的資訊。生成器為我們生成了符合sql傳回列的資料,條件查詢部分很輕松實作了。實際操作中較難序列化為父子關系的json,這裡我并沒有使用用開源的第三方json序列化工具,而是試圖像架構的代碼一樣讓對象自己實作自己的tojson(),但手寫代碼太痛苦了。參見類中注釋uroaming.processor.ThreeTableProc

至此delicay的體驗就寫完了,基于我是一個平時使用mybatis使用者情形,以上點評較多存在主觀感受。我相信較為熟悉delicay的使用者可能有更好的實踐方案,以至于更好的應用delicacy.

mybatis程式搭建:

為了與delicay一樣使用放在tomcat中的war運作項目,我特意使用的srping mvc對mybatis賦能,而不是springboot。Mvc的xml配置還是需要花很多時間,特别是貿然使用了5版本的mvc,一個坑是classPath:字元串的寫法,以前是純小寫,現在有的地方要與大寫,有的要寫小寫classpath。然後配置mybatis到spring bean視窗,直接在網上抄來了。

運作:

1.寫一個用随機id查詢使用者并傳回json的接口,也正是上文中的例子。Spring運作時報錯,無法轉換為json。加入jackson三jar後解決。 這裡需要說明一下,在delicacy中的缺少包的情況:缺少commons-fileupload包的時候,我并沒有需要上傳檔案的功能。而缺少jackson包時,我正在使用@ResponseBody要求spring将對象轉換為json。

2.對于mybatis來說,delicay 第二項和第三項應用幾乎雷同,直接執行delicacy的第三項業務,寫xml動态sql的時候,每個字母都是鍵盤敲擊出來,大量看上去有規律重複的條件讓人發狂。還要每一個屬性去核對出入參的屬性,太傷人了。

附加測試

在測試的途中,很有幸與delicacy的開發者建立聯系,開發者了解到mysql對insert批量插入語句有特殊的支援時,更改了代碼針對mysql優化,幫再此測一遍delicacy批量單線程插入性能;

Delicacy優化前:

2019-12-07 01:14:48 [TestMain]-[WARN] 插入100條資料使用時間是:168

2019-12-07 01:14:49 [TestMain]-[WARN] 插入1000條資料使用時間是:1389

2019-12-07 01:15:03 [TestMain]-[WARN] 插入10000條資料使用時間是:13736

優化後第一次

2019-12-19 22:22:09 [SingleTableInsert]-[WARN] 插入100條資料使用時間是:34

2019-12-19 22:22:10 [SingleTableInsert]-[WARN] 插入1000條資料使用時間是:60

2019-12-19 22:22:10 [SingleTableInsert]-[WARN] 插入10000條資料使用時間是:268

優化後第二次

2019-12-19 22:24:09 [SingleTableInsert]-[WARN] 插入100條資料使用時間是:34

2019-12-19 22:24:09 [SingleTableInsert]-[WARN] 插入1000條資料使用時間是:61

2019-12-19 22:24:10 [SingleTableInsert]-[WARN] 插入10000條資料使用時間是:258

優化後第三次

2019-12-19 22:24:22 [SingleTableInsert]-[WARN] 插入100條資料使用時間是:32

2019-12-19 22:24:22 [SingleTableInsert]-[WARN] 插入1000條資料使用時間是:60

2019-12-19 22:24:22 [SingleTableInsert]-[WARN] 插入10000條資料使用時間是:264

分析:

優化後的性能發生質的飛躍,同時超過了mybatis的性能。

Delicay的sql解析與mybatis的動态sql測試

由于sql解析後查資料庫的性能在之前的測試中已經有了較為詳細的比較,并且我認為sql解析的時間會遠小于資料庫的查詢執行時間,是以為了避免資料庫的查詢時間抖動讓解析性能測不出,我在這項測試中用動态修改位元組碼的技術,把程式解析完成之後,準備發送到jdbc驅動以查詢資料庫之前的時間計算出來,把真實查詢資料庫的過程祛除。測試sql有15個條件全部設定查詢值。

Delicacy第一次

2019-12-21 00:00:36 [uroaming.SqlParseTest]-[WARN] delicacy解析10000次條件查詢sql的時間是:1163

Delicacy第二次

2019-12-21 00:00:51 [uroaming.SqlParseTest]-[WARN] delicacy解析10000次條件查詢sql的時間是:1170

Delicacy第三次

2019-12-21 00:01:03 [uroaming.SqlParseTest]-[WARN] delicacy解析10000次條件查詢sql的時間是:1148

Mybatis第一次

2019-12-21 01:42:32 [fu.dan.qi.testmybatis.SqlParseTest]-[WARN] mybatis 解析10000次條件查詢sql的時間是:1551

Mybatis第二次

2019-12-21 01:42:53 [fu.dan.qi.testmybatis.SqlParseTest]-[WARN] mybatis 解析10000次條件查詢sql的時間是:1544

Mybatis第三次

2019-12-21 01:43:03 [fu.dan.qi.testmybatis.SqlParseTest]-[WARN] mybatis 解析10000次條件查詢sql的時間是:1553

分析:mybatis解析sql的速度慢于delicacy,這當中已經祛除了真實的查資料庫的影響。隻有查詢,固定傳回空結果,封裝java對象。我認為原因是mybatis使用反射,加載插件(這次沒有插件)等因素導緻代碼調用鍊複雜所緻。而生成器後成的delicacy代碼是已知類型操作,代碼調用鍊較短。

寫在最後

本人水準有限,這次對比結果到這裡就告一段落,總體的感覺是delicay用了不同于國内流行的正常開發方法,不同的設計結構,利用了生成器對特定項目的優勢,巧妙的提升了不少的性能,而對新工具,方法的熟練需要項目來鍛煉,如能精通則開發效率的提高還有更大的空間。

————————————————

版權聲明:本文為CSDN部落客「磊醒」的原創文章,遵循 CC 4.0 BY-SA 版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/u013468378/article/details/103694472

繼續閱讀