可以在Java應用程式中嵌入的資料引擎看起來比較豐富,但其實並不容易選擇。 Redis運算能力很差,只適合簡單查詢的場景。 Spark架構複雜沉重,部署維護很麻煩。 H2\HSQLDB\Derby等內嵌資料庫倒是架構簡單,但運算能力又不足,連基本的視窗函數都不支援。
相較之下,SQLite在架構性和運算能力上取得了較好的平衡,是應用較廣的Java嵌入資料引擎。
SQLite適應常規基本應用場景
SQLite架構簡單,其核心雖然是C語言開發的,但封裝得比較好,對外呈現為一個小巧的Jar包,能方便地集成在Java應用中。 SQLite提供了JDBC接口,可以被Java調用:
Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:"); Statement st = connection.createStatement(); st.execute("restore from d:/ex1"); ResultSet rs = st.executeQuery("SELECT * FROM orders");
SQLite提供了標準的SQL語法,常規的資料處理和計算都沒有問題。特別地,SQLite已經能支援視窗函數,可以方便地實現許多組內運算,運算能力比其他內嵌資料庫更強。
SELECT x, y, row_number() OVER (ORDER BY y) AS row_number FROM t0 ORDER BY x; SELECT a, b, group_concat(b, '.') OVER ( ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS group_concat FROM t1;
SQLite面對複雜場景尚有不足
SQLite的優點亮眼,但對於複雜應用場景時還是有些缺點。
Java應用程式可能處理的資料來源多種多樣,例如csv檔案、RDB、Excel、Restful,但SQLite只處理了簡單情況,即對csv等文字檔案提供了直接可用的命令列載入程序:
.import --csv --skip 1 --schema temp /Users/scudata/somedata.csv tab1
對於其他大部分數據源,SQLite都沒有提供方便的接口,只能硬寫程式碼載入數據,需要多次調用命令行,整個過程很繁瑣,時效性也差。
以載入RDB資料來源為例,一般的做法是先用Java執行命令列,把RDB庫表轉為csv;再用JDBC存取SQLite,建立表格結構;之後再用Java執行命令列,將csv檔案匯入SQLite;最後為新表建索引,以提高效能。這個方法比較死板,如果想靈活定義表結構和表名,或透過計算確定載入的數據,程式碼就更難寫了。
類似地,對於其他資料來源,SQLite也不能直接載入,同樣要透過繁瑣地轉換過程才可以。
SQL接近自然語言,學習門檻低,容易實現簡單的計算,但不擅長複雜的計算,例如複雜的集合計算、有序計算、關聯計算、多步驟計算。 SQLite採用SQL語句做計算,SQL優點和缺點都會繼承下來,勉強實作這些複雜運算的話,程式碼會顯得繁瑣難懂。
例如,某檔股票最長的上漲天數,SQL要這樣寫:
select max(continuousDays)-1 from (select count(*) continuousDays from (select sum(changeSign) over(order by tradeDate) unRiseDays from (select tradeDate, case when price>lag(price) over(order by tradeDate) then 0 else 1 end changeSign from AAPL) ) group by unRiseDays)
這也不單是SQLite的難題,事實上,由於集合化不徹底、缺乏序號、缺乏物件引用等原因,其他SQL資料庫也不擅長這些運算。
業務邏輯由結構化資料運算和流程控制組成,SQLite支援SQL,具有結構化資料運算能力,但SQLite沒有提供儲存過程,不具備獨立的流程控制能力,也不能實現一般的業務邏輯,通常要利用Java主程式的判斷和循環語句。由於Java沒有專業的結構化資料物件來承載SQLite資料表和記錄,轉換過程麻煩,處理過程不暢,開發效率不高。
前面提過,SQLite核心是C程序,雖然可以被整合到Java應用中,但並不能和Java無縫集成,和Java主程式交換資料時要經過耗時的轉換才能完成,在涉及資料量較大或互動頻繁時效能就會明顯不足。同樣因為核心是C程序,SQLite會在一定程度上破壞Java架構的一致性和健全性。
對於Java應用程式來講,原生在JVM上的esProc SPL是更好的選擇。
SPL全面支援各種資料來源
esProc SPL是JVM下開源的嵌入資料引擎,架構簡單,可直接載入資料來源,可以透過JDBC介面被Java整合調用,並且方便地進行後續計算。
SPL架構簡單,無須獨立服務,只要引進SPL的Jar包,就可以部署在Java環境中。
直接載入資料來源,程式碼簡短,過程簡單,時效性強。例如載入Oracle:
A | |
#=connect( "orcl") | |
2 | =A1.query@x("select OrderID,Client,SellerID,OrderDate,Amount from orders order by OrderID") |
3 | >env(orders,A2) |
对于SQLite擅长加载的csv文件,SPL也可以直接加载,使用内置函数而不是外部命令行,稳定且效率高,代码更简短:
=T("/Users/scudata/somedata.csv")
多种外部数据源。除了RDB和csv,SPL还直接支持txt\xls等文件,MongoDB、Hadoop、redis、ElasticSearch、Kafka、Cassandra等NoSQL,以及WebService XML、Restful Json等多层数据。比如,将HDSF里的文件加载到内存:
A | |
1 | =hdfs_open(;"hdfs://192.168.0.8:9000") |
2 | =hdfs_file(A1,"/user/Orders.csv":"GBK") |
3 | =A2.cursor@t() |
4 | =hdfs_close(A1) |
5 | >env(orders,A4) |
JDBC接口可以方便地集成。加载的数据量一般比较大,通常在应用的初始阶段运行一次,只须将上面的加载过程存为SPL脚本文件,在Java中以存储过程的形式引用脚本文件名:
Class.forName("com.esproc.jdbc.InternalDriver"); Connection conn =DriverManager.getConnection("jdbc:esproc:local://"); CallableStatement statement = conn.prepareCall("{call init()}"); statement.execute();
SPL的计算能力更强大
SPL提供了丰富的计算函数,可以轻松实现日常计算。SPL支持多种高级语法,大量的日期函数和字符串函数,很多用SQL难以表达的计算,用SPL都可以轻松实现,包括复杂的有序计算、集合计算、分步计算、关联计算,以及带流程控制的业务逻辑。
丰富的计算函数。SPL可以轻松实现各类日常计算:
A | B | |
1 | =Orders.find(arg_OrderIDList) | //多键值查找 |
2 | =Orders.select(Amount>1000 && like(Client,\"*S*\")) | //模糊查询 |
3 | = Orders.sort(Client,-Amount) | //排序 |
4 | = Orders.id(Client) | //去重 |
5 | =join(Orders:O,SellerId; Employees:E,EId).new(O.OrderID, O.Client,O.Amount,E.Name,E.Gender,E.Dept) | //关联 |
标准SQL语法。SPL也提供了SQL-92标准的语法,比如分组汇总:
$select year(OrderDate) y,month(OrderDate) m, sum(Amount) s,count(1) c from {Orders} Where Amount>=? and Amount<? ;arg1,arg2
函数选项、层次参数等方便的语法。功能相似的函数可以共用一个函数名,只用函数选项区分差别,比SQL更加灵活方便。比如select函数的基本功能是过滤,如果只过滤出符合条件的第1条记录,可使用选项@1:
T.select@1(Amount>1000)
二分法排序,即对有序数据用二分法进行快速过滤,使用@b:
T.select@b(Amount>1000)
有序分组,即对分组字段有序的数据,将相邻且字段值相同的记录分为一组,使用@b:
T.groups@b(Client;sum(Amount))
函数选项还可以组合搭配,比如:
Orders.select@1b(Amount>1000)
结构化运算函数的参数有些很复杂,比如SQL就需要用各种关键字把一条语句的参数分隔成多个组,但这会动用很多关键字,也使语句结构不统一。SPL使用层次参数简化了复杂参数的表达,即通过分号、逗号、冒号自高而低将参数分为三层:
join(Orders:o,SellerId ; Employees:e,EId)
更丰富的日期和字符串函数。除了常见函数,比如日期增减、截取字符串,SPL还提供了更丰富的日期和字符串函数,在数量和功能上远远超过了SQL,同样运算时代码更短。比如:
季度增减:elapse@q(“2020-02-27”,-3) //返回2019-05-27
N个工作日之后的日期:workday(date(“2022-01-01”),25) //返回2022-02-04
字符串类函数,判断是否全为数字:isdigit(“12345”) //返回true
取子串前面的字符串:substr@l(“abCDcdef”,“cd”) //返回abCD
按竖线拆成字符串数组:“aa|bb|cc”.split(“|”) //返回[“aa”,“bb”,“cc”]
SPL还支持年份增减、求季度、按正则表达式拆分字符串、拆出SQL的where或select部分、拆出单词、按标记拆HTML等大量函数。
简化有序运算。涉及跨行的有序运算,通常都有一定的难度,比如比上期和同期比。SPL使用"字段[相对位置]"引用跨行的数据,可显著简化代码,还可以自动处理数组越界等特殊情况,比SQL窗口函数更加方便。比如,追加一个计算列rate,计算每条订单的金额增长率:
=T.derive(AMOUNT/AMOUNT[-1]-1: rate)
综合运用位置表达式和有序函数,很多SQL难以实现的有序运算,都可以用SPL轻松解决。比如,根据考勤表,找出连续 4 周每天均出勤达 7 小时的学生:
A | |
1 | =Student.select(DURATION>=7).derive(pdate@w(ATTDATE):w) |
2 | =A1.group@o(SID;~.groups@o(W;count(~):CNT).select(CNT==7).group@i(W-W[-1]!=7).max(~.len()):weeks) |
3 | =A2.select(weeks>=4).(SID) |
简化集合运算,SPL的集合化更加彻底,配合灵活的语法和强大的集合函数,可大幅简化复杂的集合计算。比如,在各部门找出比本部门平均年龄小的员工:
A | |
1 | =Employees.group(DEPT; (a=~.avg(age(BIRTHDAY)),~.select(age(BIRTHDAY) |
2 | =A1.conj(YOUNG) |
计算某支股票最长的连续上涨天数:
A | |
1 | =a=0,AAPL.max(a=if(price>price[-1],a+1,0)) |
简化关联计算。SPL支持对象引用的形式表达关联,可以通过点号直观地访问关联表,避免使用JOIN导致的混乱繁琐,尤其适合复杂的多层关联和自关联。比如,根据员工表计算女经理的男员工:
=employees.select(gender:"male",dept.manager.gender:"female")
方便的分步计算,SPL集合化更加彻底,可以用变量方便地表达集合,适合多步骤计算,SQL要用嵌套表达的运算,用SPL可以更轻松实现。比如,找出销售额累计占到一半的前n个大客户,并按销售额从大到小排序:
A | B | |
2 | =sales.sort(amount:-1) | /销售额逆序排序,可在SQL中完成 |
3 | =A2.cumulate(amount) | /计算累计序列 |
4 | =A3.m(-1)/2 | /最后的累计即总额 |
5 | =A3.pselect(~>=A4) | /超过一半的位置 |
6 | =A2(to(A5)) | /按位置取值 |
流程控制语法。SPL提供了流程控制语句,配合内置的结构化数据对象,可以方便地实现各类业务逻辑。
分支判断语句:
A | B | |
2 | … | |
3 | if T.AMOUNT>10000 | =T.BONUS=T.AMOUNT*0.05 |
4 | else if T.AMOUNT>=5000 && T.AMOUNT | =T.BONUS=T.AMOUNT*0.03 |
5 | else if T.AMOUNT>=2000 && T.AMOUNT | =T.BONUS=T.AMOUNT*0.02 |
循环语句:
A | B | |
1 | =db=connect("db") | |
2 | =T=db.query@x("select * from sales where SellerID=? order by OrderDate",9) | |
3 | for T | =A3.BONUS=A3.BONUS+A3.AMOUNT*0.01 |
4 | =A3.CLIENT=CONCAT(LEFT(A3.CLIENT,4), " co.,ltd.") | |
5 | … |
与Java的循环类似,SPL还可用break关键字跳出(中断)当前循环体,或用next关键字跳过(忽略)本轮循环,不展开说了。
计算性能更好。在内存计算方面,除了常规的主键和索引外,SPL还提供了很多高性能的数据结构和算法支持,比大多数使用SQL的内存数据库性能好得多,且占用内存更少,比如预关联技术、并行计算、指针式复用。
优化体系结构
SPL支持JDBC接口,代码可外置于Java,耦合性更低,也可内置于Java,调用更简单。SPL支持解释执行和热切换,代码方便移植和管理运营,支持内外存混合计算。
外置代码耦合性低。SPL代码可外置于Java,通过文件名被调用,既不依赖数据库,也不依赖Java,业务逻辑和前端代码天然解耦。
对于较短的计算,也可以像SQLite那样合并成一句,写在Java代码中:
Class.forName("com.esproc.jdbc.InternalDriver"); Connection conn =DriverManager.getConnection("jdbc:esproc:local://"); Statement statement = conn.createStatement(); String arg1="1000"; String arg2="2000" ResultSet result = statement.executeQuery(=Orders.select(Amount>="+arg1+" && Amount<"+arg2+"). groups(year(OrderDate):y,month(OrderDate):m; sum(Amount):s,count(1):c)");
解释执行和热切换。业务逻辑数量多,复杂度高,变化是常态。良好的系统构架,应该有能力应对变化的业务逻辑。SPL是基于Java的解释型语言,无须编译就能执行,脚本修改后立即生效,支持不停机的热切换,适合应对变化的业务逻辑。
方便代码移植。SPL通过数据源名从数据库取数,如果需要移植,只要改动配置文件中的数据源配置信息,而不必修改SPL代码。SPL支持动态数据源,可通过参数或宏切换不同的数据库,从而进行更方便的移植。为了进一步增强可移植性,SPL还提供了与具体数据库无关的标准SQL语法,使用sqltranslate函数可将标准SQL转为主流方言SQL,仍然通过query函数执行。
方便管理运营。由于支持库外计算,代码可被第三方工具管理,方便团队协作;SPL脚本可以按文件目录进行存放,方便灵活,管理成本低;SPL对数据库的权限要求类似Java,不影响数据安全。
内外存混合计算。有些数据太大,无法放入内存,但又要与内存表共同计算,这种情况可利用SPL实现内外存混合计算。比如,主表orders已加载到内存,大明细表orderdetail是文本文件,下面进行主表和明细表的关联计算:
A | |
1 | =file("orderdetail.txt").cursor@t() |
2 | =orders.cursor() |
3 | =join(A1:detail,orderid ; A2:main,orderid) |
4 | =A3.groups(year(main.orderdate):y; sum(detail.amount):s) |
SQLite使用简单方便,但数据源加载繁琐,计算能力不足。SPL架构也非常简单,并直接支持更多数据源。SPL计算能力强大,提供了丰富的计算函数,可以轻松实现SQL不擅长的复杂计算。SPL还提供多种优化体系结构的手段,代码既可外置也可内置于Java,支持解释执行和热切换,方便移植和管理运营,并支持内外存混合计算。
以上是Java嵌入資料引擎從SQLite到SPL實例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

記事本++7.3.1
好用且免費的程式碼編輯器