Big Data Management笔记05:Mining Data Streams

  • Data Streams(数据流)
  • Characteristics of Data Streams(数据流的特点)
  • The Stream Model(数据流模型)
  • Problems on Data Streams(数据流的主要问题)
  • Sampling data from a stream(数据流采样)
  • Sampling a Fixed Proportion(固定比例抽样)
  • Maintaining a Fixed-size Sample(固定数量抽样)
  • Queries over sliding windows(使用滑动窗口进行查询)
  • Filtering a data stream(过滤数据流)
  • First Cut
  • Bloom Filter
  • Counting distinct elements(计算不同的元素)


Data Streams(数据流)

首先,我们要知道什么是数据流(Data Stream)。

在进行数据挖掘(Data Mining)的过程中,我们经常会遇到一种情况:我们预先不知道数据集的整体大小。 这时候,输入速率由外部控制(Externally Controlled),数据流管理就显得非常重要。这里的输入速率(Input Rate)一般由用户(Users)控制,比如Twitter或Facebook状态更新。

我们可以把这些数据看做是无限(Infinite)且非平稳的(Non-stationary)。 这里的“无限”很好理解,就是数据源源不断地涌入,我们不知什么时候停止。这里的“非平稳”指的是数据的分布会随着时间的变化而改变,举一个具体的例子:比如Twitter里的话题,二月的时候最热话题可能是COVID-19,上个月可能是BLM,这个月可能又是COVID-19。这就是“数据分布会随着时间变化”。

Characteristics of Data Streams(数据流的特点)

接下来我们看一看Data Stream的特点。

我们首先回顾一下传统的数据库系统(DBMS):数据存储在有限(Finite)的持久(Persistent)数据集中。
而Data Stream与其有很大的不同。Data Stream主要是

  1. 分布式存储(Distributed)(它可能来自/存储于不同的机器/节点/源之中)
  2. 持续的(Continuous) (Continuous Data & Continuous Query)
  3. 无界的(Unbounded)(我们不知道数据的边界在哪里)
  4. 快速的(Rapid)
  5. 时变的(Time varying)
  6. 有噪声的(Noisy)

总结一下,Data Stream的特点主要如下:

  • 大量连续数据,可能是无限的(Huge volumes of continuous data, possibly infinite)
  • 快速变化,需要快速、实时的响应(Fast changing and requires fast, real-time response)
  • 随机访问代价高昂-单扫描算法(只能看一次)(Random access is expensive—single scan algorithm (can only have one look))。这里稍作解释,所谓的Random Access主要是针对Disk(磁盘)的访问,因此读写IO开销会很大。值域Single Scan指的是因为数据流的“流动的”,因此我们认为,每个数据只会被看一次
  • 仅存储到目前为止所见数据的摘要(Store only the summary of the data seen thus far)。这里主要存储于内存(Memory),因为内存有限,因为我们只存储数据的摘要,无法存储所有数据

海量数据流(Massive Data Streams) 是我们在学习Data Stream需要面对的最大挑战。指的是 数据持续增长的速度超过了我们存储或索引数据的能力

The Stream Model(数据流模型)

在该模型中,输入元素在一个或多个输入端口上快速输入。这里,我们把数据流中元组(Stream Tuples)称作数据流的元素。 我们之前已经说过,数据流的具体规模有多大谁也不知道,因为我们无法将整个数据流都存储下来再进行访问。

因此,该模型中,我们最关心的问题就是:
我们如何用有限的内存资源去对Data Stream进行相关的重要计算(Critical Calculations)?

我们先用两张图来看看传统的DBMS和数据流管理系统(Data Stream Management System(DSMS) )如何处理数据:

datastudio可以编译mysql吗 datastream数据库 大学_Data


datastudio可以编译mysql吗 datastream数据库 大学_hadoop_02


接下来,用一张图直观对比DBMS和DSMS:

datastudio可以编译mysql吗 datastream数据库 大学_hadoop_03


在DBMS中,Main Memory会和Disk/External Memory(外部存储)进行数据交换。但是对于DSMS,我们可以看到没有disk/外部存储,有的只有Data Stream不断流入流出。再次重申,我们无法存储所有数据,因为内存是有限的。这里我们需要注意,Query也是持续性的(Continuous Query),它的形式和SQL Query是一样的,比如我们想知道:该列中的最大值、一共有多少列等。但是,Continuous Query的answer是随时间/data变化的。 这很好理解,因为随着数据的不断流入,最大值可能会发生变化。

