数据切分策略

背景

随着业务的快速发展,海量数据与其高效使用已成为企业关注的重点问题。当数据量达到一定限制之后,企业面临着如何缓解数据库的性能问题,初步的方案可以为优化数据库索引,但它不可能成为持久的解决方案。为了持续保证数据库性能健壮,务必从数据层面对问题寻找方案,即需以有效的策略对数据进行切分

数据切分

数据库分片,即Sharding的基本思想是将海量数据切分到不同的数据库中,从而缓解单一数据库的性能压力。数据库分片的意义在于把数据分到不同的物理机的数据库中,增加主机的数量,这样可以减轻单一物理机的CPU、内存、网络IO方面的压力,为此实现提升性能。

数据切分分为两种:垂直切分水平切分


垂直切分

垂直切分分为 垂直分库垂直分表
垂直分库是根据业务需求,将关联度较低的数据表分散存储到不同的数据库中;垂直分表是当某一表字段较多时,可以通过创建新的扩展表的形式对“大表”进行拆分,将不常用的字段或者长度较长的字段拆分到另一张扩展表中,以主键进行关联。

垂直分库

简单来讲就是以不同的表或者表字段为单位,把数据存储到不同的数据库中。

优点

  1. 业务明确,降低各业务间的耦合度;
  2. 在高并发的场景中,一定程度上能缓解网络IO以及数据库性能问题。

缺点

  1. 无法进行join操作,只能通过接口聚合方式解决,提升了开发的复杂度;
  2. 对于单表数据量行数过大的情况,效果不大。

水平切分

对于单表数据量行数过大的情况,存在单表读写、存储性能瓶颈,单靠垂直切分不够,需要考虑水平切分。水平切分是在同一个表里,根据特定规则,将数据按行分散存储到不同的数据库中,这样每个数据库只存储部分数据,达到分布式的效果。

优点

  1. 对于单表数据行过大的情况,有效避免性能瓶颈;
  2. 不需要额外拆分业务。

缺点

  1. 跨库的join操作较缓慢;
  2. 后期修改表结构或者维护困难;

水平切分时,一般通过特定规则,将一张表拆分存储在不同的数据库的数据表中。以下为较为常见的切分方法:

1. 根据数值范围切分

根据数值范围切分

根据业务需求,可通过数据表的ID或者时间范围来进行切分。如图所示,将 key 的取值范围为[1,100000]的数据分到第一个库中,取值范围为[100001,200000]的数据分到第二个库中,取值范围为[200001,300000]的数据分到第三个库中。若以根据时间范围切分举例,可以把把1,2,3月份数据放到第一个数据库中,4,5,6月份数据放到第二个数据库中,以此类推。

优点

  1. 可以有效控制单表数据量过大的情况;
  2. 便于后期扩容,只需要添加节点即可;
  3. 当查询连续数据时,可快速在定位分片里进行查询,不用跨分片查询;

缺点

连续分片可能存在热点数据,即有些分数据频繁被读写,而有些很少被调用。

2. 根据数值取模切分

一般采用hash取模的方式,对数据进行切分。例如,将对 key 模 3 取余数,余数为0的数据分到第一个数据库中,余数为1的数据分到第二个数据库中,余数为2的数据分到第三个数据库中。

优点

数据分片相比较均匀,出现热点数据的可能性较小;

缺点

  1. 如果查询条件中不包括此 key,则较难确定数据具体分布在哪个数据库,所以需要向所有数据库进行查询,并在内存中合并数据,取交集返回。

分库分表带来的问题及解决方案

1. 跨库join查询问题

在应用中通过join来关联查询多个表是较为普遍的场景,而将数据切分到不同的数据库之后,join操作就变得不简单了,跨库的join则带来性能和安全方面的问题,考虑到架构规范也是不提倡的。

解决方案

  1. 全局表
    维护一些表,用来存放所有模块可能共同依赖的字段,为了避免垮库join查询,所有节点都需维护这些表。在这里需要注意的是,这些字段应当为修改频率较低的字段,比如id、日期这种属性,不然维护起来难度较大,可能会带来数据不一致的新问题。

  2. 字段冗余
    这是一种以空间换时间的思想。例如,设计订单表的时候,除了定义userId之外,再定义usnerName冗余一份,这样可以不同通过join来关联查询。

2. ID主键重复问题

主键为保证某一条数据唯一性的唯一标识。在分库的情况下,如果通过自增ID的方式创建主键的话,会导致多条数据主键重复的问题,这样显然不可行。为了保证主键的唯一性,必须设计一个满足要求的特殊形式ID。

解决方案

1. UUID

UUID = 4个连字号(-) + 32个字节长的字符串,总共36个字节长。比如:

550e8400-e29b-41d4-a716-446655440000

优点:
能够保证 独立性,程序可以在不同的 数据库间迁移,效果不受影响。

缺点:

  1. 不易于存储 / 性能问题:UUID太长,128位、36长度字符,不利于Mysql索引(在InnoDB引擎下,UUID的无序性 可能会引起数据位置频繁变动,严重影响性能),很多场景不适用。
  2. 采用无意义字符串,数据量增大时造成访问过慢,且不宜排序
  3. 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,这个漏洞曾被用于寻找梅丽莎病毒的制作者位置。
2. 雪花算法

snowflake-64bit
算法描述:

  • 最高位是符号位,始终为0,不可用。
  • 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序
  • 10位的机器 标识,10位的长度最多支持部署1024个节点。
  • 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

此外,还存在事务一致性、跨节点分页、排序、函数等问题。

总结

那我们该什么情况下需要考虑数据的切分?其实,能不切法尽量避免切分。数据切分所带来的问题较难控制,有些解决方案也不够成熟。只有在数据量过大,正常运维影响业务访问,或者随着业务发展,需要对某些字段垂直切分的情况下,可以考虑数据切分。此外,数据切分可以在一定程度下提升数据的安全性和可用性,当某个节点出现问题的时候,不会影响到所有的业务处理,因为数据是分布存储到多个节点的,存储在其他数据库的数据将不会受到影响。

基于RNN的LSTM长短期记忆神经网络

RNN 神经网络

RNN(Recurrent Neural Network, 循环神经网络) 的目的是用来处理序列数据。在传统的神经网络模型中,是从输入层到隐含层再到输出层,层与层之间是全连接的,而每层之间的节点是无连接的。但是这种普通的神经网络对于很多问题却无能无力。例如,你要预测句子的下一个单词是什么,一般需要用到前面的单词,因为一个句子中前后单词并不是独立的。RNNs之所以称为循环神经网路,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的 节点 不再无连接而是 有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。

其展开图如下:

RNN展开图

其中,h1 状态包含着 x0x1 的信息,h1 状态包含着 x0, x1, x2 的信息,以此类推,ht 里包含着所有的输入信息, 可以把 ht 看作为从 x0, x1 ··· xt 整个输入信息里提取到的特征向量。

RNN单层图

更新每个状态 h 的时候需要用到参数矩阵 A, 需要注意的是整个RNN只有一个参数矩阵 A,一开始 A 随机初始化,然后利用训练数据来学习 A. 但在此过程中,如果矩阵向量 A 的特征值大于 1,或者小于 1,并且循环次数很大的情况下,可能会变成接近于 0,或者变成很大的值。从而导致 梯度消失梯度爆炸

LSTM 神经网络

LSTM(Long Short Term Memory, 长短时记忆网络) 模型,是在RNN模型的基础上作出改进,解决了RNN短期记忆的问题 (梯度消失和梯度爆炸)。

LSTM图

LSTMRNN的基础结构上增加了遗忘门、输入门、输出门三个单元,它有四种参数矩阵。

遗忘门

遗忘门
遗忘门ft 的方法 : 先把上一时刻的 h_t-1 与当前输入 xt 连接,然后与遗忘权重矩阵 Wf 相乘,结果做 sigmoid

ft 的每一个值都在 0-1 之间,比如为0时,就是要忘记,为1时,就是全部通过。所以遗忘门 f 可以有选择地让传输带 C 的值通过。即C * f = output

输入门

输入门
输入门ft 的方法 :和遗忘门类似,把上一时刻的 h_t-1 与当前输入 xt 连接,然后与输入权重矩阵 Wi 相乘,再通过 sigmoid 函数得到 i_t.

new value

![new value](/Users/paper/Documents/paper/hexo-blog/source/images/new value.png)
new value_Ct 的方法 :与遗忘门输入门类似,但是使用的激励函数不一样,一般使用tanh函数,所以向量的每一个值都在 -1~1 之间。计算 _Ct 也需要一个权重矩阵 Wc.

更新传输带Ct

更新传输带Ct的值
我们已经求出遗忘门ft输入门ft,还有new value_Ct,我们还知道传输带旧的值 C_t-1,现在可以更新传输带 Ct 了。

更新传输带 Ct的方法(如上图):

  1. 遗忘门ft 选择性地遗忘旧传输带 Ct-1 中的一些元素。

    ft * Ct-1 (1)

  2. 现在要往传输带上添加一些新的信息。也就是输入门ft 向量与new value_Ct 向量相乘。

    i_t * _Ct (2)

  3. 新的传输带 Ct 值就是上一时刻的值 Ct-1 通过遗忘门删除一些值,通过输入门添加一些新值得到的,也就是 (1) + (2)

    Ct = ft * Ct-1 + i_t * _Ct

