今日指数

1.项目介绍

【今日指数】是基于股票实时交易产生的数据分析产品,旨在为特定用户和机构提供定制化的股票数据分析和展示服务;

项目的核心功能以数据分析和展示为主,主要包含了沪深指数、沪深板块、沪深个股和K线的实时行情查询等功能,内容包含了国内实时指数、涨幅榜、个股涨跌、个股秒级行情、实时日K线行情等;

image-20221229150523028

2.项目架构

2.1今日指数技术选型

【1】前端技术

名称 技术 场景
基本骨架 vue-cli+vue+element+axios
报表 echartsjs
前端支持 node webpack 等

【2】后端技术栈

名称 技术 场景
基础框架 springboot、mybatis-springboot、springMVC 项目基础骨架
安全框架 boot-security+jwt 认证与授权
缓存 redis 缓存
excel表格导出 easyexcel
小组件 jode-date 、gson 、guava 、httpClient \ restTemplate 、线程池
定时任务 xxljob
文档框架 swagger
分库分表 sharding-JDBC
部署 nginx

【3】整体概览

image-20221229150714502

2.2 核心业务介绍

image-20221229150800167

【2】业务功能简介

1
2
3
4
5
6
7
8
9
10
11
12
1.定时任务调度服务
XXL-JOB通过RestTemplate+多线程动态拉去股票接口数据,刷入数据库;
2.国内指数服务
3.板块指数服务
4.涨幅榜展示功能
5.涨停跌停数展示功能
6.成交量对比展示功能
7.个股涨停服务展示功能
8.个股详情展示功能
包含分时行情、日k线、周K线图等
9.个股描述服务;
10.报表导出服务

3.SQL相关

3.1SQL基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1.时间函数date_formate(dateTime,'%Y%m%d')
2.索引失效情况:
select * from user where id = 20 (id为primary key)
2.1 索引字段使用函数 导致索引失效,导致全表查询
eg:select * from user where trim(id) = 20
2.2 对索引字段进行数学运算
eg:select * from user where id + 5 = 20
2.3 使用反向查询导致索引失效 (!=not like,not in)
eg:select * from user where id != 20
2.4 索引字段频繁的修改
会造成b+tree频繁的维护(16kb页的分裂等)
3.关于分组group by
user:id,age,name,sex
eg:
select sex,* from group by sex;是错误的,一般group by要结合聚合函数处理数据,此时* 仅显示第一行记录,无意义数据
如果仅仅为了获取分组后的字段,eg:
select sex from user group by sex;
强烈建议改为:select distinct sex from user;
4.业务分析通透后再落地SQL
2:8定律;

3.2 业务分析

1
2
3
4
5
6
7
1.拿到需求,然后独立分析(第一次确认);
任务交接方会提供一些需求文档:需求说明书,产品原型
2.独立分析后,把潜在的问题(有歧义的问题) 整理集中找相关负责人对接,做开发需求的二次确认
这个过程会加深对需求的理解
甚至在探讨过程中负责人会给你一些建设性的开发建议;
3.拆解业务,SQL分步骤落地;
sql先在客户端运行,然后复制到xml中;

