Comments

前段时间有个需求,需要从 Phoenix 里全量导出一个大表到 Hive 用于后续的查询和分析。经过一番调研和对比,最终用 Python 实现了一版直接从 HBase 并行扫描导出的程序。全表大约 230 亿行记录,同步程序峰值导出速度大约 12 万行每秒,整个同步过程历时 55 个小时。

数据表介绍

Apache Phoenix 是一个基于 Apache HBase 的 OLTP 和业务数据分析引擎,数据存储在 HBase,对外提供标准 SQL 和 JDBC API. 我们目前用 Phoenix/HBase 存储了几百 TB 的爬虫数据,日常的读写都是通过 QueryServer 调用 SQL 实现。Phoenix 支持二级索引,但我们在使用的时候出现过导致 HBase RegionServer 挂掉的情况(有可能是表太大或者使用方式不对),因此实际上基本没用二级索引

前面提到的大表是个评论内容表(后文简称 comment 表),当前的表结构大致如下:

CREATE TABLE COMMENT
(
    OBJECT_ID    BIGINT NOT NULL,     -- 评论的对象 ID,可理解为电商的商品 ID,论坛的贴子 ID 等
    COMMENT_TIME TIMESTAMP NOT NULL,  -- 评论的时间
    COMMENT_ID   BIGINT NOT NULL,     -- 本条评论记录的 ID
    UPDATE_TIME  TIMESTAMP,           -- 插入时间
    ORIGIN_DATA  VARBINARY,           -- 原始数据,是个用 snappy 压缩过的 JSON 对象
    CONSTRAINT PK PRIMARY KEY (OBJECT_ID, COMMENT_TIME, COMMENT_ID)  -- 联合主键
) DATA_BLOCK_ENCODING='FAST_DIFF', COMPRESSION = 'GZ';

其中 COMMENT_ID 是事实上的唯一主键,但由于查询条件主要是基于 OBJECT_IDCOMMENT_TIME,因此在建表的时候就用了 OBJECT_ID, COMMENT_TIME, COMMENT_ID 作为联合主键。OBJECT_ID 是评论对象的 ID,取值范围大,分布非常稀疏(10^6 ~ 10^12)。另外由于不同对象的热度差异也很大,从 OBJECT_ID 的维度看存在数据倾斜,有的 OBJECT_ID 可能只有几条评论,有的却几千万。ORIGIN_DATA 是个用 snappy 压缩过的 JSON 对象,内部包含评论的所有详情信息,其中有个属性记录了评论对象的标签(后文称 tag)。

Read on →
Comments

背景

大约在 2018 年 8 月份开始正式接触 ClickHouse,当时机房没有合适的服务器,就在 Azure 开了一台虚拟机来部署。平稳运行了两年,支撑了 YiDrone 和 YiSonar 两个重要的产品的底层数据存储和查询。前段时间采购服务器的时候预留了一些资源,加上 Azure 的免费订阅即将到期,于是准备把 ClickHouse 迁回到机房。数据量不大,只有一个节点,硬盘上的数据加起来 500G 左右。

方案调研

迁移集群实际上就是要把所有数据库(system 除外)的表结构和数据完整的复制一遍。ClickHouse 官方和社区有一些现成的解决方案,也可以自己实现。

Read on →
Comments

有时候需要通过 S3 给外部用户交付数据,可通过这种方式实现:创建一个新的 IAM 用户和 S3 bucket,给该用户赋予对应的读写权限。

创建 IAM 用户

创建新的 IAM 用户,不赋予任何权限,生成 access key. 假设新用户的 ARN 是 arn:aws-cn:iam::123456789012:user/exampleuser.

配置 S3 bucket 权限

创建 S3 bucket,假设名字为 example-bucket,于是对应的 ARN 为 arn:aws-cn:s3:::example-bucket . 进入 Permissions 页面,编辑 Bucket Policy.

Read on →
Comments

Impala 支持 C++ 和 Java 编写的 UDF, 把对应的 so 或 jar 文件放到 HDFS,再注册一下就能使用。官方推荐使用 C++ 编写 UDF,相比 Java 的实现有 10 倍性能提升。Hive 有丰富的函数,可以添加到 Impala 里。

  • 首先在 HDFS 创建目录保存 UDF 文件,并把 Hive 的 jar 包上传进去
hdfs dfs -mkdir /user/hive/udfs

hdfs dfs -copyFromLocal /opt/cloudera/parcels/CDH/lib/hive/lib/hive-exec-1.1.0-cdh5.14.2.jar /user/hive/udfs/hive-builtins.jar
Read on →
Comments