下面我们给出DBMS和DSMS之间的直观比较:

DBMS

DSMS

存储的相对静态记录集,没有预先定义的时间概念

瞬态流(Transient streams),支持对快速变化的数据流进行在线分析

适用于需要持久数据存储和复杂查询的应用程序

数据流:实时,连续,有序(隐式地按到达时间或显式地按时间戳排序)序列,太大而无法完全存储,几乎无限

One-time queries(一次性查询)

Continuous Query(连续查询)

Random Access(随机访问)

Sequential access(顺序访问)

“Unbounded” disk store(无限磁盘存储)

Bounded main memory(有限的主内存)

Only current state matters(仅当前状态很重要)

Historical data is important(历史数据也很重要)

No real-time services(无实时服务)

Real-time requirements(可满足实时需求)

Relatively low update rate(更新率相对较低)

Possibly multi-GB arrival rate(可能有数GB的到达率)

Data at any granularity(任意粒度的数据)

Data at fine granularity(细粒度的数据)

Assume precise data(一般认为数据精准)

Data stale/imprecise(数据可能陈旧/不精确,有噪声)

Access plan determined by query processor, physical DB design(由查询处理器确定的访问计划,物理数据库设计)

Unpredictable/variable data arrival and characteristics(不可预测/可变的数据到达和特征,难以制定访问计划)

Problems on Data Streams(数据流的主要问题)

在这里,我们讨论几个关于Data Stream的重要问题,而这些问题,也正是我们需要针对Data Stream进行的关键计算(Critical Calculations)

  • 从数据流进行采样(Sampling data from a stream)。构建一个随机样本
  • 用滑动窗口进行查询(Queries over sliding windows)。比如,求在数据流中过去的k个元素中,有多少个x类型的对象
  • 过滤数据流(Filtering a data stream)。从数据流中筛选出具备属性x的元素
  • 计算不同的元素(Counting distinct elements)

这里我们一般考虑k远远大于Main Memory,即k元素中仅有一部分能被存储于Main Memory,否则不成问题

以上这些针对Data Stream进行的关键计算,是一切数据流应用的实现基础。

这里有一个具体的例子,就是IP网络数据。互联网本身就是海量数据的来源,每个IP路由器每小时处理的元数据(Metadata)甚至难以用GB衡量。因此,对于数据流的分析有一个最基本的问题:过多的数据需要进行存储和传输。所以,我们需要在数据一到达,就开始对其的处理。因此,如果可以保证结果的质量,对于许多问题,我们认为近似的答案(Approximate answers)也是可以接受的。

Sampling data from a stream(数据流采样)

我们已经多次提及,鉴于数据流的庞大体量以及流动性,我们不可能存储所有的数据,因此,最明显的一个解决方式,就是存储数据流的一个样本。

但在进行采样的时候,我们需要注意,样本需要能够表现原本数据流的特征。比如,在下图的采样中,我们仍能体现不同颜色的数据之间的量级差异。

datastudio可以编译mysql吗 datastream数据库 大学_hadoop_04

这里,我们有两种不同的采样方法:

  • Sampling a Fixed Proportion(固定比例抽样)。采样流中固定比例的元素。如果比例很高,可能会更好地表示原数据流,但是对内存有很高的要求。如果比例很低,有可能无法充分表示数据流的特征
  • Maintaining a Fixed-size Sample(固定数量抽样)。在潜在的无限流上保持固定大小的随机样本。样本数量不会改变

但不管使用哪一种方法,我们需要保证,任何时候,所有元素被采样的几率都是相同的。

Sampling a Fixed Proportion(固定比例抽样)

首先,针对这个问题,我们假想一个场景:搜索引擎查询数据流。 该数据流中的元组(Tuples)为 (User, Query, Time),内存(Memory)只够存储数据流的1/10。

我们希望能够回答如下问题: 用户一天内执行相同查询的频率(重复查询的占比)

最简单的方法(Naive Solution)就是为每个Query分配一个0~9的随机数,然后将所有被分配0的Query存储下来。但这个方法存在问题。

我们假设每个用户发出x个查询一次,d个查询两次,因此总共发出了 x + 2d次查询。此时,执行了相同查询的频率应为 d / x + d。d表示重复查询的数量,x + d表示不重复查询总数。