3.3业务举例说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
-- 以周k线展示功能为例分析
-- 业务概述: 周k线就是统计过去每周产生的数据,形成的k线图
-- 包涵的数据: 周1的开盘价,周五的收盘价,一周内的最高价,平均价格,股票code等.
-- SQL分析:
-- 1.先根据周分组,分组后获取周1的开盘的时间点,周五收盘的时间点,max最高价,min最低价等
-- 2.将步骤1的开盘收盘时间点结合股票code查询出对应的价格,就是一周内的开盘价和收盘价
-- 具体SQL:
SELECT
stock_code,
DATE_FORMAT(cur_time,'%Y-%u') AS week,
MAX(cur_time) AS mxTime, //收盘时间点 对应价格 收盘价
MIN(cur_time) AS miTime,// 开盘时间点 对应价格 开盘价
MAX(cur_price) AS maxPrice,
MIN(cur_price) AS minPrice,
AVG(cur_price) AS avgPrice
FROM stock_rt_info
WHERE stock_code=#{stockCode} and cur_time between #{startTime} and #{endTime}
GROUP BY week
-- 步骤2:将步骤1的结果作为一张表与股票流水表关联查询
SELECT tmp.stock_code,tmp.maxPrice,tmp.minPrice,tmp.avgPrice,s1.cur_price AS openPrice,s2.cur_price AS closePrice,
DATE_FORMAT(tmp.mxTime,'%Y%m%d') AS mxTime FROM
(
SELECT
stock_code,
DATE_FORMAT(cur_time,'%Y-%u') AS week,
MAX(cur_time) AS mxTime,
MIN(cur_time) AS miTime,
MAX(cur_price) AS maxPrice,
MIN(cur_price) AS minPrice,
AVG(cur_price) AS avgPrice
FROM stock_rt_info
WHERE stock_code=#{stockCode} and cur_time between #{startTime} and #{endTime}
GROUP BY week
) AS tmp LEFT JOIN stock_rt_info AS s1 ON s1.stock_code=tmp.stock_code AND s1.cur_time=tmp.miTime
LEFT JOIN stock_rt_info AS s2 ON s2.stock_code=tmp.stock_code AND s2.cur_time=tmp.mxTime ORDER BY mxTime ASC
-- 正常情况下 股票数据肯定是要有的,没必要采用外连接查询,但是如果防止关联查询时,表中无数据则可使用外连接

4.数据采集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.Spring提供的模拟浏览器请求行为的对象RestTemplate
核心API:
getForObject,
getForEntity,
PostForObject,
PostForEntity
2.解析采集的数据的思路
2.1 如果采集的数据时标准的json数据 直接借助jackson\fastjson\gson等工具解析
甚至RestTemplate本身也可以解析
2.2 如果是复杂的非标准的格式,先以String 接收,然后使用正则匹配
3.采集后的数据保存问题
采集数据IO开销比较大,且操作比较耗时
思路:将采集的数据partion分区分片,然后再逐次保存;
多线程+线程池异步并发采集数据,并操作;

5.多线程和线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1.构建线程对象的几种方式
1.1继承Thread类
1.2实现Runable接口
1.3实现Callable接口
1.4从线程池获取(底层也实现了Runable接口)
2.线程池专题
2.1线程池核心参数
核心线程数 最大线程数 任务队列 存活时间大小 存活时间单位 拒绝策略 线程工厂treadFactory
2.2线程池拒绝策略有哪些
AbordPolicy:抛出异常 中断执行
DiscardPolicy:丢掉任务,不抛异常
DiscardOldestPolicay:淘汰老的任务
CallerRunsPolicy:将处理不了的任务交给主线程处理,给主线程一个叫正向反馈 如果主线在执行任务时也阻塞了那就不会接受新的任务,这样任务线程就有时间缓慢处理当前的任务
自定义任务拒绝策略
实现RejectedExcuetionHandler接口下的rejectedExceution
2.3 线程池工作机制(工作流程)
0) 线程初始化时,核心线程数是多少?
零,线程是比较重的对象,构建对象时,需要与系统底层交互,所以尽量按需加载(懒加载)
1)当前线程数<核心线程数,此时如果有新的任务进来 会线程复用?
不会
2)当前线程数= 核心线程数,此时并发任务数小于等于核心线程数+任务队列长度,会构建新的线程吗?
不会,超过核心线程的任务会加入到任务队列中,异步处理
3) 并发任务数 > 核心线程数 + 工作队列长度,但是小于等于 最大线程数 + 工作队列长度,会构建新的线程(临时线程),但是这些临时线程一旦空闲,就会回收
4) 并发任务数 > 最大线程数 + 工作队列长度 线程池如何处理?
触发拒绝策略
3.线程池参数设置原则
3.1 先确定线程池使用场景
cpu密集型
如果是cpu密集型,那么会高频占用cpu,这样线程数尽量不要设置过大,因为线程设置过大,会导致多线程竞争cpu资源,这样会导致线程上下文的切换,带来开销相对较大,所以一般建议核心线程数=Cpu核心数+1
IO密集型(常见)
网络IO,磁盘IO等,比较耗时,单数cpu大多数时间都是处于空闲状态的,所以为了更加高效利用cpu
核心线程数=2Cpu核心数+1

