有什么简单轻量的内存数据库技术
HANA/SPARK/Redis都是常见的内存数据库,但架构复杂沉重,很多场景并不适用。提到简单轻量的内存数据技术,SQLite是比较常见的,优点是体积轻巧,架构简单,可直接嵌入JAVA代码。SQLite的缺点是缺乏独立服务,不适合追求稳定的应用环境,不支持存储过程,计算能力不强,而且速度也比较慢,使用外部数据必须经过繁琐的入库过程。
简单轻量的内存数据库技术,集算器SPL比SQLite好用多了。
SPL是开源的JAVA计算类库,架构简单且灵活,不仅可以直接嵌入JAVA代码,也支持独立服务。SPL有多种性能优化手段,有强大的计算能力,可直接使用外部数据。
SPL架构简单,无须独立服务,只要引入SPL的Jar包,就可以嵌入JAVA代码进行计算。先把外存数据加载到内存,比如从Oracle加载:
A | |
1 | =connect("orcl") |
2 | =A1.cursor@x("select OrderID,Client,SellerID,OrderDate,Amount from orders order by OrderID") |
3 | =A2.memory(OrderID).index() |
4 | >env(orders,A3) |
加载之后,就可以用SPL简单直观的语法进行计算内存。
A | |
1 | =Orders.select(Amount>=arg1 && Amount<arg2) |
2 | =A1.groups(year(OrderDate):y,month(OrderDate):m; sum(Amount):s,count(1):c) |
SPL提供了JDBC接口,可以被JAVA方便地集成。加载的数据量一般比较大,通常在应用的初始阶段运行一次,只须将上面的加载过程存为SPL脚本文件,在JAVA中以存储过程的形式引用脚本文件名:
… Class.forName("com.esproc.jdbc.InternalDriver"); Connection conn =DriverManager.getConnection("jdbc:esproc:local://"); CallableStatement statement = conn.prepareCall("{call init()}"); statement.execute(); ...
内存计算通常要反复执行,并发也大。较长的计算适合外置的脚本文件,修改后无须编译。对于较短的计算,可以像SQL语句那样合并成一句,写在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提供了丰富的计算函数,可以轻松实现内存计算,下面是一些例子:
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) |
//关联 |
SPL还支持标准SQL语法,包括丰富的字符串函数和日期函数,还支持关联计算、集合计算、子查询。比如,前面的计算可以改写为下面的SQL:
$select year(OrderDate) y,month(OrderDate) m, sum(Amount) s,count(1) c from {Orders} Where Amount>=? and Amount<? ;arg1,arg2
SPL架构灵活,不仅支持嵌入计算,也支持独立服务,适合对稳定性要求严格的应用。值得一提的是,计算代码和集成方式无需改变,只要注明“服务端执行”:
… Class.forName("com.esproc.jdbc.InternalDriver"); Connection conn =DriverManager.getConnection("jdbc:esproc:local://onlyServer=true"); Statement statement = conn.createStatement(); String arg1="1000"; String arg2="2000" ResultSet result = statement.executeQuery(=Orders.select@m(Amount>="+arg1+" && Amount<"+arg2+"). gr也oups(year(OrderDate):y,month(OrderDate):m; sum(Amount):s,count(1):c)"); ...
SPL计算能力强大,可简化分步计算、有序计算、分组后计算等逻辑较复杂的计算,SQL和存储过程难以实现的计算,用SPL解决起来就很轻松。比如,找出销售额累计占到一半的前n个大客户,并按销售额从大到小排序:
A | B | |
2 | =sales.sort(amount:-1) | /销售额逆序排序 |
3 | =A2.cumulate(amount) | /计算累计序列 |
4 | =A3.m(-1)/2 | /最后的累计即总额 |
5 | =A3.pselect(~>=A4) | /超过一半的位置 |
6 | =A2(to(A5)) | /按位置取值 |
在内存计算方面,除了常规的主键和索引外,SPL还提供了很多高性能的数据结构和算法支持,比大多数使用SQL的内存数据库性能好得多,且占用内存更少。需要HANA/Spark集群才能完成的运算,在SPL中常常用单机就解决了。
SPL支持并行计算,只须对原代码进行简单修改,就可以利用多核CPU提升性能。比如:
A | |
1 | =Orders.select@m(Amount>=arg1 && Amount<arg2) |
2 | =A1.groups(year(OrderDate):y,month(OrderDate):m; sum(Amount):s,count(1):c) |
SPL支持指针式复用,可以节省内存。SPL的数据以指针的形式参与计算,多步骤算法和不同的算法只是复用同一个表,而不像SQL那样每次都要复制记录。
SPL支持预关联技术,可以提升计算性能。SQL只能用复制记录的方式实现关联,会占用大量内存,SPL支持指针式复用,加载阶段的预关联几乎不占内存。内存计算时,可以用"."引用关联字段,更加直观方便:
=callRecord.sum(OUTID.BRANCHID.OUTCOST+INID.BRANCHID.INCOST+OUTID.AGENTID.OUTCOST+INID.AGENTID.INCOST)
SPL还支持内存压缩,允许将更多的数据加载到内存,且计算代码无须修改。
SPL支持多种外部数据源,可避免入库或格式转换的麻烦。除了关系型数据库,还包括txt\csv\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 | =A3.memory(OrderID).index() | |
6 | >env(orders,A5) |
SPL支持内外存混合计算。有些数据太大,无法放入内存,但又要与内存表共同计算,这种情况可利用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) |