HBase第二天 Hbase表设计要点 行健设计 行健不能改变,唯一可以改变的方式是先删除后插入 长度原则 Rowkey 是一个二进制码流,最大长度是64KB,建议越短越好。10-100长度即可,不要超过 16 个 字节。 数据的持久化文件 HFile 中是按照 KeyValue 存储的,如果 Rowkey 过长比如 100 个字 节, 1000 万列数据光 Rowkey 就要占用 100*1000 万=10 亿个字节,将近 1G 数据,这会极大 影 响 HFile 的存储效率; MemStore 将缓存部分数据到内存,如果 Rowkey 字段过长内存的有效利用率会降低, 系统 将无法缓存更多的数据,这会降低检索效率。因此 Rowkey 的字节长度越短越好。 目前操作系统是都是 64 位系统,内存 8 字节对齐。控制在 16 个字节,8 字节的整数 倍利用 操作系统的最佳特性 散列原则 如果 Rowkey 是按时间戳的方式递增,不要将时间放在二进制码的前面 建议将 Rowkey 的高位作为散列字段,低位放时间字段,这样将提高数据均衡分布在每个 Regionserver 实现负载均衡的几率。 如果没有散列字段,首字段直接是时间信息将产生所有 新数据都在一个 RegionServer 上堆积的热 点现象, 这样在做数据检索的时候负载将会集中 在个别 RegionServer,降低查询效率。 唯一原则 必须在设计上保证其唯一性。rowkey 是按照字典顺序排序存储的 设计 rowkey 的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会 被访问 的数据放到一块。 数据热点 原因: 热点发生在大量的 client 直接访问集群的一个或极少数个节点(访问可能是读, 写或者其他 操作)。 大量访问会使热点region所在的单个机器超出自身承受能力,性能下降甚至 region 不可用, 主机无法服务其他 region 的请求 反转策略 反转固定长度或者数字格式的 rowkey。这样可以使得 rowkey 中经常改变的部分放在前 面。这样可以有效的随机 rowkey,但是牺 牲了 rowkey 的有序性。 加盐策略 在 rowkey 的前面增加随机数,具体就是给 rowkey 分配一个随机前缀以使得它和之前 的 rowkey 的开头不同。 哈希策略 哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是 可以预测的。 使用确定的哈希可以让客户端重构完整的 rowkey,可以使用 get 操作准确获取 某一个 行数据 列簇设计 追求的原则是:在合理范围内能尽量少的减少列簇就尽量减少列簇 最优设计是:将所有相关性很强的 key-value 都放在同一个列簇下 这样既能做到查询效率最高 也能保持尽可能少的访问不同的磁盘文件。 列簇名的长度要尽量小,一个为了节省空间,另外加快效率,比如d表示data,v表示value 列簇属性 HFile数据块,默认是64KB,数据库的大小影响数据块索引的大小。 数据块大的话一次加载进内存的数据越多,扫描查询效果越好。但是数据块小的话,随机查询性能 更好 数据块缓存,数据块缓存默认是打开的,如果一些比较少访问的数据可以选择关闭缓存 数据压缩,压缩会提高磁盘利用率,但是会增加CPU的负载,看情况进行控制 HBase常用优化 HBase表优化 预分区 Pre-Creating Regions 默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的 HBase客户端都向这一个region写数据,直到这个region足够大了才进行切分。 有种加快批量写入速度的方法是通过预先创建一些空的regions,这样当数据写入HBase时,会按 照region分区情况,在集群内做数据的负载均衡。 rowkey HBase中row key用来检索表中的记录,支持以下三种方式: 通过单个row key访问:即按照某个row key键值进行get操作; 通过row key的range进行scan:即通过设置startRowKey和endRowKey,在这个范围内进行 扫描; 全表扫描:即直接扫描整张表中所有行记录。 row key可以是任意字符串,最大长度64KB,实际应用中一般为10~100bytes,存为byte[]字节数 组,一般设计成定长的。 row key是按照字典序存储,因此,设计row key时,将经常一起读取的数据存储到一块,将最近可 能会被访问的数据放在一块。 举个例子:如果最近写入HBase表中的数据是最可能被访问的,可以考虑将时间戳作为row key的一部分,由于是字典序排序,所以可以使用Long.MAX_VALUE - timestamp作为row key,这样能保证新写入的数据在读取时可以被快速命中。 Rowkey规则: 1、 越小越好 2、 Rowkey的设计是要根据实际业务来 3、定长 3、 散列性 a) 取反 001 002 100 200 b) Hash c) 加盐 ColumnFamily 不要在一张表里定义太多的column family。目前Hbase并不能很好的处理超过2~3个column family的表。因为某个column family在flush的时候,它邻近的column family也会因关联效应被触 发flush,最终导致系统产生更多的I/O。 Version 创建表的时候,可以通过HColumnDescriptor.setMaxVersions(int maxVersions)设置表中数据的 最大版本 如果只需要保存最新版本的数据,那么可以设置setMaxVersions(1) Time To Live 创建表的时候,可以通过HColumnDescriptor.setTimeToLive(int timeToLive)设置表中数据的存 储生命期,过期数据将自动被删除,例如如果只需要存储最近两天的数据,那么可以设置 setTimeToLive(2 * 24 * 60 * 60)。 compact & Split 在HBase中,数据在更新时首先写入WAL 日志(HLog)和内存(MemStore)中,MemStore中的数据 是排序的,当MemStore累计到一定阈值时,就会创建一个新的MemStore,并且将老的 MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。于此同时, 系统 会在zookeeper中记录一个redo point,表示这个时刻之前的变更已经持久化了(minor compact)。 StoreFile是只读的,一旦创建后就不可以再修改。因此Hbase的更新其实是不断追加的操作。当一 个Store中的StoreFile数量达到一定的阈值后,就会进行一次合并(major compact),将对同一个 key的修改合并到一起,形成一个大的StoreFile,当Region的大小达到一定阈值后,又会对 StoreFile进行分割(split),逻辑等分为两个StoreFile。 由于对表的更新是不断追加的,处理读请求时,需要访问Store中全部的StoreFile和MemStore, 将它们按照row key进行合并,由于StoreFile和MemStore都是经过排序的,并且StoreFile带有内 存中索引,通常合并过程还是比较快的。 实际应用中,可以考虑必要时手动进行major compact,将同一个row key的修改进行合并形成一 个大的StoreFile。同时,可以将StoreFile设置大些,减少split的发生。 minor compaction:较小、很少文件的合并 数量 大小 的设置 major compaction:完整性合并 hbase.hregion.majorcompaction 默认7天 In Memory 创建表的时候,通过HColumnDescriptor.setInMemory(true)将表放到RegionServer的缓存 中,保证在读取的时候被cache命中 WAL FLag 在HBae中,客户端向集群中的RegionServer提交数据时(Put/Delete操作),首先会先写WAL(Write Ahead Log)日志(即HLog,一个RegionServer上的所有Region共享一个HLog),只有当WAL日志写 成功后,再接着写MemStore,然后客户端被通知提交数据成功;如果写WAL日志失败,客户端则被通 知提交失败。这样做的好处是可以做到RegionServer宕机后的数据恢复。 因此,对于相对不太重要的数据,可以在Put/Delete操作时,通过调用Put.setWriteToWAL(false)或 Delete.setWriteToWAL(false)函数,放弃写WAL日志,从而提高数据写入的性能。 值得注意的是:谨慎选择关闭WAL日志,因为这样的话,一旦RegionServer宕机,Put/Delete的数据将 会无法根据WAL日志进行恢复。 批量写 通过调用Table.put(Put)方法可以将一个指定的row key记录写入HBase,同样HBase提供了另一个方 法:通过调用Table.put(List)方法可以将指定的row key列表,批量写入多行记录,这样做的好处是批量 执行,只需要一次网络I/O开销,这对于对数据实时性要求高,网络传输RTT高的情景下可能带来明显的 性能提升。 HBase读取优化 作为NoSQL数据库,增删改查是其最基本的功能,其中查询是最常用的一项。 显示的指定列 当使用Scan或者GET获取大量的行时,最好指定所需要的列,因为服务端通过网络传输到客户端,数据 量太大可能是瓶颈。如果能有效过滤部分数据,能很大程度的减少网络I/O的花费。 关闭ResultScanner 如果在使用table.getScanner之后,忘记关闭该类,它会一直和服务端保持连接,资源无法释放,从而 导致服务端的某些资源不可用。 所以在用完之后,需要执行关闭操作,这点与JDBS操作MySQL类似 scanner.close() 查询结果 对于频繁查询HBase的应用场景,可以考虑在应用程序和Hbase之间做一层缓存系统,新的查询先去缓 存查,缓存没有再去查Hbase。 多Table并发读 scanner cache hbase.client.scanner.caching配置项可以设置HBase scanner一次从服务端抓取的数据条 数,默认情况下一次一条。通过将其设置成一个合理的值,可以减少scan过程中next()的时间 开销,代价是scanner需要通过客户端的内存来维持这些被cache的行记录 HBase的conf配置文件中进行配置-->整个集群 Table.setScannerCaching(int scannerCaching)进行配置-->本次对表的链接 Scan.setCaching(int caching)-->本次查询 优先级 从本次查询--》本次链接--》整个集群 Scan指定列族或者列 scan时指定需要的Column Family,可以减少网络传输数据量 Close ResultScanner 通过scan取完数据后,记得要关闭ResultScanner,否则RegionServer可能会出现问题(对应 的Server资源无法释放) 批量读 禁用块缓存 如果批量进行全表扫描,默认是有缓存的,如果此时有缓存,会降低扫描的效率。 scan.setCacheBlocks(true|false); 对于经常读到的数据,建议使用默认值,开启块缓存 缓存查询结果 对于频繁查询HBase的应用场景,可以考虑在应用程序和Hbase之间做一层缓存系统,新的查询先去缓 存查,缓存没有再去查Hbase。 Blockcache 隶属于RegionServer mem刷新的时机 当前mem达到128M 集群mem总内存使用量达到阈值 读请求先到Memstore中查数据,查不到就到BlockCache中查,再查不到就会到磁盘上读, 并把读的结果放入BlockCache。 Regionserver上有一个BlockCache和N个Memstore,它们的大小之和不能大于等于 heapsize * 0.8 默认BlockCache为0.2,而Memstore为0.4。对于注重读响应时间的系统,可以将 BlockCache设 大些,比如设置BlockCache=0.4,Memstore=0.39,以加大缓存的命中率 Protobuf数据压缩 简介 protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数 据)通信协议、数据存储等。 Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。 你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写 和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。 安装 上传解压 [root@node01 ~]# tar -zxvf protobuf-2.5.0.tar.gz [root@node01 ~]# mkdir -p /opt/yjx/protobuf-2.5.0 配置安装 [root@node01 ~]# yum install gcc-c++ -y [root@node01 ~]# cd protobuf-2.5.0 [root@node01 ~]#./configure--prefix=/opt/yjx/protobuf-2.5.0 [root@node01 ~]# make && make install 使用 流程 先编写protobuf语言代码,设置压缩的字段和范围 执行bin下的proto程序,将protobuf的程序编译为java程序 使用hbaseapi的时候(加载或者读取),使用protobuf里边的方法 优缺点 优点:Protobuf 有如 XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成 器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻 松读写。 它有一个非常棒的特性,即“向后”兼容性好,人们不必破坏已部署的、依靠“老”数据格式的程序就可以对 数据结构进行升级。这样您的程序就可以不必担心因为消息结构的改变而造成的大规模的代码重构或者 迁移的问题。因为添加新的消息中的 field 并不会引起已经发布的程序的任何改变。 Protobuf 语义更清晰,无需类似 XML 解析器的东西(因为 Protobuf 编译器会将 .proto 文件编译生成 对应的数据访问类以对 Protobuf 数据进行序列化、反序列化操作)。 使用 Protobuf 无需学习复杂的文档对象模型,Protobuf 的编程模式比较友好,简单易学,同时它拥有 良好的文档和示例,对于喜欢简单事物的人们而言,Protobuf 比其他的技术更加有吸引力。 缺点:Protbuf 与 XML 相比也有不足之处。它功能简单,无法用来表示复杂的概念。 XML 已经成为多种行业标准的编写工具,Protobuf 只是 Google 公司内部使用的工具,在通用性上还差 很多。 由于文本并不适合用来描述数据结构,所以 Protobuf 也不适合用来对基于文本的标记文档(如 HTML)建模。另外,由于 XML 具有某种程度上的自解释性,它可以被人直接读取编辑,在这一点上 Protobuf 不行,它以二进制的方式存储,除非你有 .proto 定义,否则你没法直接读出 Protobuf 的任何 内容 PHENIX 简介 Phoenix是构建在HBase上的一个SQL层,能让我们用标准的JDBC APIs而不是HBase客户端APIs 来创建表,插入数据和对HBase数据进行查询。Phoenix完全使用Java编写,作为HBase内嵌的 JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase扫描,并编排执行以生成标准 的JDBC结果集。 Apache Phoenix是使用Apache HBase作为后备存储的开源,大规模并行,关系数据库引擎,支 持OLTP for Hadoop 。Phoenix提供了一个JDBC驱动程序,该驱动程序隐藏了noSQL存储的复杂 性,使用户可以创建,删除和更改SQL表,视图,索引和序列。批量插入和删除行;并通过SQL查 询数据。Phoenix将查询和其他语句编译到本机的noSQL存储区API中,而不是使用MapReduce来 在noSQL存储区之上构建低延迟应用程序。 软件功能 1. 表抽样。通过实现一个过滤器来支持 TABLESAMPLE 子句,该过滤器使用由统计信息收集建立的路 标仅返回一定百分比的行。4.12版本中可用 2. 减少磁盘存储。通过以下方式减少磁盘存储,以提高性能:①将所有值打包到每个列族的单个单元 中;②提供列名和列限定符之间的间接寻址。4.10版本中可用 3. 原子更新。现在可以在UPSERT VALUES语句中进行原子更新,以支持计数器和其他用例。4.9版本 中可用 4. 默认声明。现在,在定义列时,可以为初始值提供DEFAULT声明。4.9版本中可用 5. 命名空间映射。将Phoenix模式映射到HBase命名空间,以改善不同模式之间的隔离。4.8版本中可 用 6. Hive整合。使Hive与Phoenix一起使用,以支持将大型表连接到其他大型表。4.8版本中可用 7. 本地索引的改进。重新设计了本地索引实现,以确保表和索引数据的共置,并使用受支持的HBase API以获得更好的可维护性。4.8版本中可用 8. DISTINCT查询优化。在主键的前导部分上将查找逻辑推入服务器,以进行SELECT DISTINCT和 COUNT DISTINCT查询,从而带来显着更好的性能。4.8版本中可用 9. 交易支持。通过与Tephra集成来支持交易。4.7版本中可用 10. 时间序列优化。为更详细地解释优化对时间序列数据的查询这里。4.6版本中可用 11. 异步索引填充。允许使用map reduce作业异步创建索引。4.5版本中可用 12. 用户定义的功能。允许用户创建自己的自定义或特定于域的用户定义功能并将其部署到群集。4.4 版本中可用 13. 功能指标。允许将索引定义为表达式,而不仅仅是列名,并在查询包含该表达式时使用索引。4.3 版本中可用 14. 映射减少集成。通过实现自定义输入和输出格式,支持与Phoenix的常规map-reduce集成。3.3 / 4.3版本中可用 15. 统计收集。收集表的统计信息以改善查询并行化。3.2 / 4.2版本中可用 16. 加入改进 。改进现有的哈希联接实现。 多对多连接。双方都太大而无法容纳内存的支撑连接。3.3 / 4.3版本中可用 优化外键联接。利用我们的跳过扫描过滤器优化外键联接。3.2 / 4.2版本中可用 半/反联接。通过标准[NOT] IN和[NOT] EXISTS关键字支持半/反子查询。3.2 / 4.2版本中可用 17. 子查询支持WHERE子句中的独立子查询和相关子查询以及FROM子句中的子查询。3.2 / 4.2版本中 可用 18. 追踪。允许查看 UPSERT 或 SELECT 语句的各个步骤,以及每个步骤在集群中所有计算机上花费的 时间。4.1版本中可用 19. 本地索引。一个新的,互补分stragegry写沉重,空间受限的使用情况。使用本地索引,索引和表 数据共存于同一服务器上,因此在写入过程中不会发生网络开销。即使查询未完全覆盖,也可以使 用局部索引(即Phoenix通过针对数据表的指向获取自动检索不在索引中的列)。4.1版本中可用 20. 派生表。允许在FROM子句中使用 SELECT 子句来定义派生表(包括联接查询)。3.1 / 4.1版本中 可用 21. Apache Pig加载程序。支持Pig加载器在通过Pig处理数据时利用Phoenix的性能。3.1 / 4.1版本中 可用 22. 概观。允许使用同一物理HBase表创建多个表。3.0 / 4.0版本中可用 23. 多租户。允许不同租户在每个连接的基础上创建独立视图,这些视图均共享相同的物理HBase表。 3.0 / 4.0版本中可用 24. 序列。已实现对CREATE / DROP SEQUENCE,NEXT VALUE FOR和CURRENT VALUE FOR的支 持。3.0 / 4.0版本中可用 25. 数组类型。支持标准JDBC ARRAY类型。3.0 / 4.0版本中可用 26. 二级索引。允许用户在可变或不可变数据上创建索引。 27. 分页查询。通过行值构造函数进行分页查询,这是一种标准SQL构造,可以有效地将行定位在组合 键值处或之后。启用更多查询功能,可以有效地逐步浏览数据并优化复合键值的IN列表以获取积 分。 28. CSV批量加载程序。通过map-reduce或客户端脚本将CSV文件批量加载到HBase中。 29. 聚合增强。现在支持 COUNT DISTINCT , PERCENTILE 和 STDDEV 。 30. 类型添加。该 FLOAT , DOUBLE , TINYINT 和 SMALLINT 现在支持。 31. IN / OR / Like优化。当使用前导行键列的查询中出现IN(或等效的OR)和LIKE时,请将其编译为 跳过扫描过滤器,以更有效地检索查询结果。 32. 支持主键列的ASC / DESC声明。允许将主键列声明为升序(默认)或降序,以使行键顺序可以匹配 所需的排序顺序(从而防止进行额外的排序)。 33. 盐化行键。为了防止写入时出现热点,可以通过在行密钥中插入前导字节来“加盐”,这是整个行密 钥的N个存储桶上的mod模。当行键是单调递增的值(通常是表示当前时间的时间戳)时,这可确 保写入的均匀分布。 34. TopN查询。当与TopN结合使用时,通过对ORDER BY的支持,支持返回前N行的查询 35. 动态列。对于某些用例,很难预先对模式进行建模。您可能有一些只想在查询时指定的列。在 HBase中这是可能的,因为每一行(和列族)都包含一个带有可在运行时指定的键的值的映射。因 此,我们希望支持这一点。 36. Apache Bigtop包含。有关更多信息,请参见BIGTOP-993。 Phoenix系统架构 重客户端架构 从其架构来看,Phoenix结构上划分为客户端和服务端两部分: 客户端包括应用程序开发,将SQL进行解析优化生成QueryPlan,进而转化为HBase Scans,调用 HBase API下发查询计算请求,并接收返回结果; 服务端主要是利用HBase的协处理器(Phoenix-core包里面包含hbase-client,以及hbase-server 包),处理二级索引、聚合及JOIN计算等。 这种架构我们称之为重客户端架构,也是目前Phoenix使用最广泛的方式,但是这种方式存在一些使用 上的缺陷: 1. 应用程序与Phoenix core绑定使用,需要引入Phoenix内核依赖,目前一个单独Phoenix重客户端 集成包已达120多M; 2. 运维不便,Phoenix仍在不断优化和发展,一旦Phoenix版本更新,那么应用程序也需要对应升级 版本并重新发布; 3. 仅支持Java API,其他语言开发者不能使用Phoenix。 轻客户端架构 轻客户端架构将Phoenix分为三部分: 瘦客户端是用户最小依赖的JDBC驱动程序,与Phoenix依赖进行解耦,支持Java、Python、Go等 多种语言客户端; QueryServer是一个单独部署的HTTP服务,接收轻客户端的RPC请求,并将SQL转发给Phoenix Core进行解析优化执行; Phoenix Server与重客户端架构相同。 QueryServer基于Calcite的Avatica组件实现,内部嵌入了独立的Jetty HttpServer,支持Protobuf和 JSON两种RPC传输协议,其中Protobuf是默认协议,提供比JSON更高效的通信方式。 由于QueryServer是无状态的,可以部署在HBase集群的每台RegionServer上,通过HTTP负载均衡器将 多个客户端的请求分发在多个QueryServer上。 Phoenix数据模型