穆公(朱金清 suinking@gmail.com) 微博:淘穆公 2013.4.21 阿里HBase业务设计实践 穆公(朱金清 suinking@gmail.com) 微博:淘穆公 2013.4.21
大纲 简介 数据模型 业务设计 产品线使用建议 监控 总结
简介 Nosql: column-based storage system Large volume of data High write (esp. random ) through-put / Good ramdon read performance Range query Row-base transaction Auto-sharding Compare to Bigtable Hbase Based on Hadoop HDFS or other HDFS Bigtable based on GFS
Large volume 三层索引结构 Region的大小默认最大是256M 按照平均128M算; 假设:一个rowkey 1KB Root table: 128M=128*1024KB 即2^7 * 2^10 = 2^17 bucket Meta table: (2^17)^2 = 2 ^34 bucket 记录数:2^51 条记录
其它特征 三层B+树的扩展LSMTree[1] Row-base Auto-sharding 适合于范围查询 Rowkey的字母顺序来排序(byte数组存储) Row-base 事务级别仅限于rowkey级别 Auto-sharding Region的自动split/move 问题:牺牲了CAP中的? [1] Jim Gray and Franco Putzolu, "The Five Minute Rule for Trading Memory for Disk Accesses and The 10 Byte Rule for Trading Memory for CPU Time", Proceedings of the 1987 ACM SIGMOD Conference, pp 395-398.
已有适合的使用场景 海量数据写入 消息类(类似Facebook的message) Schema-free LOG-Append类的业务 历史数据 批量写入 消息类(类似Facebook的message) 消息类 Schema-free 业务监控 LOG-Append类的业务 全网HSF日志 全网每天上百亿 大表的复杂/多维度索引 检索索引,主数据在mysql 分析类 大批量读取 HBase+缓存TAIR
现有集群状况 集群名称 TPS(avg) 11.11最高 QPS(avg) 版本 业务 7k 1.8w 1.6w 3.4w 0.90.2 0.94 4w 20w (压测) 3w(压测) 每天2-3kw - RT在ms级别 0.90.2-定制版 25w 15w 100w 3k 1.5w 8k
与MYSQL的对比 场景 HBase优点 HBase缺点 MySQL优点 MySQL缺点 业务表使用 使用简单,一张表即可 不过没有SQL 分库后 更新模式 插入多的适合 RKupdate差 DML 二级索引策略 需借助索引表 强 DDL问题 客户端接口 灵活自己掌握 无标准SQL SQL 写性能 非常强 顺序写入时瓶颈在一台rs 较强 几千tps/单套库 读性能 较强;支持scan 依赖内存 很强;支持scan 依赖索引 可扩展性 借助愚公/datax工具可动态扩展 弱 运维方便 自己定制 不够成熟 成熟 DDL 时间短;92版本可以在线 若有索引表,需要自己填充 Create index即可 时间长;block 读写 稳定性CAP CP A AP C
NoSQL使用情况 TAOBAO BAIDU TENCENT FACEBOOK OTS/HBase BAILING/ARMOR/HYPERTABLE(HCE) TENCENT TDB/TSSD FACEBOOK HBASE
大纲 简介 数据模型 业务设计 产品线使用建议 监控 总结
架构图 客户端 Q LSM C0树 同一机器,目的? LSM C1树 … … … … … … … 写memstore成功立即返回 Hbase Client Hbase Client Zookeeper cluster Master Back up Master Back up Master … … Q Region Server Region Server Region Region Store memStore StoreFile … Store 写memstore成功立即返回 读blockcache、memstore、storefile LSM C0树 … … … 同一机器,目的? split事务 1 在zk创建一个splitint状态的region节点 2 在HDFS上创建用于split的目录 3 关闭region (1 等待正在进行的flush和compact完成) preFlush 获得region写锁 进行flush,关闭所有文件,设置region关闭标识 释放region写锁 4 下线region (从rs的online region列表拿掉) 5 切分Hfile 6 创建子分区1-request count 7 创建子分区2-request count 8 更新meta表 《删除一行记录,增加两行记录》 9 将两个子分区上线 10 将zk中对应node的状态修改为split状态 HDFS client HDFS NameNode DataNode DataNode … LSM C1树
数据模型 V ts1 K ts2 … … … cell column Column-family cell row column table row … Column-family … row
示例 消息表 表结构: message [CF: message [col: autoCommit, deviceUuid, status, type]] http://dbidea.corp.taobao.com:8888/
message对应到RDBMS deviceUuid appid startTime expired type… 00001cc7d162302482b1cfff35301183 9223370683466836065 2012.11.21 2012.12.11 系统消息 9223370683531713740
表在hdfs的存储 结构 /hbase/Table目录/region目录 Region的具体存储 (/hbase/Table目录/region目录/CF目录/具体文件)
大纲 简介 数据模型 业务设计 产品线使用建议 监控 总结
业务设计 适合场景 (综合考虑) 表数据量大(至少亿级别以上) 日志append型业务,(比如定期保留10天数据等) 原则上: 能分库分表来用mysql就用mysql来解决 mysql 单表一般500w,能使用mysql的场景 无跨行跨表事务要求 写入量大 (每天千万及以上) 读取量相对少 (读取:写入 <= 1/10) 读取场景简单、不经常变化 无正序、逆序的排序要求 类似dw等全量读取,不太合适。 Rowkey不经常更新 (必须先删除再添加)
业务设计 典型的设计 最终方案: Rowkey非单一ID (单一ID可以用mysql解决) Rowkey为组合性 表名 CF属性(一行一个) 适合场景 (综合考虑) 表数据量大(至少亿级别以上) 日志append型业务,(比如定期保留10天数据等) 无跨行跨表事务要求 写入量大 (每天千万及以上) 读取量相对少 (读取:写入 <= 1/10) 读取场景简单、不经常变化 无正序、逆序的排序要求 分库分表类似dw等全量读取,不太合适。 Rowkey不经常更新 (否则必须先删除再添加) 典型的设计 Rowkey非单一ID (单一ID可以用mysql解决) Rowkey为组合性 最终方案: 表名 CF属性(一行一个) rowkey的信息 A address latlng date areaID_geohash_companyId 长度:6位数字 + 12位小写字母 + 小于5位数字 前面的6为数字穷举约在4000个左右 表名 列名/读取 写入 rowkey无法覆盖的查询 lA scan() scan的时候进行前缀匹配,rowkey中的areaId是必须的参数,设置startRow limit set()每天定时插入数据,数据量在1000W,同时删除一个月前的那一天的数据 业务查询基本上是对于rowkey的查询,只有在删除数据的时候,是根据value中的date来进行的 表名 CF列表(一行一个) rowkey的信息 A Info (address、latlng) areaID_geohash_companyId 长度:6位数字 + 12位小写字母 + 小于5位数字 前面的6为数字穷举约在4000个左右
Toid: ***;content:*** 业务设计 适合场景 (综合考虑) 表数据量大(至少亿级别以上) 日志append型业务,(比如定期保留10天数据等) 无跨行跨表事务要求 写入量大 (每天千万及以上) 读取量相对少 (读取:写入 <= 1/10) 读取场景简单、不经常变化 无正序、逆序的排序要求 分库分表类似dw等全量读取,不太合适。 Rowkey不经常更新 (否则必须先删除再添加) 典型的设计 交互性的应用消息 数据双写,当做索引(类似买卖家) 查找穆公最近一周发布的消息? 查找穆公最近一周发送给马云的消息? 发送给马云的消息? Rowkey Column Value fromID + time Toid: ***;content:***
业务设计 表结构设计 id是订单ID,可以是业务主键 能否覆盖查询: 穆公一个星期内买的商品? 穆公一个月买的书? Rowkey 适合场景 (综合考虑) 表数据量大(至少亿级别以上) 日志append型业务 无跨行跨表事务要求 写入量大 (每天千万及以上) 读取量相对少 (读取:写入 <= 1/10) 读取场景简单、不经常变化 无正序、逆序的排序要求 分库分表类似dw等全量读取,不太合适。 Rowkey不经常更新 (否则必须先删除再添加) 表结构设计 id是订单ID,可以是业务主键 能否覆盖查询: 穆公一个星期内买的商品? 穆公一个月买的书? Rowkey Column Value Id valueString ( 93 fields ) 默认用户订单索引表 Rowkey Column Value UId + time + id valueString (32 fields )
业务设计 分词索引表 能否覆盖查询: 穆公一个星期内买的商品? 穆公一个月买的书? 结论: 覆盖搜索场景、无法用数据库解决 查询类型固定 适合场景 (综合考虑) 表数据量大(至少亿级别以上) 日志append型业务 无跨行跨表事务要求 写入量大 (每天千万及以上) 读取量相对少 (读取:写入 <= 1/10) 读取场景简单、不经常变化 无正序、逆序的排序要求 (单向时间排序ok) 分库分表类似dw等全量读取,不太合适。 Rowkey不经常更新 (否则必须先删除再添加) 特殊的搜索固定需求 分词索引表 能否覆盖查询: 穆公一个星期内买的商品? 穆公一个月买的书? 结论: 覆盖搜索场景、无法用数据库解决 查询类型固定 只会按照时间排序 永久的大表保存 数据一致性要求低 Rowkey Column Value UId + 分词 + time + id null Rowkey Column Value UID+ time + id null UID+ 分词 + time + id
业务设计-无结构化数据 SchemaFree业务 Rowkey Column Value id 列数不定,有的有2个列;有的有N个列
大纲 简介 数据模型 业务设计 产品线使用建议 监控 总结
产品线、客户端使用建议 海量数据,rowkey范围和分布已知,建议进行预分配 CF设计:尽量少,建议CF数量在1-2个 flush和compaction是region级别;某个CF引发其它CF的memstore的flush大量store file 导致compaction(当store file的个数>value) 问题: 还有其他的原因么? 1CF -> 6CF Rowkey设计:写入要分散;如历史交易订单:biz_order_id做reverse后做rowkey
产品线、客户端使用建议(2) Autoflush参数设置为true;否则极端情况下会丢失数据 Hbase client的重试次数为3次以上。否则会由于split导致region not onle;从而导致写入失败(udc集群出现过)。 hbase.rpc.timeout 一次rpc的timeout;默认60秒 hbase.client.pause 客户端的一次操作失败,到下次重试之间的等待时间 hbase.client.retries.number 客户端重试的次数 hbase.regionserver.lease.period 客户端租期超时阀值;scan量大时可以考虑增大;否则”Lease Exception: lease -70000000000000001 does not exist”
产品线、客户端使用建议(3) ZK连接/HTable对象的使用注意 Configure对象的使用 必须是static or singleton模式 默认:每台机器与zk直接的连接数不超过30个 HTable的使用 线程不安全 使用HTableV2 HTablePool (推荐的方式)
产品线、客户端使用建议(3) HTablePool(自己控制Htable数量) /** * 返回htablepool连接池中的一个htable * @param tableName * @return */ public static synchronized HTable getHtable(String tableName){ if(hTablePool!=null) return (HTable) hTablePool.getTable(tableName);//如果hTablePool对象已经存在,直接取出一个htable else{// hTablePool不存在则先new一个htablepool对象,然后再取 } } /**HTable是非线程安全的 在多线程环境下使用HTablePool是一个好的解决方案, * 参数MAX_TABLE_COUNT 是 pool保持的每个Htable实例的最大数量 , * 比如为10 如果有100个线程getTable() 同一张表 则他们会共用 pool中的该表的10个实例,其它的单独申请 * 使用的时候 就不要new Htable了,直接从pool中取 * 用完再putTable 放回去 * * 在0.92以上的版本 则不用放回去 直接table.close() 即可 putTable 被标记为 @Deprecated * 0.90.2 版本使用 putTable 下面的代码都没有 做 这些操作 * 避免 不同版本 出问题 */
业务接入Ork平台http://nosql.*.com
影响写测试
影响汇总 1、对于写速度而言,影响因素的效果主要为: 写hlog > split > compact; 2、对于写速度波动而言,想完全不波动是不可能,影响因素的效果主要为:split > 写hlog > compact; 3、对于写频率较高的应用而言,一台region server上不适合有太多的region; (hbase.hregion.max.filesize = 64G) 4、Pre-Sharding可以不做,建议做; 5、对于日志应用可以考虑关闭compact/split hbase.regionserver.regionSplitLimit 1关闭split hbase.hstore.compactionThreshold Integer.MAX_VALUE关闭Compact hbase.hstore.blockingStoreFiles Integer.MAX_VALUE
风险点——集群稳定/容灾 regionserver的单点问题 跨机房容灾 实现: 导致部分数据短暂不可用 目前还只是部署在单个机房 跨机房性能衰减 实现: 程序双写 复制的测试(push的replication已经上线、pull在研) 消息中间件实现(异步消息)
大纲 简介 数据模型 业务设计 产品线使用建议 监控 总结
监控类型 主机监控 端口监控 错误日志监控 写入监控 GC监控 性能指标 http://beidou.*/hbase.php http://hbplus.*/sch/monitor.jsp http://hbase.*/hbase/overview.php 开发常用系统\hbaseplus\websqlplus
Thanks! Q&A 穆公/朱金清 微博:淘穆公 http://www.weibo.com/suinking