推荐系统与Lambda架构
背景介绍
- 电子商务网站
- 浏览记录、购买记录等
- 推荐平台
- 将各种数据整合在一起,以便于挖掘,推荐和广告
推荐系统基本架构
数据加载
- 数据源多样性
- HDFS:用户点击日志等
- MySQL/Oracle:用户基本信息、用户购买记录等
- 数据加载
- 将不同的数据源中的数据导入Hbase
- Hbase中以用户为单位组织数据
- 实现方案
- MapReduce并行导入
- 问题:与MySQL并发连接数过多,怎么办?(代理!AmoebaforMySQL)
- 多线程自己实现
- MapReduce并行导入
HBase数据组织
- 以用户为单位组织数据
- Rowkey 为Uid
- 将表分成多个column family
- Basic features :age ,birthday ,address,….
- Click/buy behavior: ids
- 利用hbase 特有的特性
- 设置最大版本数 , 自动删除过期数据
- 根据uid 获取某用户所有信息
- 易于扩展
场景
- 基于规则的推荐
- 北京地区 , 年龄在18~25 岁的女士 , 推荐某个商品
- 基于模型的广告推荐
- 逻辑回归 :
- 输入性别 , 年龄 , 点击和购买记录
- 是否会购买商品X
- 逻辑回归 :
- 计算模型
- MapReduce 或Spark
导入服务层
- 为什么不直接让Hbase提供服务
- RegionServer挂掉,一段时间内不能对外服务
- HBase负载过重
- 可选的服务层
- Memcached、redis
- 自己进行sharding,replication等
- Cassandra、couchbase
- 自动sharding和replication
- 如何调优,运维等
- Memcached、redis
推荐平台基本架构改进:LA
分布式数据收集
推荐系统数据收集背景
背景介绍
- 推荐系统数据收集需求:
- 将各种数据收集到一个中央化的存储系统中
- 有利于进行集中式的数据分析与数据共享
- 将各种数据收集到一个中央化的存储系统中
- 用户行为数据是多样化的 :
- 用户访问日志
- 产品信息
- 用户基本信息
- 推荐系统数据收集难点 :
- 数据分散在各个离散的设备上
- 数据保存在传统的存储设备和系统中
常见数据来源
- 分散的数据源(由不同系统组件产生,部署在不同机器上):
- 用户访问日志
- 用户购买日志
- 用户收藏日志
- 用户搜索日志
- 关系型数据:
- 用户注册信息:保存在关系型数据库MySQL、Oracle等
常见的开源数据收集系统
- 数据收集
- Flume/ /Scribe
- 传统数据库与Hadoop 同步
- Sqoop
Flume
- 由Cloudera公司开源
- 分布式、可靠、高可用的海量日志采集系统
- 数据源可定制,可扩展
- 数据存储系统可定制,可扩展
- 中间件:屏蔽了数据源和数据存储系统的异构性
Flume特点
- 可靠性
- 保证数据不丢失
- 可扩展性
- 各组件数目可扩展
- 高性能
- 吞吐率很高,能满足海量数据收集需求
- 可管理性
- 可动态增加和删除组件
- 文档丰富,社区活跃
- 已成为Hadoop生态系统标配
Flume两个版本
- Flume OG
- OG:“Original Generation”
- 0.9.x或cdh3以及更早版本
- 由agent、collector、master等组件构成
- Flume NG
- NG:“Next/New Generation”
- 1.x或cdh4以及之后的版本
- 由Agent、Client等组件构成
- 为什么要推出NG 版本
- 精简代码
- 架构简化
FlumeOG基本架构
Flume NG核心概念
- Event
- Client
- Agent
- Source
- Channel
- Sink
- 其他组件 :Interceptor 、Channel Selector 、Sink Processor
Event
- Event 是Flume 数据传输的基本单元
- Flume 以事件的形式将数据从源头传送到最终的目的
- Event 由可选的header和载有数据的一个byte array构成
- 载有的数据对flume是不透明的
- Header是容纳了key-value字符串对的无序集合,key在集合内是唯一的
- Header可以在上下文路由中使用扩展
Client
- Client是一个将原始log包装成events并且发送它们到一个或多个agent的实体。
- 目的是从数据源系统中解耦Flume
- 在flume的拓扑结构中不是必须的
- Client实例
- Flume log4j Appender
- 可以使用Client SDK (org.apache.flume.api)定制特定的Client
Agent
- 一个Agent包含Source, Channel, Sink和其他组件;
- 它利用这些组件将events从一个节点传输到另一个节点或最终目的;
- Agent是flume流的基础部分;
- Flume为这些组件提供了配置、生命周期管理、监控支持
Agent之Source
- Source负责接收event或通过特殊机制产生event,并将events批量的放到一个或多个Channel
- 包含event驱动和轮询2种类型
- 不同类型的Source:
- 与系统集成的Source: Syslog, Netcat
- 自动生成事件的Source: Exec
- 用于Agent和Agent之间通信的IPC Source: Avro、Thrift
- Source必须至少和一个channel关联
Agent之Channel
- Channel位于Source和Sink之间,用于缓存event;
- 当Sink成功将event发送到下一跳的channel或最终目的,event从Channel移除。
- 不同的Channel提供的持久化水平也是不一样的:
- Memory Channel: volatile
- File Channel: 基于WAL(预写式日志Write-Ahead Logging)实现
- JDBC Channel: 基于嵌入Database实现
- Channel支持事务,提供较弱的顺序保证
- 可以和任何数量的Source和Sink工作
Agent之Sink
- Sink负责将event传输到下一跳或最终目的,成功完成后将event从channel移除。
- 不同类型的Sink:
- 存储event到最终目的的终端Sink. 比如: HDFS, HBase
- 自动消耗的Sink. 比如: Null Sink
- 用于Agent间通信的IPC sink: Avro
- 必须作用于一个确切的channel
Agent之其他重要组件
- Interceptor
- 作用于Source,按照预设的顺序在必要地方装饰和过滤events
- Channel Selector
- 允许Source基于预设的标准,从所有Channel中,选择一个或多个Channel
- Sink Processor:
- 多个Sink可以构成一个Sink Group。Sink Processor可以通过组中所有Sink实现负载均衡;也可以在一个Sink失败时转移到另一个
常用拓扑实例
第一层配置实例
a1.channels = c1
a1.sources = r1
a1.sinks = k1 k2
a1.sinkgroups = g1
a1.sinkgroups.g1.processor.type = LOAD_BALANCE
a1.sinkgroups.g1.processor.selector = ROUND_ROBIN
a1.sinkgroups.g1.processor.backoff = true
a1.channels.c1.type = FILE
a1.sources.r1.channels = c1
a1.sources.r1.type = AVRO
a1.sources.r1.bind = 0.0.0.0
a1.sources.r1.port = 41414
a1.sinks.k1.channel = c1
a1.sinks.k1.type = AVRO
a1.sinks.k1.hostname = a21.example.org
a1.sinks.k1.port = 41414
a1.sinks.k2.channel = c1
a1.sinks.k2.type = AVRO
a1.sinks.k2.hostname = a22.example.org
a1.sinks.k2.port = 41414
第二层配置实例
a2.channels = c1
a2.sources = r1
a2.sinks = k1
a2.channels.c1.type = FILE
a2.sources.r1.channels = c1
a2.sources.r1.type = AVRO
a2.sources.r1.bind = 0.0.0.0
a2.sources.r1.port = 41414
a2.sinks.k1.channel = c1
a2.sinks.k1.type = HDFS
a2.sinks.k1.hdfs.path = hdfs://namenode.example.org
a2.sinks.k1.hdfs.fileType = DataStream
Sqoop
Sqoop:SQL-to-Hadoop
连接 传统关系型数据库 和 Hadoop 的桥梁
把关系型数据库的数据导入到 Hadoop 系统 ( 如 HDFS、HBase 和 Hive) 中;
把数据从 Hadoop 系统里抽取并导出到关系型数据库里
利用MapReduce 加快数据传输速度
批处理方式进行数据传输
Sqoop优势
高效、可控地利用资源
任务并行度 , 超时时间等
数据类型映射与转换
可自动进行 , 用户也可自定义
支持多种数据库
MySQL
Oracle
PostgreSQL
Sqoop import及使用方法
将数据从关系型数据库导入Hadoop 中
步骤1:Sqoop与数据库Server通信,获取数据库表的元数据信息;
步骤2:Sqoop启动一个Map-Only的MR作业,利用元数据信息并行将数据写入Hadoop
sqoop import
–connect jdbc:mysql://mysql.example.com/sqoop
–username sqoop
–password sqoop
–table cities
–connnect: 指定JDBC URL
–username/password :mysql 数据库的用户名
–table :要读取的数据库表
Sqoop export及使用方法
将数据从Hadoop 导入关系型数据库导中
步骤1:Sqoop与数据库Server通信,获取数据库表的元数据信息
步骤2:并行导入数据 :
将Hadoop 上文件划分成若干个split
每个split 由一个Map Task 进行数据导入
sqoop export
–connect jdbc:mysql://mysql.example.com/sqoop
–username sqoop
–password sqoop
–table cities
–export-dir cities
--connnect: 指定JDBC URL
–username/password :mysql 数据库的用户名
–table : 要导入的数据库表
export-dir : 数据在HDFS 上存放目录
Sqoop与其他系统结合
sqoop import
–connect jdbc:mysql://mysql.example.com/sqoop
–username sqoop
–password sqoop
–table cities
–hive-import
sqoop import
–connect jdbc:mysql://mysql.example.com/sqoop
–username sqoop
–password sqoop
–table cities
–hbase-table cities
–column-family world
分布式数据存储
用户行为系统存储架构
数据存储:直接通过数据模块得到的数据
HDFS:用户点击日志等
MySQL/Oracle:用户基本信息、用户购买记录等
数据组织:为推荐系统准备数据
将不同的数据源中的数据导入HBase
HBase中以用户为单位组织数据
HDFS
HDFS基本概念
HDFS是用JAVA实现的文件系统
基于Google的GFS
部署在本地文件系统之上
比如:ext3,ext4或xfs
为海量数据提供冗余存储
使用很成熟的工业标准的计算机
HDFS最适合于合理数量的大文件
百万级,而不是十亿级的文件
每个文件在100MB及以上
HDFS文件是“一次写”的
不允许随机写
HDFS为大文件的连续读作了优化
而不是随机读
文件如何存储
数据文件在加载时拆分成块(默认128MB)并进行分布
每个块被复制到多个数据节点中(默认3份)
NameNode存储元数据
示例:存储和获取文件
访问HDFS的方式
Hadoop
客户端和HDFS之间的数据拷贝
API和命令行
生态项目
Flume
从网络源采集数据(比如,网站、系统日志)
Sqoop
在HDFS和RDBMSs之间传输数据
BI工具
HDFS命令行示例
从本地磁盘拷贝文件foo.txt到HDFS的用户目录
$ hdfs dfs -put foo.txt foo.txt
文件将会拷贝到/user/username/foo.txt
获取用户home目录的目录列表
$ hdfs dfs -ls
获取HDFS的根目录
$ hdfs dfs -ls /
显示HDFS文件/user/fred/bar.txt内容
$ hdfs dfs -cat /user/fred/bar.txt
拷贝文件到本地磁盘,命名为baz.txt
$ hdfs dfs -get /user/fred/bar.txt baz.txt
在用户home目录下创建input目录
$ hdfs dfs -mkdir input
删除input_old目录和所有里面的内容
$ hdfs dfs -rm -r input_old
Hbase
HBase是一个分布式的、面向列的开源数据库
HBase在Hadoop之上提供了类似于Bigtable的能力
HBase是一个适合于非结构化数据存储的数据库
HBase基于列的而不是基于行的模式
HBase存储的是松散型数据
HBase逻辑模型与数据存储
以表的形式存放数据
表由行与列组成,每个列属于某个列族,由行和列确定的存储单元称为元素
每个元素保存了同一份数据的多个版本,由时间戳来标识区分
HBase是按照列存储的稀疏行/列矩阵
空单元不存储
表中存储数据
在HBase中,数据存储为字节数组
任何可以转换成字节数组的对象都可以存储下来
字符串、数字、复杂对象和图片等
单元大小
对值大小的实际限制
通常,单元大小不能超过10MB
列族内的数据存储
HBase表特性
行键+列+时间戳 -->value
行键和值只是字节
可以存储任何能序列化成字节数组的对象
物理模型
HBase使用场景
存储大量的数据(>TB)
需要很高的写吞吐量
例如:Facebook消息
每天750亿次操作,每秒峰值达到150万操作
大规模数据集很好性能的随机访问(按列)
数据扩展
存储的数据量大,但通常访问小的数据集
数据缓存在内存中来加速查询
不需要全部的关系数据库特性,例如交叉列、交叉表,事务,连接等等
HBase Shell的使用
$ hbase shell
- 创建HBase表
通用格式
create 'tablename', {NAME => 'colfam' [, options]} [, {...}]
简写
create 'movie', {NAME => 'desc'}
create 'movie', {NAME => 'desc', VERSIONS => 2}
create 'movie', {NAME => 'desc'}, {NAME => 'media'}
create 'movie', 'desc', 'media'
- 管理HBase命名空间
HBase可以定义和管理命名空间
多个表可以属于一个命名空间
当表创建时定义表的命名空间
可以创建、修改或删除命名空间
create_namespace 'namespaceName'
drop_namespace 'namespaceName'
alter_namespace 'namespaceName' {METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'}
有两种提前定义的特殊命名空间
hbase-系统命名空间,存储HBase内部表
Default-默认
3. 在命名空间里创建表
通用格式
create 'namespace:tablename', {NAME => 'colfam' [, options]} [, {...}]
create_namespace 'entertainment'
create 'entertainment:movie', {NAME => 'desc'}
- 列举和描述表
列出所有表
hbase> list
查看表明细
查看表的所有列族,属性和值
hbase> describe 'movie'
- 禁用和启用表
禁用表使表处于维护状态
允许运行多种维护操作
阻止所有客户端访问
需要几分钟来禁用表
hbase> disable 'movie'
退出维护状态
hbase> enable 'movie'
- 删除
Drop命令删除表并移除所有HDFS的文件
需要先禁用表
hbase> disable 'movie'
hbase> drop 'movie'
Truncate命令删除表的所有行
表和列族的模式不受影响
不需要手动的禁用表
hbase> truncate 'movie'
- 插入行
使用put插入行
通用格式
hbase> put 'tablename', 'rowkey', 'colfam:col', 'value' [, timestamp]
示例
hbase> put 'movie', 'row1', 'desc:title', 'Home Alone'
hbase> put 'movie', 'row1', 'desc:title', 'Home Alone 2', 1274032629663
- 获取行
使用get获取行
通用格式:
hbase> get 'tablename', 'rowkey' [, options]
示例
hbase> get 'movie', 'row1'
hbase> get 'movie', 'row1', {COLUMN => 'desc:title'}
hbase> get 'movie', 'row1', {COLUMN => 'desc:title', VERSIONS => 2}
hbase> get 'movie', 'row1', {COLUMN => ['desc']}
- 扫描行
使用scan返回一组行
通用格式:
hbase> scan 'tablename' [,options]
示例
hbase> scan 'movie'
hbase> scan 'movie', {LIMIT => 10}
hbase> scan 'movie', {STARTROW => 'row1', STOPROW => 'row5'}
hbase> scan 'movie', {COLUMNS => ['desc:title', 'media:type']}