背景:
银行流水账单俗称银行卡存取款交易对账单,也称银行账户交易对账单。指的是客户在一段时间内与银行发生的存取款业务交易清单。一般而言,在申请贷款或者信用卡时,银行或其他金融机构会要求借款人提供申请者本人最近三个月或者以上的银行账单流水,以便银行在审核和授信时进行参考。银行流水账单查询系统为个人客户提供在线查询和打印服务。而本项目为整个系统提供数据库部分的支撑。
数据:
字段名 | 字段描述 | 字段类型 | 备注 |
account_id | 银行账号 | Int64 | |
tx_id | 交易流水号 | Int64 | 全局唯一 |
customer_name | 开户人姓名 | String | 不超过20Bytes |
tx_time | 交易时间 | String | 2014-05-13 14.03.01 |
tx_value | 交易金额 | Int32 | 数据大于0 |
account_overage | 账户余额 | Int64 | 数据大于等于0 |
tx_type | 交易类型 | Int32 | 1: ATM取款 2:ATM存款 3:网点取款 4:网点存款 5:网银转出 6:网银转入 7:消费 8:代发工资 |
other_side_account | 对方账户 | Int64 | 可能为NULL |
tx_site | 交易地点 | String | 不超过100Bytes |
summary | 摘要 | String | 不超过100Bytes |
查询需求:
1:按照交易流水号查询交易细节。发生频率:高。要求返回时间:<=100ms。最大并发数:100
2:按照账号(不包括对方账户)查询交易流水,优先显示距离查询时间较近的交易。每页最多返回10条记录。发生频率:高。要求返回时间:<=500ms。最大并发数:100
3:查询某月内交易总数。发生频率:低。要求返回时间:无。最大并发数:1
4:查询某月内总交易额。发生频率:低。要求返回时间:无。最大并发数:1
写需求
全量生成一个csv文件导入数据。
每月生成一个csv文件导入数据。
各节点数据尽可能平均化。
可靠性需求
数据做3份冗余。
一致性需求
前两条查询需要返回当前最新数据。后两条允许查询到脏数据(即不是最新状态的数据)。
课题完成步骤:
1.硬件配置
本次课题使用巨杉数据库作为底层数据库支撑,巨杉数据库是一款金融级分布式关系型数据库,主要面对高并发联机交易型场景提供高性能、可靠稳定以及无限水平扩展的数据库服务。用户可以在 SequoiaDB 巨杉数据库中创建多种类型的数据库实例,以满足上层不同应用程序各自的需求。本次课题所要完成的是银行流水相关的应用程序,因此连接mysql实例建立关系型数据库,但巨杉数据库实际上可以满足各种数据的存储与应用需求。
为进行实现,将通过docker hub 获取sequoiadb 的docker 镜像,根据相应的步骤即可完成SequoiaDB 集群的快速部署和使用。镜像下载和部署链接如下https://hub.docker.com/r/sequoiadb/sequoiadb,推荐选择巨杉数据库v5.0.1版本进行部署,部署起来更快速简洁。
部署方式选择集群部署,一次性获取三台机器,数据就有三个备份,保证数据完备,同时到达数据的高效读取,存储。下图是这次课题所使用的巨杉数据库的整体架构:
上图来自于巨杉数据库官网巨杉学的课程中。
实际部署的步骤可参考官方文档
本课题在window系统上进行部署,首先下载docker desktop,然后打开命令行,按照部署教程输入命令,最后可在docker desktop中看到部署好的镜像和容器。
在部署路径下输入命令
mysql -h127.0.0.1 -P3306 -uroot -p
,可进入mysql实例,创建数据库mydata,数据表test,建表时注意键的设置
2.数据导入
sdbimprt 是 SequoiaDB 巨杉数据库的数据导入工具,用于将 JSON 或 CSV 格式的数据导入到 SequoiaDB 数据库中。
将数据test.csv通过协调节点导入至集合 mydata.test 中:
$ sdbimprt --hosts=localhost:11810 --type=csv --file=test.csv -c mydata -l test --headerline=true
3.查询功能的实现
(1)到巨杉数据库官网下载相关jar包
为了实现数据查询的功能,并在web界面上显示,本课题使用了tomcat作为服务器,在Eclipse中编辑,以下对功能文件进行介绍:
(2)连接池配置
package com.bill.util; import com.mchange.v2.c3p0.ComboPooledDataSource; import com.sequoiadb.base.ConfigOptions; import com.sequoiadb.base.Sequoiadb; import com.sequoiadb.datasource.DatasourceOptions; import com.sequoiadb.datasource.SequoiadbDatasource; import com.sequoiadb.exception.BaseException; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; public class UtilsC3P0 { public static void close(Sequoiadb db){ if ( db != null ) { ds.releaseConnection( db ); } } public static Sequoiadb getConnection(){ Sequoiadb db = null; try { // 获取连接 db = ds.getConnection(); // 使用连接 db... } catch (BaseException | InterruptedException e) { // 异常处理 } return db; } private static SequoiadbDatasource ds; static { ArrayList<String> addrs = new ArrayList<String>(); addrs.add("127.0.0.1:11810"); // SequoiaDB 集群的协调节点地址 addrs.add("127.0.0.1:3306"); String userName = "sdbadmin"; String password = "sdbadmin"; // 连接池参数配置 DatasourceOptions dsOpt = new DatasourceOptions(); dsOpt.setMaxCount( 500 ); dsOpt.setMaxIdleCount( 50 ); // 连接参数配置 ConfigOptions nwOpt = new ConfigOptions(); nwOpt.setConnectTimeout( 200 ); nwOpt.setMaxAutoConnectRetryTime( 0 ); ds = new SequoiadbDatasource(addrs, userName, password, nwOpt, dsOpt); } }
(3)查询功能实现
使用以下语句进行连接:
UtilsC3P0.getConnection();
获取数据库,数据表:
cs = db.getCollectionSpace(CS_NAME);
cl = cs.getCollection(CL_NAME);
使用dbcursor类对象,获取sql查询,例如:
DBCursor dbcursor = conn.exec("select sum(tx_value) as total from "+ CS_NAME+"."+CL_NAME +
" where tx_time >= '"+begin+"' and tx_time < '"+end+"'");
获取某一字段对应数据:
BSONObject match = new BasicBSONObject();
match.put("tx_id",tx_id);
BSONObject selector = new BasicBSONObject();
selector.put("tx_id",null);
selector.put("account_id",null);
selector.put("customer_name",null);
selector.put("tx_time",null);
selector.put("tx_value",null);
selector.put("account_overage",null);
selector.put("tx_type",null);
selector.put("other_side_account",null);
selector.put("tx_site",null);
selector.put("summary",null);
DBCursor dbcursor = cl.query(match,selector,null,null);
BSONObject result = dbcursor.getNext();
限制获取数据个数:
DBCursor cursor = cl.query(match, selector, order, null, skipRows, returnRows);
(4)部署web界面
index.jsp为主查询界面
search对应的是每一个查询结果的返回界面
3.其他注意事项与功能实现
为保证查询1和2返回的都是最新数据,我们采取了巨杉数据库中的事物隔离等级和主备一致性进行设置,设置事物隔离等级为RR,设置写活跃节点的主备一致性。
如果后续客户有扩容需求,也能较易实现,扩容能够增加整个集群的存储空间,并提升数据库集群的处理效率:
- 通过新增服务器并在服务器内新增节点:指在现有的集群中添加新的主机,并把新的节点部署到这些新的主机上。
- 直接在原有服务器内新增节点来扩容集群:增加数据节点后将原集群中的数据切分到新的数据节点中