这时如果我们用上面说过的方法进行采样,我们的样本会包含x / 10次的单例查询和2d / 10次重复查询((x + 2d) / 10)。而在这2d / 10次重复查询中,仅有d / 100(1/10 * 1/10 * d)对是重复对,而有 18d / 100 (((1/10 * 9/10)+(9/10 * 1/10)) * d)对是不同的一对。用下图会更好理解:

datastudio可以编译mysql吗 datastream数据库 大学_hdfs_05


如此一来,我们最终得到的重复查询的占比为:

datastudio可以编译mysql吗 datastream数据库 大学_大数据_06


因此,这个时候我们需要改变方法。我们不再根据Query来进行采样,而是根据User进行采样。我们用一个预定义的Hash Function将User ID转换为一个0~9的随机数,如果User ID被转换为0,则将该Query加入样本集合。

以上的解决方案也体现了对于数据流处理的另一个重要思想:我们要根据实际情况,选择合适的键(Key)。这里的键指的是数据流元组中的元素的任意子集。

Maintaining a Fixed-size Sample(固定数量抽样)

假设我们需要维护一个大小为s个元组(Tuples)的随机样本S。假设在时间n我们已经看到n个元组。每个元组都有相同的概率(s / n)被采样。

看一个具体的例子:

datastudio可以编译mysql吗 datastream数据库 大学_Data_07


这里我们假设样本尺寸s = 2。在n = 5时,前5个元组被采样至S中的概率都为 2/5。在n = 7时,都为2/7。(注意:这里的stream为方便表示,每个元素都不同,但我们要知道,即使是相同的对象,因为有不同的时间戳,在数据流中仍会被看作不同的元组)。可以看到,随着时间的变化,实际上采样频率在变小。

这里我们介绍一种固定数量采样算法:Reservoir Sampling

  • 首先将Data Stream中的前s个元素存入S中
  • 假设我们现在是时刻n(即已经看过n - 1个元素),第n个元素到来(n > s)
    - 我们有s / n的概率将该元素存入S中,否则略过
    - 如果我们将该元素存入S中,它会随机取代S中已存的一个元素

该算法满足:在n个元素过后,样本包含至今见过的每一个元素的概率都为s / n。

我们可以用归纳法证明这个特性:
假设在n个元素过后,样本包含至今见过的每一个元素的概率都为s / n。那么,我们就需要证明,当第n + 1个元素到来时,本包含至今见过的每一个元素的概率都为s / n + 1。这部分的证明很简单,在此不做过多的解释,只把关键的式子贴出来:

datastudio可以编译mysql吗 datastream数据库 大学_spark_08

Queries over sliding windows(使用滑动窗口进行查询)

滑动窗就这个技术现在已经相当常见,不管是在ML还是在DL中都屡见不鲜。在数据流处理中,它的操作其实也基本一样。我们设定一个滑动窗口的尺寸/长度为N,因为是滑动的,所以它总是显示Stream最近的N个元素。使用Sliding Windows时,显然我们无法把所有data都放在window中,但是query的结果高度依赖window的内容。

这里我们考虑一个简单的问题:给定一个由0和1组成的数据流,我们想要知道,在过去的k个元素中,有多少个1?(k ≤ N)

最简单的方法就是我们用一个长度为N的滑动窗口,将最近的N个元素存下来,并且每当有新的元素存入,就把最旧的元素抛弃。

datastudio可以编译mysql吗 datastream数据库 大学_Data_09


但是,这个方法有着一个很现实的问题,那就是如果我们不能把整个窗口都存下来,那么无法得到一个准确的答案。可现实是,我们经常会遇到窗口尺寸N很大,无法完全存下来的情况。这时,我们很乐意用一个近似结果(Approximate Answer)来进行回答。

基于均匀性假设(Aniformity Assumption),我们可以用以下方法:我们维持两个计数:S(从开始到现在的1的个数)和Z(从开始到现在的0的个数)。如此一来,我们可以用计算最近N个元素中,1个个数为:N * (S / (S + Z))

而针对不满足均匀性假设的数据流,我们可以采用另一种算法:The Datar-Gionis-Indyk-Motwani (DGIM) Algorithm。使用该算法,我们对每个Stream只需要存储O(log2N)个元素,同时,它给出的近似结果与实际结果的误差不会超过50%(使用更复杂的算法和成比例地存储更多元素,可以将误差因子减小到任何大于0的分数)。

