-
Notifications
You must be signed in to change notification settings - Fork 11
/
content.json
1 lines (1 loc) · 142 KB
/
content.json
1
{"meta":{"title":"谷震平的博客","subtitle":"写点一路的风景,都很普通,主要还是留给自己。","description":"写点一路的风景,都很普通,主要还是留给自己。","author":"谷震平[guzhenping, [email protected]]","url":"http://guzhenping.com"},"pages":[],"posts":[{"title":"【置顶】个人简介","slug":"README","date":"2048-10-24T09:04:48.000Z","updated":"2019-03-19T05:52:26.163Z","comments":true,"path":"2048/10/24/README/","link":"","permalink":"http://guzhenping.com/2048/10/24/README/","excerpt":"导读 这里是谷震平个人的博客文章,描述了关于大数据技术的一些实践结果和理论知识。包括,集群搭建、大数据工具学习、运维、学术资料整理。请记住这里:http://guzhenping.com。 笔者作为一名数据仓库工程师,经历过两个数据仓库系统,一大一小。大的让笔者学到了很多,小的是笔者自己从0到1攒的。","text":"导读 这里是谷震平个人的博客文章,描述了关于大数据技术的一些实践结果和理论知识。包括,集群搭建、大数据工具学习、运维、学术资料整理。请记住这里:http://guzhenping.com。 笔者作为一名数据仓库工程师,经历过两个数据仓库系统,一大一小。大的让笔者学到了很多,小的是笔者自己从0到1攒的。 图1 基于hadoop的大数仓 图2 基于PG 10的小数仓 正在开启第三段职业生涯。 文章列表 集群搭建与运维 [2016-11 集群搭建指南(上卷)] [2016-11 集群搭建指南(中卷)] [2016-11 集群搭建指南(下卷)] [2016-12 Hadoop学习指南(集群运维篇)] [2017-01 Hadoop学习指南(HA配置)] [2017-01 HA升级过程] [2017-02 HA成功升级的总结] [2017-10 大数据开发学习(Fabric)] 数据仓库 [2016-07 数据仓库学习(概念篇)] [2017-05 数据仓库学习(ETL)] [2018-01 数据仓库学习(维度建模)] Hadoop生态圈 [2016-11 Hadoop学习指南(HDFS篇)] [2016-11 大数据开发学习(Hive)] [2016-11 大数据开发学习(ZooKeeper)] [2017-12 Hadoop学习指南(HBase篇)] OLAP/数据查询与可视化 [2017-12 大数据开发学习(Redash)] [2018-01 大数据开发学习(Kylin)] [2018-01 Redash使用指南] [2018-01 Redash权限分配] [2018-04 大数据开发学习(Clickhouse)] 区块链 2018-07 区块链简史 2018-07 区块链分类 工作关系,学习到一点区块链的知识。特地开了一个公众号进行写作,欢迎扫码关注:","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"数据仓库","slug":"数据仓库","permalink":"http://guzhenping.com/tags/数据仓库/"}]},{"title":"区块链学习笔记--EKT设计要素","slug":"区块链学习笔记-EKT设计要素","date":"2019-03-19T08:22:17.000Z","updated":"2019-03-19T08:29:16.415Z","comments":true,"path":"2019/03/19/区块链学习笔记-EKT设计要素/","link":"","permalink":"http://guzhenping.com/2019/03/19/区块链学习笔记-EKT设计要素/","excerpt":"","text":"前言笔者做了一段时间的区块链底层开发,深知架构设计的重要性。对于高手来说,没有的轮子是可以自己造的,造个大规模消息/任务队列都只是想不想写的事情。但在企业中开发,追求的是稳定、性能、成本等等,所以通常希望使用开源组件,二次开发。 解析过EKT项目,鉴于自己还不是高手,把认为对自己有用的点都总结下。 账户设计和ETH类似,用了账户模型,结合Merkle树进行设计,通过记录nonce值防止双花攻击。 核心逻辑: func GenerateKeyPair() (pubkey, privkey []byte) { key, err := ecdsa.GenerateKey(S256(), rand.Reader) if err != nil { panic(err) } pubkey = elliptic.Marshal(S256(), key.X, key.Y) return pubkey, math.PaddedBigBytes(key.D, 32) } EKT采用ECDSA(椭圆曲线数字签名算法)生成地址,secp256k1方法作为该算法参数。 工程中,ecdsa和sha3_256算是两个主流加密算法。ecdsa(椭圆曲线数字签名算法)是一种非对称公钥加密算法,也是数字签名算法类比中的佼佼者,用于防止数据串改和验证数据真实性,对标RSA算法。sha3_256是一种哈希算法,也叫摘要技术,防止数据被篡改。 ECDSA相比于RSA有如下特点: ECDSA的加密密钥更短 ECDSA的加密运算更快而安全性和RSA相当 RSA的私钥和公钥是可以互换加解密的,但ECDSA只能私钥加密公钥解密 ECDSA的核心是利用数论中大数分解比较困难。这里列出一些推荐的扩展阅读: 椭圆曲线算法(ECC)学习(一) 椭圆曲线算法(ECC)学习(二)之Secp256k1 质数 费马小定理 存储相关EKT的数据库采用LevelDB和sync.map。LevelDB是Key-Value型数据库,用于数据持久化。sync.map是一种GO语言的数据结构,可用于缓存。EKT封装了sync.map,开发了自己的内存型K-V数据库。 早期,有两个核心的文件:db/levedb.go和db/MemKVDatabase.go。 在实际代码中,EKT将本地KV和内存KV组装在一起,构成混合型KV数据库。核心文件db/ComposedKVDatabase.go 代码: type ComposedKVDatabase struct { mem *MemKVDatabase // 引用内存型K-V数据库 levelDB *LevelDB // 引用本地K-V数据库 } // 抽象该混合型KV数据库 func NewComposedKVDatabase(filePath string) *ComposedKVDatabase { return &ComposedKVDatabase{ mem: NewMemKVDatabase(), levelDB: NewLevelDB(filePath), } } 该数据库只有三个常用方法: Set(key, value []byte):插入数据 Get(key []byte):查找数据 Delete(key []byte):删除数据 因为采用线性结构的区块链基因,所以并不会涉及update。 随着代码的迭代,笔者实测后发现:数据存在丢失的情况。之后,EKT官方去掉了自己的内存型K-V数据库,仅保留了leveldb相关。 这里,再次证明,稳定性好的东西,实在不好做。 链结构相关链的结构包含了14个元素,依赖了外部包:i_consensus/consensus.go, pool/TxPool.go, police.go, block_manager.go type BlockChain struct { ChainId int64 Consensus i_consensus.ConsensusType // 确认采用DPoS,Pow, Pos currentLocker sync.RWMutex currentBlock Block currentHeight int64 Locker sync.RWMutex Status int Fee int64 Difficulty []byte Pool *pool.TxPool // 交易池 BlockInterval time.Duration Police BlockPolice // 用于记录从其他节点过来的block BlockManager *BlockManager // 区块管理器 PackLock sync.RWMutex } 各字段的解释官方没有给出,之后通过对代码的详细分析,再给出精准定义。 简单提下创世过程。当主链在启动时发现没有区块的时候,将执行写创世区块的功能。 创世核心源码: // 将创世块写入数据库 accounts := conf.EKTConfig.GenesisBlockAccounts block = &blockchain.Block{ Height: 0, Nonce: 0, Fee: dpos.Blockchain.Fee, TotalFee: 0, PreviousHash: nil, CurrentHash: nil, BlockBody: blockchain.NewBlockBody(), Body: nil, Timestamp: 0, Locker: sync.RWMutex{}, StatTree: MPTPlus.NewMTP(db.GetDBInst()), StatRoot: nil, TxTree: MPTPlus.NewMTP(db.GetDBInst()), TxRoot: nil, TokenTree: MPTPlus.NewMTP(db.GetDBInst()), TokenRoot: nil, } // 为每个创世账户更新默克尔树根 for _, account := range accounts { block.CreateGenesisAccount(account) } // 更新默克尔树根,改变StatRoot,使得block.StatRoot = block.StatTree.Root block.UpdateMPTPlusRoot() // 计算当前区块Hash值 block.CaculateHash() // 持久化 dpos.Blockchain.SaveBlock(*block) 获取创世区块的账户(可以是多个账户),由主链启动时配置得到。生成首个区块的数据,做了一些改动后,写入数据库。 主链启动经过对主链、共识机制的初始化,再运行共识模块的Run()即可启动。 主要有两步: 从本地数据库中恢复当前节点已同步的区块 同步区块 其中,同步区块方面有3个核心步骤: 从其他节点同步,执行dpos.SyncHeight(Height) 当区块同步失败,尝试3次,3次之后判断是否超级节点 如果当前节点同步失败,且是超级节点,则通过投票结果来同步区块,执行dpos.startDelegateThread()进入打包区块的流程 主网启动流程图如下: 数据同步与恢复一般是刚启动的节点从其他节点同步数据。 第一步:GetRound()获取当前打包节点信息 第二步:循环向各个节点发送请求,执行getBlockHeader()获取区块数据 第三步:再请求该区块的投票结果,执行getVotes()获取投票结果 第四步:执行Validate()校验投票结果的完整性和真实性,不合法重复第二步 第五步:校验合法后,执行getBlockEvents()获取交易明细数据,再执行ValidateNextBlock()验证交易明细数据和区块数据是否合法,不合法重复第二步 第六步:以上都合法,执行RecieveVoteResult()写入区块 流程图如下: 后续会对RecieveVoteResult()单独分析,该函数集成的功能较多,包括:验证投票、管理区块、改变状态、记录打包间隔、写区块等功能。 本地数据恢复流程,一图可以描述,不再多说。 结语这篇文章的内容已经足够长且多。如果反响不错,会继续在深入写下对别人有价值的东西。 真正理解,还需要多多阅读源码。","categories":[],"tags":[{"name":"区块链","slug":"区块链","permalink":"http://guzhenping.com/tags/区块链/"}]},{"title":"Redash 二次开发入门","slug":"Redash-二次开发入门","date":"2019-02-14T02:36:41.000Z","updated":"2019-02-26T02:37:35.769Z","comments":true,"path":"2019/02/14/Redash-二次开发入门/","link":"","permalink":"http://guzhenping.com/2019/02/14/Redash-二次开发入门/","excerpt":"前言在Redash二次开发上做了不少工作,修改bug,定制样式,定制功能,增加数据源等等。聊聊自己的二次开发经验。 懂分享的人,一定会快乐!","text":"前言在Redash二次开发上做了不少工作,修改bug,定制样式,定制功能,增加数据源等等。聊聊自己的二次开发经验。 懂分享的人,一定会快乐! 环境准备Redash依赖的外部环境比较多,特别是Python相关的包,大多数时候一次装不成功,需要单独装或者更新安装其他系统依赖库,祝大家一次成功! 环境准备可以参考另一篇blog,没用全说,其余的自己动动脑筋比较好。 附传送门:Redash开发指南 系统架构介绍总的来说,Redash分为2部分:前端展示,后端数据库连接查询。在这中间,Redash采用celery进行任务调度,满足大量用户查询。 接下来会从三个方面进行总结: 总体结构 前端逻辑 后端逻辑 总体结构 Redash在应用层、前后端均带有自己的核心模块。总而言之,当用户在使用Query、Dashboard、Alert功能时,会由前端提供基础的展示, 再把用户的请求通过JS和后端的Server层交互。用户所有的请求会放在任务队列中,对于需要查询的任务,则会去找对应的数据查询引擎。 前端逻辑前端主要是控制页面元素和CSS样式,提供可视化组件和基本的功能。所有与后端交互的部分由JS业务逻辑控制,实现数据传输与功能跳转。 后端逻辑 后端的大部分功能依赖Celery进行完成,使用Redis做消息中间件。在任务队列中,大多数任务互相独立,进行数据查询时,worker起到主要作用。 Redash的一个特色是连接了28种以上的数据源,所以自带的数据查询引擎模块融合了这些数据库(引擎)的Python调用接口,十分丰富。 代码结构介绍Redash的模块化做的非常好,基本上可以通过目录和文件名分析出代码的功能定位。嗯,看过的都说好,改过的都说爽。 一图其实可以省略很多字。实在不明白,欢迎留言评论,加小密圈哈。 案例上手Redash有一个潜在的性能问题:Hive获取几万张表的schema信息时,促使celery worker任务队列堵塞,资源响应过慢。 所以,就以这个问题来看看源码修改。 问题分析:Reash采用desc命令获取hive table schema信息(db名,table列名),每张表执行一次desc,速度过慢。 解决方案:直接通过hive metadata获取,即,从mysql database中获取schema信息。(不懂hive的metadata为啥用mysql的,可以去查查) 准备查询mysql的SQL代码: # 获取DB名 db_sql = "select name as db from DBS" # 获取table名 db_list = ['xx1', 'xx2'] for db in db_list: tabel_sql = "select tbl_name as table_name from DBS as db inner join TBLS as tbl on db.db_id = tbl.db_id where db.name = '{}'".format(db[0]) # 获取column名 table_list = ['t1', 't2'] for table in table_list: sql = "select * from ( " sql += "select col.column_name, col.type_name as column_type from DBS as db inner join TBLS as tbl on db.db_id = tbl.db_id inner join SDS as sds on tbl.sd_id = sds.sd_id inner join COLUMNS_V2 as col on sds.cd_id = col.cd_id" sql += " where db.name = '{}' and tbl_name = '{}'".format(db[0], table[0]) sql += " union all " sql += "select pt.PKEY_NAME as column_name, pt.PKEY_TYPE as column_type from DBS as db inner join TBLS as tbl on db.db_id = tbl.db_id left join PARTITION_KEYS as pt on tbl.tbl_id = pt.tbl_id" sql += " where db.name = '{}' and tbl_name = '{}'".format(db[0], table[0]) sql += " ) as t where t.column_name is not null" 前端添加hive数据源连接串的地方,增加mysql配置信息: # 修改前端配置 def configuration_schema(cls): return { "type": "object", "properties": { "host": { "type": "string" }, "port": { "type": "number" }, "database": { "type": "string" }, "username": { "type": "string" }, "mysql_host": { "type": "string", }, "mysql_port": { "type": "number", "default": 3306 }, "mysql_database": { "type": "string", "default": "hive_test" }, "mysql_username": { "type": "string", "default": "root" }, "passwd": { "type": "string", "title": "Mysql Password" } }, "order": ["host", "port", "database", "username", "mysql_host", "mysql_port", "mysql_database", "mysql_username", "passwd"], "required": ["host", "mysql_host", "mysql_port", "mysql_database", "mysql_username", "passwd"], 'secret': ['passwd'] } 修改获取schema信息的处理函数_get_table(self, schema), 将其改为通过配置拿mysql连接串,联系上述SQL逻辑,获取schema信息即可。此处不做演示了。 结语以上,算是笔者总结的二次开发入门资料吧。如有问题,欢迎加入小圈子,一起交流!","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Redash","slug":"Redash","permalink":"http://guzhenping.com/tags/Redash/"}]},{"title":"前端开发学习笔记(一)","slug":"前端开发学习笔记(一)","date":"2018-11-29T09:04:48.000Z","updated":"2018-12-14T03:42:51.852Z","comments":true,"path":"2018/11/29/前端开发学习笔记(一)/","link":"","permalink":"http://guzhenping.com/2018/11/29/前端开发学习笔记(一)/","excerpt":"前言在做一个叫计算平台的项目,主要是将机器学习、深度学习和大数据结合在一起,对标该领域中,阿里的计算平台、第四范式的先知平台等知名项目。目前,我们实现了基于Spark的可视化ETL功能,还有很多事情要做。 在这个项目中,用到了Vue.js的技术栈。为了加快项目进度,笔者还是在全沾的道路上越走越远,所以特地记录一路上的问题。","text":"前言在做一个叫计算平台的项目,主要是将机器学习、深度学习和大数据结合在一起,对标该领域中,阿里的计算平台、第四范式的先知平台等知名项目。目前,我们实现了基于Spark的可视化ETL功能,还有很多事情要做。 在这个项目中,用到了Vue.js的技术栈。为了加快项目进度,笔者还是在全沾的道路上越走越远,所以特地记录一路上的问题。 Vue.js入门Vue.js不算特别难,一定要多看几遍官方文档!一个完整的初级Demo大致涨这样: 项目Demo地址: hello-vue 初学时,搞不清单文件组件。没人带,就直接想用高级的姿势,是可怕的。既然初学,还是得一步一步来… 前后端通讯:unicode/stringCase: 前端通过JSON将string数据转发的Python后端,结果该类型变成了Unicode类型。废掉了如下代码: if isinstance(data, str): do somethings.... 按道理说,data应该是string类型才对。 后端将js ajax转发来的数据通过flask.request.get_json()方法解析,相关方法: // 获取数据 s = request.get_json() // get_json()方法 def get_json(self, force=False, silent=False, cache=True): """Parse and return the data as JSON. If the mimetype does not indicate JSON (:mimetype:`application/json`, see :meth:`is_json`), this returns ``None`` unless ``force`` is true. If parsing fails, :meth:`on_json_loading_failed` is called and its return value is used as the return value. :param force: Ignore the mimetype and always try to parse JSON. :param silent: Silence parsing errors and return ``None`` instead. :param cache: Store the parsed JSON to return for subsequent calls. """ if cache and self._cached_json[silent] is not Ellipsis: return self._cached_json[silent] if not (force or self.is_json): return None data = self._get_data_for_json(cache=cache) try: rv = json.loads(data) except ValueError as e: if silent: rv = None if cache: normal_rv, _ = self._cached_json self._cached_json = (normal_rv, rv) else: rv = self.on_json_loading_failed(e) if cache: _, silent_rv = self._cached_json self._cached_json = (rv, silent_rv) else: if cache: self._cached_json = (rv, rv) return rv 原因: # 导入环境 from flask import json # 测试数据集 s = '{"user_id":"zhangshan","plan_id":"m00_for_newOperator","operator_type":"map","fast_mode":false,"module_name":"test_map1","func_name":["map1"],"operator_resource":["test_map1"],"input_rows":[{"table_name":"id_t3","fields":[{"col_name":"e","data_type":"string"},{"col_name":"f","data_type":"boolean"},{"col_name":"g","data_type":"array<double>[1]"},{"col_name":"h","data_type":"binary"},{"col_name":"i","data_type":"array<binary>[1]"},{"col_name":"a","data_type":"string"},{"col_name":"b","data_type":"double"},{"col_name":"c","data_type":"long"},{"col_name":"d","data_type":"string"}],"values":[["2018-11-28 12:22:59.862776",true,[11],"f1 e4",["01 02"],"hello",3.14,1,"2018-11-28"]]}]}' # python 2.7 >>> json.loads(s) >>> {u'plan_id': u'm00_for_newOperator', u'input_rows': [{u'values': [[u'2018-11-28 12:22:59.862776', True, [11], u'f1 e4', [u'01 02'], u'hello', 3.14, 1, u'2018-11-28']], u'fields': [{u'col_name': u'e', u'data_type': u'string'}, {u'col_name': u'f', u'data_type': u'boolean'}, {u'col_name': u'g', u'data_type': u'array<double>[1]'}, {u'col_name': u'h', u'data_type': u'binary'}, {u'col_name': u'i', u'data_type': u'array<binary>[1]'}, {u'col_name': u'a', u'data_type': u'string'}, {u'col_name': u'b', u'data_type': u'double'}, {u'col_name': u'c', u'data_type': u'long'}, {u'col_name': u'd', u'data_type': u'string'}], u'table_name': u'id_t3'}], u'func_name': [u'map1'], u'fast_mode': False, u'operator_resource': [u'test_map1'], u'module_name': u'test_map1', u'user_id': u'zhangshan', u'operator_type': u'map'} # python 3.5 >>> json.loads(s) >>> {'user_id': 'zhangshan', 'plan_id': 'm00_for_newOperator', 'func_name': ['map1'], 'input_rows': [{'fields': [{'data_type': 'string', 'col_name': 'e'}, {'data_type': 'boolean', 'col_name': 'f'}, {'data_type': 'array<double>[1]', 'col_name': 'g'}, {'data_type': 'binary', 'col_name': 'h'}, {'data_type': 'array<binary>[1]', 'col_name': 'i'}, {'data_type': 'string', 'col_name': 'a'}, {'data_type': 'double', 'col_name': 'b'}, {'data_type': 'long', 'col_name': 'c'}, {'data_type': 'string', 'col_name': 'd'}], 'values': [['2018-11-28 12:22:59.862776', True, [11], 'f1 e4', ['01 02'], 'hello', 3.14, 1, '2018-11-28']], 'table_name': 'id_t3'}], 'module_name': 'test_map1', 'operator_resource': ['test_map1'], 'fast_mode': False, 'operator_type': 'map'} 做的项目同时支持python2和3的话,需要注意该问题。 求树的深度Case: 需要获取用户输入的多维数组的维度。该问题,即求解树的深度: // 获取数组的维度 getArrayDim: function getDeepth(array) { function sum(arr, flag) { return arr.reduce(function (total, item) { var totalDeepth ; if (Array.isArray(item)) { totalDeepth = sum(item, flag + 1); } return totalDeepth > total ? totalDeepth : total; }, flag) } return sum(array, 1); } 递归遍历多维数组Case: 用户输入一个多维数组,该数组的值类型已被固定(long, int, string…)。 // 判断array内部元素的值类型 checkArrayValueType: function checkArrayValue(type, realType, obj) { var res = { status: 0, msg: ""} ; for (var a in obj) { if (obj[a] instanceof Array) { res = checkArrayValue(type,obj[a]); //递归遍历 } else { if(typeof(obj[a]) === type){ continue } res.status = 1; res.msg = '数据项:' + obj[a] +', 不是' +realType + '类型; '; return res; } } return res; }, 检测Binary(hex)Case: 用户输入一段字符串,需要知道是否是hex型的binary。用户输入要求为:按空格分割为2位, 不能以0x开头,如:’f1 01 e4’, ‘01 02 03’。 // check for binary, 需要其他地方做split和trim checkBinary: function isBinary(item) { var validChars = "0123456789abcdef"; var isBinary = true; var char; for (var i = 0; i < item.length && isBinary == true; i++){ char = item.toLowerCase().charAt(i); if (validChars.indexOf(char) == -1) { isBinary = false; } } return isBinary; }, 结语记录了一些demo和实际开发的case, 有时间继续更。","categories":[{"name":"前端","slug":"前端","permalink":"http://guzhenping.com/categories/前端/"}],"tags":[{"name":"前端开发","slug":"前端开发","permalink":"http://guzhenping.com/tags/前端开发/"},{"name":"Vue.js","slug":"Vue-js","permalink":"http://guzhenping.com/tags/Vue-js/"}]},{"title":"大数据开发学习--Redis","slug":"大数据开发学习--Redis","date":"2018-11-02T09:04:49.000Z","updated":"2018-12-01T06:50:31.928Z","comments":true,"path":"2018/11/02/大数据开发学习--Redis/","link":"","permalink":"http://guzhenping.com/2018/11/02/大数据开发学习--Redis/","excerpt":"一 前言经常使用redis, 特地进行总结。","text":"一 前言经常使用redis, 特地进行总结。 二 基础安装下载安装包,或者: $ wget http://download.redis.io/releases/redis-3.2.0.tar.gz $ tar xzf redis-3.2.0.tar.gz $ cd redis-3.2.0 $ make 就是make命令~中间过程,报什么错搞定什么即可。 Ubuntu下比较简单,就是apt-get install redis-server 默认安装位置: 利用whereis redis 去找redis.conf ,需要修改是否后台运行daemonize (为yes)等属性 完成后,自动启动,可用命令:ps -aux| grep redis redis是依赖,因为我们用python,不得不装一个wrapper——redis-py:sudo pip install redis 启动要让Redis-server在后台运行! 1.简单的启动: 进到redis目录下的src文件夹下,输入:redis-server 2.后台启动: 先去找redis.conf文件,修改daemonize属性,从no变为yes进到redis目录下的src文件夹下,输入:redis-server & 3.通过配置文件启动: 需要配置启动文件,在Redis工程目录下有个redis.conf文件,修改: #修改daemonize为yes,即默认以后台程序方式运行(还记得前面手动使用&号强制后台运行吗)。 daemonize yes #可修改默认监听端口,别改了,万一你忘了 port 6379 #修改生成默认日志文件位置 logfile "/home/futeng/logs/redis.log" #配置持久化文件存放位置 dir /home/futeng/data/redisData 配置完以后,启动:还是 src目录下,redis-server ./redis.conf if 改了端口,使用redis-cli命令连接时,需要带上端口,比如:redis-cli -p xxxx [再次强调:仍在src目录下] 4.使用redis启动脚本,设置开机自启在生产环境中使用这种方式。 5.启动无密码验证的 redis-server --protected-mode no 设置连接数 redis-server --protected-mode no --maxclients 100000 客户端还是进到redis目录下的src文件夹下,输入:redis-cli 在弹出的交互界面测试下: redis> set foo bar OK redis> get foo "bar"","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Redis","slug":"Redis","permalink":"http://guzhenping.com/tags/Redis/"}]},{"title":"Redash开发指南","slug":"大数据开发学习--Redash","date":"2018-11-02T09:04:48.000Z","updated":"2019-02-14T03:09:38.984Z","comments":true,"path":"2018/11/02/大数据开发学习--Redash/","link":"","permalink":"http://guzhenping.com/2018/11/02/大数据开发学习--Redash/","excerpt":"一 Redash介绍Redash是一款融合多数据源的可视化查询工具,用于Ad-hoc查询再好不过。除了官方支持的数据源,还可以通过复用代码开发支持Kylin、Clickhouse、TiDB、Palo、Druid等。 写给刚刚接触Redash的同学看,大佬请绕路哈。","text":"一 Redash介绍Redash是一款融合多数据源的可视化查询工具,用于Ad-hoc查询再好不过。除了官方支持的数据源,还可以通过复用代码开发支持Kylin、Clickhouse、TiDB、Palo、Druid等。 写给刚刚接触Redash的同学看,大佬请绕路哈。 二 测试环境官方提供的测试环境启动方式redash开发文档 : https://redash.io/help-onpremise/ 自主搭建安装虚拟环境管理工具anaconda, 创建虚拟环境redash:conda create -n redash python=2.7,激活该环境:source activate redash。 创建虚拟环境(也可以不创建),激活后。利用pip安装redash所需要的包。 pip install -r requirements.txt # 程序基础依赖 pip install -r requirements_all_ds.txt # 所有数据库的依赖 在上述安装过程中大概率会出现安装错误(遇到过10多回)。请将出错的包单独安装。 安装npm,在主目录执行: npm install # 安装node模块 npm run build # 编译前端的东西 启动服务器: bin/run ./manage.py runserver --debugger --reload 启动celery的woker和调度器: ./bin/run celery worker --app=redash.worker --beat -Qscheduled_queries,queries,celery -c2 介绍下单独启动的方式:分开启动woker和调度器,调度器启动: bin/run celery worker --app=redash.worker -c4 -Qscheduled_queries --maxtasksperchild=10 -Ofair worker启动: bin/run celery worker --app=redash.worker --beat -c8 -Qqueries,celery --maxtasksperchild=10 -Ofair 根据机器机器实际情况,调节-c后的参数,用来指定启动多少个进程数量。 此处就不放PostgreSQL(Redash暂时只支持PG)和redis的环境准备了。搭好基础环境后,修改redash/settings/init.py文件的配置。 启动方式除了上面的启动方式,还推荐使用guncoin的启动方式。 启动服务器: # 前台启动 gunicorn -b 127.0.0.1:5000 --name redash -w 4 --max-requests 1000 redash.wsgi:app # 后台启动 nohup /home/hadoop/anaconda3/envs/redash/bin/python /home/hadoop/anaconda3/envs/redash/bin/gunicorn -b 0.0.0.0:5000 --name redash -w 4 --max-requests 1000 redash.wsgi:app >> redash.log & 没有gunicorn命令的,需要装Python包。 启动调度器进程: # 前台启动 bin/run celery worker --app=redash.worker -c4 -Qscheduled_queries --maxtasksperchild=10 -Ofair # 后台启动 nohup /home/hadoop/anaconda3/envs/redash/bin/celery worker --app=redash.worker -c4 -Qscheduled_queries --maxtasksperchild=10 -Ofair >> schedular.log & 启动worker进程: # 前台启动 bin/run celery worker --app=redash.worker --beat -c8 -Qqueries,celery --maxtasksperchild=10 -Ofair # 后台启动 nohup /home/hadoop/anaconda3/envs/redash/bin/celery worker --app=redash.worker --beat -c8 -Qqueries,celery --maxtasksperchild=10 -Ofair >> worker.log & 也可以用nginx做下代理,配置文件: worker_processes 4; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 3600; upstream rd_servers { server 127.0.0.1:5000; } server { server_tokens off; listen 5123 default; access_log /var/log/nginx/rd.access.log; gzip on; gzip_types *; gzip_proxied any; location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://rd_servers; } } } 启动NGINX: cd /usr/local/nginx/sbin sudo ./nginx 三 Redash VS Superset关于Redash:https://redash.io/; 与Superset的区别与联系:https://www.zhihu.com/question/60369195/answer/258298127。 这个答案里面有对比两边代码的描述,二次开发上,Redash可能更胜一筹。 四 结语关于Redash的环境就写到这,后续继续增加Redash相关的技术文章。如有问题讨论,欢迎来我的小圈子~","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Redash","slug":"Redash","permalink":"http://guzhenping.com/tags/Redash/"}]},{"title":"大数据开发学习(Flink)","slug":"大数据开发学习--Flink","date":"2018-10-29T09:04:48.000Z","updated":"2018-11-30T02:46:37.477Z","comments":true,"path":"2018/10/29/大数据开发学习--Flink/","link":"","permalink":"http://guzhenping.com/2018/10/29/大数据开发学习--Flink/","excerpt":"前言Flink在实时流处理领域越来越热,以阿里为首的企业正在投入更多的资源。在实际工作中也遇到了流处理的场景,特此学习一下。","text":"前言Flink在实时流处理领域越来越热,以阿里为首的企业正在投入更多的资源。在实际工作中也遇到了流处理的场景,特此学习一下。 Demo制作先看一个demo: WordCount.java package com.gzp.batch; import org.apache.flink.api.common.functions.FlatMapFunction; import org.apache.flink.api.java.DataSet; import org.apache.flink.api.java.ExecutionEnvironment; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.api.java.utils.ParameterTool; import org.apache.flink.util.Collector; public class WordCount { public static void main(String[] args) throws Exception { final ParameterTool params = ParameterTool.fromArgs(args); final ExecutionEnvironment env = ExecutionEnvironment.createCollectionsEnvironment(); DataSet<String> text = WordCountData.getDefaultTextLineDataSet(env); DataSet<Tuple2<String, Integer>> counts = // split up the lines in pairs (2-tuples) containing: (word,1) text.flatMap(new Tokenizer()) .groupBy(0) .sum(1); counts.print(); } public static final class Tokenizer implements FlatMapFunction<String, Tuple2<String, Integer>> { @Override public void flatMap(String value, Collector<Tuple2<String, Integer>> out) { // emit the pairs for (String token : value.toLowerCase().split("\\\\W+")) { if (token.length() > 0) { out.collect(new Tuple2<>(token, 1)); } } } } } WordCountData.java package com.gzp.batch; import org.apache.flink.api.java.DataSet; import org.apache.flink.api.java.ExecutionEnvironment; public class WordCountData { public static final String[] WORDS = new String[] { "To be, or not to be,--that is the question:--", "Whether 'tis nobler in the mind to suffer", "The slings and arrows of outrageous fortune", "Or to take arms against a sea of troubles,", "And by opposing end them?--To die,--to sleep,--", "No more; and by a sleep to say we end", "The heartache, and the thousand natural shocks", "That flesh is heir to,--'tis a consummation", "Devoutly to be wish'd. To die,--to sleep;--", "To sleep! perchance to dream:--ay, there's the rub;", "For in that sleep of death what dreams may come,", "When we have shuffled off this mortal coil,", "Must give us pause: there's the respect", "That makes calamity of so long life;", "For who would bear the whips and scorns of time,", "The oppressor's wrong, the proud man's contumely,", "The pangs of despis'd love, the law's delay,", "The insolence of office, and the spurns", "That patient merit of the unworthy takes,", "When he himself might his quietus make", "With a bare bodkin? who would these fardels bear,", "To grunt and sweat under a weary life,", "But that the dread of something after death,--", "The undiscover'd country, from whose bourn", "No traveller returns,--puzzles the will,", "And makes us rather bear those ills we have", "Than fly to others that we know not of?", "Thus conscience does make cowards of us all;", "And thus the native hue of resolution", "Is sicklied o'er with the pale cast of thought;", "And enterprises of great pith and moment,", "With this regard, their currents turn awry,", "And lose the name of action.--Soft you now!", "The fair Ophelia!--Nymph, in thy orisons", "Be all my sins remember'd." }; public static DataSet<String> getDefaultTextLineDataSet(ExecutionEnvironment env) { return env.fromElements(WORDS); } } 上述代码核心是将一段文本按单词切割,统计词频。flatMap()调用udf将文本切割并生成结构化数据,按单词分组后再sum。 不得不说,java写的flink task代码太长…. Core Concepts#### Flink Case命令行使用简介 参考资料、 核心概念翻译 Flink架构、原理与部署测试详解 简单的Scala Demo应用: flink demo 关于动态表: 在数据流中使用SQL查询:Apache Flink中的动态表的持续查询","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"实时流","slug":"实时流","permalink":"http://guzhenping.com/tags/实时流/"},{"name":"Flink","slug":"Flink","permalink":"http://guzhenping.com/tags/Flink/"}]},{"title":"Kylin应用篇","slug":"大数据开发学习--Kylin应用","date":"2018-10-26T09:04:48.000Z","updated":"2018-11-30T02:56:21.818Z","comments":true,"path":"2018/10/26/大数据开发学习--Kylin应用/","link":"","permalink":"http://guzhenping.com/2018/10/26/大数据开发学习--Kylin应用/","excerpt":"简介Kylin是一款处理海量数据,提供SQL和多维度分析的OLAP工具。Kylin用于处理hadoop/spark场景下大量数据的预聚合,用户可自定义数据模型用于解决超过100亿+条记录的查询。","text":"简介Kylin是一款处理海量数据,提供SQL和多维度分析的OLAP工具。Kylin用于处理hadoop/spark场景下大量数据的预聚合,用户可自定义数据模型用于解决超过100亿+条记录的查询。 常见问题Not Support 建表或构建模型时,请勿使用中文列名。 不支持临时创建新列的统计 原表有两个字段a和b,通过concat进行拼接,然后做count()或者count(distinct *)。Kylin并不支持上述做法,因为无法命中相关的cube。 不能统计下述例子: count(distinct concat(cast(a as varchar),b)) select count(distinct concat(cast(a as varchar),b)) from table_a where dt = '2018-05-01' Cube命中所有的SQL需要命中相关Cube才可以使用。如果不是使用姿势问题,请联系管理员构建新的Cube。 在join时,需要用事实表join维度表,负责容易出现: No realization found for OLAPContext, CUBE_NOT_READY, CUBE_NOT_READY, CUBE_NOT_READY, MODEL_UNMATCHED_JOIN, MODEL_UNMATCHED_JOIN 在写子查询时,不能将事实表写在查询中,Cube可能无法命中。 调度脚本import datetime, requests auth_str = "Basic YWRtaW46S1lMSU6=" url_str = "http://xxxxx.com:7070/kylin/api" def auth(): """ 用户认证 :return: """ url = "{url_str}/user/authentication".format(url_str=url_str) payload = "=" headers = { 'Content-Type': "application/x-www-form-urlencoded", 'Authorization': auth_str, 'Cache-Control': "no-cache" } response = requests.request("POST", url, data=payload, headers=headers) print(response.text) def get_cube(): """ 获取cube信息 :return: """ url = "{url_str}/cubes".format(url_str=url_str) querystring = {"cubeName": "test_join"} headers = { 'Cache-Control': "no-cache", 'Authorization': auth_str } response = requests.request("GET", url, headers=headers, params=querystring) print(response.json()) def build_cube(cube_name, start_date, end_date, build_type): """ 构建指定cube startTime 和 endTime 应该是utc时间。 buildType 可以是 BUILD 、 MERGE 或 REFRESH。 - BUILD 用于构建一个新的segment, - REFRESH 用于刷新一个已有的segment, - MERGE 用于合并多个已有的segment生成一个较大的segment :return: """ url = "{url_str}/cubes/{cube_name}/rebuild".format(cube_name=cube_name, url_str=url_str) start_stamp = int(datetime.datetime.strptime(start_date, '%Y-%m-%d %H:%M:%S').timestamp() * 1000) end_stamp = int(datetime.datetime.strptime(end_date, '%Y-%m-%d %H:%M:%S').timestamp() * 1000) payload = "{\\"startTime\\": %d, \\"endTime\\": %d, \\"buildType\\": \\"%s\\"}" % (start_stamp, end_stamp, build_type) headers = { 'Content-Type': "application/json", 'Authorization': auth_str, 'Cache-Control': "no-cache" } response = requests.request("PUT", url, data=payload, headers=headers) print(response.json()) if __name__ == '__main__': cube_name = "test_api" start_date = '2018-05-01 04:00:00' end_date = '2018-05-01 08:00:00' build_type = 'BUILD' build_cube(cube_name, start_date, end_date, build_type)","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"大数据开发","slug":"大数据开发","permalink":"http://guzhenping.com/tags/大数据开发/"},{"name":"OLAP","slug":"OLAP","permalink":"http://guzhenping.com/tags/OLAP/"},{"name":"Kylin","slug":"Kylin","permalink":"http://guzhenping.com/tags/Kylin/"}]},{"title":"Clickhouse 高级SQL","slug":"Clickhouse 高级SQL","date":"2018-07-27T09:04:48.000Z","updated":"2018-11-30T02:43:37.103Z","comments":true,"path":"2018/07/27/Clickhouse 高级SQL/","link":"","permalink":"http://guzhenping.com/2018/07/27/Clickhouse 高级SQL/","excerpt":"","text":"前言测试数据集: ca cb cc A W 1 A W 2 B X 1 B Z 2 B Z 4 按最大/最小值/TOP1去重按ca和cb取cc最小值取值: select ca, cb, min(cc) from table group by ca, cb column_A column_B column_C A W 1 B X 1 B Z 2 取最大,则使用max()函数。取一组值的TOP1也是同理。 按列合并多行(多->少)select ca, cb, groupUniqArray(cc) from table group by ca, cb column_A column_B column_C A W [2,1] B X [1] B Z [4,2] 这是一种分组的概念,将相同的数据放在一起,需要被统计的数据放在array数据类型中。统计array,可以使用length(),获取数组长度,相当于分组count 此外,groupArray也可以满足需求。 分组排序后取TopN SELECT ca, groupArray(1)(cc) FROM ( SELECT * FROM table ORDER BY ca, cb, cc ) GROUP BY ca column_A column_B column_C A W [1] B X [1] B Z [2] 以上是按cc列的值升序去top1。通过order by x改变顺序,再用groupArry(N)()函数处理获取top值。","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"OLAP","slug":"OLAP","permalink":"http://guzhenping.com/tags/OLAP/"},{"name":"Clickhouse","slug":"Clickhouse","permalink":"http://guzhenping.com/tags/Clickhouse/"}]},{"title":"Redash Model源码分析","slug":"Redash Model整理分析","date":"2018-07-26T09:04:48.000Z","updated":"2018-11-30T02:43:09.000Z","comments":true,"path":"2018/07/26/Redash Model整理分析/","link":"","permalink":"http://guzhenping.com/2018/07/26/Redash Model整理分析/","excerpt":"前言掌握Redash执行原理,对于深度的二次开发至关重要。","text":"前言掌握Redash执行原理,对于深度的二次开发至关重要。 query功能SQL查询是Redash的核心功能之一。通常情况下,用户在前端会生成如下参数: query.data_source:下列列表中的数据源,必须选 parameter_values:用户自定义的参数值,可无 query.query_text:用户自定义的SQL文本,必须写 query.id:前端自动生成的查询id,系统生成 在上述条件具备后,将会调用Redash API: /api/queries/进行任务提交。 所有任务将会被redash.handlers.query_result.run_query()方法接受处理。此处将进行参数校验,语法解析,任务传输等方面的处理。 多数的查询场景,先将SQL文本hash后,去缓存队列和后台数据库(Redash的Postgre)进行寻找,找不到再发给celery让worker执行该SQL。 负责具体执行:redash.models.QueryResult()类实例 和redash.tasks.queries.enqueue_query()方法。前者:负责查现有的结果;后者:立即执行相关SQL语句。 在enqueue_query()方法里,先连Redis,将任务加入队列,持续监听查询结果。 核心代码如下: while try_count < 5: try_count += 1 pipe = redis_connection.pipeline() try: pipe.watch(_job_lock_id(query_hash, data_source.id)) job_id = pipe.get(_job_lock_id(query_hash, data_source.id)) if job_id: logging.info("[%s] Found existing job: %s", query_hash, job_id) job = QueryTask(job_id=job_id) if job.ready(): logging.info("[%s] job found is ready (%s), removing lock", query_hash, job.celery_status) redis_connection.delete(_job_lock_id(query_hash, data_source.id)) job = None if not job: pipe.multi() time_limit = None if scheduled_query: queue_name = data_source.scheduled_queue_name scheduled_query_id = scheduled_query.id else: queue_name = data_source.queue_name scheduled_query_id = None time_limit = settings.ADHOC_QUERY_TIME_LIMIT result = execute_query.apply_async(args=(query, data_source.id, metadata, user_id, scheduled_query_id), queue=queue_name, time_limit=time_limit) job = QueryTask(async_result=result) tracker = QueryTaskTracker.create( result.id, 'created', query_hash, data_source.id, scheduled_query is not None, metadata) tracker.save(connection=pipe) logging.info("[%s] Created new job: %s", query_hash, job.id) pipe.set(_job_lock_id(query_hash, data_source.id), job.id, settings.JOB_EXPIRY_TIME) pipe.execute() break except redis.WatchError: continue 以上是Query功能的分析。主要组件: celery分布式框架 redis缓存和任务队列 postgre后台DB。 相关功能的设计比较中规中矩,报错异常的检测较少,后续值得优化。 Model层model层是Redash的设计核心,每个类对应后台数据库一张表和一个功能。 access_permission, 权限表 alembic_version,版本号 alert_subscriptions,报警描述 alerts,报警列表 api_keys, api key管理 changes,升级改动 dashboards,报表存储 data_source_groups,每个组对应的数据源 data_sources,所有数据源 events,后台日志 groups,所有分组列表 notification_destinations,报警的模板和目的地 organizations, 组织 queries,所有queris query_results,query的结果,另一种缓存 query_snippets,SQL的评论 users, 用户已列表 visualizations,可视化图表存储 widgets,可视化控件","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"},{"name":"可视化","slug":"大数据/可视化","permalink":"http://guzhenping.com/categories/大数据/可视化/"}],"tags":[{"name":"大数据开发","slug":"大数据开发","permalink":"http://guzhenping.com/tags/大数据开发/"},{"name":"Redash","slug":"Redash","permalink":"http://guzhenping.com/tags/Redash/"}]},{"title":"大数据开发学习(Kudu)","slug":"大数据开发学习--Kudu","date":"2018-05-25T09:04:48.000Z","updated":"2018-11-30T02:57:14.186Z","comments":true,"path":"2018/05/25/大数据开发学习--Kudu/","link":"","permalink":"http://guzhenping.com/2018/05/25/大数据开发学习--Kudu/","excerpt":"","text":"前言kudu,作为OLAP工具十分强劲。本文记录了笔者对其学习和使用的过程。 注明:这篇文章是研究Kudu在OLAP的场景,不准备讨论其他Case。 安装与启动最新版安装:Installing Apache Kudu 推荐使用VM进行快速体验,Apache Kudu Quickstart。 当需要使用kudu client功能时,需要安装:kudu-client、kudu-client-devel这两个C++的库。 常规使用教学Demo参见:Apache Kudu Quickstart 以上内容是对静态数据的使用,如果是一条实时的数据流,则采用kudu API的方式。需要去做定制开发。 架构体系kudu架构: 关于运行原理的文章,推荐:Kudu设计原理初探 OLAP服务作为OLAP服务,ETL环节的处理至关重要。以下列举了小米公司的场景: 对比: 可以看出,除了日志数据外,线上业务数据都可以实时同步到kudu里。基于kudu对外提供OLAP服务,数据的实时性非常可观。 在小米,采用impala作为查询(计算)引擎,但是网上也有presto on kudu的组件可供选型(传送门)。 使用场景微店: Kudu+Impala介绍 Kudu的Schema表结构设计 参考资料运行原理:kudu内部机制 关于Kudu的介绍: Introducing Apache Kudu 基于Kudu搭建OLAP工具:小米:使用Kudu搭建OLAP服务 基于Kudu的实际应用:使用Spark Streaming + Kudu + Impala构建一个预测引擎","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"大数据开发","slug":"大数据开发","permalink":"http://guzhenping.com/tags/大数据开发/"},{"name":"OLAP","slug":"OLAP","permalink":"http://guzhenping.com/tags/OLAP/"},{"name":"Kudu","slug":"Kudu","permalink":"http://guzhenping.com/tags/Kudu/"}]},{"title":"数据仓库学习(概念篇)","slug":"数据仓库学习--概念篇","date":"2018-05-24T09:04:48.000Z","updated":"2018-12-18T03:03:06.769Z","comments":true,"path":"2018/05/24/数据仓库学习--概念篇/","link":"","permalink":"http://guzhenping.com/2018/05/24/数据仓库学习--概念篇/","excerpt":"前言写点数据仓库(DW)的一些常用知识,架构等方面。加深基本功,保持进阶的心。","text":"前言写点数据仓库(DW)的一些常用知识,架构等方面。加深基本功,保持进阶的心。 典型架构 基本概念 数据仓库 数据仓库是一个面向主题的、集成的、非易失的(nonvolatile)、随时间变化的(time-variant)用来支持管理人员决策的数据集合。 —-William H.Inmon 以上也是数据仓库区别于业务系统的4个特征。 OLAP 联机分析处理(On-line Analytical Processing),该概念由数据库创始人E. F. Codd于1993年提出。OLAP理事会(OLAP Council)进一步完善:OLAP是一种软件技术,它使分析人员、经理和执行官能够迅速、一致、交互的从各方面观察信息,以达到深入理解数据的目的。 OLTP 联机事务处理(On-line Transaction Processing) ODS 操作数据存储(Operational Data Store, 简称ODS)。在数据仓库系统中,ODS存储了原始数据经过集成统一后的数据。 DW 数据仓库(Data Warehouse,简称DW)。在数据仓库系统中,DW数据库存储了整个企业的所有历史数据,是狭义上的数据仓库。DW数据库需要满足企业数据分析的各种需求,以及各个部门建设数据集市的需求,通常存储企业的基础数据和通用数据。 数据集市(Data Market) 数据集市是指针对特定部门和主题的小型数据仓库,数据从DW中获取。 ETL ETL(Extract,Transform,Load)表示抽取、转换和装载,数据从多个同构或异构的数据源抽取出来,经过自定义的转换操作,最终装载进入目标表的过程叫做一次ETL。ETL是数据进入ODS、DW、Data Market的主要方式。 建模方法 维度建模法(Dimensional Modeling) Ralph Kimball主张。 维度建模以分析决策的需求为出发点构建模型,一般有较好的大规模复杂查询的响应性能,更直接面向业务,典型的代表是我们比较熟知的星形模型,以及在一些特殊场景下适用的雪花模型。 实体-关系建模(Entity-Relationship Medeling) 实体-关系建模也叫做第三范式建模(Third Normal Form, 3NF),是William H.Inmon主张的一种数据仓库建模方法。 用实体加关系描述的数据模型描述企业业务架构,在范式理论上符合3NF,其是站在企业角度进行面向主题的抽象,而不是针对某个具体业务流程的,它更多是面向数据的整合和一致性治理,正如Inmon所希望达到的“single version of the truth”。 From: http://lxw1234.com/archives/2018/01/890.htm 数据与建模OLAP中roll-up和drill-down和slicing? 真正好的建模,应该契合这些功能转换。见过Tableau做的很好,但不算是OLAP系统自带的功能。 数仓进化史比较推荐这篇文章:从数据仓库到大数据,数据平台这25年是怎样进化的? 在架构方面,偏理论一点的文章:大数据分析的下一代架构–IOTA 参考资料 hadoop HDFS常用文件操作命令 HDFS 文件操作命令","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"数据仓库","slug":"数据仓库","permalink":"http://guzhenping.com/tags/数据仓库/"}]},{"title":"大数据开发学习(Clickhouse)","slug":"大数据开发学习--Clickhouse","date":"2018-05-24T09:04:48.000Z","updated":"2018-11-30T02:10:54.811Z","comments":true,"path":"2018/05/24/大数据开发学习--Clickhouse/","link":"","permalink":"http://guzhenping.com/2018/05/24/大数据开发学习--Clickhouse/","excerpt":"前言1年前,用过Greenplum,这是第一次接触MPP结构的OLAP系统。今天市面上常见的MPP架构工具,还有Clickhouse和Palo等。(当然,SQL on Hadoop体系的Presto和Impala也算是MPP结构,只是数据存储方面没有自己的东西,都是依赖hdfs,mysql等。) 工作需要,对Clickhouse进行学习。","text":"前言1年前,用过Greenplum,这是第一次接触MPP结构的OLAP系统。今天市面上常见的MPP架构工具,还有Clickhouse和Palo等。(当然,SQL on Hadoop体系的Presto和Impala也算是MPP结构,只是数据存储方面没有自己的东西,都是依赖hdfs,mysql等。) 工作需要,对Clickhouse进行学习。 安装与启动Ubuntu上比较好安装,但是一般公司用的服务器都是Centos。这里只讨论在Centos 7的安装方式,centos 6请在Altinity公司提供的下载界面中自行寻找。 安装命令如果是装54362版本的包,其余所有依赖都需要是一致的。 准备源依赖,由Altinity公司提供: curl -s https://packagecloud.io/install/repositories/Altinity/clickhouse/script.rpm.sh | sudo bash server-common: sudo yum install clickhouse-server-common-1.1.54362-1.el7.x86_64 sudo yum install clickhouse-server-common-1.1.54380-1.el7.x86_64 server: sudo yum install clickhouse-server-1.1.54362-1.el7.x86_64 sudo yum install clickhouse-server-1.1.54380-1.el7.x86_64 client: sudo yum install clickhouse-client-1.1.54362-1.el7.x86_64 sudo yum install clickhouse-client-1.1.54380-1.el7.x86_64 关于Altinity公司的其他版本,可访问这里下载。 以上安装如有疑问,可以使用下方安装方式: https://github.com/red-soft-ru/clickhouse-rpm 启动server端: sudo /etc/rc.d/init.d/clickhouse-server start client端: clickhouse-client 使用经验常用SQL-- 查看集群情况 select * from system.clusters; -- 查看分区情况 select partition, name, rows from system.parts; MergeTree选择engine时,尽量用merge tree.MergeTree引擎支持以主键和日期作为索引,提供实时更新数据的可能性。这是Clickhouse中最先进的表引擎。 数据表将数据分割为小的索引块进行处理。每个索引块之间依照主键排序。每个索引块记录了指定的开始日期和结束日期。插入数据时,MergeTree会对数据进行排序,以保证存储在索引块中的数据有序。当写入新数据时,会放在新的文件夹下,索引块之间的合并过程会在系统后台定期自动执行。MergeTree引擎会选择几个相邻的索引块进行合并,然后对二者合并排序。 Distributed相当于数据库的视图,并不存储数据,而是用来做分布式的写入和查询,与其他引擎结合使用。 一种集群拓扑结构: 实际问题 关键字大小写敏感,对强转支持不太好。 特性上不支持事务,不支持update/delete。 关于分布式 在分布式表上执行count()时,发现结果不一致。 暂无解决办法。问题:https://github.com/yandex/ClickHouse/issues/1443 查询时,内存超出限制: Progress: 4.84 million rows, 42.70 MB (45.34 million rows/s., 399.59 MB/s.) 87% Received exception from server: Code: 241. DB::Exception: Received from localhost:9000, ::1. DB::Exception: Memory limit (for query) exceeded: would use 12.15 GiB (attempt to allocate chunk of 4294967296 bytes), maximum: 9.31 GiB. 0 rows in set. Elapsed: 48.023 sec. Processed 4.84 million rows, 42.70 MB (100.89 thousand rows/s., 889.21 KB/s.) Progress: 4.89 million rows, 43.81 MB (23.23 thousand rows/s., 208.09 KB/s.) 88% Received exception from server: Code: 241. DB::Exception: Received from localhost:9000, ::1. DB::Exception: Memory limit (for query) exceeded: would use 80.15 GiB (attempt to allocate chunk of 17179869184 bytes), maximum: 74.51 GiB. 3668208133 rows in set. Elapsed: 235.536 sec. Processed 4.89 million rows, 43.81 MB (20.77 thousand rows/s., 186.00 KB/s.) Received exception from server (version 1.1.54362): Code: 241. DB::Exception: Received from localhost:9000, ::1. DB::Exception: Memory limit (for query) exceeded: would use 96.14 GiB (attempt to allocate chunk of 17179869184 bytes), maximum: 93.13 GiB. 8816716667 rows in set. Elapsed: 428.543 sec. Processed 4.89 million rows, 43.81 MB (11.41 thousand rows/s., 102.23 KB/s.) 当处理的数据集超过设定的阈值以后,会触发限制,并返回已经处理好的结果。 解决办法:修改max_memory_usage的值(user.xml) 不同的节点可以处理设置不同。在高并发分流的时候,尽量把query分摊到各个机器上,否则会将某一节点资源耗尽。 这个问题导致Clickhouse的高并发特性很差。在前端暴露端口时,不能单独暴露一个host,需要做成有命名空间的方式,或者有个query平衡器的机制。 解决办法:就上述问题,发现一个公司采用如下架构: 即:域名轮询。 SQL 实战第一步建表: create table test_analysis (created_at DateTime, dt Date, user String, page_id String ) ENGINE=MergeTree(dt, (user, dt), 8192); 插入测试数据: insert into table test_analysis Format Values ('2018-4-24 18:45','2018-4-24','A','首页'), ('2018-4-24 18:46','2018-4-24','A','购物车'), ('2018-4-24 18:45','2018-4-24,'B','首页'), ('2018-4-24 18:48','2018-4-24','B','商品详情'), ('2018-4-24 18:49','2018-4-24','B','购物车'), ('2018-4-24 18:46','2018-4-24','C','商品详情'); 第二步,建立模型: SELECT `user`, created_at, page_id, gap1/60 AS "与第一个动作的间隔时间", if(gap1 == 0, 0, runningDifference(gap1)/60) AS "与上一个动作的间隔时间" FROM (SELECT `user`, created_at, fist_created_at, page_id, created_at-fist_created_at AS gap1 FROM test_analysis ANY LEFT JOIN (SELECT `user` , min(created_at) AS fist_created_at FROM test_analysis GROUP BY `user`) using(`user`)) AS t 获得的结果: clickhouse 实战 参考资料官方文档What is ClickHouse 官方写的体系结构文章:Overview of ClickHouse architecture 翻译:ClickHouse 内部架构介绍 配置文件ClickHouse相关配置剖析 ClickHouse的分布式引擎 用户权限ClickHouse 用户名密码设置 ClickHouse之访问权限控制 引擎介绍ClickHouse MergeTree引擎介绍 ClickHouse Distribute 引擎深度解读 实时大数据分析引擎ClickHouse介绍 数据同步kafka->Clickhouse:Hangout with ClickHouse mysql->Clickhouse:使用ClickHouse一键接管MySQL数据分析 杂文奶clickhouse的文章:ClickHouse Beijing Meetup-数据分析领域的黑马-ClickHouse-新浪-高鹏","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"OLAP","slug":"OLAP","permalink":"http://guzhenping.com/tags/OLAP/"},{"name":"Clickhouse","slug":"Clickhouse","permalink":"http://guzhenping.com/tags/Clickhouse/"}]},{"title":"Redash权限管理","slug":"Redash 权限管理","date":"2018-05-21T09:04:48.000Z","updated":"2018-11-30T02:42:24.499Z","comments":true,"path":"2018/05/21/Redash 权限管理/","link":"","permalink":"http://guzhenping.com/2018/05/21/Redash 权限管理/","excerpt":"前言Reash是一个数据查询平台,必定会涉及权限管理。主要由3个概念:组(group),用户(user), 数据源(data source)。 group在最上层,一个group对应多个user 和 data source。反之也可行,但不利于权限的管理。 权限功能主要基于组(group)和所属数据源(data source)来控制。一个用户(user)必须属于一个或多个组,当新用户进入时,该用户默认在“default组”。在新增一个数据源时,该数据源默认归属于”default组”。","text":"前言Reash是一个数据查询平台,必定会涉及权限管理。主要由3个概念:组(group),用户(user), 数据源(data source)。 group在最上层,一个group对应多个user 和 data source。反之也可行,但不利于权限的管理。 权限功能主要基于组(group)和所属数据源(data source)来控制。一个用户(user)必须属于一个或多个组,当新用户进入时,该用户默认在“default组”。在新增一个数据源时,该数据源默认归属于”default组”。 数据源数据源有两个权限设置: Full access,即:该组的用户可操作被保存的查询(可以改SQL源码),创建一个新的查询 View Only access,即:该组的用户只能阅读被保存的查询(看不到SQL源码)及其结果 对于不需要写SQL取结果的用户群来说,应该与需要写SQL取结果的用户群体区分在不同组(group)中。比如:基础架构查看报表组,基础架构制作报表组。 如果想对一个组的用户做table级别的查询限制,官方提供的方案: The idea is to leverage your database’s security model and hence create a user with access to the tables/columns you want to give access to. Create a data source that’s using this user and then associate it with a group of users who need this level of access. 翻译过来:在建立数据源时,配置一个带有权限控制的数据库用户即可。比如,连接mysql时,用一个只能查询测试db/table的用户名进行连接。把这个数据源赋给某个组,然后该组的所有用户,只能看到这个数据源里的测试db/table。 组组可设置的权限有: admin/super_admin,管理员/超级管理员,可用所用功能 create_dashboard,创建dashboard create_query,创建SQL查询 edit_dashboard,编辑自己/别人的dashboard edit_query,编辑自己/别人的SQL查询 view_query,查看已经存在的SQL view_source,查看SQL源码 execute_query,执行SQL list_users,看到所有用户 schedule_query,设置定时刷新 list_dashboards,看到所用的dashboard list_alerts,看到所有的提醒任务 list_data_sources,看到所有的数据源 将以上权限赋给不同的组,每个组的用户就可以实现不同的功能。Redash不强调对用户做太多的权限控制,因为一个用户必须要归属于一个组。所以,对组做现在即可。","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"},{"name":"可视化","slug":"大数据/可视化","permalink":"http://guzhenping.com/categories/大数据/可视化/"}],"tags":[{"name":"大数据开发","slug":"大数据开发","permalink":"http://guzhenping.com/tags/大数据开发/"},{"name":"Redash","slug":"Redash","permalink":"http://guzhenping.com/tags/Redash/"}]},{"title":"大数据开发学习(Kylin)","slug":"大数据开发学习--Kylin","date":"2018-01-24T09:04:48.000Z","updated":"2018-11-30T02:55:33.282Z","comments":true,"path":"2018/01/24/大数据开发学习--Kylin/","link":"","permalink":"http://guzhenping.com/2018/01/24/大数据开发学习--Kylin/","excerpt":"一 Kylin介绍kylin是一款OLAP工具,背靠Hadoop,HBase,Spark,Kafka等大山,提供神奇体验。","text":"一 Kylin介绍kylin是一款OLAP工具,背靠Hadoop,HBase,Spark,Kafka等大山,提供神奇体验。 Kylin 安装与启动除了常规环境,还需要hadoop的historyjobserver启动。 具体参见官网,启动较为容易。 度量计算目前提供9种计算方法。 sum min max count count_distinct top_n raw extended_column percentile 建模心得在通过星形模型建立事实表+维度表的过程中,操作比较复杂。但是,通过view的方式,就比较简单。 view建模的方式,所有结果都在一张表里,只需要对该表进行维度和度量的划分即可。 建模结果测试结果1: 测试结果2: 实际问题问题1:在同一个project里的Insight,可以看到所用cube的维度,但是不能共用,会报:No model found for rel 问题2:kylin不支持复杂的列,比如map,array类型。问题参见:https://mail-archives.apache.org/mod_mbox/kylin-dev/201512.mbox/%[email protected]%3E 解决方法:创建该表的视图,剔除复杂列 问题3:kylin建model时,想做增量构建,在指定某列时间作为partition时,该列内容应该满足‘yyyy-MM-dd HH:mm:ss’的样子,不要使用unix_timestamp的类型。否则,构建后cube大小为零。 问题4:kylin建cube所用的字段最好不要采用kylin 关键字,例如:year, month, day, hour等。否则写SQL时,不太友好。例如: 想查一段 简单的pv和uv SQL, select platform, year, month, day ,count(*) as pv count(distinct guid) as uv from kylin_tracking_view group by platform, year, month, day 在Kylin的查询界面中,应当这么写: select platform, "YEAR", "MONTH", "DAY", count(*) as pv, count(distinct guid) as uv from kylin_tracking_view group by platform, "YEAR", "MONTH", "DAY" 可以看出,关键词必须全部大写,且被双引号(必须是双引号,单引是自定义常量)包住。 建议提前规范好数据源,免得造成巨大的返工。 问题5:build维度过不去具体报错: [BadQueryDetector] service.BadQueryDetector:160 : System free memory less than 100 MB. 0 queries running 暂无解决办法。 这个问题可能是,kylin维度较多,把regionserver搞成僵死,进而导致的。 问题6: kylin 不支持中文列名。kylin在创建中间表时,会使用中文+英文的方式做拼接,这个过程会报错。 问题7:kylin 2.2.0 版本用户相关比较难搞。默认账户 ADMIN有问题,暂无好的解决办法。一种解决方式:升级到 2.3.0版本,修复了230个bug,对用户更加友好。 问题8:kylin 2.3以后采用spark 2.1.2版本,需要进行相关的配置。解决方式:https://kylin.apache.org/docs23/tutorial/cube_spark.html Kylin的一些问题基础关于Kylin的架构和原理,有图可供参考:Kylin 的架构和原理 Kylin比较详细的介绍:Kylin对大数据量的多维分析 关于Kylin Cube构建原理,落地到HBase的过程: Apache Kylin Cube 构建原理 关于Kylin SQL 语法:SQL language 关于Kylin的关键字:关键字源码 关于一次正常查询的运行原理:Kylin进阶之路 Kylin使用calcite做sql解析,可以参考calcite的语法文档:https://calcite.apache.org/ 维度问题关于维度的聚合组中各个含义,请参考https://kylin.apache.org/blog/2016/02/18/new-aggregation-group/ Kylin Mandatory Dimension(必要维度):【技术帖】Apache Kylin高级设置: 必要维度 (Mandatory Dimension)原理解析 Kylin Hierarchy Dimension(层级维度):【技术帖】Apache Kylin 高级设置:层级维度(Hierarchy Dimension)原理解析 Kylin Joint Dimension(联合维度):【技术帖】Apache Kylin 高级设置:联合维度(Joint Dimension)原理解析 Kylin Aggregation Group(聚合组):【技术帖】Apache Kylin 高级设置:聚合组(Aggregation Group)原理解析 Server端配置负载均衡:【技术贴】如何部署Apache Kylin集群实现负载均衡? 进阶优化Kylin cube算法:Apache Kylin的快速数据立方体算法——概述 Kylin cube介绍:Kylin使用之创建Cube和高级设置 别人的Cube优化案例:Apache Kylin cube优化指南 优化案例:Apache Kylin 深入Cube和查询优化 提升Cube API开发脚本触发增量更新:Kylin定时增量build Kylin+superset 可视化方案案例:Kylin初体验总结 参考很不错的博文: Apache Kylin 框架介绍 Apache Kylin在美团数十亿数据OLAP场景下的实践","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"大数据开发","slug":"大数据开发","permalink":"http://guzhenping.com/tags/大数据开发/"},{"name":"OLAP","slug":"OLAP","permalink":"http://guzhenping.com/tags/OLAP/"},{"name":"Kylin","slug":"Kylin","permalink":"http://guzhenping.com/tags/Kylin/"}]},{"title":"数据仓库学习(维度建模)","slug":"数据仓库学习(维度建模)","date":"2017-07-11T09:04:48.000Z","updated":"2018-12-14T05:09:54.312Z","comments":true,"path":"2017/07/11/数据仓库学习(维度建模)/","link":"","permalink":"http://guzhenping.com/2017/07/11/数据仓库学习(维度建模)/","excerpt":"前言之前做了一个关于数仓的talk, 给小白用户做了一些建模的举例,特地放在这里。都是比较常见模型。","text":"前言之前做了一个关于数仓的talk, 给小白用户做了一些建模的举例,特地放在这里。都是比较常见模型。 一 雪花模型 二 星形模型 三 雪花 vs 星形可参考这篇:星型模型和雪花型模型比较 四 参考资料 漫谈数据仓库之维度建模","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"数据仓库","slug":"数据仓库","permalink":"http://guzhenping.com/tags/数据仓库/"}]},{"title":"Python学习(迭代器&生成器)","slug":"Python学习--迭代器&生成器","date":"2017-05-22T09:04:48.000Z","updated":"2018-11-30T02:42:52.947Z","comments":true,"path":"2017/05/22/Python学习--迭代器&生成器/","link":"","permalink":"http://guzhenping.com/2017/05/22/Python学习--迭代器&生成器/","excerpt":"前言一直对Python的生成器、yield相关的东西比较晕。也终于到了这一天,算算总账,一口气理解这些东西。 这篇文章的背景是对yield/生成器/协程知识的总结,用于自我提升。请静下心来读,走马观花就不会有自己的思考。 懂分享的人,一定会快乐。笔者的内心期待着大家一起进步。所以,一起来分享自己的理解,留下你的评论吧。","text":"前言一直对Python的生成器、yield相关的东西比较晕。也终于到了这一天,算算总账,一口气理解这些东西。 这篇文章的背景是对yield/生成器/协程知识的总结,用于自我提升。请静下心来读,走马观花就不会有自己的思考。 懂分享的人,一定会快乐。笔者的内心期待着大家一起进步。所以,一起来分享自己的理解,留下你的评论吧。 行文介绍因为这是一整块知识,请不要拆散理解(好处是更易融会贯通)。所以,从头到尾的必备知识: 可迭代对象(iterable) 迭代器(iterator) 迭代(iterator) yield表达式 生成器(generators) 本文会分别介绍上面的知识,最后介绍协程。剧透一下,协程和生成器很相似,所以学习协程,必备生成器相关的知识。 啥是iterable?Python中任意的对象,只要它定义了可以返回一个迭代器的iter方法,或者定义了可以支持下标索引的getitem方法,那么它就是一个可迭代对象。 简单说,可迭代对象就是能提供迭代器的任意对象。 啥是iterator?任意对象,只要定义了__iter__方法和next(Python2) 或者next(Python3)方法,它就是一个迭代器。__iter__()返回迭代器对象本身;next()或者__next__()返回容器的下一个元素,在结尾时引发StopIteration异常退出。 对于可迭代对象,可以使用内建函数iter()来获取它的迭代器对象。 # python3中 >>> test = [1,2,3] >>> item = iter(test) # 获取迭代器对象 >>> print(item) # 打印该对象的类型、地址 <list_iterator object at 0x10231ab38> >>> item.__next__() # 获取第一个元素 1 >>> item.__next__() # 获取第二个元素 2 >>> item.__next__() # 获取第三个元素 3 >>> item.__next__() # 获取StopIteration Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 可以对string和dict做上面的事情,看看效果。扩展一点:在for循环中,for语法会自动调用迭代器的next()或next(),并能在遇到StopIteration时正常退出循环。 作为强化,给出一个自定义iterator的实例: class MyIteratorFab(): """Python3实现生成斐波那契数Fibonacci 输出最后一个数据不大于max """ def __init__(self, max): self.max = max self.a = 0 self.b = 1 def __iter__(self): return self def __next__(self): """ 返回容器中下一个元素,没有(符合标准的)元素后, 抛出StopIteration python2中请将__next__替换成next """ if self.b <= self.max: r = self.b # 用于记录倒数第二个b的值,该值才是小于self.max的 self.a, self.b = self.b, self.a + self.b return r # 不可返回self.b, 该值会比self.max大一点点 else: raise StopIteration() # 这条代码很重要,删除这不能正常退出for循环 # 测试 test = MyIteratorFab(100) for item in test: print(item) ############################################# # 附一段生成fibonacci数的函数,同上述自定义迭代器是 # 同一功能. ############################################# def fib_func2(max): """一个普通的的生产fibonacci数的函数 输出最后一个数据不大于max """ a, b = 0, 1 while b <= max: print(b) a, b = b, a+b 最后,在《Python迭代器和生成器》一文中提到过:对于一个可迭代对象,如果它本身又是一个迭代器对象,那么没办法支持多次迭代。感兴趣可戳过去阅读。 文中给出该问题的解法是,对迭代器对象类再包一个可迭代对象,实现多次迭代。拿上述MyIteratorFab()自定义迭代器举例: # 问题: fab = MyIteratorFab(10) print([item for item in fab]) # output: [1, 1, 2, 3, 5, 8] print([item for item in fab]) # output: [] # 解决方法: class BetterFab(): """实现生成斐波那契数Fibonacci 输出最后一个数据不大于max 可多次迭代 """ def __init__(self, max): self.max = max def __iter__(self): return MyIteratorFab(self.max) # 测试 fab2 = BetterFab(10) print([item for item in fab2]) # output: [1, 1, 2, 3, 5, 8] print([item for item in fab2]) # output: [1, 1, 2, 3, 5, 8] # 暴力的解法,但不推荐: print([item for item in MyIteratorFab(10)]) # output: [1, 1, 2, 3, 5, 8] print([item for item in MyIteratorFab(10)]) # output: [1, 1, 2, 3, 5, 8] 到此,iterator就算是说完了。这是三个以itera*开头的概念中最核心的一个概念。当然,它也是生成器(generator)的基础。 啥是iteration?提醒一下,这是一个名词。用简单的话讲,它就是从某个地方(比如一个列表)取出一个元素的过程。当我们使用一个循环来遍历某个东西时,这个过程本身就叫迭代。 如何理解yield表达式?下面的内容摘自Python3官方文档翻译: yield_atom ::= "(" yield_expression ")" yield_expression ::= "yield" [expression_list | "from" expression] yield表达式仅在定义生成器函数时使用,因此只能用在函数定义的主体中。在函数体中使用yield表达式会使该函数成为生成器。 当生成器函数被调用时,它返回一个称为生成器的迭代器。然后,生成器控制生成器函数的执行。当生成器的一个方法被调用时,执行开始。此时,执行进行到第一个yield表达式,在那里它被再次挂起,将expression_list的值返回给生成器的调用者。挂起,我们的意思是保留所有局部状态,包括局部变量的当前绑定,指令指针,内部计算栈和任何异常处理的状态。当通过调用其中一个生成器的方法来恢复执行时,函数可以像yield表达式只是另一个外部调用一样继续进行。恢复后的yield表达式的值取决于恢复执行的方法。如果使用__next__()(通常通过for或next()内置函数),则结果为None。否则,如果使用send(),则结果将是传递到该方法的值。 摘自:http://python.usyiyi.cn/translate/python_352/reference/expressions.html#yieldexpr yield 是一个类似 return 的关键字,只是这个函数返回的是个生成器。 另外,官网也提到yield其实和Coroutine类似: 所有这些使生成器函数与协程非常相似;它们产生多次,它们具有多个入口点并且它们的执行可以被挂起。唯一的区别是生成器函数不能控制在它yield后继续执行的位置;控制总是转移到生成器的调用者。 啥是generators?下面的内容摘自Python3官方文档翻译: generator_expression ::= "(" expression comp_for ")" 生成器表达式产生一个新的生成器对象。它的语法与推导式的语法相同,除了它被括在括号中而不是括号或花括号中。 当生成器对象调用__next__()方法时,生成器表达式中使用的变量将被懒惰地计算(以与正常生成器相同的方式)。但是,最左边的for子句会立即被求值,所以它产生的错误可以在生成器表达式代码中的任何其它可能的错误之前发现。后续for子句无法立即计算,因为它们可能取决于之前的for循环。例如:(x*y for x in range(10) for y in bar(x))。 摘自:http://python.usyiyi.cn/translate/python_352/reference/expressions.html#yieldexpr 生成器也是一种迭代器,但是你只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。你通过遍历来使用它们,要么用一个“for”循环,要么将它们传递给任意可以进行迭代的函数和结构。大多数时候生成器是以函数来实现的。然而,它们并不返回一个值,而是yield(暂且译作“生出”)一个值。 生成器最佳应用场景是:你不想同一时间将所有计算出来的大量结果集分配到内存当中,特别是结果集里还包含循环。 协程(Coroutine)常见于合作式多任务,迭代器,无限列表,管道。 协程是基于单个线程的,Python的线程是基于单核的并发实现。 并发对应计算机中充分利用单核(一个CPU)实现(看起来)多个任务同时执行。实现并发编程可以用多进程、多线程、异步、协程。 从上面这段引用可以看出:为啥很多人讨论协程与其他并发编程方式的异同优劣?但是今天我们不聊并发,所以关注该话题的童鞋请自己Google文章。 深挖Coroutine的本质: allowing multiple entry points for suspending and resuming execution at certain locations. 允许多个入口对程序进行挂起、继续执行等操作。 这和生成器很相似,但有区别: 生成器是数据的生产者 协程则是数据的消费者 协程会消费掉发送给它的值。 协程常用的方法:next()send()close() 结语有件事情必须要说明白: 我是宇宙中微不足道的一粒沙。即使作为沙粒,我想浸在湛蓝的海水中,也想沐浴在灿烂的阳光下,更想陪着孩童们构建沙滩城堡。自然清楚自己是一粒沙,但还是想要这样的生活。 所以,我愿意写这些微不足道的东西,来分享自己的价值。尽管,我也微不足道。 参考资料 Python 线程与协程 Python关键字yield的解释 Python yield 使用浅析 Python高级编程之生成器(Generator)与coroutine(一):Generator Python高级编程之生成器(Generator)与coroutine(二):coroutine介绍 Python高级编程之生成器(Generator)与coroutine(三):coroutine与pipeline(管道)和Dataflow(数据流_ Python高级编程之生成器(Generator)与coroutine(四):一个简单的多任务系统 Python迭代器和生成器","categories":[{"name":"编程语言","slug":"编程语言","permalink":"http://guzhenping.com/categories/编程语言/"}],"tags":[{"name":"Python","slug":"Python","permalink":"http://guzhenping.com/tags/Python/"}]},{"title":"HA成功升级的总结","slug":"HA成功升级的总结","date":"2017-02-02T09:04:48.000Z","updated":"2018-11-30T02:38:07.484Z","comments":true,"path":"2017/02/02/HA成功升级的总结/","link":"","permalink":"http://guzhenping.com/2017/02/02/HA成功升级的总结/","excerpt":"前言从升级到现在,一共过了1个半月,到昨天(20170511)总算踏实了。踩的那些坑,真的教会了笔者咋做人。升了,笔者并不后悔。当然脸被打了这么多下,也高兴不起来。 升级过程的所有问题,发生在当事人无法注意到、无法理解的地方。然而,只要时间充足,精力充沛,严谨严格,肯定能克服。 必须申明:本文是基于个体实际经验所写,难免片面,可能不具备参考价值。其次,本文是对一个已经存在数年有大数量的集群所做的一些升级,情况较为特殊。","text":"前言从升级到现在,一共过了1个半月,到昨天(20170511)总算踏实了。踩的那些坑,真的教会了笔者咋做人。升了,笔者并不后悔。当然脸被打了这么多下,也高兴不起来。 升级过程的所有问题,发生在当事人无法注意到、无法理解的地方。然而,只要时间充足,精力充沛,严谨严格,肯定能克服。 必须申明:本文是基于个体实际经验所写,难免片面,可能不具备参考价值。其次,本文是对一个已经存在数年有大数量的集群所做的一些升级,情况较为特殊。 升级准备这里就不多说了,参见《Hadoop Namenode HA升级》。 总结一句,准备和成功概率成正比,有付出,才有收获。 问题1:异构的配置文件对于hadoop集群来说,很多人都觉得hadoop配置文件是一样的。错了,真的错了。hadoop集群支持异构模式。所以,配置可以不同。 在本次升级过程中,配置上,遇到的问题:个别机器的盘符不同于大部分机器,个别机器的datanode数据所在盘没有填写在<name>dfs.datanode.data.dir</name>下。 对于linux盘符不太熟悉的观众请戳:《Linux硬盘盘符分配》 例如,有以下两种(及以上)配置: <property> <name>dfs.datanode.data.dir</name> <value>/home/deploy/hadoopdata/datanode/,/mnt/sdb/hadoopdata/datanode/</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/home/deploy/hadoopdata/datanode/,/mnt/sdc/hadoopdata/datanode/,/mnt/sdd/hadoopdata/datanode/</value> </property> 这种错误的配置,会导致namenode在启动过程中无法找到block,处于安全模式无法退出。对于集群停机不能超过5小时的公司/团体来说,只能强制立刻安全模式。此时,将会产生坏块。 对于hadoop集群来说,必须清除坏块。换句话说,因为配置的失误,会导致datanode丢数据。这种丢法,和没有挂上的那个/些盘有关,而且一丢就是丢一个盘,后果严重。 当然hadoop的块备份是大于等于2,如果只是一个盘,对于集群来说就相当于没有丢。反之,则是随机丢失块数据。 问题2:Hadoop堆内存先说结论:对于HA来说,两个namenode(active/standby)的堆内存应该要比hadoop集群metadata大至少1倍。这个结论并没有理论根据,但确实实践所得的最可靠数据。假设一个运行5年的集群有20G metadata数据,则namenode(a/s)需要40G以上。 为啥这么说?因为,namenode主备自动切换时(即主namenode异常,备namenode启动),standby namenode需要将metadata读到进程堆内存中。堆内存不住,NameNode进程会报GC堆已满。GC堆相关问题请戳:《深入理解JVM—JVM内存模型》 理解主备热切,大致有两个要点,本文不多说。只点一下,第一,单节点Namenode启动过程发生了哪些事情;第二,HA集群namenode启动中发生了哪些事情。主要就是围绕FSImage和EditsLog展开。请戳下方链接: 《NameNode启动过程详细剖析 NameNode中几个关键的数据结构 FSImage》 《Hadoop-2.X HA模式下的FSImage和EditsLog合并过程》 对于拥有20G左右metadata的集群,standby需要10-20分钟内就可以启动(堆内存够大)。如果热切时,standby namenode的内存就16G,那么半个小时差不多只能走到25%左右。 堆内存大点很易理解,数据多嘛。而且,网上有很多hadoop在提交作业时报jvm堆内存不足的问题,可以参考。 笔者的小伙伴在解决standby namenode启动过慢这个问题时,通过修改安装目录的../etc/hadoop/hadoop-env.sh文件中的HADOOP_HEAPSIZE这个参数。一开始,该参数同集群metadata数据大小无异,第二次改成约1.5倍,第三次改成约2倍。 HADOOP_HEAPSIZE的值会成为本地(不是每台机器共用的,可以不同)的JVM的堆大小。本地的各个Java守护进程都会共享这个堆,此时进程NameNoe能满足快速启动的条件。虽然会有其他java进程也用,但是HA模式下的namenode没有过多的java进程(其实就是DFSZKFailoverController)。 问题3:NameNode RPC调用方式未升级前,连接hadoop集群通过<name>fs.defaultFS</name>下的hdfs://IP:PORT来连。升级后,需要采用命名空间的方式: <property> <name>fs.defaultFS</name> <value>hdfs://mainhadoop</value> </property> 即:hdfs://命名空间。连接上命名空间后,zookeeper集群会自动分配程序连接到当前active的namenode。 对此,受影响较大的是一些执行脚本和已存在的hive表。执行脚本一般写死,只能一个一个修改。 对于hive表,需要进入到元数据所在数据库,修改数据Location指向。如果hive是以mysql作元数据存储,则需连上mysql,修改SDS和DBS两张表的数据。将“hdfs://ip:port/XXXXXXXXXX”改成新的hadoop命名空间。","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://guzhenping.com/tags/Hadoop/"},{"name":"集群运维","slug":"集群运维","permalink":"http://guzhenping.com/tags/集群运维/"}]},{"title":"Hadoop 2.7.2 HA升级方案","slug":"HA升级过程","date":"2017-01-02T09:04:48.000Z","updated":"2018-11-30T02:39:17.615Z","comments":true,"path":"2017/01/02/HA升级过程/","link":"","permalink":"http://guzhenping.com/2017/01/02/HA升级过程/","excerpt":"","text":"前言本次升级主要对Hadoop的core-site.xml和hdfs-site.xml文件进行修改,暂时不涉及其他配置。 这次升级过程,大致是三步:备份数据文件,修改配置文件,启动集群。如果升级中发现异常,启动回滚方案。 备份数据备份配置文件需要执行fabric脚本,在每台机器上进行备份。将core-site.xml和hdfs-site.xml分别cp为core-site.xml.ha.back和hdfs-site.xml.ha.back 以下是本次要修改的core-site.xml和hdfs-site.xml配置文件原内容。 core-site.xml <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://sha2hdpn01:9000</value> </property> <property> <name>io.file.buffer.size</name> <value>131072</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/home/deploy/hadoopdata/tmp</value> </property> <property> <name>hadoop.proxyuser.deploy.groups</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.deploy.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.hue.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.hue.groups</name> <value>*</value> </property> <property> <name>fs.trash.interval</name> <value>10080</value> </property> <property> <name>topology.script.file.name</name> <value>/usr/local/hadoop-default/etc/hadoop/topology.sh</value> </property> <property> <name>net.topology.script.file.name</name> <value>/usr/local/hadoop-default/etc/hadoop/topology.sh</value> </property> </configuration> hdfs-site.xml <configuration> <property> <name>dfs.namenode.secondary.http-address</name> <value>sha2hdpn02:50090</value> </property> <property> <name>dfs.namenode.http-address</name> <value>sha2hdpn01:50070</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/home/deploy/hadoopdata/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/home/deploy/hadoopdata/datanode/,/mnt/sdb/hadoopdata/datanode/</value> </property> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.checkpoint.dir</name> <value>/home/deploy/hadoopdata/checkpoint</value> <final>true</final> </property> <property> <name>dfs.namenode.checkpoint.edits.dir</name> <value>/home/deploy/hadoopdata/checkpoint</value> </property> <property> <name>dfs.namenode.checkpoint.period</name> <value>600</value> </property> <property> <name>dfs.datanode.du.reserved</name> <value>107374182400</value> </property> <property> <name>dfs.datanode.fsdataset.volume.choosing.policy</name> <value>org.apache.hadoop.hdfs.server.datanode.fsdataset.AvailableSpaceVolumeChoosingPolicy</value> </property> <property> <name>dfs.datanode.balance.bandwidthPerSec</name> <value>10485760</value> </property> <property> <name>dfs.hosts.exclude</name> <value>/usr/local/hadoop-default/etc/hadoop/exclude</value> </property> <property> <name>dfs.webhdfs.enabled</name> <value>true</value> </property> <property> <name>dfs.datanode.balance.bandwidthPerSec</name> <value>52428800</value> </property> <property> <name>dfs.datanode.max.transfer.threads</name> <value>8192</value> <description> Specifies the maximum number of threads to use for transferring data in and out of the DN. default is 4096,###Modified### </description> </property> <property> <name>dfs.datanode.socket.write.timeout</name> <value>480000</value> </property> <property> <name>dfs.client.socket-timeout</name> <value>300000</value> </property> <property> <name>dfs.datanode.max.xcievers</name> <value>8192</value> </property> <property> <name>dfs.namenode.handler.count</name> <value>80</value> </property> </configuration> 备份namenode数据需要关闭集群后,在sha2hdpn01上备份namenode的元数据,位置:/home/deploy/hadoopdata,大小:25G。 备份secondary namenode的数据需要关闭集群后,在sha2hdpn02上备份snn的元数据,位置:/home/deploy/hadoopdata, 大小:774G。 修改配置文件修改core-stie.xml<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://hacluster</value> </property> <property> <name>io.file.buffer.size</name> <value>131072</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/home/deploy/hadoopdata/tmp</value> </property> <property> <name>ha.zookeeper.quorum</name> <value>sha2hb01:2181,sha2hb02:2181,sha2hb03:2181</value> </property> <property> <name>hadoop.proxyuser.deploy.groups</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.deploy.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.hue.hosts</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.hue.groups</name> <value>*</value> </property> <property> <name>fs.trash.interval</name> <value>10080</value> </property> <property> <name>topology.script.file.name</name> <value>/usr/local/hadoop-default/etc/hadoop/topology.sh</value> </property> <property> <name>net.topology.script.file.name</name> <value>/usr/local/hadoop-default/etc/hadoop/topology.sh</value> </property> </configuration> 修改hdfs-site.xml先删除secondary namenode配置,在添加和ha配置相关的内容。 <configuration> <property> <name>dfs.namenode.name.dir</name> <value>/home/deploy/hadoopdata/namenode</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>/home/deploy/hadoopdata/datanode/,/mnt/sdb/hadoopdata/datanode/</value> </property> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.nameservices</name> <value>hacluster</value> </property> <property> <name>dfs.ha.namenodes.hacluster</name> <value>nn1,nn2</value> </property> <property> <name>dfs.namenode.rpc-address.hacluster.nn1</name> <value>sha2hdpn01:9000</value> </property> <property> <name>dfs.namenode.rpc-address.hacluster.nn2</name> <value>sha2hdpn02:9000</value> </property> <property> <name>dfs.namenode.http-address.hacluster.nn1</name> <value>sha2hdpn01:50070</value> </property> <property> <name>dfs.namenode.http-address.hacluster.nn2</name> <value>sha2hdpn02:50070</value> </property> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://sha2hdpw46:8485; sha2hdpw47:8485;sha2hdpw48:8485/hacluster</value> </property> <property> <name>dfs.journalnode.edits.dir</name> <value>/home/deploy/hadoopdata/journaldata</value> </property> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <property> <name>dfs.client.failover.proxy.provider.hacluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <property> <name>dfs.ha.fencing.methods</name> <value> sshfence shell(/bin/true) </value> </property> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/home/deploy/.ssh/id_rsa</value> </property> <property> <name>dfs.ha.fencing.ssh.connect-timeout</name> <value>30000</value> </property> <property> <name>dfs.datanode.du.reserved</name> <value>107374182400</value> </property> <property> <name>dfs.datanode.fsdataset.volume.choosing.policy</name> <value>org.apache.hadoop.hdfs.server.datanode.fsdataset.AvailableSpaceVolumeChoosingPolicy</value> </property> <property> <name>dfs.datanode.balance.bandwidthPerSec</name> <value>10485760</value> </property> <property> <name>dfs.hosts.exclude</name> <value>/usr/local/hadoop-default/etc/hadoop/exclude</value> </property> <property> <name>dfs.webhdfs.enabled</name> <value>true</value> </property> <property> <name>dfs.datanode.balance.bandwidthPerSec</name> <value>52428800</value> </property> <property> <name>dfs.datanode.max.transfer.threads</name> <value>8192</value> <description> Specifies the maximum number of threads to use for transferring data in and out of the DN. default is 4096,###Modified### </description> </property> <property> <name>dfs.datanode.socket.write.timeout</name> <value>480000</value> </property> <property> <name>dfs.client.socket-timeout</name> <value>300000</value> </property> <property> <name>dfs.datanode.max.xcievers</name> <value>8192</value> </property> <property> <name>dfs.namenode.handler.count</name> <value>80</value> </property> </configuration> 启动集群先启动zookeeper集群,并确定状态; 再启动journalnode集群,用于nn间数据同步(注意文件夹存储的位置和权限); 在主节点上启动namenode; 在副节点上,同步namenode的元数据:hdfs namenode -bootstandby(metadata为7.3G, 注意磁盘空间及时长); 在副节点上启动namenode; 在任意节点上,启动dfs: sh start-dfs.sh; 启动其他的。 回滚方案此处升级,主要是对core-site.xml和hdfs-site.xml文件进行修改,所以回滚方案的主要逻辑就是恢复这两份文件。 利用fabric写脚本,先每台机器上备份(cp命令),如需回滚,先关闭集群,再把该文件替换回来(mv命令)即可。 附脚本rollback_hadoop.py: from fabric.api import run,sudo,roles,env,cd,execute env.roledefs = { 'all_node': ['sha2hdpn01','sha2hdpn02','sha2hdpw01','sha2hdpw02','sha2hdpw03','sha2hdpw04','sha2hdpw05','sha2hdpw06','sha2hdpw07','sha2hdpw08','sha2hdpw09','sha2hdpw10','sha2hdpw11','sha2hdpw12','sha2hdpw13','sha2hdpw14','sha2hdpw15','sha2hdpw16','sha2hdpw17','sha2hdpw18','sha2hdpw19','sha2hdpw20','sha2hdpw21','sha2hdpw22','sha2hdpw23','sha2hdpw24','sha2hdpw25','sha2hdpw26','sha2hdpw27','sha2hdpw28','sha2hdpw29','sha2hdpw30','sha2hdpw31','sha2hdpw32','sha2hdpw33','sha2hdpw34','sha2hdpw35','sha2hdpw36','sha2hdpw37','sha2hdpw38','sha2hdpw39','sha2hdpw40','sha2hdpw41','sha2hdpw42','sha2hdpw43','sha2hdpw44','sha2hdpw45','sha2hdpw46','sha2hdpw47','sha2hdpw48'], 'namenode': ['sha2hdpn01'], 'test_node': ['sha2hdpw48'] } env.user = 'deploy' env.password = 'XXXXXX' # yourself # env.shell = '/bin/sh -c' @roles('all_node') def showfile(): # run('ll /usr/local/hadoop-default/etc/hadoop')i with cd('/usr/local/hadoop-default/etc/hadoop'): run("ls core-site.xml hdfs-site.xml") @roles('namenode') def start_hadoop(): print('start hadoop cluster...') # run('sh /usr/local/hadoop-default/sbin/start-all.sh') print('done...') @roles('all_node') #@roles('test_node') def backup(): print('start backup...') with cd('/usr/local/hadoop-default/etc/hadoop'): run('cp core-site.xml core-site.xml.ha.back') run('cp hdfs-site.xml hdfs-site.xml.ha.back') print('done...') @roles('all_node') #@roles('test_node') def rollback(): print('start roolback...') with cd('/usr/local/hadoop-default/etc/hadoop'): run('mv core-site.xml.ha.back core-site.xml') run('mv hdfs-site.xml.ha.back hdfs-site.xml') print('done...') def deploy(): execute(showfile) def run_backup(): execute(backup) def run_rollback(): execute(rollback) execute(start_hadoop) 测试方案重跑airflow中金融/流量的报表任务,用于提交job,查看是否能够完整跑完。这些任务中有使用了hdfs及yarn的操作,如果成功,说明hadoop ha集群可以用。 结语烧香拜佛,希望成功!","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://guzhenping.com/tags/Hadoop/"},{"name":"集群运维","slug":"集群运维","permalink":"http://guzhenping.com/tags/集群运维/"}]},{"title":"Hadoop学习指南(HA配置)","slug":"Hadoop学习指南--HA配置","date":"2017-01-02T09:04:48.000Z","updated":"2018-11-30T02:50:33.953Z","comments":true,"path":"2017/01/02/Hadoop学习指南--HA配置/","link":"","permalink":"http://guzhenping.com/2017/01/02/Hadoop学习指南--HA配置/","excerpt":"前言本篇介绍Hadoop的一些常用知识。要说和网上其他manual的区别,那就是这是笔者写的一套成体系的文档,不是随心所欲而作。 HA模式,主要是将namenode及resourcemanager都变成主备两个。这里先不讨论resourcemanager,主要针对namenode。 将namenode变成可主备自动切换的,主要是通过zookeeper集群对namenode的健康状态进行监控,然后选举一个健康的namenode做active(主)的,另一个成为standby(备)。因此,保证zookeeper集群的配置是正确且不易挂掉,是HA的基石。同时,注意HA升级过程中的相关进程的启动步骤即可完成。","text":"前言本篇介绍Hadoop的一些常用知识。要说和网上其他manual的区别,那就是这是笔者写的一套成体系的文档,不是随心所欲而作。 HA模式,主要是将namenode及resourcemanager都变成主备两个。这里先不讨论resourcemanager,主要针对namenode。 将namenode变成可主备自动切换的,主要是通过zookeeper集群对namenode的健康状态进行监控,然后选举一个健康的namenode做active(主)的,另一个成为standby(备)。因此,保证zookeeper集群的配置是正确且不易挂掉,是HA的基石。同时,注意HA升级过程中的相关进程的启动步骤即可完成。 官网有两种配置:NFS和QJM,两者区别参见:《HDFS v2 HA方案对比》 本文以NFS为例进行讨论。 首先声明环境: CentOS 6.5 64位 Hadoop 2.7.1 Java 1.8 所有操作均基于拥有第一代hadoop集群的环境之上。同时满足Centos 6.5系统内网络互通、机器免密码登陆、防火墙关闭。重要的是/etc/hosts文件如下: 192.168.20.2 hadoop1 192.168.20.3 hadoop2 192.168.20.4 hadoop3 192.168.20.5 hadoop4 192.168.20.6 hadoop5 另外,温馨提示:在hadoop的各种文件配置中,最好不要出现空格。例如: <property> <name>ha.zookeeper.quorum</name> <value>hadoop2:2181, hadoop3:2181, hadoop4:2181</value> </property> hadoop将无法找到hadoop3和hadoop4这种IP。如果手贱多敲,那么将会浪费很多时间。 进程功能介绍zk:维护共享锁保证只有一个active的namenode journalnode:在两个nn间同步元数据 配置zookeeper集群该集群最少需要3台机器(用于选举)。下面详述配置过程。 假设1: 3台机器是这样: IP 标识 192.168.20.3 hadoop2 192.168.20.4 hadoop3 192.168.20.5 hadoop4 假设2: 下载完的zookeeper源码包位于:/home/deploy/zookeeper-3.4.9。 假设3:java环境为1.8版本,位于:/home/deploy/jdk1.8.0_111。 第一步,配置zoo.cfg:vi /home/deploy/zookeeper-3.4.9/conf/zoo.cfg 修改配置: # The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit=10 # The number of ticks that can pass between # sending a request and getting an acknowledgement syncLimit=5 # the directory where the snapshot is stored. # do not use /tmp for storage, /tmp here is just # example sakes. dataDir=/home/deploy/zookeeper-3.4.9/data # logs dir dataLogDir=/home/deploy/zookeeper-3.4.9/logs # the port at which the clients will connect clientPort=2181 server.1=hadoop3:2888:3888 server.2=hadoop4:2888:3888 server.3=hadoop2:2888:3888 第二步,配置myid文件 在zoo.cfg中dataDir路径:dataDir=/home/deploy/zookeeper-3.4.9/data下,新建myid文件。vi myid在hadoop3的机器上,该文件内容为1,hadoop4机器上,该文件内容为2,hadoop5机器上,该文件内容为3。内容应保持同zoo.cfg中的server.x的x值相同 第三步,开/关zookeeper集群: 开:sh /home/deploy/zookeeper-3.4.9/bin/zkServer.sh start 关:sh /home/deploy/zookeeper-3.4.9/bin/zkServer.sh stop 请在将三台机器全部开启后,查看状态:zkServer.sh status。 配置core-site.xml在原文件上进行添加: <!-- 指定hdfs的nameservice为h01,需与dfs.nameservices一致 --> <property> <name>fs.defaultFS</name> <value>hdfs://mycluster</value> </property> <!-- 指定zookeeper地址 --> <property> <name>ha.zookeeper.quorum</name> <value>hadoop2:2181,hadoop3:2181,hadoop4:2181</value> </property> 配置hdfs-site.xml这里有两步,第一步是删除关于secondary namenode的配置,第二步是添加HA的配置。 删: <!-- 以下3个 property 的配置,是非HA模式下的,即一个集群只有一个namenode,在这里不可使用 --> <property> <name>dfs.http.address</name> <value>h01.vm.com:50070</value> <description>Secondary get fsimage and edits via dfs.http.address</description> </property> <property> <name>dfs.secondary.http.address</name> <value>h02.vm.com:50090</value> </property> <property> <name>dfs.namenode.checkpoint.dir</name> <value>/home/vagrant/VMBigData/hadoop/data/namesecondary</value> </property> 添: <!-- 命名空间的逻辑名称 --> <property> <name>dfs.nameservices</name> <value>mycluster</value> </property> <!-- 命名空间中所有NameNode的唯一标示。该标识指示DataNode集群中有哪些NameNode --> <property> <name>dfs.ha.namenodes.mycluster</name> <value>nn1,nn2</value> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn1</name> <value>hadoop1:9091</value> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn2</name> <value>hadoop2:9091</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn1</name> <value>hadoop1:9092</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn2</name> <value>hadoop2:9092</value> </property> <!-- JournalNode URLs,ActiveNameNode 会将 Edit Log 写入这些 JournalNode 所配置的本地目录即 dfs.journalnode.edits.dir --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://hadoop2:8485;hadoop3:8485;hadoop4:8485/mycluster</value> </property> <!-- JournalNode 用于存放 editlog 和其他状态信息的目录 --> <property> <name>dfs.journalnode.edits.dir</name> <value>/home/deploy/hadoop-2.7.1/journaldata</value> </property> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <!-- 一种关于 NameNode 的隔离机制(fencing) --> <property> <name>dfs.ha.fencing.methods</name> <value> sshfence shell(/bin/true) </value> </property> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/home/deploy/.ssh/id_rsa</value> </property> <property> <name>dfs.ha.fencing.ssh.connect-timeout</name> <value>30000</value> </property> 修改slaves文件主要是把作为namenode节点的ip从该文件中删除,视各自机器环境而定。 启动HA集群启动顺序,非常讲究。。。 重要的一点:不容许使用 hdfs namenode -format的命令,此命令会删除原集群的所有数据。 第一步,先关所有的集群进程。 第二步,打开zookeeper集群:sh /home/deploy/zookeeper-3.4.9/bin/zkServer.sh start 第三步,打开journalnode进程,该进程在两个nn间同步元数据。在hadoop2\\3\\4上都执行:sh hadoop-daemon.sh start journalnode 第四步,在原namenode节点上执行:sh hadoop-daemon.sh start namenode。此操作会将namenode状态变成active。 第五步,在备(standby)节点执行同步namenode数据的命令:hdfs namenode -bootstrapStandby。切记不要使用scp的方式同步元数据,会导致文件权限问题。 第六步,启动备namendoe:sh hadoop-daemon.sh start namenode 第七步,初始化zkfc。在主备两台上任意一台执行:hdfs zkfc -formatZK 第七步,启动zk(DFSZKFailoverController),该进程维护共享锁保证只有一个active的namenode。分别在两台作为NN的节点上执行:sh hadoop-daemon.sh start zkfc 第八步,启动hdfs集群(就是打开所有的datanode进程):sh start-dfs.sh 第九步,打开没有任何变化的yarn:sh start-yarn.sh。上面说了暂时不讨论resourcemanger的升级。 至此,hadoop集群双NN的升级就完成了。 效果展示第一步, 打开两个NN的监控页: 第二步:kill hadoop1的namenode进程,查看hadoop2中namenode的状态由standby变为active:重复几次,主备仍能自切。 参考资料 官方文档: HDFS High Availability 官方文档: ResourceManager High Availability Hadoop NameNode 高可用 (High Availability) 实现解析 Hadoop的HA机制(Zookeeper集群+Hadoop集群)配置记录 hadoop HA高可用集群模式搭建指南 Journal Storage Directory not formatted","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://guzhenping.com/tags/Hadoop/"},{"name":"集群运维","slug":"集群运维","permalink":"http://guzhenping.com/tags/集群运维/"}]},{"title":"Hadoop学习指南(集群运维篇)","slug":"Hadoop学习指南--集群运维篇","date":"2016-12-09T09:04:48.000Z","updated":"2018-11-30T02:34:31.633Z","comments":true,"path":"2016/12/09/Hadoop学习指南--集群运维篇/","link":"","permalink":"http://guzhenping.com/2016/12/09/Hadoop学习指南--集群运维篇/","excerpt":"前言本篇介绍Hadoop的一些常用知识。要说和网上其他manual的区别,那就是这是笔者写的一套成体系的文档,不是随心所欲而作。","text":"前言本篇介绍Hadoop的一些常用知识。要说和网上其他manual的区别,那就是这是笔者写的一套成体系的文档,不是随心所欲而作。 常用HDFS命令 hadoop fs -ls URI hadoop fs -du -h URI hadoop fs -cat URI [文件较大,hadoop fs -cat xxxx | head] hadoop fs -put URI hadoop fs -get URI hadoop fs -rmr URI hadoop fs -stat %b,%o,%n,%r,%y URI (%b:文件大小, %o:Block 大小, %n:文件名, %r:副本个数, %y 或%Y:最后一次修改日期和时间) hadoop fs -tail [-f] URI hdfs dfsadmin -report hadoop fs -appendToFile URI1[,URI2,…] URI(hadoop fs -appendToFile helloCopy1.txt helloCopy2.txt /user/tmp/hello.txt) hadoop fsck / -files -blocks 关键的clusterID这是hadoop集群的id号,只有拥有相同id的各个节点才能加入的这个集群中来。 大致是在:hdfs namenode -format命令之后生成这个id。 很多情况下,你的集群如果可能拥有不同的id号哦。就比如: 启动datanode是遇到: “DataNode: Initialization failed for Block pool” 此时应当查看:cat /home/deploy/hadoop-2.7.1/hdfs/name/current/VERSION cat /home/deploy/hadoop-2.7.1/hdfs/data/current/VERSION 一个是datanode数据的文件夹,一个是namenode数据的文件夹。 #Fri Feb 17 15:48:44 CST 2017 namespaceID=74707331 clusterID=CID-e3e7c80e-3099-461d-9fa9-404f4910def5 cTime=0 storageType=NAME_NODE blockpoolID=BP-1729560578-192.168.20.2-1487317724421 layoutVersion=-63 #Thu Dec 22 09:57:13 CST 2016 storageID=DS-63997596-6d60-46e8-a08c-94426208f9d9 clusterID=CID-076b4e8a-9ed9-47e9-b6e0-4c16440c33e8 cTime=0 datanodeUuid=3ae0642e-2d72-4b45-ba63-de5e6ca1c7c7 storageType=DATA_NODE layoutVersion=-56 添加删除节点Hadoop添加删除节点 重启丢失节点子节点DataNode丢失sbin/hadoop-daemon.sh start datanode 子节点NodeManager丢失sbin/yarn-daemon.sh start nodemanager 主节点丢失sbin/start-all.sh or sbin/hadoop-daemon.sh start namenode sbin/hadoop-daemon.sh start secondarynamenode sbin/yarn-daemon.sh start resourcemanager 配置文件出错管理hadoop集群,会经常遇到配置文件的相关问题。这里举一个例子,比如yarn的nodemanager起不来的问题。 yarn的相关配置文件有两个:yarn-site.xml和yarn-env.sh 在yarn-site.xml文件: <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>1024</value> </property> 在yarn-env.sh文件: JAVA_HEAP_MAX=-Xmx1024m 应该保证yarn-site.xml中的memory-mb数值比yarn-env.sh中JAVA_HEAP_MAX的数值小。yarn-site.xml的配置是要求nodemanager启动的最少内存,低于该值无法启动。实际启动时,使用yarn-env.sh中的配置。修改比如:JAVA_HEAP_MAX=-Xmx2048m no xxx to stophadoop会经常有这个问题,大概就是没有找到该进程的PID文件,所以报错。 具体参见连接:解决关闭Hadoop时no namenode to stop异常 每次启动hadoop(./start-all.sh)时,PID文件被生成,存储进程号。关闭时,PID文件被删除。 在hadoop2.7.1版本中,关于HADOOP_PID_DIR(文件路径:../etc/hadoop/hadoop-env.sh)的描述是这样的: # The directory where pid files are stored. /tmp by default. # NOTE: this should be set to a directory that can only be written to by # the user that will run the hadoop daemons. Otherwise there is the # potential for a symlink attack. export HADOOP_PID_DIR=${HADOOP_PID_DIR} export HADOOP_SECURE_DN_PID_DIR=${HADOOP_PID_DIR} 最好将PID文件放在只写目录中。 关于mapred-site.xml配置参见blog:《如何给运行在Yarn的MapReduce作业配置内存》 参考资料-如何给运行在Yarn的MapReduce作业配置内存 hadoop HDFS常用文件操作命令 HDFS 文件操作命令","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://guzhenping.com/tags/Hadoop/"},{"name":"集群运维","slug":"集群运维","permalink":"http://guzhenping.com/tags/集群运维/"}]},{"title":"集群搭建指南(上卷)","slug":"集群搭建指南--上卷","date":"2016-11-16T09:04:48.000Z","updated":"2018-11-30T02:36:17.890Z","comments":true,"path":"2016/11/16/集群搭建指南--上卷/","link":"","permalink":"http://guzhenping.com/2016/11/16/集群搭建指南--上卷/","excerpt":"前言阅读本文,需要具备Linux、计算机网络的基础知识。所以在文中出现的相关基础知识,均以链接的形式给出,务必理解该链接的内容后,继续阅读本指南。 集群搭建的环境多种多样,本文采用VitualBox安装5台虚拟机构建集群。具体环境: CentOS 6.5 64位 VirtualBox 5.1.10 Mac版 本机macOS Sierra 10.12.1 i7 8G内存","text":"前言阅读本文,需要具备Linux、计算机网络的基础知识。所以在文中出现的相关基础知识,均以链接的形式给出,务必理解该链接的内容后,继续阅读本指南。 集群搭建的环境多种多样,本文采用VitualBox安装5台虚拟机构建集群。具体环境: CentOS 6.5 64位 VirtualBox 5.1.10 Mac版 本机macOS Sierra 10.12.1 i7 8G内存 单台装机先下载CentOS的iso格式纯净镜像,可以下载LiveDVD和minimal两个版本。本文采用LiveDVD版做演示。 打开virtualbox: 点击”新建”按钮: 选择版本,并命名: 点击”继续”按钮,改内存大小: 点击”继续”按钮,改虚拟硬盘: 点击“创建”按钮,改硬盘类型:关于磁盘类型,参考:https://zhidao.baidu.com/question/1302436594642278379.html 点击”继续”按钮,改存储方式: 继续,改文件位置和大小: 点击”创建”,主界面显示该机器: 选中该机器,点击主界面”设置”按钮: 点击”系统”,修改”启动顺序”: 点击”网络”,修改网卡1: 为修改网卡2,先关闭该对话框,打开vitualbox管理界面: 点击偏好设置: 点击”网络”: 选择”仅主机(Host-Only)网络”: 点击添加按钮(右边绿色按钮),新增vboxnet1: 点击OK保存,再次选中该虚机(hadoop6),打开主界面的”设置”按钮,点击”网络”,点击”网卡2”,勾选”启用网络连接”,选择连接方式: 选vboxnet1,则其余所有节点均需保持一致。点击OK保存。此时点击主界面的”启动”按钮: 选择Centos的iso文件所在位置,点击”启动”。在弹出黑色界面时(有9秒时间),按一次任意的某个键,进入Boot界面。选择第一种安装方式:verify and boot: 按下回车键,进入centos系统: 双击系统桌面的”install to hard drive”: 进入安装界面: 一路点击next按钮,点击yes即可。时区选亚洲上海,root账户的密码要牢记。然后,进入等待界面: 点击”close”,关闭安装界面: 关闭该虚拟机,调整系统启动顺序。选中该虚机,点击”设置”,点击”系统”,调整”启动顺序”,将”硬盘”放在第一位,将”光驱”放在最后一位: 点击ok保存,再次启动该虚机,进入设置页面: 一路forward即可,创建用户名和密码自己定义: 为集群选择同一时间,需要勾选synchronize date and time over the network.防止某个节点挂了以后和其他节点的系统时间不一致: 一路forward,点击finish,进入登陆界面: 注:如果采用minimal安装方法,装机方式仅有两处与上述不同: 选第一种安装方式 选skip,跳过检测 其余过程,同上述LiveDVD版安装过程。 集群搭建重复上述步骤,安装5台虚拟机。此时需要配置集群的网络:配置网卡,修改hostname,添加host解析,添加普通用户。 配置网卡通过上述步骤的装机方法,每台机器中都将有两个网卡eth0和eth1。eth0作为虚机网络的公网网口,eth1作为虚拟机网络的内网网口。 配置网卡,需要在目录:/etc/sysconfig/network-scripts下新建以ifcfg-ethX(X是一个数字,从0开始,一般到3结束。)文件。所以,关于eth0的配置在:/etc/sysconfig/network-scripts/ifcfg-eth0; 关于eth1的配置在:/etc/sysconfig/network-scripts/ifcfg-eth1。 如果有不明白的地方,可以参考下文—-CentOS网络配置详解。 配置eth0在System->Preferences->Network Connections,进行配置。 先编辑Auto eth0: 连接名修改为eth0,勾选所有用户可用,设置IPv4,选择DHCP即可: 点击Apply,输入root账户验证即可。 如果采用minimal版本的CentOS安装,文件配置方法,需要在/etc/sysconfig/network-scripts/ifcfg-eth0中修改: ONBOOT=yes BOOTPROTO=dhcp 其他参数均由系统自动生成: 其余几台节点的eth0和上述相同。这样所有节点的公网IP是相同的。只要宿主机可以上网, 那么所有节点均可上网。 配置eth1eth1采用host-only模式,在Manual方法下填写ip。网段确定在192.168.xx.xx中。对于5个节点的集群,主机名为hadoop1-hadoop5,则ip分别是: 192.168.20.2 hadoop1 192.168.20.3 hadoop2 192.168.20.4 hadoop3 192.168.20.5 hadoop4 192.168.20.6 hadoop5 先编辑Auto eth1, 设置IPv4,选择Manual,添加ip,子网掩码,网关: 点击Apply,验证root账户,即可成功。这样192.168.20.2就分配给这台机器了(下面说把这台机器变成hadoop2)。 如果采用minimal版本的CentOS安装,文件配置方法,需要在/etc/sysconfig/network-scripts/ifcfg-eth1中修改: ONBOOT=yes BOOTPROTO=none IPADDR=192.168.20.2 NETMASK=255.255.255.0 其他参数(注意不要配置gateway)均由系统自动生成: 其余几台机器同上述配置过程,只需要更改IP(IPADDR参数)即可,子网掩码和网关(minimal不用配)均相同。 修改hostname一般是localhost开头,但是不容易标识机器。改成可标识的。有5台机器,那么这5台机器可以对应hadoop1-5。 修改文件的目录在: /etc/sysconfig/network 将HOSTNAME这个参数改掉即可。对于5个节点,分别在每台机器上修改为hadoop1-5。 添加host解析每台虚机都有ip,但是ip难记,加个host解析,方便使用。 配置文件在:/etc/hosts 先把127.0.0.1改成你已经修改的hostname的值,比如在hadoop1(192.168.20.2)这台机器上,需要改成: 127.0.0.1 hadoop1 ::1 hadoop1 这里,每个节点是不同的。 所有的节点均需添加内容: 192.168.20.2 hadoop1 192.168.20.3 hadoop2 192.168.20.4 hadoop3 192.168.20.5 hadoop4 192.168.20.6 hadoop5 保存即可。 添加普通用户添加普通账号,比如:deploy,分配给使用者。配置/etc/sudoers文件,使得该账户也可以进行sudo操作。 此处较为简单,参考下文—-linux的账户。 装机的思考为什么需要两个网卡?如果只使用默认的 NAT,会发现一旦宿主机断开公网,自己的几台虚机之间也会无法通。最简单的解决方案是双网卡,如下图所示: 为什么修改两次系统启动顺序?第一次装机是系统是来自iso文件的(光驱启动)。每次都从该文件启动,则无法对系统进行定制修改。将其装在硬盘上(自己的虚拟硬盘),每次从硬盘启动即可对系统进行定制修改。 所以,需要改变启动顺序。装到硬盘后,从硬盘启动。 linux的账户root账户权限太大,必须给开发者一个使用账户。 相关链接:http://linuxme.blog.51cto.com/1850814/347086/ CentOS网络配置详解包含网络配置的很多东西:http://blog.chinaunix.net/uid-26495963-id-3230810.html","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://guzhenping.com/tags/Hadoop/"},{"name":"集群运维","slug":"集群运维","permalink":"http://guzhenping.com/tags/集群运维/"}]},{"title":"集群搭建指南(下卷)","slug":"集群搭建指南--中卷","date":"2016-11-09T09:04:48.000Z","updated":"2018-11-30T02:35:30.864Z","comments":true,"path":"2016/11/09/集群搭建指南--中卷/","link":"","permalink":"http://guzhenping.com/2016/11/09/集群搭建指南--中卷/","excerpt":"前言本文的搭建基于上卷的配置,环境不再一一赘述。网络配置好的5台节点均可相互ping通,对于节点hadoop1(192.168.20.2)可以ping同其余四台hadoop2(192.168.20.3)、hadoop3(192.168.20.4)、hadoop4(192.168.20.5)、hadoop5(192.168.20.6)。其余节点同理。 本文继续进行集群的搭建—-SSH配置。禁止root账户进行ssh登陆,只允许指定用户进行ssh登陆。","text":"前言本文的搭建基于上卷的配置,环境不再一一赘述。网络配置好的5台节点均可相互ping通,对于节点hadoop1(192.168.20.2)可以ping同其余四台hadoop2(192.168.20.3)、hadoop3(192.168.20.4)、hadoop4(192.168.20.5)、hadoop5(192.168.20.6)。其余节点同理。 本文继续进行集群的搭建—-SSH配置。禁止root账户进行ssh登陆,只允许指定用户进行ssh登陆。 定义用户添加用户已经在上卷中提及,仅供参考。这里假设所有节点均有一个账户:deploy,密码:hadoop。 同时,已在/etc/sudoers文件提高了deploy的权限。 SSH配置修改ssh配置文件配置文件是:/etc/ssh/sshd_config。找到相关变量,修改为: PermitRootLogin no RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys PasswordAuthentication no 以上配置作用分别是:禁止root用户远程ssh,开启RSA验证,开启公钥验证,设置验证密钥文件在~/.ssh/authorized_keys中,禁止密码登录。 保存,重启ssh服务生效。重启命令:/etc/init.d/sshd restart 生成公钥私钥以deploy登录,进入~目录,输入ssh-keygen -t rsa命令,一路回车生成id_rsa(私钥)和id_rsa.pub(公钥)。 生成的文件在目录:~/.ssh/下。 在该目录下新建authorized_keys文件,将id_rsa.pub的内容拷贝的该文件中,后续有ssh请求均会来此文件验证内容。 同时,给相关文件赋予权限:chmod 700 ~/.sshchmod 600 ~/.ssh/authorized_keys 此时,即可利用私钥id_rsa用deploy账户登陆该机器。 拷贝各节点的公钥在各个节点重复上述过程,修改ssh的配置文件,在deploy账户下生成密钥,赋予文件权限。 为了让集群的节点能相互ssh登陆,需要讲个节点的公钥全部放置到deploy账户下的~/.ssh/authorized_keys(/home/deploy/.ssh/authorized_keys)文件中。每个节点的authorized_keys应该是相同的。 配置总结SSH是会验证不同用户账户下的~/.ssh/authorized_keys的公钥。在Linux系统中,~是指当前账户所在的目录。因为Linux支持多账户,所以,用不同的账户登录,~就代表不同路径。以deploy账户登录,则~代表:/home/deploy/;以deploy2账户登录,~就代表:/home/deploy2/。 如果指定某账户能ssh登录,则必须在登录该账户的情况下,在~目录下生成公钥和私钥。","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://guzhenping.com/tags/Hadoop/"},{"name":"集群运维","slug":"集群运维","permalink":"http://guzhenping.com/tags/集群运维/"}]},{"title":"Hadoop学习指南(HDFS篇)","slug":"Hadoop学习指南--HDFS篇","date":"2016-11-02T09:04:48.000Z","updated":"2018-11-30T02:41:15.542Z","comments":true,"path":"2016/11/02/Hadoop学习指南--HDFS篇/","link":"","permalink":"http://guzhenping.com/2016/11/02/Hadoop学习指南--HDFS篇/","excerpt":"","text":"# 前言本篇介绍Hadoop的一些常用知识。要说和网上其他manual的区别,那就是这是笔者写的一套成体系的文档,不是随心所欲而作。 Hadoop DFS介绍 Hadoop的分布式文件系统旨在:能够在跨大型集群中的机器上可靠地存储非常大的文件。它的\b灵感来自Google文件系统。Hadoop DFS将每个文件存储为一个块序列,文件中除最后一个块之外的所有块都具有相同的大小。属于文件的块将被复制以实现容错。块大小(block size)和复制因子(replication factor)可以按文件配置。HDFS中的文件是“一次写入”,并且在任何时间都有一个写入程序。 Architecture 像Hadoop Map/Reduce一样,HDFS遵循主/从架构。HDFS的安装包含一个Namenode,即一个主服务器,用于管理文件系统命名空间并控制客户端对文件的访问。此外,还有一些Datanodes,管理存储附加到他们运行的节点。Namenode进行文件系统命名空间操作,例如:通过RPC接口打开、关闭、重命名等文件和目录。它还确定块(block)到Datanodes的映射。Datanodes负责从文件系统客户端提供读取和写入请求,它们还根据Namenode的指令执行块创建、删除和复制。 翻译自hadoop官网wiki Hadoop常用命令 hadoop fs -ls URI hadoop fs -du -h URI hadoop fs -cat URI [文件较大,hadoop fs -cat xxxx | head] hadoop fs -put URI hadoop fs -get URI hadoop fs -rmr URI hadoop fs -stat %b,%o,%n,%r,%y URI (%b:文件大小, %o:Block 大小, %n:文件名, %r:副本个数, %y 或%Y:最后一次修改日期和时间) hadoop fs -tail [-f] URI hdfs dfsadmin -report hadoop fs -appendToFile URI1[,URI2,…] URI(hadoop fs -appendToFile helloCopy1.txt helloCopy2.txt /user/tmp/hello.txt) hadoop fsck / -files -blocks 列出文件系统中各个文件由哪些块构成。 请将URI替换成自己想要查看的文件路径或文件夹路径。 参考资料 hadoop HDFS常用文件操作命令 HDFS 文件操作命令","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://guzhenping.com/tags/Hadoop/"},{"name":"集群运维","slug":"集群运维","permalink":"http://guzhenping.com/tags/集群运维/"}]},{"title":"集群搭建指南(下卷)","slug":"集群搭建指南--下卷","date":"2016-11-02T09:04:48.000Z","updated":"2018-11-30T02:33:15.385Z","comments":true,"path":"2016/11/02/集群搭建指南--下卷/","link":"","permalink":"http://guzhenping.com/2016/11/02/集群搭建指南--下卷/","excerpt":"前言什么是Hadoop? Apache Hadoop is a framework for running applications on large cluster built of commodity hardware. The Hadoop framework transparently provides applications both reliability and data motion. Hadoop implements a computational paradigm named Map/Reduce, where the application is divided into many small fragments of work, each of which may be executed or re-executed on any node in the cluster. In addition, it provides a distributed file system (HDFS) that stores data on the compute nodes, providing very high aggregate bandwidth across the cluster. Both MapReduce and the Hadoop Distributed File System are designed so that node failures are automatically handled by the framework. 本文是用于搭建hadoop集群的学习性文章,主要对hadoop的一些基础知识进行解释,中间穿插着hadoop的安装配置过程。 环境问题: 系统:Centos 6.5 64位 minamal版本 hadoop版本:2.7.1 java版本:1.8 可能不具备通用性,仅供参考。","text":"前言什么是Hadoop? Apache Hadoop is a framework for running applications on large cluster built of commodity hardware. The Hadoop framework transparently provides applications both reliability and data motion. Hadoop implements a computational paradigm named Map/Reduce, where the application is divided into many small fragments of work, each of which may be executed or re-executed on any node in the cluster. In addition, it provides a distributed file system (HDFS) that stores data on the compute nodes, providing very high aggregate bandwidth across the cluster. Both MapReduce and the Hadoop Distributed File System are designed so that node failures are automatically handled by the framework. 本文是用于搭建hadoop集群的学习性文章,主要对hadoop的一些基础知识进行解释,中间穿插着hadoop的安装配置过程。 环境问题: 系统:Centos 6.5 64位 minamal版本 hadoop版本:2.7.1 java版本:1.8 可能不具备通用性,仅供参考。 安装概览安装完系统,调通集群内的网络(ssh),配置好java环境,再进行hadoop的安装。 安装hadoop时,集中精力配置namenode节点这一台机器,修改相关的配置文件,然后使用同步文件(scp)到其他机器上去,hadoop集群搭建即可完成。 值得一提,集群间必须网络通畅,否则无法完成分布式部署。有关问题参见—-集群搭建指南(中卷)。 另外,在生成环境中,hadoop的安装均在普通账户下(比如:deploy)完成。所以,新手请注意将java及hadoop安装在开发账户下(是deploy账户的话,位置一般是:/home/deploy/jdk1.8.0_111, /home/deploy/hadoop-2.7.1)。再通过修改账户目录下的 ~/.bash_profile文件修改环境变量。 安装步骤对于5个节点的集群(hadoop1-hadoop5),配置节点hadoop1即可。hadoop集群有很多特性,这里仅设置低配版的hadoop,不添加太多配置属性值,以减轻学习压力。 This document does not cover advanced topics such as Security or High Availability. 尽管只添加部分属性值,也需要配7个文件: core-site.xml hdfs-site.xml yarn-site.xml mapred-site.xml hadoop-env.sh yarn-env.sh slaves 7个文件的解释,参见:hadoop基础知识->1. 配置文件说明。 配置前,假设是: 在deploy账户下进行配置 hadoop文件在:/home/deploy/hadoop-2.7.1/ java文件在:/home/deploy/jdk1.8.0_111 在/home/deploy/hadoop-2.7.1/目录下,新建文件夹:tmp, hdfs, hdfs/data, hdfs/name 完成后,文件结构是这样: 接下来准备配置,如果在配置过程中发现没有该文件,可以自己新建。如果文件已有内容,可以全部删除/注释,然后添加下述配置。另外,本节给出的多有示例配置的端口都可自己定义,不必追求一致。 core-site.xml命令:vi /home/deploy/hadoop-2.7.1/etc/hadoop/core-site.xml <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://hadoop1:9091</value> <final>true</final> </property> <property> <name>hadoop.tmp.dir</name> <value>file:/home/deploy/hadoop-2.7.1/tmp</value> <description>Abasefor oteh temporary directories.</description> </property> </configuration> hdfs-site.xml命令:vi /home/deploy/hadoop-2.7.1/etc/hadoop/hdfs-site.xml <configuration> <!--配置NameNode--> <property> <name>dfs.namenode.name.dir</name> <value>file:/home/deploy/hadoop-2.7.1/hdfs/name</value> </property> <!--配置DataNode--> <property> <name>dfs.datanode.data.dir</name> <value>file:/home/deploy/hadoop-2.7.1/hdfs/data</value> </property> <!--配置Secondary Namenode --> <property> <name>dfs.namenode.secondary.http-address</name> <value>hadoop1:9095</value> </property> <!--修改默认的HDFS配置--> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.webhdfs.enabled</name> <value>true</value> </property> </configuration> yarn-site.xml命令:vi /home/deploy/hadoop-2.7.1/etc/hadoop/yarn-site.xml <configuration> <!--下述配置NodeManger--> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>1024</value> </property> <property> <name>yarn.nodemanager.resource.cpu-vcores</name> <value>1</value> </property> <!--下述配置ResourceManager --> <property> <name>yarn.resourcemanager.webapp.address</name> <value>hadoop1:9099</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>hadoop1</value> </property> </configuration> mapred-site.xml命令:vi /home/deploy/hadoop-2.7.1/etc/hadoop/mapred-site.xml <configuration> <!--配置MaoReduce应用--> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration> hadoop-env.sh命令:vi /home/deploy/hadoop-2.7.1/etc/hadoop/hadoop-env.sh 找到export JAVA_HOME的地方,将原来的内容注释,换成:export JAVA_HOME=/home/deploy/jdk1.8.0_111 yarn-env.sh命令:vi /home/deploy/hadoop-2.7.1/etc/hadoop/yarn-env.sh 找到export JAVA_HOME的地方,将原来的内容注释,换成:export JAVA_HOME=/home/deploy/jdk1.8.0_111 slaves命令:vi /home/deploy/hadoop-2.7.1/etc/hadoop/slaves 添加你所有datanode节点的hostname,假设有5个节点,名称是hadoop1-5(hadoop1作为namenode),则添加内容: hadoop2 hadoop3 hadoop4 hadoop5 启动hadoop集群配置完所有的文件,在该机器(假设是hadoop1)上使用scp命令到其他4台机器上。命令: scp -r /home/deploy/hadoop-2.7.1/* deploy@hadoop2:/home/deploy/hadoop-2.7.1/ scp -r /home/deploy/hadoop-2.7.1/* deploy@hadoop3:/home/deploy/hadoop-2.7.1/ scp -r /home/deploy/hadoop-2.7.1/* deploy@hadoop4:/home/deploy/hadoop-2.7.1/ scp -r /home/deploy/hadoop-2.7.1/* deploy@hadoop5:/home/deploy/hadoop-2.7.1/ 这样集群的配置文件就都一样了。然后,在hadoop1上执行命令,将集群格式化成一个分布式文件系统,执行:/home/deploy/hadoop-2.7.1/bin/hdfs namenode -format (注:该命令只能执行一次,多次执行的话,需清空前一次执行产生的hdfs/data/、hdfs/name/和tmp/文件夹下生成的文件。但是这样,老集群的数据就会丢失。相关问题已网上的解答为准。) 接下来启动集群:bash /home/deploy/hadoop-2.7.1/sbin/start-all.sh 查看相关的UI Web界面: 按这里的配置,Namenode程序的可视化界面在hadoop1:50070/(使用的默认端口),如图: Secondery Namenode程序的可视化界面在hadoop1:9095/(使用的自定义端口), 如图: ResourceManager程序的可视化界面在hadoop1:9099/(使用的自定义端口),如图: 最后,关闭集群:bash /home/deploy/hadoop-2.7.1/sbin/stop-all.sh hadoop基础知识1. 配置文件说明简单的配置无安全性的hadoop集群,需要修改: core-site.xml hdfs-site.xml yarn-site.xml mapred-site.xml hadoop-env.sh yarn-env.sh slaves 这7个文件均在同一目录:hadoop安装包名/etc/hadoop/。 To configure the Hadoop cluster you will need to configure the environment in which the Hadoop daemons execute as well as the configuration parameters for the Hadoop daemons. Hadoop由HDFS、Yarn、MapReduce等组成,hadoop的后台程序,其实就是他们的后台程序。 HDFS后台程序有NameNode,SecondaryNode和DataNode。YARN后台程序有ResourceManager,NodeManager和WebAppProxy。如果MapReduce框架被启用了,MapReduce Job History Server将会运行。(安装完hadoop后,使用jps命令可以看到这些后台程序的名称。) 1.1 配置hadoop后台程序的环境管理员可以使用hadoop-env.sh,也可选择性使用mapred-env.sh和yarn-env.sh脚本定制haddoop后台程序的环境。 至少,必须指定JAVA_HOME,以确保正确定义每一个远程节点。 可以配置的选择,在下图: 1.2 配置hadoop后台程序这部分处理在给定的配置文件中需要被指定的重要参数。 etc/hadoop/core-site.xml etc/hadoop/hdfs-site.xml 配置NameNode: 配置DataNode: etc/hadoop/yarn-site.xml 配置ResourceManager和NodeManager: 配置ResourceManager: 配置NodeManager: 配置History Server: 配置NodeManagers的健康监控: etc/hadoop/mapred-site.xml 配置MapReduce应用: 配置MapReduce JobHistory Server: slaves List all slave hostnames or IP addresses in your etc/hadoop/slaves file, one per line. Helper scripts (described below) will use the etc/hadoop/slaves file to run commands on many hosts at once. It is not used for any of the Java-based Hadoop configuration. In order to use this functionality, ssh trusts (via either passphraseless ssh or some other means, such as Kerberos) must be established for the accounts used to run Hadoop. 2. Hadoop Map/Reduce框架2.1 Programming model and execution frameworkMap/Reduce是一种编程范式,用于将大型分布式计算表达为对键/值对数据集的分布式操作序列。Hadoop Map/Reduce框架利用一组机器,并在集群中的所有节点上执行用户定义的Map/Reduce作业。一个Map/Reduce计算包含两个阶段:map阶段和reduce阶段。 在map阶段,框架将输入数据集拆分为大量片段,并将每个片段分配给map任务。框架还在其操作的节点集群上分布许多map任务。每个map任务从其分配的片段消耗键/值对,并产生一组中间键/值对。 对于每个输入的键/值对(K,V),map任务将调用户自定义的map函数。该函数将输入变换为不同的键/值对(K’,V’)。 map阶段过后,Map/Reduce框架通过键对中间数据集进行排序,并产一组(K‘,V’*)元组,使得与特定键相关联的所有值一起出现。它还将元组的集合(这组(K’,V’)元组)分割成等于reduce任务的数目的多个片段。 在reduce阶段,每个reduce任务消耗分配给他的(K’,V’*)元组的片段。对于每个这样的元组,它调用用户自定义的reduce函数,其将元组变换为输出值键/值对(K,V)。框架又一次在节点集群上分配许多reduce任务,并处理将合适的中间数据片段运送到每个reduce任务的事情。 关于hadoop的任务(tasks),有三个特点: 每个阶段的任务以容错方式(a fault-tolerant manner)执行,如果节点(多个节点)在计算中间失败,则分配给它(它们)的任务在其余节点之间重新分布 任务较多(有许多map和reduce)时,能够实现良好的负载平衡 允许失败的任务以最小的运行时间开销重新运行 2.2 ArchitectureHadoop Map/Reduce框架是主(master)/从(slave)架构。它有一个主服务器或jobtracker和几个从服务器或tasktrackers。jobtracker是用户和框架之间的交互点。用户向jobtracker提交map/reduce作业,将作业(job/jobs)放在待处理作业的队列中,并按先到先服务(first-come/first-served)的方式执行。jobtracker管理map和reduce任务到tasktracker的分配。tasktracker根据jobtracker的指令执行任务,并处理map和reduce阶段之间的数据移动。 2.3 Map/Reduce框架小结网上有很多以上原理的过程图,在hadoop官网上没有找到,但言语上描述的相当清楚。如有需求,自行网上找图。 3. Hadoop DFS3.1 介绍Hadoop的分布式文件系统旨在:能够在跨大型集群中的机器上可靠地存储非常大的文件。它的\b灵感来自Google文件系统。Hadoop DFS将每个文件存储为一个块序列,文件中除最后一个块之外的所有块都具有相同的大小。属于文件的块将被复制以实现容错。块大小(block size)和复制因子(replication factor)可以按文件配置。HDFS中的文件是“一次写入”,并且在任何时间都有一个写入程序。 3.2 Architecture像Hadoop Map/Reduce一样,HDFS遵循主/从架构。HDFS的安装包含一个Namenode,即一个主服务器,用于管理文件系统命名空间并控制客户端对文件的访问。此外,还有一些Datanodes,管理存储附加到他们运行的节点。Namenode进行文件系统命名空间操作,例如:通过RPC接口打开、关闭、重命名等文件和目录。它还确定块(block)到Datanodes的映射。Datanodes负责从文件系统客户端提供读取和写入请求,它们还根据Namenode的指令执行块创建、删除和复制。 参考资料 Hadoop参数汇总 Hadoop官网文档–cluster setup instructions in latest 2.x stable release docs hadoop中NameNode、DataNode、Secondary、NameNode、JobTra","categories":[{"name":"大数据","slug":"大数据","permalink":"http://guzhenping.com/categories/大数据/"}],"tags":[{"name":"Hadoop","slug":"Hadoop","permalink":"http://guzhenping.com/tags/Hadoop/"},{"name":"集群运维","slug":"集群运维","permalink":"http://guzhenping.com/tags/集群运维/"}]}]}