结论

  • HDFS 支持配置多个数据目录,同一节点默认按照 Round Robin 策略写入。硬盘不做 RAID,每块盘单独挂载。
  • HDFS 支持异构存储,即不同的存储类型和存储策略,可用于实现冷热分级,从而降低成本

存储类型

按访问速度降序:

  • RAM_DISK: 即内存
  • SSD: SSD,OLTP 类场景(如 HBase)可以考虑使用
  • DISK: 普通硬盘
  • ARCHIVE: 归档存储,可使用廉价、高容量存储(甚至单机超百 T)
Read on →
Comments

在不同的数据库系统之间做数据同步是大数据领域里常见的需求。一个典型的场景是,业务系统因为需要事务和随机查询,一般会使用 MySQL 这种数据库;数据仓库使用 Hive;ETL 之后的结果再放到 MySQL、AWS Redshift 等系统给 BI 和报表工具使用。

首先梳理一下需求和目标:

  • 实时性:非实时,离线同步,一般为 T+1,或最细到小时粒度
  • 扩展性:需要支持多种异构数据源,如 MySQL, Hive, ElasticSearch 等
  • 性能要求:因为是离线系统,对性能要求无严格要求,但最好尽可能快,并有可能调优
  • 复杂度:复杂度低,依赖少,易使用,易运维
  • 功能要求:要满足全量同步和增量数据同步
Read on →
Comments

计算机领域有很多概念都来自数学,今天要讨论的幂等性就是其中之一。在程序世界,幂等的意义是对于某个操作,执行一次和多次所产生的影响应该相同。比如赋值操作是幂等的,a = 1 无论运行多少次,最终的影响都是一样;而计数则不是。幂等在很多系统中都很重要,结合自己的经历,聊聊 HTTP 的幂等性和 ETL 场景里的幂等。

HTTP 的幂等性

HTTP RFC 规范里有关于幂等方法的讨论:

Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request. The methods GET, HEAD, PUT and DELETE share this property. Also, the methods OPTIONS and TRACE SHOULD NOT have side effects, and so are inherently idempotent.

Read on →
Comments

逛 V2EX 的时候无意间看到了有个叫 牛客网 的网站,里面有很多公司的笔试真题和大家分享的面经。 出于好奇,看了一下 字节跳动(今日头条)的后端题

一共有 5 题,3 道编程,2 道问答。时候发现前面 4 题跟算法有关,其中 3 道要实现,1 道是纠错和优化,最后一题是系统设计。 做得比较差,只完成了前面两道算法题。用 Go 语言实现,代码如下。

Read on →
Comments

最近一段时间都在读 Designing Data-Intensive Applications 这本书,中文名叫《数据密集型应用系统设计》。进度比较慢,但感觉很有意思,获益匪浅。在读第四章 Encoding and Evolution (数据编码与演化)时,脑海里时常浮现出自己的开发经历,颇有共鸣。因此准备结合书本内容和自身体验,总结成文字作为记录。这一篇主要讨论编码。

编码和解码

在程序世界里,数据通常有两种不同的表现形式:内存和文件(网络)。在内存中,数据保存在对象、结构体、列表、哈希表等结构中,这些数据结构针对 CPU 的高效访问和操作进行了优化。而把数据写入文件或通过网络发送时,需要将其转换成字节序列。

从内存中的表示到字节序列的转化称为编码或序列化,反之称为解码或反序列化。

Read on →
Comments

2012 年大四的时候写过一篇 Python 时间戳和日期相互转换,当时是初学 Python,对标准库也理解不深;随便找到一种解决方案就记录下来并发到博客上了。现在回看起来,其实太过繁琐了。然而从 Google Analytics 后台看,这竟然是点击率第二的文章,着实让我感到诧异。本着对读者负责的态度,有必要结合这些年的开发经验,再写一篇日期和时间处理的博客。 首先再次回答「Python 时间戳和日期相互转换」的问题。 时间戳转日期 import datetime import time t = time.time() print('Timestamp', t) dt = datetime.datetime.fromtimestamp(t) print('Datetime', dt) 输出: Timestamp 1527927420.684622 Datetime 2018-06-02 16:17:00.684622 日期转时间戳 import datetime now = datetime.datetime.now() print('Datetime', now) print('Timestamp', now.timestamp()) 输出: Datetime 2018-06-02 16:18:42.170874 Timestamp 1527927522.170874
getElementsByTagName('BODY')[0]).appendChild(s); }()); getElementsByTagName('BODY')[0]).appendChild(s); }()); getElementsByTagName('BODY')[0]).appendChild(s); }());