DGIM算法的思想并不复杂,将Stream划分为很多块,根据1的数量来决定各个块(block)的尺寸。同时,让块(block)的尺寸(即1的数量)指数增长。

datastudio可以编译mysql吗 datastream数据库 大学_大数据_10


在该算法中,我们有两点需要注意:

  1. Timestamp(时间戳):==时间戳可以帮助我们丢掉不再需要的block。==数据流中的每一个元素,在本题中也就是bit,都有一个时间戳,从数据流的起点开始,为1, 2, 3…。但在该算法中,我们只需要记录Timestamp和窗口尺寸N的取余(modulo)运算结果,因此我们可以用O(log2N)bits来表示相对时间戳(Relevant Timestamp),因此时间戳永远小于N。我们只记录block最右端的Timestamp
  2. datastudio可以编译mysql吗 datastream数据库 大学_Data_11

  3. 在上图中,红色箭头表示的窗口尺寸N=12,因此,其中的bit的时间戳分别为(1, 2, 3, …, 11, 0),蓝色窗口为下一刻的窗口,其中的时间戳分别为(2, 3, …, 11, 0, 1)
  4. Block(块):在DGIM中的block是一个由Timestamp(O(log2N)bits)和其中1的数量(O(loglogN)bits)组成的记录。它有个限制:1的数量一定是2的次方

针对使用Blocks表示Stream,有着以下几个要求:

  1. Block左右结束位置上必须是1
  2. 所有1都在Block中
  3. 相同尺寸的Block最多只有2个
  4. Block不会重叠
  5. Block由尺寸排序,越早的Block尺寸会大于等于后面的Block
  6. Block超出Windows时会被丢弃

根据这些条件,我们可以清楚地了解到该如何更新Block:

datastudio可以编译mysql吗 datastream数据库 大学_spark_12


这是一开始的Stream

datastudio可以编译mysql吗 datastream数据库 大学_hadoop_13


当一个新的bit(value = 1)到来,我们为这个新的1组建一个新的block,由于此时size = 1的block有3个,超过2个的限制,我们将前面两个size = 1的block合并为一个新的size = 2 的block,结果如下:

datastudio可以编译mysql吗 datastream数据库 大学_spark_14


然后,另一个bit(value = 1)到来,我们为其组建一个新的block,此时所有size的block都有2个,符合要求。之后有一个bit(value = 0)到来,我们无需进行操作。之后又有一个bit(value = 1)到来,我们在此创建新的size = 1的block,此时size = 1的block有三个:

datastudio可以编译mysql吗 datastream数据库 大学_hadoop_15


我们又需要进行合并:

datastudio可以编译mysql吗 datastream数据库 大学_大数据_16


前两个size = 1合并之后,导致现在有三个size = 2,因此继续合并,直到满足要求:

datastudio可以编译mysql吗 datastream数据库 大学_spark_17


在了解了如何更新blocks之后,我们现在来看看如何进行查询。为了直到最近的N个bits(也就是window)中,有多少个1,我们需要汇总除了窗口最左的其他所有窗口的尺寸,然后加上最左边的Block的尺寸的一半,即为最后结果:

datastudio可以编译mysql吗 datastream数据库 大学_hadoop_18


上图中,窗口中的右边7个block的尺寸汇总为28,加上最左端block的一半尺寸8,最后结果为36。

我们这么计算的原因在于,我们不知道最左边block中有多少1还在window范围内,因此最后给出的是一个近似结果。

而它的错误率最多只有50%的原因在于。
我们认为最左端的block尺寸为2r,我们假设它有一半的1在窗口内,即2r/2 = 2r-1 ,因此误差的最大值为2r-1
因为所有尺寸小于2r的block的尺寸最小从1开始,因此,最终的真实汇总最小为:1 + 2 + 4 +… + 2r-1 = 2r - 1,但是最左边的block至少有其中最右的1在window中,否则这个block已经被丢弃,因此真实汇总的最小值应为 2r - 1 + 1 = 2r
因此,错误率为 2r-1 / 2r = 50%.(为了进一步降低误差率,我们可以考虑解除统一尺寸block的数量限制,可以将错误率将至O(1/r))。

Filtering a data stream(过滤数据流)