6.分库分表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1.sharding-jdbc框架
1.1 相关名词
物理表(数据库中真实存在的表)
逻辑表(逻辑上操作,是为了sharding-jdbc操作物理表而存在的表)
分片键(物理表中的某个字段)
节点(物理库.物理表)
广播表(公共表特点:数据量少,但是各个数据库都会用到)
动态表(随着某种规则而动态生成的表 比如:股票流水,我们是按照年分库,月分表,但是2023年的月中也有对应的表,表随着时间而动态生成)
1.2 sharding-配置
分库分表数据源
指定分库的片键,分表的片键
默认数据源
不知道片键的操作,默认就会操作指定的默认数据源
公共表配置
广播表:broadcast
2.如何根据业务分析项目组分库分表的策略?
根据数据量和维护性的角度,选择是否水平分库分表
根据冷热数据 垂直分表
eg:以股票相关数据为例,分析:
大盘,板块每年产生的数据不大,但是股票的数据是以时间维度动态生成,考虑维护成本,我们是按照年为单位维护;
但是对于股票流水表,每个月产生数据动辄800w左右,所以需要水平分表,同时按照年分库;
代码落地思路:
都是按照年分库,所以可以将年分库的算法公共提取,
其他的分片算法,可自定义
同时应该选择时间作为数据分片的片键,因为查询大量数据关系到时间

7.权限相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1.SpringSecurity
核心的类:
UserDetailService方法:loadUserByName(name)
UserDetail:封装了用户的名称,密码,权限集合等信息
UsernamePasswordAuthentionFilter:认证(登录)过滤器
权限定义的2种方式:
方式1:基于配置类配置资源对应的权限
eg:antMatcher("资源路径").hasAuthorty("xxxx")
.permitAll()
方式2:基于注解
@PreAuthority("hasAuthority("xxx")")
@PostAuthority(xxx)
2.自定义认证过滤器和授权过滤器
定义了2个过滤器:
认证过滤器:模拟UsernamePasswordAuthentionFilter自定义实现
授权过滤器:继承OnecePerRequestFilter,自定过滤器
实现UserDetailService接口方法
重写权限拒绝策略:AccessDenyHandler接口
3.RBAC模型
核心表结构:5张表
用户 用户-角色 角色 角色-权限 权限表
RBAC 两层意思?
基于角色的权限控制,问题:角色改动后,代码要做调整 维护性差
基于资源的权限控制,维护性较好
4.前端展示需要菜单栏
菜单栏是树状结构,逻辑层通过递归查询数据,并组装

8.部署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.nginx
核心命令:
nginx 启动
nginx -s stop 停止 (-s作用:优雅关闭nginx,如果当前nginx正在处理请求,则暂时不关闭,如果有新的请求过来,则不处理,直到没有要处理的任务 则关闭)
nginx -s reload 重新加载
nginx -t 测试nginx.conf
2.nginx的作用?
2.1 部署静态资源(前后端分离后,前端的静态资源)
nginx优势在于静态资源的处理和反向代理,对于动态请求则交给tomcat处理(动静分离)
2.2 反向代理
与正向代理的本质区别:
正向代理是客户端知道要访问什么服务器,通过代理服务器代理执行
反向代理是客户端不知道代理服务器做了什么处理
(一个找黄牛,一个开分店)
2.3 负载均衡
1.轮询
2.权重
3.ip-hash
4.url-hash
5.least-connection 最小连接数
6.fair 相应时间
3.项目部署
理解基于脚本部署的意义和简单使用;