Java 生態圈中的嵌入式數據庫,哪家強?
嵌入式數據庫一個很陌生的詞匯,以前只是聽說,但是沒有真正使用過,今天小編和大家一起來揭開它的面紗。
一、介紹
初次接觸嵌入式數據庫(Embedded Database)可能有點模糊,什么是嵌入式數據庫?為什么要使用嵌入式數據庫?怎么使用嵌入式數據庫?如何選擇?本文將帶大家一起揭曉答案。
從軟件角度來說,數據庫分類為兩種:
- 第一種:數據庫服務器(Database Server)
- 第二種:嵌入式數據庫(Embedded Database)
像 Oracle、PostgreSQL、MySQL 和 SQL Server 等這些大家熟知的數據庫,我們一般稱它為數據庫服務器,當然也不排除某些數據庫也提供嵌入式版本,例如 MySQL Embedded 就是一個嵌入式的數據庫。
而像 SQLite、Berkeley DB、Derby、H2、HSQL DB 等數據庫,一本內嵌在應用程序中,與應用程序一起運行,我們稱它為嵌入式數據庫。
嵌入式數據庫跟數據庫服務器最大的區別在于它們運行的地址空間不同。通常,數據庫服務器獨立地運行一個守護進程(daemon),而嵌入式數據庫與應用程序運行在同一個進程。
在實際開發中,平時接觸最多的應該是數據庫服務器,對嵌入式數據庫使用的比較少。
但是為何會出現嵌入式數據庫呢?
在小型的應用程序中,例如小型掌上游戲機,不適合部署高達幾百兆的數據庫服務器,同時也沒有聯網的必要,一種輕量級的數據庫需求由此誕生!
與常見的數據庫相比,嵌入式數據庫具有體積小、功能齊備、可移植性、健壯性等特點,例如我們所熟知的 SVN 版本控制軟件就使用到了 SQLite 作為內置數據庫,SQLite 的安裝包只有不到 350 KB,在微型機中也有著廣泛的應用,例如安卓、IOS 等移動設備操作系統都內置了 SQLite 數據庫!
鑒于嵌入式數據庫的種類比較多,有商業收費的、也有開源免費的!本文主要介紹開源免費版的,例如 Derby、SQLite、H2 等,下面我們就一起來實踐一下各個數據庫的配置和用法。
二、Derby
Derby 可以說是 100% 由 Java 編寫的一款數據庫,而且是開源免費的,非常小巧,核心部分derby.jar只有 2M!
很多人可能覺得 Derby 不是很熱門,但 Derby 已經開發了將近二十年!
1996 年,一個叫做 Cloudscape, Inc 的新公司成立了,公司的目標是構建一個用 Java 語言編寫的數據庫服務器。
公司的第一個發行版在一年之后推出,后來產品的名稱變成 Cloudscape。
1999 年,Cloudscape, Inc. 被大型數據庫廠商 Informix Software, Inc. 收購。
Informix Software 在 2001 年又被 IBM 收購,然后 IBM Cloudscape™ 數據庫系統在許多 IBM 的產品中被用作內嵌的數據庫引擎。
2004 年 4 月,IBM 把 Cloudscape 數據庫軟件贈送給 Apache 軟件基金會,從此 Apache Derby 項目誕生了。
接著 SUN 也為 Derby 捐獻了一個團隊。在JavaSE6.0中,SUN 將其命名為JavaDB。
2.1、項目引入
因為 Derby 是用 java 編寫的,所以集成的時候比較容易,直接通過maven在pom.xml中依賴庫文件,即可進行開發!
- <!--derby數據庫-->
- <dependency>
- <groupId>org.apache.derby</groupId>
- <artifactId>derby</artifactId>
- <version>10.14.1.0</version>
- <scope>runtime</scope>
- </dependency>
2.2、環境配置
和所有的數據庫連接一樣,基本都是配置驅動類、連接地址、賬號、密碼等信息。
- String DRIVER_CLASS = "org.apache.derby.jdbc.EmbeddedDriver";
- String JDBC_URL = "jdbc:derby:derbyDB;create=true";
- String USER = "root";
- String PASSWORD = "root";
說明:
- org.apache.derby.jdbc.EmbeddedDriver表示使用derby嵌入式數據庫模式。
- JDBC_URL中的derbyDB表示創建一個名為derbyDB的臨時數據庫,如果沒有會自動創建。
- USER、PASSWORD主要用于客戶端登錄使用。
2.3、單元測試應用
下面,我們使用JDBC編寫一個測試類,來測試一下derby是否可以正常使用。
- public class DerbyTest {
- /**
- * 以嵌入式(本地)連接方式連接數據庫
- */
- private static final String JDBC_URL = "jdbc:derby:derbyDB;create=true";
- private static final String DRIVER_CLASS = "org.apache.derby.jdbc.EmbeddedDriver";
- private static final String USER = "root";
- private static final String PASSWORD = "root";
- public static void main(String[] args) throws Exception {
- //與數據庫建立連接
- Class.forName(DRIVER_CLASS);
- Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
- Statement statement = conn.createStatement();
- //刪除表
- statement.execute("DROP TABLE USER_INF");
- //創建表
- statement.execute("CREATE TABLE USER_INF(id VARCHAR(50) PRIMARY KEY, name VARCHAR(50) NOT NULL, sex VARCHAR(50) NOT NULL)");
- //插入數據
- statement.executeUpdate("INSERT INTO USER_INF VALUES('1', '程咬金', '男') ");
- statement.executeUpdate("INSERT INTO USER_INF VALUES('2', '孫尚香', '女') ");
- statement.executeUpdate("INSERT INTO USER_INF VALUES('3', '猴子', '男') ");
- //查詢數據
- ResultSet resultSet = statement.executeQuery("select * from USER_INF");
- while (resultSet.next()) {
- System.out.println(resultSet.getInt("id") + ", " + resultSet.getString("name") + ", " + resultSet.getString("sex"));
- }
- //關閉連接
- statement.close();
- conn.close();
- }
- }
輸出結果:
- 1, 程咬金, 男
- 2, 孫尚香, 女
- 3, 猴子, 男
當程序運行完之后,會在當前項目根目錄生成一個derbyDB文件夾,里面會存放一些持久化的數據,當下次再連接derbyDB數據庫名稱時,可以查詢出之前插入的歷史數據,這個特性可以防止數據丟失!
值得注意的是:derby 對很多 mysql 的關鍵字并不支持,同時 derby 不支持插入空值。
在之后的版本中,derby 還可以作為一個數據庫服務器,通過jar啟動單獨部署在一臺服務器上,在連接地址上加上 IP 和端口號,例如jdbc://derby://localhost:1527/derbyDB。
如果想使用可視化客戶端工具來訪問和管理derby,可以使用SQuirreL SQL Client客戶端,下載地址http://www.squirrelsql.org/#installation。
在目前絕大多數的關于嵌入式數據庫應用中,derby 的出場次數還是較少。
三、SQLite
SQLite 是 D.RichardHipp 用一個小型的C庫開發的一種強有力的嵌入式關系數據庫,雖然功能較 Berkeley DB(商業數據庫)稍顯遜色,但它簡單易學、速度較快,同時提供了豐富的數據庫接口,提供了對 SQL92 的大多數支持:支持多表和索引、事務、視圖、觸發和一系列的用戶接口及驅動。
SQLite 不僅支持 Windows/Linux/Unix 等等主流的操作系統,同時能夠跟很多程序語言相結合,比如 Tcl、C#、PHP、Java等,還有ODBC接口,比起 Mysql、PostgreSQL 這兩款開源的世界著名數據庫管理系統來講,它的處理速度比他們都快,全部源碼大致3萬行C代碼,文件大約 350KB,支持數據庫大小至 2TB!
幾乎常年占居嵌入式數據庫的第一位!
3.1、SQLite安裝
3.1.1、windows安裝
1.訪問 SQLite 下載頁面https://www.sqlite.org/download.html。
2.下載sqlite-tools-win32-*.zip和sqlite-dll-win32-*.zip壓縮文件。
3.創建文件夾C:\sqlite,并在此文件夾下解壓上面兩個壓縮文件,將得到 sqlite3.def、sqlite3.dll和sqlite3.exe文件。
4.添加C:\sqlite到PATH環境變量。
最后在命令提示符下,輸入sqlite3命令,顯示如下結果表示安裝成功!
- C:\>sqlite3
- SQLite version 3.7.15.2 2013-01-09 11:53:05
- Enter ".help" for instructions
- Enter SQL statements terminated with a ";"
3.1.2、linux安裝
目前,幾乎所有版本的 Linux 操作系統都附帶 SQLite。所以,只需要在命令行輸入輸入sqlite3,即可檢查出機器上是否已經安裝了 SQLite!
- $ sqlite3
- SQLite version 3.7.15.2 2013-01-09 11:53:05
- Enter ".help" for instructions
- Enter SQL statements terminated with a ";"
如果沒有看到上面的結果,安裝也很簡單!
1.訪問 SQLite 下載頁面https://www.sqlite.org/download.html。
2.下載sqlite-autoconf-*.tar.gz并上傳到 linux 服務器。
3.執行安裝操作
例如,安裝步驟示例:
- $ tar xvzf sqlite-autoconf-3071502.tar.gz
- $ cd sqlite-autoconf-3071502
- $ ./configure --prefix=/usr/local
- $ make
- $ make install
3.1.3、mac安裝
mac安裝操作與linux類似。
3.2、項目引入
sqlite 也可以直接通過maven在pom.xml中依賴庫文件,即可進行開發!
- <!--sqlite-->
- <dependency>
- <groupId>org.xerial</groupId>
- <artifactId>sqlite-jdbc</artifactId>
- <version>3.7.2</version>
- </dependency>
3.3、環境配置
- String DRIVER_CLASS = "org.sqlite.JDBC";
- String JDBC_URL = "jdbc:sqlite:sqliteDB.db";
- String USER = "root";
- String PASSWORD = "root";
說明:
- org.sqlite.JDBC表示使用sqlite嵌入式數據庫模式。
- JDBC_URL中的sqliteDB表示創建一個名為sqliteDB的臨時數據庫,如果沒有會自動創建。
- USER、PASSWORD主要用于客戶端登錄使用。
3.4、單元測試應用
下面,來測試一下sqlite是否可以正常使用。
- public class SQLiteTest {
- /**
- * 以嵌入式(本地)連接方式連接數據庫
- */
- private static final String JDBC_URL = "jdbc:sqlite:sqliteDB.db";
- private static final String DRIVER_CLASS = "org.sqlite.JDBC";
- private static final String USER = "root";
- private static final String PASSWORD = "root";
- public static void main(String[] args) throws Exception {
- //與數據庫建立連接
- Class.forName(DRIVER_CLASS);
- Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
- Statement statement = conn.createStatement();
- //刪除表
- statement.execute("DROP TABLE IF EXISTS USER_INF");
- //創建表
- statement.execute("CREATE TABLE USER_INF(id VARCHAR(50) PRIMARY KEY, name VARCHAR(50) NOT NULL, sex VARCHAR(50) NOT NULL)");
- //插入數據
- statement.executeUpdate("INSERT INTO USER_INF VALUES('1', '程咬金', '男') ");
- statement.executeUpdate("INSERT INTO USER_INF VALUES('2', '孫尚香', '女') ");
- statement.executeUpdate("INSERT INTO USER_INF VALUES('3', '猴子', '男') ");
- //查詢數據
- ResultSet resultSet = statement.executeQuery("select * from USER_INF");
- while (resultSet.next()) {
- System.out.println(resultSet.getInt("id") + ", " + resultSet.getString("name") + ", " + resultSet.getString("sex"));
- }
- //關閉連接
- statement.close();
- conn.close();
- }
- }
輸出結果:
- 1, 程咬金, 男
- 2, 孫尚香, 女
- 3, 猴子, 男
當程序運行完之后,也會在當前項目的根目錄下生成上文自定義的一個名為sqliteDB的文件。
每次操作名為sqliteDB的數據庫時候,數據會持久化到sqliteDB文件中,從而防止數據丟失。
如果想使用可視化客戶端工具來訪問和管理sqliteDB,可以使用navicat來連接,選擇生成的sqliteDB文件,輸入相應的賬號、密碼,便可進行管理維護!
四、H2
h2 是一款純java編寫的另一款嵌入式數據庫,它本身只是一個類庫,即只有一個 jar 文件,可以直接嵌入到應用項目中,同時還提供了非常友好的基于 web 的數據庫管理界面。
網上有很多開發者拿它與 derby 做對比,稱它與 mysql 數據庫兼容性強,口碑較好。
具體是否真的如此,在后文我們會進行相應的性能測試,下面一起來看看在開發中如何使用。
4.1、項目引入
既然 h2 是純java編寫,可以直接通過maven在pom.xml中依賴庫文件,即可進行開發!
- <!--h2數據庫-->
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- <version>1.4.200</version>
- <scope>runtime</scope>
- </dependency>
4.2、環境配置
- String DRIVER_CLASS = "org.h2.Driver";
- String JDBC_URL = "jdbc:h2:mem:h2DB";
- String USER = "root";
- String PASSWORD = "root";
說明:
- org.h2.Driver表示使用h2嵌入式數據庫模式。
- JDBC_URL中的h2DB表示創建一個名為h2DB的臨時數據庫。
- USER、PASSWORD主要用于客戶端登錄使用。
4.3、單元測試應用
下面,來測試一下h2是否可以正常使用。
- public class H2Test {
- /**
- * 以嵌入式(本地)連接方式連接H2數據庫
- */
- private static final String JDBC_URL = "jdbc:h2:mem:h2DB";
- private static final String DRIVER_CLASS = "org.h2.Driver";
- private static final String USER = "root";
- private static final String PASSWORD = "root";
- public static void main(String[] args) throws Exception {
- //與數據庫建立連接
- Class.forName(DRIVER_CLASS);
- Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
- Statement statement = conn.createStatement();
- //刪除表
- statement.execute("DROP TABLE IF EXISTS USER_INF");
- //創建表
- statement.execute("CREATE TABLE USER_INF(id VARCHAR(50) PRIMARY KEY, name VARCHAR(50) NOT NULL, sex VARCHAR(50) NOT NULL)");
- //插入數據
- statement.executeUpdate("INSERT INTO USER_INF VALUES('1', '程咬金', '男') ");
- statement.executeUpdate("INSERT INTO USER_INF VALUES('2', '孫尚香', '女') ");
- statement.executeUpdate("INSERT INTO USER_INF VALUES('3', '猴子', '男') ");
- //查詢數據
- ResultSet resultSet = statement.executeQuery("select * from USER_INF");
- while (resultSet.next()) {
- System.out.println(resultSet.getInt("id") + ", " + resultSet.getString("name") + ", " + resultSet.getString("sex"));
- }
- //關閉連接
- statement.close();
- conn.close();
- }
- }
輸出結果:
- 1, 程咬金, 男
- 2, 孫尚香, 女
- 3, 猴子, 男
h2 作為嵌入式數據庫應用可以選擇兩種類型的url,第一種是內存模式運行,這種模式將數據臨時放到內存中,程序結束后數據表和數據立即被銷毀,就不存在了;第二種是使用本地文件方式,將數據持久化到文件中,當再次連接數據庫時,可以獲取歷史數據。
- #第一種,內存模式運行
- jdbc:h2:mem:testDB
- #第二種,使用本地文件方式
- jdbc:h2:file:./target/testDB
h2 還可以作為數據庫服務器使用,單獨部署在服務器上,應用程序通過遠程連接進行操作,連接方式如下:
- #連接語法
- jdbc:h2:tcp://<server>[:<port>]/[<path>]<databaseName>
- #范例:
- jdbc:h2:tcp://localhost:8080/~/test
如果想使用可視化客戶端工具來訪問和管理h2,可以通過它自帶的 web 頁面進行管理。
下面我們以springboot項目為例,通過配置使用h2自帶的管理頁面來維護。
新建一個WebConfig類,配置h2的 web 控制臺,如下:
- @Configuration
- public class WebConfig {
- /**
- * 添加h2控制臺的映射地址
- * @return
- */
- @Bean
- ServletRegistrationBean h2servletRegistration(){
- ServletRegistrationBean registrationBean = new ServletRegistrationBean(new WebServlet());
- registrationBean.addUrlMappings("/h2-console/*");
- return registrationBean;
- }
- }
啟動springboot項目,打開瀏覽器,輸入http://127.0.0.1:8080/h2-console訪問管理頁面!
填寫連接地址、賬號、密碼之后,進行連接!
可以在工作臺,編寫 sql 語句進行查詢,同時對查詢結果還可以進行編輯操作。
五、性能測試
上文中我們介紹了三者數據庫的使用,下面,我們以循環插入1000、10000、100000次操作,分別來測試三個數據庫的性能,看看他們的表現如何?
從結果上看:
- derby 與 sqlite 性能方面相差不大,但是隨著插入數量越多,derby 比 sqlite 稍遜一些。
- 三者相比,h2 性能最好,無論是將數據寫入內存,還是寫入到數據庫,性能都是最好的。
六、總結
- derby 作為嵌入式數據庫,在性能和易用性都不錯,數據庫是以一個目錄存儲的,但只能用于Java程序中。
- sqlite 因為支持多種語言,也提供了對 SQL92 的大多數支持,執行效率也不錯,作為嵌入式數據庫應用最廣,但如果想查詢數據只能本地連接不能遠程連接。
- h2 作為嵌入式數據庫的新秀,主要優勢:超輕量級,可以支持內存模式,高效高速,能支持基本的全文搜索,同時與 mysql 數據庫兼容性最強,一般使用場景最多的就是在開發環境上進行回歸測試使用。