回顾一下我们之前对数据流的设定:数据流(Data Stream)中的每个元素是一个元组(Tuple)。我们现在给定一个键组成的列表S,我们想要知道,那些Tuple的键在S之中。这就是对数据集进行过滤的基本命题。

我们在这里介绍两种方法:First Cut 和 Bloom Filter

First Cut
  • 给定一组我们想要过滤的键(Keys)S。
  • 创建一个n位(bits)的数组B,初始全部为0。
  • 选择一个Hash Function h,该函数得范围为[0, n)。
  • 用h将任意s ∈ S转换为n个值中的任意一个,并将B中对应的n设为1,即 B[h(s)] = 1
  • 对Data Stream中的每个元素a调用h,仅输出那些对应B中为1的元素,即Output a if B[h(a)] == 1

如下图所示:

datastudio可以编译mysql吗 datastream数据库 大学_hadoop_19


这一算法会产生False Positive结果,但不会产生False Negative结果。因为如果对象a在S中,我们一定会将其输出,但是如果不在,我们仍有可能会将其输出。

假如数组B中共有N bits,S中共有M个keys,那么,False Positive的概率为 1 - e-m/n

Bloom Filter

类似之前的设想,我们设置|S| = m, |B| = n。这次,我们用k个独立的Hash Function,h1, h2, … , hk

  • 首先,我们同样把B中的元素全部设为0
  • 对任意s ∈ S,用每一个hi对其进行计算,并设置 B[hi(s)] = 1 (i = 1, 2, …, k)。即对S中的每一个元素s,我们将B中的k个元素设置为1.
  • 当数据流中,具有键(key)为x的数据到来,我们用这k个Hash Functions对x进行计算
    - 如果B[hi(x)] = 1 (i = 1, 2, …, k),则x在S中
    - 否则,略过

这里看一个具体的例子,设m = 10,Hash Function的数量k = 3,用H(x)表示三个Hash Functions的结果。

一开始,我们将10bits数组B全部设为0:

datastudio可以编译mysql吗 datastream数据库 大学_hadoop_20


对于s0 ∈ S,我们有H(s0) = {1, 4, 9},因此:

datastudio可以编译mysql吗 datastream数据库 大学_spark_21


对于s1 ∈ S,我们有H(s1) = {4, 5, 8},因此:

datastudio可以编译mysql吗 datastream数据库 大学_Data_22


这时Query y0到来,我们有H(y0) = {0, 4, 8},因为在B中0的值为0,不满足条件,因此y0不在S中。

另一个Query y1到来,我们有H(y1) = {1, 5, 8},在B中,这三个位置对应的值都为1,因此,我们会认为y1在S中,但实际上,S中的固有组合只有 {1, 4, 9}和{4, 5, 8},因此,y1其实不在S中,这是个False Positive结果。该算法返回False Positive结果的概率为:(1 - e-km/n)k.

我们设m = 1, n =8,随着k的增长,我们可以得到以下曲线图:

datastudio可以编译mysql吗 datastream数据库 大学_大数据_23


最佳k为:n/m * ln(2) = 6

此时的False Positive = (1 - e-6/8)6 = 0.0216

总结一下Bloom Filter的特点:

  • Bloom Filter和First Cut一样,确保不会有False Negative结果,并且使用有线的内存资源即可进行计算
  • 适用于硬件实施
  • 对于使用1个比较大的数组B和使用k个小的数组Bs,结果一样,但前者更容易实现

Counting distinct elements(计算不同的元素)

这个问题不难理解,就是说我们要维持一个对到目前为止所有见过元素中所有不同元素的计数。

我们平时在用Python解决类似问题的一个通用手段是维护一个到目前为止看到的元素集合,然后取其尺寸即为计数。这个方法本质上来说就是维护一个到目前为止看到的所有不同元素的哈希表。

但是在处理数据流(Data Stream)的实际情况中,我们通常要面对的一个问题是我们可能没有充足的空间去保存这个哈希表/集合。同样的,对于这个问题我们也接受近似答案,换言之接受计数可能有一点误差,但要限制误差很大的可能性。

这里大多会使用一种模板(Sketch)方法进行处理。本质上,Sketch就是输对入的线性变换,把数据流看做向量,Sketch是将流向量乘以(隐式)矩阵的结果,一个比较有名的方法是Flajolet-Martin Sketch,这里我们不做过多介绍。