输出门

输出门
遗忘门输入门算法一样。输出门也有自己的参数矩阵 Wo.

更新状态向量 h_t

更新状态向量h_t
求出 h_t的方法 :

  1. 传输带 Ct每一个元素求tanh双曲正切,把元素都压到 -1~1 区间。
  2. ht = Ot * tanh(Ct)

这样,可以认为所有输入的 x 信息都积累在状态向量 ht 里面。

小波分析具体理解

简介

小波分析继承和发展了短时傅立叶变换局部化的思想,同时又克服了窗口大小不随频率变化等缺点,能够提供一个随 频率 改变的“时间-频率”窗口,是进行信号时频分析和处理的理想工具。

小波函数不同于傅里叶变换的正弦波函数,它是个快速衰减并且有限长的波函数,同时满足 积分为 0 的条件。

能对时间(空间)频率的 局部化分析,通过 伸缩平移 运算对信号(函数)逐步进行多尺度细化,最终达到高频处时间细分,低频处频率细分,能 自动适应 时频信号分析的要求,从而可聚焦到信号的任意细节,解决了Fourier变换的困难问题,成为继Fourier变换以来在科学方法上的重大突破。

小波变换的作用

通过变换能够充分 突出 问题某些方面(信号)的 特征

小波变换

小波变换的定义如下:

小波变换定义
小波变换过程

f(t)为被分析的信号,phi(t-b/a)为小波函数,phi(t-b/a)通过 平移 在每一个点上求它们的乘积,并且求积分。这就是小波变换的整个过程。

CWT 连续小波变换

小波函数与原信号对应点相乘,再相加,得到对应点的小波变换系数,平移小波基函数,再计算小波函数与原信号对应点相乘,再相加,这样就得到一系列的小波系数。

DWT 离散小波变换

实际上,离散小波变换是对连续小波变换尺度位移 按照 2 的幂次进行离散化得到的。

重要概念

小波系数小波基函数原信号 相似的系数,是小波分解中的一些参数,通过这些参数可以重构得到原始信号。

基本小波(母小波函数):是一具有特殊性质的实值函数,是具有快速衰减的波函数(有限长),在数学上满足 积分为 0 的条件。

小波函数(小波基函数):通过将母小波函数 伸缩、平移 得到的。

尺度函数:通过一个母小波函数改变 伸缩 、平移 两个参数得到的 所有 小波函数的函数,可以以这 一组尺度函数 来代表原来的信息。

支撑长度:表示 滤波器 的长度(滤波的频率范围),滤波器的长度越短,小波变换的计算量就越低。

消失矩:对 基本小波 施加所谓的 消失矩 条件,使尽量多的 小波系数0 或者产生尽量 的非零小波系数,这样有利于数据压缩和消除噪声。消失矩 越高,高频子带的小波系数越小,并且接近 0 的 小波系数 越多。

紧支撑小波:若函数在 [a,b] 外恒为 0,则称该函数 紧支撑 在这个区间上,具有该性质的小波称为 紧支撑小波

消失矩定义

其中,phi(t) 为基本小波,0 <= p < N*。则称小波函数具有 *N 阶消失矩。从上式还可以得出,同任意 n-1 阶多项式正交。

小波的消失矩特性使函数在 小波展开 时消去了其 高阶平滑部分,因此 小波系数 将仅仅反映函数的 高阶变化部分,使我们能研究函数的高阶变化和某些高阶导数中可能的奇异性信息。

一般来说,这个消失矩的数字越大,这个小波光滑(长的小波滤波器)

小波分解与重构

信号分解

小波分解的意义就在于能够在 不同尺度 上对信号进行分解,而且对不同尺度的选择可以根据不同的目标来确定。

对于许多信号,低频 成分相当重要,它常常蕴含着信号的特征,而 高频 成分则给出信号的细节或差别。人的话音如果去掉 高频 成分,听起来与以前可能不同,但仍能知道所说的内容;如果去掉足够的 低频 成分,则听到的是一些没有意义的声音。

小波重构是用处理后的系数重构信号。

Daubechies(dbN) 小波

dbnn 表示 消失矩

支撑长度2n-1

小波滤波器 的长度 = 2n
s
其理论意义在于,如果你感兴趣的信号在一个区间上是一个 N 阶多项式的形式,并且使用消失矩为 N 的小波,这个小波系数在这个区间内将会是 0

消失矩为 N 的小波正交于最多 N 阶多项式。

因此,如果一个多项式信号的阶数最高是 1 的话,在一个区间上一个 ‘db1’ 小波的小波系数是 0

持续更新。