目标1:掌握Netty的使用
目标2:数据库设计规范
目标3:数据库建模理论
目标4:PowerDesigner使用
目标5:数据库调优
目标6:数据库设计之交易模块
建立两个工程, 通讯服务端与通讯客户端, 可以相互发送消息, 做个简易聊天演示。
netty-server
netty-client
io.netty
netty-all
4.1.16.Final
NettyProviderServer类:
public class NettyProviderServer {
private int port;
public NettyProviderServer (int port){
this.port = port;
}
// netty 服务端启动
public void runServer() throws Exception{
// 用来接收进来的连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 用来处理已经被接收的连接,一旦bossGroup接收到连接,就会把连接信息注册到workerGroup上
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// nio服务的启动类
ServerBootstrap sbs = new ServerBootstrap();
// 配置nio服务参数
sbs.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 说明一个新的Channel如何接收进来的连接
.option(ChannelOption.SO_BACKLOG, 128) // tcp最大缓存链接个数,它是tcp的参数, tcp_max_syn_backlog(半连接上限数量, CENTOS6.5默认是128)
.childOption(ChannelOption.SO_KEEPALIVE, true) //保持连接
.handler(new LoggingHandler(LogLevel.INFO)) // 打印日志级别
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//管道注册handler
ChannelPipeline pipeline = socketChannel.pipeline();
//编码通道处理
pipeline.addLast("decode", new StringDecoder());
//转码通道处理
pipeline.addLast("encode", new StringEncoder());
// 处理接收到的请求
pipeline.addLast(new NettyServerHandler()); // 这里相当于过滤器,可以配置多个
}
});
System.err.println("-------server 启动------");
// 监听输入端消息并发送给所有客户端
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();
if (NettyServerHandler.channelList.size() > 0) {
for (Channel channel : NettyServerHandler.channelList) {
channel.writeAndFlush(str);
}
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
}).start();
// 绑定端口,开始接受链接
ChannelFuture cf = sbs.bind(port).sync();
cf.channel().closeFuture().sync();
} finally{
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
// 开启netty服务线程
public static void main(String[] args) throws Exception {
new NettyProviderServer(9911).runServer();
}
}
服务端监听9911端口, 客户端连接时需指定此端口。
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
public static List channelList = new ArrayList();
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
channelList.add(ctx.channel());
}
// 读取数据
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server--收到消息: " + msg);
}
// 出现异常的处理
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.err.println("server--读取数据出现异常:");
cause.printStackTrace();
ctx.close();
}
}
处理channel所接收的消息。
NettyClientServer类
public class NettyClientServer {
// 要请求的服务器的ip地址
private String ip;
// 服务器的端口
private int port;
public NettyClientServer(String ip, int port){
this.ip = ip;
this.port = port;
}
// 启动服务
private void runServer() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
Bootstrap bs = new Bootstrap();
bs.group(bossGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//管道注册handler
ChannelPipeline pipeline = socketChannel.pipeline();
//编码通道处理
pipeline.addLast("decode", new StringDecoder());
//转码通道处理
pipeline.addLast("encode", new StringEncoder());
// 处理来自服务端的响应信息
socketChannel.pipeline().addLast(new NettyClientHandler());
}
});
System.out.println("-------client 启动------");
// 客户端开启
ChannelFuture cf = bs.connect(ip, port).sync();
String reqStr = "客户端发起连接请求";
Channel channel = cf.channel();
// 发送客户端的请求
channel.writeAndFlush(reqStr);
new Thread(new Runnable() {
@Override
public void run() {
try {
while(true) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String msg = in.readLine();
channel.writeAndFlush(msg);
}
}catch(Exception e) {
e.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) throws Exception {
new NettyClientServer("127.0.0.1", 9911).runServer();
}
}
NettyClientHandler类
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
public static Channel serverChannel = null;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
serverChannel = ctx.channel();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("client--收到消息:" + msg);
}
// 出现异常的处理
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.err.println("client 读取数据出现异常");
cause.printStackTrace();
ctx.close();
}
}
在互联网项目,分工明确,拆分细致, 但一个项目往往会涉及众多参与人员,包括架构师、高级工程师、研发工程师、测试工程师、DBA等不同角色。 数据库是一个项目的最底层设计, 如果设计不清晰,结构模糊, 规范混乱,是很难做成一个成功的项目,而这些需要我们从最基本的开始,对数据库有一套明确的设计规范。
传统数据库设计往往会遵循设计范式(三范式), 随着不断发展, 范式设计往往难以满足现在项目的要求, 出现了反范式设计。
范式设计:
第一范式:数据库表中的每一列都是不可分割的基本数据项, 比如股票名称、股票代码。
第二范式:数据库表所有属性都必须和主键有完全依赖关系, 比如用户编号为主键,那么与之依赖的有用户名、用户地址、用户性别和身份证等, 这些属性完全与具体用户产生依赖。
第三范式:数据库表中属性不能存在传递关系, 比如A->B->C, 属性之间存在这样关系则不符合第三范式。再具体点, 比如股票表(股票编号, 股票名称,股票市场,股票交易手续费),这个表就不符合第三范式,因为股票交易手续费是依赖股票市场来确定, 需要再拆分为(股票编号, 股票名称)和(股票市场,股票交易手续费)多张表。
反范式设计:
范式设计虽然关系层次明确, 但是给业务查询带来了相当的复杂度,中间关系表膨胀, 查询需要连接更多的表, 导致性能严重下降。
反范式并不是完全打破范式设计, 而是在范式设计与查询性能上找到平衡点,适合业务的设计才是好的设计。
在实际业务中, 如何去设计呢? 做好需求分析, 理解业务流程, 弄清业务概念, 技术是生产工具, 服务于业务才能体现其价值,所以明白业务需求才知道怎么去设计。 业务设计三步法则:
规范并不是千遍一律, 但要在自己的项目体系内形成一套有效规范, 以下规范可作参考:
命名规范
1.命名有意义,一眼知道这张表是干什么用的
2.数据库,表都用小写
数据库形如:backend
数据表形如:client_device_info(客户端设备信息),不要缩写,字母全小写
3.索引命名以idx_为前缀
4.命名不要过长(应尽量少于25字符)
5.不要使用保留字
6.同一字段在不同的表中也应是相同的类型和长度
7.同一数据库下有不同的模块,可以考虑对表名用不同的前缀标识
8.备份表时加上时间标识
表设计规范
1.如果没有特殊情况,建议选择InnoDB引擎
2.每个表都应该有主键,可选择自增字段,或长整型字段。例外情况,如果有多个字段可以组成唯一, 并且需要频繁查询的, 那么可以采用复合主键,比如用户编号+交易商编号做为复合主键,因为可以确保不产生重复数据, 同时节省空间。
3.(不做强制要求)尽量将字段设置为NOT NULL。因为NULL值的存储需要额外的空间,且会导致比较运算更为复杂,会使得优化器更难以优化sql。空值是不占用空间, 但NULL列需要占用一个额外字节。
4.使用更短小的列,比如整型列。整型列的执行速度往往更快。
5.存储精确浮点数必须使用DECIMAL代替float和double。
6.建议使用unsigned类型存储非负值
7.建议使用 int unsigned存储ipv4
8.整型定义中不添加显示长度的值,使用int,而不是int(4)
9.尽可能不要使用text,blob类型
10.varchar(n) n表示字符数而不是字节数,比如varchar(255)最大可存储255个汉字,需根据实际字符长度选择n的值。
11.字符集建议选择utf-8
12.存储年时使用year类型
13.存储日期时使用date类型
14.存储时间时,建议使用timestamp类型,因为timestamp使用的是4字节,datetime使用的是8字节。
15.不要在数据库中使用varbinary或blob存储图片及文件,mysql 并不适合大量存储这类型文件
16.join 操作的字段,在不同表中的类型及命名要一致
17.如果更改表结构会影响性能,需要找DBA、表设计人员进行联合评审。
核心原则
不在数据库做运算; cpu计算务必移至业务层;
控制每个表的列数量(字段少而精,字段数建议在20以内);
平衡范式与冗余(效率优先;往往牺牲范式)
拒绝3B(拒绝大sql语句:big sql、拒绝大事物:big transaction、拒绝大批量:big batch);
字段类原则
用好数值类型(用合适的字段类型节约空间);
字符转化为数字(能转化的最好转化,同样节约空间、提高查询性能);
避免使用NULL字段(NULL字段很难查询优化、NULL字段的索引需要额外空间、NULL字段的复合索引无效);
少用text类型(尽量使用varchar代替text字段);
索引类原则
合理使用索引(改善查询,减慢更新,索引一定不是越多越好);
字符字段大量重复属性时, 尽量建前缀索引(比如地区chinaXXX, alter table x_test add index(x_city(6)) ); 不在索引做列运算;
innodb主键推荐使用自增列(主键建立聚簇索引,主键不应该被修改,字符串不应该做主键) 不用外键(由程序保证约束);
SQL类原则
sql语句尽可能简单(一条sql只能在一个cpu运算,大语句拆小语句,减少锁时间,一条大sql可以堵死整个库);
避免使用trig/func(触发器、函数, 采用程序取而代之);
大批量SQL的更新操作要开启事务,越多效果性能越明显。
数据模型是数据库的基础,数据是对客观事物的符号表示,模型是现实世界的抽象。数据模型是对数据特征的抽象。数据库模型主要包含概念数据模型、逻辑数据模型与物理数据模型。
数据库的类型是根据数据模型来划分的,而任何一个DBMS也是根据数据模型有针对性地设计出来的,这就意味着必须把数据库组织成符合DBMS规定的数据模型。目前成熟地应用在数据库系统中的逻辑数据模型有:层次模型(树状结构表示的数据模型)、网状模型(有向图表示)和关系模型(表格表示)。
概念模型->逻辑模型->物理模型
概念模型是面向用户、面向现实世界的数据模型, 与DBMS无关,与业务相关,按用户的观点来对数据和信息建模。
了解用户的需求,用户的业务领域工作情况, 经过分析和总结,提炼出来的用以描述用户业务需求的一些概念的东西。例如金融交易市场中存在众多概念, 一个完整的交易,能够抽取出的概念有, 客户、 股票、订单、持仓等。
简单概括为 是什么
逻辑模型就是要将概念模型具体化,就是要实现概念模型所描述的东西,需要哪些具体的功能,处理哪些具体的信息,是对概念模型的进一步细化。例如金融交易市场中的客户,信息包含: 客户名称, 客户账号,客户电话、客户地址等属性; 股票包含:股票代码, 股票名称, 股票市场,股票单位等; 订单包含客户账户, 股票代码,交易手数, 交易金额等。
这个阶段需要清楚系统要划分多少个模块, 按计算机系统的观点对数据建模,要建立多少个数据表, 每张表具体包含的属性特征, 主要用于DBMS的实现。
简单概括为 做什么
物理模型是对真实数据库的描述, 对数据最低层的抽象, 描述数据在系统内部的表示方式和存取方法,是面向计算机系统的。如关系数据库中的一些对象: 表、视图、字段、数据类型、长度、主键、外键、索引、约束、是否可为空、默认值。
这个阶段需要采用数据库语言来描述真实世界的模型, 形成具体的SQL语句。
简单概括为 怎么做
PowerDesigner是一款功能非常强大的建模工具软件,也是最流行的建模软件之一。它的发展历程是怎样? 相比其他主流工具有什么区别? 比如Rational Rose, 它是专攻UML对象模型的建模工具,之后才向数据库建模发展;而PowerDesigner正好相反,它是以数据库建模起家,后来才发展为一款综合全面的Case工具。
选择物理模型
去除drop index脚本
drop index 不支持 if exists, 直接运行生成的脚本会出现错误, 这里我们把它删掉。
选择“Edit Current DBMS"
清空此处脚本语句, 点击确定。
选择“Generate Database” 或者直接快捷键“Ctrl + G”
如果出现错误, 请将“Check model”的勾选去除。
出现此提示为成功生成
执行脚本
脚本执行成功, 没有出现错误。
MySQL是目前使用最多的开源数据库,但是MySQL数据库的默认设置性能并非最优,特别在生产环境当中, 如果不去合理优化,不能发挥最佳性能, 甚至会产生各种各样的问题。 因此数据库必须进行必要的调整与优化。
那么如何进行调优? 有哪些方法? 每个系统环境配置不一样, 如何去设置? 线上出现的慢查询, 又如何去定位分析? 带着这些问题, 我们逐一去分析解决。
联机分析处理(OLAP,On-line Analytical Processing),数据量大,DML少。
联机事务处理(OLTP,On-line Transaction Processing),数据量少,DML频繁。
调优方法
业务设计的合理性
业务设计并不是满足一切业务需求, 在碰到性能瓶颈之处, 要学会权衡取舍, 比如一些业务数据报表, 不需要很强的实时性, 那么就不用直接查询核心数据表增加压力, 采用预生成方式, 这样既满足业务要求, 又能保持核心数据表的操作性能。
增加索引
索引可以极大优化查询性能, 索引包含主键索引(PRIMAY KEY)、唯一索引(UNIQUE)、常规索引(INDEX)和全文索引(FULLTEXT)。
索引底层可以采用Btree索引和Hash索引:
*B-TREE*, B树是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。B树为系统大块数据的读写操作做了优化。B树可以减少定位记录时所经历的中间过程,从而加快存取速度。
B-TREE特征:
B-TREE通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离都相同。
B-TREE索引能够加快访问数据的速度,是因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始搜索。
B-TREE对索引列是顺序组织存储的,所以很适合查找范围数据。
Hash索引:
Hash是采用哈希算法计算,得到哈希值,排序在哈希数组上。所以Hash索引可以一次定位,其效率很高,而Btree索引需要经过多次的磁盘IO,但是Hash索引也存在缺点:
注意: 索引并不是一味的增加, 一般一张表索引的数量控制4-5个之内。
分库分表
分库可以分解数据压力, 采用集群方式,或业务拆分, 从根本上提升整个系统的I/O吞吐能力。
分表在于单表集中压力的化解, 一般可通过垂直/水平方式进行拆分。分表一般都是单维度拆分, 利用某个字段取模或者根据时间字段进行拆分; 对于多维度的拆分,可采用基因法, 基因法是根据某个字段的特征, 再融入到其他维度字段中,利用基因进行数据定位。 例如, 通过uid分库,分为8个库,采用uid%8的方式进行路由,此时是由uid的最后3bit来决定这行User数据具体落到哪个库上,那么这3bit可以看为分库基因。
分表也并不是万能, 比如关联查询、分页查询、以及数据路由带来的开销。
读写分离
读写分离,基本的原理是让主数据库处理事务性的增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。
在实际的生产环境中,对数据库的读和写都在同一个数据库服务器中,是不能满足实际需求的。无论是在安全性、高可用性还是高并发等各个方面都是完全不能满足实际需求的。 因此,通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力。
SQL语句调优
通过SQL语句的调整来优化响应时间。常见的SQL语句优化点包:
mysql配置优化
其他优化
什么是执行计划
执行计划就是分析执行Sql语句的过程,包括Sql查询的顺序、是否使用索引、以及使用的索引信息等内容。
一个执行计划由若干基本操作组成. 例如, 遍历整张表, 利用索引, 执行一个嵌套循环或Hash连接等等。
explain关键字与输出项说明
要生成执行计划, 在相应的语句之前加上explain关键字:
explain select ...
explain只是预估, 并非实际执行。
EXPLAIN输出项
Column | JSON Name | Meaning |
---|---|---|
id | select_id | The SELECT identifier(选择标识符) |
select_type | None | The SELECT type (查询类型) |
table | table_name | The table for the output row (输出结果的表名) |
partitions | partitions | The matching partitions(匹配的分区信息) |
type | access_type | The join type(表示的连接类型) |
possible_keys | possible_keys | The possible indexes to choose (可能使用到的索引) |
key | key | The index actually chosen(实际使用的索引) |
key_len | key_length | The length of the chosen key(索引字段的长度) |
ref | ref | The columns compared to the index(表的连接匹配条件, 哪些列或常量被用于查找索引列上的值) |
rows | rows | Estimate of rows to be examined(扫描估算的行数) |
filtered | filtered | Percentage of rows filtered by table condition(按条件过滤的行百分比) |
Extra | None | Additional information(执行计划的情况的描述和说明) |
注意:在5.7以前的版本中,想要显示partitions需要使用explain partitions命令;想要显示filtered需要使用explain extended命令。在5.7版本后,默认explain直接显示partitions和filtered中的信息。
通过实际SQL来解读执行计划信息
通过脚本,创建好用于SQL执行计划的数据。
下面分析两条SQL语句的执行计划:
第一条语句是要查询用户所关联的角色信息, USER_ACCOUNT为唯一索引。
EXPLAIN SELECT
u.id,
u.USER_ACCOUNT,
r.ROLE_NAME
FROM
s_authority_user u
LEFT JOIN s_authority_user_role ur ON u.id = ur.USER_ID
LEFT JOIN s_authority_role r ON ur.role_id = r.id
WHERE
u.USER_ACCOUNT = 'admin'
第二条语句是一个多SQL查询:
EXPLAIN SELECT
u1.*
FROM
s_authority_user u1 UNION
SELECT
u2.*
FROM
s_authority_user u2
下面, 详细分析执行计划各字段含义:
id属性
id为SELECT的标识符, 如果这一行表示其他行的union结果,这个值可以为空。在这种情况下,table列会显示为形如
union
等查询语句中。select_type属性
常见的属性值
id | select_type | description |
---|---|---|
1 | SIMPLE | 不包含任何子查询或union等查询 |
2 | PRIMARY | 包含子查询最外层查询就显示为 PRIMARY |
3 | SUBQUERY | 在select 或 where 字句中包含的查询 (in匹配不会生效, 要采用等号) |
4 | DERIVED | from 字句中包含的查询 |
5 | UNION | 出现在union 后的查询语句中 |
6 | UNION RESULT | 从UNION中获取结果集,例如上面的例子 |
除此之外还有, derived:from字句中出现的子查询; materialized:被物化的子查询等, 这些用得相对较少。
table属性
显示的查询表名,如果查询使用了别名,那么这里显示的是别名,如果不涉及对数据表的操作,那么这显示为null,如果显示为尖括号括起来的就表示这个是临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生。如果是尖括号括起来的
例如:
EXPLAIN SELECT
u.id,
u.USER_ACCOUNT,
r.ROLE_NAME
FROM
s_authority_user u
LEFT JOIN (select USER_ID, role_id from s_authority_user_role group by user_id) ur ON u.id = ur.USER_ID
LEFT JOIN s_authority_role r ON ur.role_id = r.id
WHERE
u.USER_ACCOUNT = 'admin'
partitions属性
该列显示的为分区表命中的分区情况,非分区表该字段为空(null)。
例如, 创建一个支持分区的表:
drop table if exists tmp_partitions;
create table tmp_partitions (
id int unsigned not null AUTO_INCREMENT,
name varchar(255),
PRIMARY KEY (id)
) engine = innodb
partition by key (id) partitions 3;
查看其执行计划:
type属性
表示表查询的连接类型
id | type | description |
---|---|---|
1 | ALL | 扫描全表数据 |
2 | index | 遍历索引 |
3 | range | 索引范围查找 |
4 | index_subquery | 在子查询中使用 ref |
5 | unique_subquery | 在子查询中使用 eq_ref |
6 | UNION ref_or_null | 对Null进行索引的优化的 ref |
7 | fulltext | 使用全文索引 |
8 | ref | 使用非唯一索引查找数据 |
9 | eq_ref | 在join查询中使用PRIMARY KEY或UNIQUE NOT NULL索引关联 |
10 | const | 使用主键或者唯一索引,且匹配的结果只有一条记录 |
11 | system const | 连接类型的特例,查询的表为系统表 |
possible_keys属性
可能使用的索引,注意不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为 null时就要考虑当前的SQL是否需要优化了。
key属性
显示查询使用到的索引,select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。
key_len属性
索引长度,字符索引长度的计算公式:
(Character Set:utf8mb4=4,utf8=3,gbk=2,latin1=1) * 列长度 + 1(允许null) + 2(变长列)
key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。
ref属性
如果是使用的常数等值查询,这里会显示const,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func。
rows属性
返回估算的结果集数目, 因为explain并非真正执行语句, 所以并非精确值。
extra属性 常见的有:
注意: extra辅助信息中的using filesort和using temporary,这两项非常消耗性能,需要谨慎。
金融交易整个平台主要分为交易模块和非交易(业务为主)模块, 交易模块是整个系统的核心,也是整个数据层的脊骨,那么交易平台如何去设计? 整个设计遵循FIX国际金融标准协议, 借鉴行业内的标杆, 恒生和金证柜台交易系统, 并作扩充改进, 支撑更广泛的金融产品, 如外汇、期货、CFD等交易。
可参考附件《FixApi文档》。
订单表、成交记录表和仓位表组成整个交易模块的核心,此设计可以支撑多次成交,仓位合并,订单合并等复杂业务场景。
用户表:
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键标识 | bigint(20) | NO | 主键标识 |
userNo | 用户编号 | varchar(16) | NO | 用户编号 |
name | 用户名称 | varchar(16) | YES | 用户名称 |
userPwd | 用户密码 | varchar(16) | NO | 用户密码 |
phone | 电话号码 | varchar(16) | YES | 电话号码 |
companyId | 公司ID | bigint(20) | YES | 公司ID |
邮箱 | varchar(32) | YES | 邮箱 | |
address | 地址 | varchar(255) | YES | 地址 |
lastLoginIp | 最近一次用户登陆IP | varchar(32) | YES | 最近一次用户登陆IP |
lastLoginTime | 最近一次登陆时间 | datetime | YES | 最近一次登陆时间 |
status | 状态(0:有效, 1:锁定, 2:禁用) | tinyint(3) | NO | 状态(0:有效, 1:锁定, 2:禁用) |
craeteTime | 创建时间 | datetime | YES | 创建时间 |
institutionTypeId | 机构类型id | varchar(32) | YES | 机构类型id |
institutionId | 机构id | bigint(19) | YES | 机构ID |
companyName | 公司名称 | varchar(64) | YES | 公司名称 |
用户账号表
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键标识 | bigint(20) | NO | 主键标识 |
userId | 用户表ID | bigint(20) | NO | 用户表的ID |
accountNo | 交易账号 | varchar(16) | NO | 交易账号 |
balance | 余额 | bigint(21) | YES | 余额 |
tradeGroupId | 账户组ID | bigint(20) | NO | 账户组ID |
activeTime | 开户时间 | datetime | YES | 开户时间 |
status | 状态(0:有效, 1:锁定, 2:禁用) | tinyint(3) | NO | 状态(0:有效, 1:锁定, 2:禁用) |
companyId | 公司ID | bigint(20) | YES | 公司ID |
userName | 用户名称 | varchar(32) | YES | 用户名称 |
institutionId | 对应机构类型下的机构id | bigint(19) | YES | 对应机构类型下的机构id |
institutionTypeId | 机构类型id | varchar(32) | YES | 机构类型id |
tradeGroupName | 账户组名称 | varchar(32) | YES | 账户组名称 |
用户账户组表
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键标识 | bigint(20) | NO | 主键标识 |
groupNo | 账户组编号 | varchar(16) | NO | 账户组编号 |
groupName | 账户组名称 | varchar(16) | NO | 账户组名称 |
currency | 交易币种(CNY:人民币, HKD:港币,USD:美元) | varchar(12) | YES | 交易币种(CNY:人民币, HKD:港币,USD:美元) |
companyId | 公司ID | bigint(20) | NO | 公司ID |
commissionRate | 佣金比例, 单位:% | double(4,2) | YES | 佣金比例, 单位:% |
taxesRate | 税费比例, 单位:% | double(4,2) | YES | 税费比例, 单位:% |
level | 账户等级(0:普通,1:VIP) | tinyint(3) | YES | 账户等级(0:普通,1:VIP) |
status | 状态(0:启用, 1:禁用) | tinyint(3) | NO | 状态(0:启用, 1:禁用) |
craeteTime | 创建时间 | datetime | YES | 创建时间 |
companyName | 公司名称 | varchar(64) | YES | 公司名称 |
订单表
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键标识 | bigint(20) | NO | 主键标识 |
accountId | 交易账号ID | bigint(20) | NO | 交易账号ID |
stockId | 股票ID | bigint(20) | NO | 股票ID |
status | 状态(0:待成交, 1:部分成交, 2:完全成交, 3:手工撤单, 4:系统撤单) | tinyint(3) | NO | 状态(0:待成交, 1:部分成交, 2:完全成交, 3:手工撤单, 4:系统撤单) |
type | 挂单类型(0:市价单, 1:限价单) | tinyint(3) | NO | 挂单类型(0:市价单, 1:限价单) |
direction | 买卖方向 | tinyint(2) | NO | 买卖方向 |
initVolume | 挂单的数量 | int(11) | NO | 挂单的数量 |
execVolume | 成交数量 | int(11) | YES | 成交数量 |
requestPrice | 请求价格 | double(8,2) | YES | 请求价格 |
execPrice | 成交价格 | double(8,2) | YES | 成交价格 |
execTime | 成交时间 | datetime | YES | 成交时间 |
accountno | 账户编号 | varchar(32) | YES | 账户编号 |
stockcode | 股票编号 | varchar(32) | YES | 股票编号 |
stockname | 股票名称 | varchar(32) | YES | 股票名称 |
成交记录表
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键, 唯一标识 | bigint(20) | NO | 主键, 唯一标识 |
tradeOrderId | 交易订单号 | bigint(20) | NO | 交易订单号 |
positionId | 持仓记录ID | bigint(20) | NO | 持仓记录ID |
accountId | 交易账号ID | bigint(20) | NO | 交易账号ID |
stockId | 股票ID | bigint(20) | NO | 股票ID |
stockMarket | 股票市场(0: 上交所, 1:深交所,2:港股, 3:美股) | tinyint(3) | YES | 股票市场(0: 上交所, 1:深交所,2:港股, 3:美股) |
execVolume | 成交数量 | int(11) | YES | 成交数量 |
execPrice | 成交价格 | double(8,2) | YES | 成交价格 |
execTime | 成交时间 | datetime | YES | 成交时间 |
direction | 买卖方向 | tinyint(2) | NO | 买卖方向 |
profit | 盈亏 | double(8,2) | YES | 盈亏 |
commission | 佣金 | double(6,2) | YES | 佣金 |
taxes | 税费 | double(6,2) | YES | 税费 |
status | 状态(0:有效,1:无效) | tinyint(2) | NO | 状态(0:有效,1:无效) |
仓位表
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键, 唯一标识 | bigint(20) | NO | 主键, 唯一标识 |
accountId | 交易账号ID | bigint(20) | NO | 交易账号ID |
stockId | 股票ID | bigint(20) | NO | 股票ID |
costPrice | 成本价 | double(8,2) | YES | 成本价 |
volume | 持仓数量 | int(11) | NO | 持仓数量 |
stockMarket | 股票市场(0: 上交所, 1:深交所,2:港股, 3:美股) | tinyint(3) | YES | 股票市场(0: 上交所, 1:深交所,2:港股, 3:美股) |
avgPrice | 成交均价 | double(6,2) | NO | 成交均价 |
validSellVolume | 可卖数量(T+1制度使用) | int(11) | YES | 可卖数量(T+1制度使用) |
profit | 盈亏 | double(8,2) | YES | 盈亏 |
execTime | 成交时间 | datetime | YES | 成交时间 |
status | 状态(0:有效,1:无效) | tinyint(2) | NO | 状态(0:有效,1:无效) |
interest | 持仓累计利息 | double(8,2) | YES | 持仓累计利息 |
股票信息表
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键标识 | bigint(20) | NO | 主键标识 |
name | 名称 | varchar(16) | YES | 名称 |
code | 股票编号 | varchar(12) | YES | 股票编号 |
categoryId | 股票分类ID | bigint(20) | NO | 股票分类ID |
marketNo | 股票市场(0: 上交所, 1:深交所,2:港股, 3:美股) | tinyint(3) | YES | 股票市场(0: 上交所, 1:深交所,2:港股, 3:美股) |
currency | 交易币种(CNY:人民币, HKD:港币,USD:美元) | double(12,2) | YES | 交易币种(CNY:人民币, HKD:港币,USD:美元) |
unit | 股票单位(默认为:100) | int(11) | NO | 股票单位(默认为:100) |
tag | 标签(0:普通, 1:热门) | tinyint(3) | YES | 标签(0:普通, 1:热门) |
status | 状态(0:启用, 1:禁用) | tinyint(2) | NO | 状态(0:启用, 1:禁用) |
marketSource | 行情来源 | varchar(32) | YES | 行情来源 |
display_order | 显示顺序, 升序排列 | int(11) | YES | 显示顺序, 升序排列 |
updateTime | 修改时间 | datetime | YES | 修改时间 |
craeteTime | 创建时间 | datetime | YES | 创建时间 |
股票分类表
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键标识 | bigint(20) | NO | 主键标识 |
name | 名称 | varchar(16) | YES | 名称 |
code | 分类编号 | varchar(12) | YES | 分类编号 |
status | 状态(0:启用, 1:禁用) | tinyint(2) | NO | 状态(0:启用, 1:禁用) |
startMins | 交易开始时间(分钟) | int | NO | 交易开始时间(分钟) |
endMins | 交易结束时间(分钟) | int | NO | 交易结束时间(分钟) |
commissionRate | 佣金比例(%) | int | NO | 佣金比例(%) |
|
| unit | 股票单位(默认为:100) | int(11) | NO | 股票单位(默认为:100) |
| tag | 标签(0:普通, 1:热门) | tinyint(3) | YES | 标签(0:普通, 1:热门) |
| status | 状态(0:启用, 1:禁用) | tinyint(2) | NO | 状态(0:启用, 1:禁用) |
| marketSource | 行情来源 | varchar(32) | YES | 行情来源 |
| display_order | 显示顺序, 升序排列 | int(11) | YES | 显示顺序, 升序排列 |
| updateTime | 修改时间 | datetime | YES | 修改时间 |
| craeteTime | 创建时间 | datetime | YES | 创建时间 |
股票分类表
属性 | 名称 | 数据类型 | 是否为空 | 注释 |
---|---|---|---|---|
id | 主键标识 | bigint(20) | NO | 主键标识 |
name | 名称 | varchar(16) | YES | 名称 |
code | 分类编号 | varchar(12) | YES | 分类编号 |
status | 状态(0:启用, 1:禁用) | tinyint(2) | NO | 状态(0:启用, 1:禁用) |
startMins | 交易开始时间(分钟) | int | NO | 交易开始时间(分钟) |
endMins | 交易结束时间(分钟) | int | NO | 交易结束时间(分钟) |
commissionRate | 佣金比例(%) | int | NO | 佣金比例(%) |