1. 介绍

Espresso的详细说明见博文:Introducing Espresso - LinkedIn's hot new distributed document store

本文对博文内容做些简单介绍总结。

2. 动机

Espresso是一个分布式的、容错的NoSQL数据库。Linkedin有Oracle(RDBMS)和Voldemort(自己研发的Key value存储)。ESORESSO是在RDBMS和NOSQL之间的一个产品。可以借助下图来理解:

之所以要开发ESPRESSO是出于以下的需求(怕翻译不好,放原文):
Much of LinkedIn requires a primary, strongly consistent, read/write data store that generates a timeline-consistent change capture stream to fulfill nearline and offline processing requirements.

2.1 RDBMS的问题

  1. Schema Evolution: schema变化带来的代价大。schema evolution影响产品迭代
  2. Provisioning shards: Provisioning new shards is a lot of manual work and requires significant application specific configuration. All applications teams bear this burden.
  3. Data Center Failover: 数据中心做failover时需要DBA介入协调,而且常常引起downtime
  4. cost: RDBMS软硬件人力成本都很高。
  5. linkedin使用的Voldemort是一个较为纯粹的 K,V nosql,不提供RDBMS的特性,以及重要的“a timeline consistent change-capture stream(时间线一致、能捕捉变化的数据流)”

2.2 linkedin的具体需求

  1. 灵活性: 可以通过加节点来横向扩展
  2. 一致性: 支持写后读以及最终一致性;保证基于基础数据的二级索引一致性保证;单个分区上的事务更新
  3. 分布式的:一个database可以跨nodes,支持每个daatabase对应不同的分区数
  4. 容错:单个node出错,不影响整个集群
  5. 二级索引:支持文本和属性索引
  6. 支持schema变化: schema evolution不需要DBA介入,也不会引起downtime
  7. Change Capture Stream: 对主数据的改动,能够提交给下游的消费者来捕捉数据流的改动。
  8. Bulk Ingest: 能够离线产生数据集,例如通过hadoop在HDFS产生数据集

3. 数据模型

采用分层的数据模型:
database->table->collection->document.

Database and table schemas are defined in JSON. Document schemas are defined in Avro.

3.1 database

tables的容器。每个database里面的所有表是一起分区的。也就是说分区的单位是database。并且同一个database下的 tables共享相同的物理资源。database可以通过如下方式来定义:

{
  "schemaType" : "DBSchema",
  "name" : "MailboxDB",
  "version": 1,
  "doc" : "Espresso Schema for MailboxDB",
  "partitionType" : "hash",
  "numBuckets" : 1024,
  "sla": {"maxReadRequestsPerSecond" : 10000, "maxWriteRequestsPerSecond" : 2000}
}

3.2 tables

一张table下有许多同样类型的documents。每个table schema会定义key-structure。一个键结构可以包含多个部分。键结构定义了documents是如何被访问的。table schema中定义的第一个key也是分区的key。即leading key。

一个具体的table schema定义如下:

{
  "schemaType": "TableSchema",
  "name": "Messages",
  "version": 1,
  "recordType": "MailboxDB/Messages",
  "resourceKeyParts":[
    {"name":"MailboxID", "type":"STRING"},
    {"name":"MessageID", "type":"INT"}
  ]
}

这个例子中,这些table中的key部分都会用来创建一个发往Espresso的HTTP请求。一个具体的HTTP请求格式如下:

# General form of an Espresso request
/<database_name>/<table_name>/<partition_key>{/SubKey}

# A GET request for a specific message within a mailbox using the schema above
GET /MailboxDB/Messages/100/1

3.3 collections

Espresso的key空间是分层的。由partition key对应的那些documents称作collection。举个例子,URI /MailboxDB/Messages/100 对应在同一个mailbox里面的records collection

3.4 document

collection中的document是采用avro schema来定义的。采用avro来序列化。其schema定义如下,其中“indexType”用来指明在那个域上必须构建的二级索引。

{
  "schemaType": "DocumentSchema",
  "type": "record",
  "name": "Messages",
  "version": 1,
  "doc": "Espresso schema for simple email message",
  "fields" : [
    {"name":"from", "type":"string", "indexType" : "text"},
    {"name":"subject", "type":"string"},
    {"name":"body", "type":"string", "indexType":"text"}
  ]
}

4. Espresso整体架构总结

Espresso总体的结构如下图所示(可见其核心还是MYSQL得存储引擎,只不过在其基础上封装和抽象了一些概念形成了一个分布式的NewSQL database):

4.1 router

router是一个无状态的HTTP代理。router是请求发送到espresso的入口。根据URL来决定database并且对partition key进行hash从而将请求拆分发送到对应的存储节点。router本地有路由表保存了所有database对应的分区和存储节点的信息。路由表通过ZK来更新。router并行分发和收集出入存储节点的请求,并且负责合并响应给client。

Espresso对database采用基于hash的分区方法(内部的schema不采用该方法,因为内部schema可以路由到任意节点)。

4.2 Storage Node

存储节点是横向扩展的基础。存储节点的功能主要是:

4.2.1 查询处理

  1. 以Avro格式来存储主数据documents
  2. 管理document的元信息,比如checksum、上次修改时间、schema version,内部标识等

4.2.2 存储引擎

存储节点设计主要考虑使用可插拔的存储引擎。在Espresso都是使用Mysql的InnoDB.

此外,值得注意的是,除了使用MYSQL的存储引擎,MySQL binlog相关技术也被用于集群内的数据复制。这样可以保证下游消费者即使捕捉到变化(change capture stream)。在这里的消费者就是一个数据总线rpl-dbus

4.2.3 二级索引

document schema定义的时候也定义了二级索引。索引会捕捉数据数据的实时变化然后异步更新,保证数据“新”。

4.2.4 处理状态

helix(集群manager)产生的状态信息也需要Store Node处理。由于master failure、调度维护等都会造成partition重分配,从而产生状态变化。

4.2.5 本地事务支持

在单个node上是支持本地事务的。注意我们前面说的collection定义。espresso不支持跨collection的事务。因为collection都是在一个node上的。

4.2.6 复制commit log

Storage Node负责提供一个有序的提交日志。这些事务提交日志可以被下游的databus和其他slave消费。

4.2.7 实用功能

支持一些一致性检查、数据校验等实用功能。

4.2.8 备份调度

进行周期的本地数据备份

4.3 Helix

采用Apache Helix来进行集群管理。helix把理想的分区分布称作“IdeaState”。helix会根据自己的规则先计算出一个IdeaState,然后跟当前的current state(称作ExternalView)来做比较,然后将得到一个从ExternalView到IdeaState的状态转化过程。

解读:Espresso可以将正常时的分区分布定义为IdeaState,那么如果ExternalView和IdeaState不一致的时候就可以认为探测到了node failure或者其他问题。

Espresso状态模型有如下约束和定义:

  1. 每个分区只有1个master。每个分区可以有n个可配置的slaves
  2. 分区在所有storage nodes上均匀分布
  3. 没有副本的相同分区可能出现在一个node上
  4. 出现master failover,其中一个slave肯定会晋升为master

4.4 databus

未完待续。。。