相比之下,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)-1from (select count(*) continuousdaysfrom (select sum(changesign) over(order by tradedate) unrisedaysfrom (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
1 =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) cfrom {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)<a)):young)
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<10000 =t.bonus=t.amount*0.03
5 else if t.amount>=2000 && t.amount<5000 =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实例分析的详细内容。
