昨天重溫h2 database
的文檔時,看到一個一直被我無視的命令create linked table
!仔細研究後發現這絕對是一個NB的功能:可實現跨不一樣類型數據庫的鏈接查詢!mysql
按照官方文檔的介紹,create linked table
能夠建立一張表,連接到任何支持JDBC
的外部數據庫中的表。執行簡單查詢(無join
語句)時,會自動將查詢語句發送給外部數據庫;若是有join
語句,這查詢語句會被自動翻譯成相應的簡單查詢語句,再發送給外部數據庫。sql
例如,MySQL
中有用戶表users (id, name)
,PostgreSQL
中有收藏夾表favorites (id, user_id, name, link)
。要求查詢得到id
爲1
的用戶名和收藏中全部的連接,一般須要分兩步:數據庫
users
中查詢id
爲1
的記錄;favorites
中查詢user_id
的連接。利用linked table
,解決方案是:服務器
users
表的映射,指定使用的JDBC
驅動、地址、用戶名、密碼和表名。favorites
表的映射,參數與#1相同。inner join
查詢得到指定用戶的名字與收藏夾中的連接。create linked table users ('com.mysql.jdbc.Driver', 'jdbc:mysql://host/database_name', 'username', 'password', 'users'); create linked table favorites ('org.postgresql.Driver', 'jdbc:postgresql://host/database_name', 'username', 'password', 'favorites'); select users.name, favorites.link from favorites inner join users on favorites.user_id = users.id where user.id = 1;
根據官網的介紹,我YY了一下linked table
底層的實現原理,應該是先把查詢翻譯成簡單查詢語句後,再從遠程數據庫獲取數據並存入臨時表中,在本地作join等相關的處理後,最後將結果返回給調用者。所以上述的查詢會被拆分紅:post
並分別發送到相應的外部數據庫上執行。性能
從上述的原理能夠看出這種解決方案的一個不足之處:favorites
表的查詢語句沒有帶上條件語句,即全表查詢。由於自動生成簡單查詢語句時,只能從原始語句中提取每張表本身相關的條件語句,若是改用name
爲關鍵字查詢,此時就沒法預測與favorites
表join
時user_id
的範圍。測試
此外limit
或top
這種分頁語句,在包含join
的語句中也沒法預測最終被選中的是哪幾條數據,所以也不會發送給外部數據庫。例如,select * from favorites limit 1
,服務器上真正執行的是select * from favroites
,limit 1
這個操做是在h2
的內存中實現的,若是遠程favorites
表很是大,這條語句執行會很是慢,甚至Out of Memory
。翻譯
對於上例,解決方法是在原始語句中爲favorites
也添加相應的過濾條件:select users.name, favorites.link from favorites inner join users on favorites.user_id = users.id where user.id = 1 and favorites.user_id = 1
。但這顯然不是萬能的,只能在全部表都肯定範圍的狀況下才能使用。設計
遺留系統中還會遇到許多采用物理分表設計的數據庫,例如把users
拆分紅users_00
、users_01
等一百張表,而不是使用分區,查詢前不得不先動態計算表名。postgresql
在嘗試了合併分庫後,我又測試了經過view
合併拆分的linked table
的方案:
create linked table users_00 ('com.mysql.jdbc.Driver', 'jdbc:mysql://host/database_name', 'username', 'password', 'users_00'); create linked table users_99 ('com.mysql.jdbc.Driver', 'jdbc:mysql://host/database_name', 'username', 'password', 'users_01'); ... create linked table users_99 ('com.mysql.jdbc.Driver', 'jdbc:mysql://host/database_name', 'username', 'password', 'users_99'); create view users (id, name) as select id, name from users_00 union all select id, name from users_01 ... union all select id, name from users_99;
我在線上條了一個分100張的表,每張表的數據量約200萬行。測試了一條比較複雜的查詢,結果是直接使用union all
在原始庫中查詢,執行時間月130ms;經過h2
的view
查詢,約330ms
。性能僅相差2-3倍!
通過上述測試,我認爲經過h2
跨數據庫合表查詢的方案可用於查詢不復雜且性能非重要指標的場景,例如我打算下次把它用在報表系統中~