[{"content":"这两年，AI技术越来越成熟，在很多场景上给不同的岗位提供了大量的生产力。 但是作为数据行业从业人员，其中有个场景我想泼一下冷水。 比如被讲得很热的ChatSQL。\n接个大模型，挂上数据库，做个对话框，让业务一句话取数，仿佛传统 BI 的门槛一下子就要被抹平。\n这种想象不是完全没有根据。只要场景足够干净，问题足够标准，今天的模型确实已经能生成一段相当像样的 SQL，甚至把结果也答得像模像样。\n但我对 ChatSQL 的判断一直比较克制。不是因为它没用，而是因为它在企业里真正要解决的，从来都不是\u0026quot;写出一条 SQL\u0026quot;这么简单。\n一，ChatSQL 最难的不是生成，而是语义 很多人第一反应是：模型还不够强，等下一代就好了。\n这个判断的问题在于，它把 ChatSQL 的核心难点理解成了生成能力。\n可在企业环境里，模型最先撞墙的地方往往不是不会写 SQL，而是不知道你们公司到底在说什么。\n\u0026ldquo;活跃客户\u0026quot;怎么算？\n\u0026ldquo;新增付费用户\u0026quot;包不包含试用转正？\n\u0026ldquo;业绩下滑\u0026quot;看收入、利润，还是回款？\n这些问题本质上都不是自然语言理解问题，而是业务语义问题。模型可以把一句话翻成查询，但它翻不了一家企业内部本来就没有统一好的口径。\n所以 ChatSQL 如果一上来就做成\u0026quot;人话转 SQL\u0026rdquo;，最后大概率不是输在模型，而是输在语义基础太薄。\n二，语义层很重要，但它不是银弹 讲到这里，很多团队会自然转向另一个答案：那就建语义层。\n这个方向没错，但也很容易被误解。\n语义层的价值在于，把常见指标、维度、实体关系和口径规则结构化，让模型不必每次从数据库字段里重新猜业务世界。没有这层东西，系统几乎很难稳定。\n但语义层本身不是一个轻巧组件，它更像是一种治理成果的交付形式。\n换句话说，语义层不是补丁，而是地基的外化。\n如果企业本来就没有统一指标、没有稳定口径、没有明确的责任边界，那语义层也只是把混乱包了一层更漂亮的壳。它让问题显得更工程化，但并不会自动把问题解决。\n所以我更愿意把它理解成入场券，而不是终局方案。没有它，很难上线；只有它，也过不了关。\n三，Demo 证明不了产品成立 ChatSQL 最容易骗人的地方，就是 Demo 总是很好看。\n因为 Demo 里的问题通常都被精心挑过：定义清楚，范围有限，口径提前对齐，数据表也知道该怎么连。这样的条件下，模型表现出色并不奇怪。\n但产品成立的标准完全不是这个。\n真正的用户问题通常带着模糊、跳跃和上下文缺失。很多人问的不是一个可以直接翻译的问题，而是一个需要先被澄清的问题。\n比如：\u0026ldquo;最近业绩下滑的团队有哪些？\u0026rdquo;\n一个靠谱的分析师听到这句话，第一反应不会是立刻写 SQL，而是先追问。最近是多久，业绩看什么，下滑按什么基线，异常值要不要去掉，外包团队算不算。\n也就是说，真实分析过程本来就不是一次翻译，而是一段澄清和收敛的过程。\n而大多数 ChatSQL 产品，目前只接住了\u0026quot;生成查询\u0026quot;这一步，却没有接住分析师原本承担的职责：澄清、解释、回退、修正、多轮一致性。\n所以 Demo 证明的是模型有翻译能力，不等于产品已经具备交付能力。\n四，ChatSQL 最脆弱的地方是信任，不是准确率均值 很多讨论喜欢问：\u0026ldquo;准确率做到 80% 多，能不能上线？\u0026rdquo;\n这个问题本身问得就有点偏。\n如果 ChatSQL 只是给分析师自己做探索，用来找方向、提假设、少写点重复 SQL，那么它不需要像财务系统一样严苛。因为分析师本身有判断力，答偏了也知道怎么纠回来。\n但如果它要直接进入经营分析、财务对账、审计追责这种高责任场景，标准就完全不同了。\n这些场景里，决定系统能不能活下来的，不是平均准确率，而是关键问题能不能稳定答对，答错了能不能被及时发现，发现之后能不能追溯。\n因为用户对这类系统的信任建立得很慢，崩塌得却很快。十次答对未必能让人真正依赖它，但一次关键错误，足以让整个部门彻底弃用。\nChatSQL 最大的风险不是答不出来，而是一本正经地答错。\n五，ChatSQL 不是一个技术试点，而是一个持续运营的数据产品 很多企业做 ChatSQL 的方式，像是在做一个 POC：找模型、接数据库、配 prompt、做验收，然后宣布上线。\n但 ChatSQL 一旦认真做，就会立刻把过去藏在后台的数据治理欠账暴露出来。\n传统 BI 时代，很多口径分歧、命名混乱、字段脏乱，其实也是存在的。只是这些问题往往被分析师、报表开发和人工校验挡在了后台，用户看到的是被处理后的结果。\nChatSQL 把这层遮蔽拿掉了。用户直接问，系统直接答，中间没有人替你翻译，也没有人替你兜底。\n于是过去那些可以在后台被消化的问题，都会以前台事故的形式爆出来。\n所以它根本不是一个\u0026quot;把大模型接上去\u0026quot;就能结束的技术项目，而是一个需要长期维护的数据产品：\n需要持续补口径 需要持续收 bad case 需要持续修澄清策略 需要明确谁对答案负责 这些活都不性感，但决定了系统到底是不是活的。\n六，ChatSQL 更适合做窄，不适合一上来做总入口 我对 ChatSQL 最强烈的一个看法是：它更适合从窄场景切进去，而不是一开始就被定义成\u0026quot;什么都能问的智能分析入口\u0026rdquo;。\n原因很简单。你一旦把范围开到全公司、全业务域、全场景，就等于同时放大了语义复杂度、维护成本、错误风险和责任压力。\n而企业里真正容易做成的，往往是这些场景：\n一个明确业务域 一批高频、模式化的问题 一组相对稳定的用户 一条清楚的责任边界 比如只做销售分析，只做财务查询，或者只先给分析师和运营自己用。先把可信度、澄清机制和维护流程做起来，再慢慢扩大边界。\n不是因为格局不能大，而是因为很多团队一开始就把格局做得太大，最后反而连最小闭环都没做出来。\n最后 我不怀疑 ChatSQL 有价值。\n它一定会替代掉一部分重复、机械、低附加值的取数和分析动作，也会逼着更多企业重新正视自己的语义治理、指标体系和分析流程。\n但它真正考验的，从来都不是模型能不能把一句话翻成 SQL。\n它考验的是：企业有没有能力把一句模糊的人话，落成一个长期可信、边界清楚、责任明确的分析服务。\n如果没有这层基础，ChatSQL 很容易变成一个会写 SQL 的演示系统。\n如果有，那它才有机会从一个新奇入口，变成一个真正能用的产品。\n","permalink":"https://leochu.work/blog/tech/ai/chatsql%E4%B9%8B%E6%88%91%E8%A7%81/","summary":"\u003cp\u003e这两年，AI技术越来越成熟，在很多场景上给不同的岗位提供了大量的生产力。\n但是作为数据行业从业人员，其中有个场景我想泼一下冷水。\n比如被讲得很热的ChatSQL。\u003c/p\u003e\n\u003cp\u003e接个大模型，挂上数据库，做个对话框，让业务一句话取数，仿佛传统 BI 的门槛一下子就要被抹平。\u003c/p\u003e\n\u003cp\u003e这种想象不是完全没有根据。只要场景足够干净，问题足够标准，今天的模型确实已经能生成一段相当像样的 SQL，甚至把结果也答得像模像样。\u003c/p\u003e\n\u003cp\u003e但我对 ChatSQL 的判断一直比较克制。不是因为它没用，而是因为它在企业里真正要解决的，从来都不是\u0026quot;写出一条 SQL\u0026quot;这么简单。\u003c/p\u003e\n\u003ch2 id=\"一chatsql-最难的不是生成而是语义\"\u003e一，ChatSQL 最难的不是生成，而是语义\u003c/h2\u003e\n\u003cp\u003e很多人第一反应是：模型还不够强，等下一代就好了。\u003c/p\u003e\n\u003cp\u003e这个判断的问题在于，它把 ChatSQL 的核心难点理解成了生成能力。\u003c/p\u003e\n\u003cp\u003e可在企业环境里，模型最先撞墙的地方往往不是不会写 SQL，而是不知道你们公司到底在说什么。\u003c/p\u003e\n\u003cp\u003e\u0026ldquo;活跃客户\u0026quot;怎么算？\u003cbr\u003e\n\u0026ldquo;新增付费用户\u0026quot;包不包含试用转正？\u003cbr\u003e\n\u0026ldquo;业绩下滑\u0026quot;看收入、利润，还是回款？\u003c/p\u003e\n\u003cp\u003e这些问题本质上都不是自然语言理解问题，而是业务语义问题。模型可以把一句话翻成查询，但它翻不了一家企业内部本来就没有统一好的口径。\u003c/p\u003e\n\u003cp\u003e所以 ChatSQL 如果一上来就做成\u0026quot;人话转 SQL\u0026rdquo;，最后大概率不是输在模型，而是输在语义基础太薄。\u003c/p\u003e\n\u003ch2 id=\"二语义层很重要但它不是银弹\"\u003e二，语义层很重要，但它不是银弹\u003c/h2\u003e\n\u003cp\u003e讲到这里，很多团队会自然转向另一个答案：那就建语义层。\u003c/p\u003e\n\u003cp\u003e这个方向没错，但也很容易被误解。\u003c/p\u003e\n\u003cp\u003e语义层的价值在于，把常见指标、维度、实体关系和口径规则结构化，让模型不必每次从数据库字段里重新猜业务世界。没有这层东西，系统几乎很难稳定。\u003c/p\u003e\n\u003cp\u003e但语义层本身不是一个轻巧组件，它更像是一种治理成果的交付形式。\u003c/p\u003e\n\u003cp\u003e换句话说，语义层不是补丁，而是地基的外化。\u003c/p\u003e\n\u003cp\u003e如果企业本来就没有统一指标、没有稳定口径、没有明确的责任边界，那语义层也只是把混乱包了一层更漂亮的壳。它让问题显得更工程化，但并不会自动把问题解决。\u003c/p\u003e\n\u003cp\u003e所以我更愿意把它理解成入场券，而不是终局方案。没有它，很难上线；只有它，也过不了关。\u003c/p\u003e\n\u003ch2 id=\"三demo-证明不了产品成立\"\u003e三，Demo 证明不了产品成立\u003c/h2\u003e\n\u003cp\u003eChatSQL 最容易骗人的地方，就是 Demo 总是很好看。\u003c/p\u003e\n\u003cp\u003e因为 Demo 里的问题通常都被精心挑过：定义清楚，范围有限，口径提前对齐，数据表也知道该怎么连。这样的条件下，模型表现出色并不奇怪。\u003c/p\u003e\n\u003cp\u003e但产品成立的标准完全不是这个。\u003c/p\u003e\n\u003cp\u003e真正的用户问题通常带着模糊、跳跃和上下文缺失。很多人问的不是一个可以直接翻译的问题，而是一个需要先被澄清的问题。\u003c/p\u003e\n\u003cp\u003e比如：\u0026ldquo;最近业绩下滑的团队有哪些？\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e一个靠谱的分析师听到这句话，第一反应不会是立刻写 SQL，而是先追问。最近是多久，业绩看什么，下滑按什么基线，异常值要不要去掉，外包团队算不算。\u003c/p\u003e\n\u003cp\u003e也就是说，真实分析过程本来就不是一次翻译，而是一段澄清和收敛的过程。\u003c/p\u003e\n\u003cp\u003e而大多数 ChatSQL 产品，目前只接住了\u0026quot;生成查询\u0026quot;这一步，却没有接住分析师原本承担的职责：澄清、解释、回退、修正、多轮一致性。\u003c/p\u003e\n\u003cp\u003e所以 Demo 证明的是模型有翻译能力，不等于产品已经具备交付能力。\u003c/p\u003e\n\u003ch2 id=\"四chatsql-最脆弱的地方是信任不是准确率均值\"\u003e四，ChatSQL 最脆弱的地方是信任，不是准确率均值\u003c/h2\u003e\n\u003cp\u003e很多讨论喜欢问：\u0026ldquo;准确率做到 80% 多，能不能上线？\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e这个问题本身问得就有点偏。\u003c/p\u003e\n\u003cp\u003e如果 ChatSQL 只是给分析师自己做探索，用来找方向、提假设、少写点重复 SQL，那么它不需要像财务系统一样严苛。因为分析师本身有判断力，答偏了也知道怎么纠回来。\u003c/p\u003e\n\u003cp\u003e但如果它要直接进入经营分析、财务对账、审计追责这种高责任场景，标准就完全不同了。\u003c/p\u003e\n\u003cp\u003e这些场景里，决定系统能不能活下来的，不是平均准确率，而是关键问题能不能稳定答对，答错了能不能被及时发现，发现之后能不能追溯。\u003c/p\u003e\n\u003cp\u003e因为用户对这类系统的信任建立得很慢，崩塌得却很快。十次答对未必能让人真正依赖它，但一次关键错误，足以让整个部门彻底弃用。\u003c/p\u003e","title":"ChatSQL之我见"},{"content":"重要场合里最容易出问题的，往往不是不知道说什么，而是太急着回应。\n人一紧张，就会本能地想立刻接话，试图证明自己听懂了、反应快、能扛得住场面。但越是这样，越容易把情绪也一起说出去。语气先乱，逻辑后补，结果一句话说偏，后面十句话都要拿来修。\n我现在更愿意把这件事理解成一个很简单的流程：一停，二判，三说。\n一停 先停 1 秒，不急着回应。\n哪怕只是点头，或者先看着对方，也够了。\n这一秒的价值，不在于礼貌，而在于切断情绪反应。很多错话不是观点错，而是反应太快。人一旦被冒犯、被质疑、被催促，就容易直接用本能接球。那时候说出来的话，通常不是“我想表达的”，而是“我当下的应激”。\n这一停，就是给自己一个重新接管表达的机会。\n二判 然后快速判断一句话：对方是在要信息，还是在施压？\n这是最关键的一步。因为很多场合里，表面上听起来像提问，实际上并不是在认真要答案，而是在测试你、逼你表态、催你让步，或者单纯想把节奏抢走。\n如果对方是在要信息，那就正常回答，越清楚越好。\n如果对方是在施压，就别急着对抗，也别急着辩解，先把情绪收住。很多人一感觉被压，就马上想证明自己没问题，结果话越说越碎，姿态越说越低。其实对方在施压时，你最该做的不是“赢回去”，而是别让自己失去控制。\n先识别场景，后决定口径。\n三说 最后再说。说的时候，不靠临场发挥，靠固定结构。\n最简单的一种结构就是：\n先接一句，再说重点。\n比如：\n“这个点我理解。 我这边的情况是……”\n或者：\n“这个问题可以分两点。 第一……第二… …”\n“接”这一下，不是示弱，而是把谈话重新拉回秩序。它先让对方知道，你听到了，也知道问题在哪。然后你再进入重点，就不会显得慌，也不会显得在乱挡。\n重要场合里，很多人输不是输在内容，而是输在开头那一下太乱。开头一乱，后面即使内容对，别人也会先感受到你的不稳。\n这个方法到底防什么 它防的不是“不会紧张”，而是“紧张时乱说话”。\n紧张本身很难完全消失。越重要的场合，越在意的人，越容易有压力。真正有用的，不是要求自己毫不紧张，而是给紧张时的自己留一条动作路径。\n一停，是防止情绪抢跑。\n二判，是防止误把压力当问题来答。\n三说，是防止表达散掉。\n这三步合起来，目的只有一个：哪怕心里已经有波动，嘴上也不要失控。\n几个很实用的替换句 有些话平时会说得太快，到了关键时刻尤其容易翻车。提前准备几句替换句，比临场硬扛有效得多。\n被突然追问时：\u0026ldquo;我先确认一下你的问题。\u0026rdquo; 被质疑时：\u0026ldquo;这个点我理解，我把情况说完整一点。\u0026rdquo; 被催着表态时：\u0026ldquo;我先说我现在能确认的部分。\u0026rdquo; 场面有点压人时：\u0026ldquo;这个问题可以分开看。\u0026rdquo; 自己脑子有点乱时：\u0026ldquo;我整理一下，直接说结论。\u0026rdquo; 这些句子看起来都不强，但它们有一个共同作用：先把节奏拿回来。\n最后 重要场合里，真正可靠的人，往往不是反应最快的人，而是能在压力下来之后，仍然把话说稳的人。\n所以以后再碰到那种一紧张就想赶紧解释、赶紧回应、赶紧证明的时刻，可以只提醒自己三件事：\n先停一下。\n先判断。\n再开口。\n","permalink":"https://leochu.work/blog/thoughts/%E8%B0%A8%E8%A8%80/","summary":"\u003cp\u003e重要场合里最容易出问题的，往往不是不知道说什么，而是太急着回应。\u003c/p\u003e\n\u003cp\u003e人一紧张，就会本能地想立刻接话，试图证明自己听懂了、反应快、能扛得住场面。但越是这样，越容易把情绪也一起说出去。语气先乱，逻辑后补，结果一句话说偏，后面十句话都要拿来修。\u003c/p\u003e\n\u003cp\u003e我现在更愿意把这件事理解成一个很简单的流程：一停，二判，三说。\u003c/p\u003e\n\u003ch2 id=\"一停\"\u003e一停\u003c/h2\u003e\n\u003cp\u003e先停 1 秒，不急着回应。\u003c/p\u003e\n\u003cp\u003e哪怕只是点头，或者先看着对方，也够了。\u003c/p\u003e\n\u003cp\u003e这一秒的价值，不在于礼貌，而在于切断情绪反应。很多错话不是观点错，而是反应太快。人一旦被冒犯、被质疑、被催促，就容易直接用本能接球。那时候说出来的话，通常不是“我想表达的”，而是“我当下的应激”。\u003c/p\u003e\n\u003cp\u003e这一停，就是给自己一个重新接管表达的机会。\u003c/p\u003e\n\u003ch2 id=\"二判\"\u003e二判\u003c/h2\u003e\n\u003cp\u003e然后快速判断一句话：对方是在要信息，还是在施压？\u003c/p\u003e\n\u003cp\u003e这是最关键的一步。因为很多场合里，表面上听起来像提问，实际上并不是在认真要答案，而是在测试你、逼你表态、催你让步，或者单纯想把节奏抢走。\u003c/p\u003e\n\u003cp\u003e如果对方是在要信息，那就正常回答，越清楚越好。\u003c/p\u003e\n\u003cp\u003e如果对方是在施压，就别急着对抗，也别急着辩解，先把情绪收住。很多人一感觉被压，就马上想证明自己没问题，结果话越说越碎，姿态越说越低。其实对方在施压时，你最该做的不是“赢回去”，而是别让自己失去控制。\u003c/p\u003e\n\u003cp\u003e先识别场景，后决定口径。\u003c/p\u003e\n\u003ch2 id=\"三说\"\u003e三说\u003c/h2\u003e\n\u003cp\u003e最后再说。说的时候，不靠临场发挥，靠固定结构。\u003c/p\u003e\n\u003cp\u003e最简单的一种结构就是：\u003c/p\u003e\n\u003cp\u003e先接一句，再说重点。\u003c/p\u003e\n\u003cp\u003e比如：\u003c/p\u003e\n\u003cp\u003e“这个点我理解。\n我这边的情况是……”\u003c/p\u003e\n\u003cp\u003e或者：\u003c/p\u003e\n\u003cp\u003e“这个问题可以分两点。\n第一……第二… …”\u003c/p\u003e\n\u003cp\u003e“接”这一下，不是示弱，而是把谈话重新拉回秩序。它先让对方知道，你听到了，也知道问题在哪。然后你再进入重点，就不会显得慌，也不会显得在乱挡。\u003c/p\u003e\n\u003cp\u003e重要场合里，很多人输不是输在内容，而是输在开头那一下太乱。开头一乱，后面即使内容对，别人也会先感受到你的不稳。\u003c/p\u003e\n\u003ch2 id=\"这个方法到底防什么\"\u003e这个方法到底防什么\u003c/h2\u003e\n\u003cp\u003e它防的不是“不会紧张”，而是“紧张时乱说话”。\u003c/p\u003e\n\u003cp\u003e紧张本身很难完全消失。越重要的场合，越在意的人，越容易有压力。真正有用的，不是要求自己毫不紧张，而是给紧张时的自己留一条动作路径。\u003c/p\u003e\n\u003cp\u003e一停，是防止情绪抢跑。\u003c/p\u003e\n\u003cp\u003e二判，是防止误把压力当问题来答。\u003c/p\u003e\n\u003cp\u003e三说，是防止表达散掉。\u003c/p\u003e\n\u003cp\u003e这三步合起来，目的只有一个：哪怕心里已经有波动，嘴上也不要失控。\u003c/p\u003e\n\u003ch2 id=\"几个很实用的替换句\"\u003e几个很实用的替换句\u003c/h2\u003e\n\u003cp\u003e有些话平时会说得太快，到了关键时刻尤其容易翻车。提前准备几句替换句，比临场硬扛有效得多。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e被突然追问时：\u0026ldquo;我先确认一下你的问题。\u0026rdquo;\u003c/li\u003e\n\u003cli\u003e被质疑时：\u0026ldquo;这个点我理解，我把情况说完整一点。\u0026rdquo;\u003c/li\u003e\n\u003cli\u003e被催着表态时：\u0026ldquo;我先说我现在能确认的部分。\u0026rdquo;\u003c/li\u003e\n\u003cli\u003e场面有点压人时：\u0026ldquo;这个问题可以分开看。\u0026rdquo;\u003c/li\u003e\n\u003cli\u003e自己脑子有点乱时：\u0026ldquo;我整理一下，直接说结论。\u0026rdquo;\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这些句子看起来都不强，但它们有一个共同作用：先把节奏拿回来。\u003c/p\u003e\n\u003ch2 id=\"最后\"\u003e最后\u003c/h2\u003e\n\u003cp\u003e重要场合里，真正可靠的人，往往不是反应最快的人，而是能在压力下来之后，仍然把话说稳的人。\u003c/p\u003e\n\u003cp\u003e所以以后再碰到那种一紧张就想赶紧解释、赶紧回应、赶紧证明的时刻，可以只提醒自己三件事：\u003c/p\u003e\n\u003cp\u003e先停一下。\u003c/p\u003e\n\u003cp\u003e先判断。\u003c/p\u003e\n\u003cp\u003e再开口。\u003c/p\u003e","title":"谨言"},{"content":"1. 什么是 LDAP LDAP（Lightweight Directory Access Protocol） 是一种用于查询和管理目录信息的协议。\n简单说：\nLDAP 是一个“按层级组织的用户信息数据库 + 查询方式”\n2. LDAP 存什么 常见内容包括：\n用户账号（用户名、ID） 密码（加密存储） 用户组（权限） 部门 / 组织结构 邮箱、电话等信息 3. 数据结构（像树） LDAP 的数据是树状结构：\ngraph TD\rA[公司] --\u0026gt; B[技术部]\rA --\u0026gt; C[人事部]\rB --\u0026gt; D[用户A]\rB --\u0026gt; E[用户B]\rC --\u0026gt; F[用户C] 每个节点都有唯一标识（DN）。\n4. LDAP 能做什么 用户登录认证（验证账号密码） 查询用户信息 管理组织结构 权限分组管理 5. 常见使用场景 公司统一账号系统 内部系统登录（单点登录基础） 权限集中管理 6. 常见实现 OpenLDAP Active Directory（微软） 7. 一句话总结 LDAP 是一个“按树组织的用户信息数据库 + 查询协议”，用于统一管理账号和组织结构\n","permalink":"https://leochu.work/blog/tech/engineering/ldap-%E7%AE%80%E6%98%8E%E7%A7%91%E6%99%AE/","summary":"\u003ch2 id=\"1-什么是-ldap\"\u003e1. 什么是 LDAP\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eLDAP（Lightweight Directory Access Protocol）\u003c/strong\u003e 是一种用于\u003cstrong\u003e查询和管理目录信息的协议\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e简单说：\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eLDAP 是一个“按层级组织的用户信息数据库 + 查询方式”\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch2 id=\"2-ldap-存什么\"\u003e2. LDAP 存什么\u003c/h2\u003e\n\u003cp\u003e常见内容包括：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e用户账号（用户名、ID）\u003c/li\u003e\n\u003cli\u003e密码（加密存储）\u003c/li\u003e\n\u003cli\u003e用户组（权限）\u003c/li\u003e\n\u003cli\u003e部门 / 组织结构\u003c/li\u003e\n\u003cli\u003e邮箱、电话等信息\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"3-数据结构像树\"\u003e3. 数据结构（像树）\u003c/h2\u003e\n\u003cp\u003eLDAP 的数据是\u003cstrong\u003e树状结构\u003c/strong\u003e：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-mermaid\" data-lang=\"mermaid\"\u003egraph TD\r\nA[公司] --\u0026gt; B[技术部]\r\nA --\u0026gt; C[人事部]\r\nB --\u0026gt; D[用户A]\r\nB --\u0026gt; E[用户B]\r\nC --\u0026gt; F[用户C]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e每个节点都有唯一标识（DN）。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"4-ldap-能做什么\"\u003e4. LDAP 能做什么\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e用户登录认证（验证账号密码）\u003c/li\u003e\n\u003cli\u003e查询用户信息\u003c/li\u003e\n\u003cli\u003e管理组织结构\u003c/li\u003e\n\u003cli\u003e权限分组管理\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"5-常见使用场景\"\u003e5. 常见使用场景\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e公司统一账号系统\u003c/li\u003e\n\u003cli\u003e内部系统登录（单点登录基础）\u003c/li\u003e\n\u003cli\u003e权限集中管理\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"6-常见实现\"\u003e6. 常见实现\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eOpenLDAP\u003c/li\u003e\n\u003cli\u003eActive Directory（微软）\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"7-一句话总结\"\u003e7. 一句话总结\u003c/h2\u003e\n\u003cblockquote\u003e\n\u003cp\u003eLDAP 是一个“按树组织的用户信息数据库 + 查询协议”，用于统一管理账号和组织结构\u003c/p\u003e\n\u003c/blockquote\u003e","title":"LDAP 简明科普"},{"content":"1. SSH 是什么 SSH（Secure Shell） 是一种用于远程登录和通信的加密协议。\n核心作用 远程登录服务器 执行命令 安全传输数据 建立加密通道 简单理解 就像一根“加密的远程操作线”，你在本地操作，实际在远程执行。\n图示 graph LR\rA[本地电脑] -- SSH连接 --\u0026gt; B[远程服务器]\rA --\u0026gt;|输入命令| B\rB --\u0026gt;|返回结果| A 2. 跳板机（Bastion Host）是什么 跳板机是一台中间服务器，用于访问内网机器。\n为什么需要 内网服务器不能直接暴露到公网 提供统一入口（安全控制、审计） 简单理解 像“门卫”，必须先经过它，才能进入内部系统。\n图示 graph LR\rA[本地电脑] --\u0026gt; B[跳板机]\rB --\u0026gt; C[内网服务器] 常见连接方式 ssh -J user@跳板机 user@内网服务器\n3. 隧道（SSH Tunnel）是什么 SSH 隧道是一种通过 SSH 转发网络流量的技术。\n本质 把本地请求“通过 SSH 转发到远程”\n3.1 本地端口转发（Local Forward） 示例 ssh -L 3307:127.0.0.1:3306 user@服务器\n含义 本地 3307 → 远程 3306 图示 graph LR\rA[本地:3307] --\u0026gt;|SSH隧道| B[远程服务器]\rB --\u0026gt; C[数据库:3306] 3.2 远程端口转发（Remote Forward） 示例 ssh -R 8080:localhost:3000 user@服务器\n含义 服务器 8080 → 本地 3000 图示 graph LR\rA[远程服务器:8080] --\u0026gt;|SSH隧道| B[本地电脑]\rB --\u0026gt; C[本地服务:3000] 4. 三者关系总结 概念 作用 类比 SSH 加密远程连接 电话线（加密） 跳板机 访问中转 门卫 隧道 数据转发 地下管道 5. 企业常见链路 graph LR\rA[本地电脑] --\u0026gt; B[跳板机]\rB --\u0026gt; C[内网服务器]\rA --\u0026gt;|SSH隧道| C 6. 总结 SSH：安全远程连接协议 跳板机：进入内网的入口 隧道：通过 SSH 转发流量 ","permalink":"https://leochu.work/blog/tech/engineering/ssh%E8%B7%B3%E6%9D%BF%E6%9C%BA%E5%92%8C%E9%9A%A7%E9%81%93/","summary":"\u003ch2 id=\"1-ssh-是什么\"\u003e1. SSH 是什么\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eSSH（Secure Shell）\u003c/strong\u003e 是一种用于远程登录和通信的加密协议。\u003c/p\u003e\n\u003ch3 id=\"核心作用\"\u003e核心作用\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e远程登录服务器\u003c/li\u003e\n\u003cli\u003e执行命令\u003c/li\u003e\n\u003cli\u003e安全传输数据\u003c/li\u003e\n\u003cli\u003e建立加密通道\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"简单理解\"\u003e简单理解\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e就像一根“加密的远程操作线”，你在本地操作，实际在远程执行。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"图示\"\u003e图示\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-mermaid\" data-lang=\"mermaid\"\u003egraph LR\r\nA[本地电脑] -- SSH连接 --\u0026gt; B[远程服务器]\r\nA --\u0026gt;|输入命令| B\r\nB --\u0026gt;|返回结果| A\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003ch2 id=\"2-跳板机bastion-host是什么\"\u003e2. 跳板机（Bastion Host）是什么\u003c/h2\u003e\n\u003cp\u003e跳板机是一台\u003cstrong\u003e中间服务器\u003c/strong\u003e，用于访问内网机器。\u003c/p\u003e\n\u003ch3 id=\"为什么需要\"\u003e为什么需要\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e内网服务器不能直接暴露到公网\u003c/li\u003e\n\u003cli\u003e提供统一入口（安全控制、审计）\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"简单理解-1\"\u003e简单理解\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e像“门卫”，必须先经过它，才能进入内部系统。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"图示-1\"\u003e图示\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-mermaid\" data-lang=\"mermaid\"\u003egraph LR\r\nA[本地电脑] --\u0026gt; B[跳板机]\r\nB --\u0026gt; C[内网服务器]\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"常见连接方式\"\u003e常见连接方式\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003essh -J user@跳板机 user@内网服务器\u003c/code\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"3-隧道ssh-tunnel是什么\"\u003e3. 隧道（SSH Tunnel）是什么\u003c/h2\u003e\n\u003cp\u003eSSH 隧道是一种通过 SSH 转发网络流量的技术。\u003c/p\u003e\n\u003ch3 id=\"本质\"\u003e本质\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e把本地请求“通过 SSH 转发到远程”\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch2 id=\"31-本地端口转发local-forward\"\u003e3.1 本地端口转发（Local Forward）\u003c/h2\u003e\n\u003ch3 id=\"示例\"\u003e示例\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003essh -L 3307:127.0.0.1:3306 user@服务器\u003c/code\u003e\u003c/p\u003e","title":"SSH、跳板机和隧道"},{"content":"1. 三个核心问题 整个体系其实在解决三件事：\n你是谁？（身份认证） 数据能不能被偷看？（加密传输） 如何安全地交换密钥？（密钥分发） 2. 非对称加密（公钥 / 私钥） 核心定义 使用一对密钥：公钥 + 私钥\n公钥：可以公开 私钥：必须保密 二者不可互相推导 两种用途（关键区分） ① 加密通信 graph LR\rA[发送方] --\u0026gt;|公钥加密| B[密文]\rB --\u0026gt; C[接收方]\rC --\u0026gt;|私钥解密| D[明文] ② 身份认证（SSH） sequenceDiagram\rparticipant A as 客户端（私钥）\rparticipant B as 服务器（公钥）\rB-\u0026gt;\u0026gt;A: challenge\rA-\u0026gt;\u0026gt;B: signature\rB-\u0026gt;\u0026gt;B: 公钥验证\rB-\u0026gt;\u0026gt;A: 通过/拒绝 关键公式 signature = Sign(私钥, challenge)\nVerify(公钥, challenge, signature) = true\n本质总结 私钥负责“生成证明”，公钥负责“验证证明”\n3. SSH：基于签名的认证机制 文件结构 ~/.ssh/id_ed25519 # 私钥 ~/.ssh/id_ed25519.pub # 公钥 ~/.ssh/authorized_keys # 服务器保存公钥 免密登录本质 服务器信任“某个公钥”，允许对应私钥登录\n认证流程（核心） sequenceDiagram\rparticipant A as 本地电脑\rparticipant B as 服务器\rA-\u0026gt;\u0026gt;B: 发起连接\rB-\u0026gt;\u0026gt;A: challenge（随机数）\rA-\u0026gt;\u0026gt;A: 私钥签名\rA-\u0026gt;\u0026gt;B: 返回 signature\rB-\u0026gt;\u0026gt;B: 公钥验证\rB-\u0026gt;\u0026gt;A: 登录成功 为什么安全 challenge 每次不同（防重放） 无法从公钥推导私钥 只有私钥能生成正确签名 4. 对称加密：真正的数据传输 定义 加密和解密使用同一把 key\n图示 graph LR\rA[发送方] --\u0026gt;|同一key加密| B[密文]\rB --\u0026gt; C[接收方]\rC --\u0026gt;|同一key解密| D[明文] 特点 ✔ 非常快（适合大量数据） ❌ 密钥分发困难 常见算法 算法 说明 AES 主流标准 DES 已淘汰 5. HTTPS：非对称 + 对称的组合 核心思想 用非对称加密交换对称密钥，再用对称加密通信\n握手流程 sequenceDiagram\rparticipant C as 客户端\rparticipant S as 服务器\rC-\u0026gt;\u0026gt;S: ClientHello\rS-\u0026gt;\u0026gt;C: 证书（含公钥）\rC-\u0026gt;\u0026gt;C: 验证证书\rC-\u0026gt;\u0026gt;S: 用公钥加密 session_key\rS-\u0026gt;\u0026gt;S: 私钥解密 session_key\rC-\u0026gt;\u0026gt;S: 对称加密通信\rS-\u0026gt;\u0026gt;C: 对称加密通信 关键公式 session_key = Random()\nencrypted = Encrypt(公钥, session_key)\n本质总结 非对称：交换 key 对称：传输数据 6. Kerberos：另一种认证体系 核心思想 不用公钥体系，而是“票据 + 对称加密”\n流程 sequenceDiagram\rparticipant C as 客户端\rparticipant K as KDC\rparticipant S as 服务\rC-\u0026gt;\u0026gt;K: 请求认证\rK-\u0026gt;\u0026gt;C: 返回 TGT\rC-\u0026gt;\u0026gt;K: 请求服务票据\rK-\u0026gt;\u0026gt;C: 返回 Ticket\rC-\u0026gt;\u0026gt;S: 带 Ticket 访问\rS-\u0026gt;\u0026gt;C: 验证通过 特点 ✔ 基于对称加密 ✔ 强依赖中心（KDC） ✔ 不直接传密码 7. 三大体系对比 系统 核心机制 加密方式 用途 SSH 签名认证 非对称 + 对称 远程登录 HTTPS 密钥交换 非对称 + 对称 安全通信 Kerberos 票据认证 对称 集中认证 8. 总结 非对称加密：解决“信任和身份问题” 对称加密：解决“数据安全高效传输问题” SSH：用私钥证明身份 HTTPS：用公钥交换密钥 Kerberos：用票据做集中认证 ","permalink":"https://leochu.work/blog/tech/engineering/%E5%8A%A0%E5%AF%86%E4%BD%93%E7%B3%BB/","summary":"\u003ch2 id=\"1-三个核心问题\"\u003e1. 三个核心问题\u003c/h2\u003e\n\u003cp\u003e整个体系其实在解决三件事：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e你是谁？（身份认证）\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e数据能不能被偷看？（加密传输）\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e如何安全地交换密钥？（密钥分发）\u003c/strong\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch2 id=\"2-非对称加密公钥--私钥\"\u003e2. 非对称加密（公钥 / 私钥）\u003c/h2\u003e\n\u003ch3 id=\"核心定义\"\u003e核心定义\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e使用一对密钥：公钥 + 私钥\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cul\u003e\n\u003cli\u003e公钥：可以公开\u003c/li\u003e\n\u003cli\u003e私钥：必须保密\u003c/li\u003e\n\u003cli\u003e二者不可互相推导\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch3 id=\"两种用途关键区分\"\u003e两种用途（关键区分）\u003c/h3\u003e\n\u003ch4 id=\"-加密通信\"\u003e① 加密通信\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-mermaid\" data-lang=\"mermaid\"\u003egraph LR\r\nA[发送方] --\u0026gt;|公钥加密| B[密文]\r\nB --\u0026gt; C[接收方]\r\nC --\u0026gt;|私钥解密| D[明文]\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003ch4 id=\"-身份认证ssh\"\u003e② 身份认证（SSH）\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-mermaid\" data-lang=\"mermaid\"\u003esequenceDiagram\r\nparticipant A as 客户端（私钥）\r\nparticipant B as 服务器（公钥）\r\n\r\nB-\u0026gt;\u0026gt;A: challenge\r\nA-\u0026gt;\u0026gt;B: signature\r\nB-\u0026gt;\u0026gt;B: 公钥验证\r\nB-\u0026gt;\u0026gt;A: 通过/拒绝\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003ch3 id=\"关键公式\"\u003e关键公式\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003esignature = Sign(私钥, challenge)\u003c/code\u003e\u003cbr\u003e\n\u003ccode\u003eVerify(公钥, challenge, signature) = true\u003c/code\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003ch3 id=\"本质总结\"\u003e本质总结\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e私钥负责“生成证明”，公钥负责“验证证明”\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch2 id=\"3-ssh基于签名的认证机制\"\u003e3. SSH：基于签名的认证机制\u003c/h2\u003e\n\u003ch3 id=\"文件结构\"\u003e文件结构\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e~/.ssh/id_ed25519      \u003cspan style=\"color:#75715e\"\u003e# 私钥\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e~/.ssh/id_ed25519.pub  \u003cspan style=\"color:#75715e\"\u003e# 公钥\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e~/.ssh/authorized_keys \u003cspan style=\"color:#75715e\"\u003e# 服务器保存公钥\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003chr\u003e\n\u003ch3 id=\"免密登录本质\"\u003e免密登录本质\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e服务器信任“某个公钥”，允许对应私钥登录\u003c/p\u003e","title":"加密体系"},{"content":"之前那篇关于“洞穴时间”的文章，言辞可能过于激烈，观点也有失偏颇。\n很多人把“洞穴时间”理解为男人的本能需求，是需要独处、放空、自我修复的一种方式。\n但这次，我想换个更刺耳的说法——女人见不得男人开心。\n关键点并不在于“见不得”本身，而是——当男人的快乐不包含她时，这种快乐，往往会变得不被允许、不被理解，甚至需要被纠正。\n他和朋友聚会很开心，会被一遍遍追问、被扫兴； 他钓鱼很开心，会被打断、被催促，直到那点兴致被消磨干净； 他打游戏很开心，会被上升为浪费时间、不务正业。\n甚至，就像前文那种情况—— 他只能在她睡着后才能安心打会游戏，不然可能被视为一种“冷落”、一种“有问题的行为”，迟早要被翻出来对账。\n于是，开心本身，开始变成一件有风险的事。\n男人只能把这些不被允许的轻松、松弛、无负担的时刻，压缩进深夜，压缩进车里那半小时，压缩进那盏不关的灯里。\n所谓“洞穴时间”，不再只是独处，而是被挤压出来的安全空间。\n最近读了点社会心理学，书上说这种冲突并不罕见。 男性更倾向于通过“切换场景”来恢复精力，而女性更倾向于通过“维持连接”来获得安全感。当一方把“离开”理解为放松，另一方却把它解读为疏远，冲突就产生了。\n但问题在于—— 如果一段关系要求一个人只能在关系之内快乐，那这段关系本身，就已经开始变得空气稀薄。\n一个更健康的状态应该是： 你可以参与我的快乐，但你不需要垄断它； 我可以离开一会儿，但不是在逃离你。\n真正稳固的关系，不是没有“洞穴时间”， 而是—— 即使不在彼此身边，也不会因此被怀疑、被惩罚、被剥夺获取快乐的权利。\n附: 男人来自火星女人来自金星 亲密关系\n","permalink":"https://leochu.work/blog/thoughts/%E5%86%8D%E8%B0%88%E6%B4%9E%E7%A9%B4%E6%97%B6%E9%97%B4/","summary":"\u003cp\u003e之前那篇关于“洞穴时间”的文章，言辞可能过于激烈，观点也有失偏颇。\u003c/p\u003e\n\u003cp\u003e很多人把“洞穴时间”理解为男人的本能需求，是需要独处、放空、自我修复的一种方式。\u003c/p\u003e\n\u003cp\u003e但这次，我想换个更刺耳的说法——女人见不得男人开心。\u003c/p\u003e\n\u003cp\u003e关键点并不在于“见不得”本身，而是——当男人的快乐不包含她时，这种快乐，往往会变得不被允许、不被理解，甚至需要被纠正。\u003c/p\u003e\n\u003cp\u003e他和朋友聚会很开心，会被一遍遍追问、被扫兴； 他钓鱼很开心，会被打断、被催促，直到那点兴致被消磨干净； 他打游戏很开心，会被上升为浪费时间、不务正业。\u003c/p\u003e\n\u003cp\u003e甚至，就像前文那种情况—— 他只能在她睡着后才能安心打会游戏，不然可能被视为一种“冷落”、一种“有问题的行为”，迟早要被翻出来对账。\u003c/p\u003e\n\u003cp\u003e于是，开心本身，开始变成一件有风险的事。\u003c/p\u003e\n\u003cp\u003e男人只能把这些不被允许的轻松、松弛、无负担的时刻，压缩进深夜，压缩进车里那半小时，压缩进那盏不关的灯里。\u003c/p\u003e\n\u003cp\u003e所谓“洞穴时间”，不再只是独处，而是被挤压出来的安全空间。\u003c/p\u003e\n\u003cp\u003e最近读了点社会心理学，书上说这种冲突并不罕见。 男性更倾向于通过“切换场景”来恢复精力，而女性更倾向于通过“维持连接”来获得安全感。当一方把“离开”理解为放松，另一方却把它解读为疏远，冲突就产生了。\u003c/p\u003e\n\u003cp\u003e但问题在于—— 如果一段关系要求一个人只能在关系之内快乐，那这段关系本身，就已经开始变得空气稀薄。\u003c/p\u003e\n\u003cp\u003e一个更健康的状态应该是： 你可以参与我的快乐，但你不需要垄断它； 我可以离开一会儿，但不是在逃离你。\u003c/p\u003e\n\u003cp\u003e真正稳固的关系，不是没有“洞穴时间”， 而是—— 即使不在彼此身边，也不会因此被怀疑、被惩罚、被剥夺获取快乐的权利。\u003c/p\u003e\n\u003cp\u003e附: \u003ca href=\"/blog/reading/%E7%94%B7%E4%BA%BA%E6%9D%A5%E8%87%AA%E7%81%AB%E6%98%9F%E5%A5%B3%E4%BA%BA%E6%9D%A5%E8%87%AA%E9%87%91%E6%98%9F/\"\u003e男人来自火星女人来自金星\u003c/a\u003e \u003ca href=\"/blog/reading/%E4%BA%B2%E5%AF%86%E5%85%B3%E7%B3%BB/\"\u003e亲密关系\u003c/a\u003e\u003c/p\u003e","title":"再谈洞穴时间"},{"content":"很多人谈 AI 编程，谈到最后都会滑向两个方向：要么研究 prompt 话术，要么研究工具排行榜。可真正把项目做下来之后会发现，决定结果的往往不是模型有多强，而是任务有没有被稳定推进。\n所以我越来越确信，AI 编程的本质不是写代码，而是 GSD: Get Shit Done。\n这不是一句鸡血口号，而是一种工作判断。AI 最擅长的是局部生成，人最不可替代的是定义目标、控制边界、校验结果、决定下一步。谁负责把事情做完，谁就掌握了 AI 编程的核心。\n什么是 GSD GSD 本来就不是 AI 圈专属概念，它说的是一种执行方法：少空转，少沉迷完美方案，尽快把任务拆开、落地、验证、收口。\n它在 AI 编程里尤其重要，因为 AI 把“产出代码”这件事变得太便宜了。过去写错一段代码，要付出几十分钟；现在模型可以在错误方向上连续输出三轮，还都看起来像那么回事。于是，真正稀缺的能力不再是“产出更多”，而是“识别什么值得继续推进”。\n从这个角度看，AI 时代没有削弱软件工程，反而把它最硬的那一面暴露出来了：任务拆分、上下文管理、验收标准、回滚意识、重构节奏，这些东西一个都没消失。\nAI 编程真正的问题 AI 编程当然快，但快不等于高效。它最常见的问题有三个。\n低效 表面上，模型替你写了很多代码；实际上，你只是把时间从“自己写”换成了“审它、修它、补上下文”。如果生成规模大于验证能力，效率往往是下降的。\n反复试错 很多人以为自己在迭代，实际上只是陷入了“生成一版、修一版、再修一版”的循环。每一轮都像在前进，但系统并没有更接近完成状态，只是在不断清理上一轮制造的噪音。\n上下文失真 模型对项目的理解不是稳定存在的，它只是在当前上下文里做局部推断。上下文一长、一脏、一旧，就会开始忘约定、乱覆盖、重复犯错。很多所谓“模型不靠谱”，本质上是上下文早就失控了。\n所以 AI 编程的问题，从来不只是模型问题，更是工作流问题。\nGSD 的核心原则 拆任务 不要让 AI 一次处理一个模糊的大目标。大任务一旦没有边界，生成结果几乎注定失真。AI 适合吃小而明确的问题，不适合替你发明完整项目。\n快速验证 每走一步，都要尽快确认这一步到底有没有真的往前。能不能运行，输出对不对，页面能不能打开，接口是不是污染了别的模块。这些检查越便宜，闭环越短，任务越稳。\n循环迭代 AI 编程最好的节奏不是一次性生成大而全，而是持续压缩闭环：\n给清晰目标\n生成最小结果\n立刻验证\n根据结果继续下一轮\nGSD 的价值，就在于它把“生成”放回“推进”之内，而不是把生成本身误当成进展。\n一个够用的标准流程 如果要把 GSD 落到 AI 编程里，我觉得最实用的流程就是五步：\n需求 先把目标说清楚。不是“我想做个什么”，而是输入输出是什么、约束是什么、做到什么算完成。没有完成标准，后面的每一轮生成都会发飘。\nPrompt Prompt 的重点不是修辞，而是约束。你至少要说清背景、任务、涉及范围、禁止事项和输出形式。不给边界，模型就会自动脑补。\n生成 不要追求“最终答案”，而要追求“可验证的下一步”。一轮生成如果已经大到你自己都不愿意审，那通常就已经超出合理规模了。\n校验 至少检查三件事：它说的和它做的是不是一致，它生成的东西能不能跑，它有没有破坏原来的逻辑。AI 编程里，校验不是附属动作，而是主流程本身。\n重构 先让功能成立，再让结构变好。把“先做出来”和“再整理干净”分成两轮，AI 的价值会稳定很多。它很适合做第二轮的清理、提取、收束，而不是在第一轮就替你发明完美架构。\n工具不是答案，组合才是 GSD 不依赖某个神奇模型，也不依赖某个 IDE。工具的意义，只在于它被安排在什么位置。\nClaude 适合理解长上下文、拆任务、看结构\nGPT 适合快速生成和高频往返\nCursor 适合把读代码、改代码、跑结果串成一个闭环\nCopilot 适合局部补全和碎片化提速\n重点从来不是“谁最强”，而是谁负责哪一段链路。如果所有工具都在同时生成，而没人负责收口，那工作流照样会散。\n常见误区 把 AI 当最终裁判 AI 生成的是候选方案，不是真理。它可以是搭档、草稿机、重构助手，但不能代替验收。\n以为 prompt 越长越专业 没有结构的长 prompt 只会让上下文变脏。真正有效的不是信息越多越好，而是约束越清楚越好。\n一次做太多 这是最常见的失控源头。AI 很容易制造一种“顺手把这个也做了”的冲动，但任务一旦过大，验证成本会指数上升。\n不管理上下文 会话里堆满失败尝试、过时代码和旧约定，只会让模型越来越不稳定。该重开就重开，该重写摘要就重写摘要。\n为什么 GSD 适合 AI 时代 因为 AI 让“生成”变便宜了，但没有让“完成”变自动。\n真正决定项目成败的，仍然是这些老问题：目标清不清楚，任务拆得对不对，校验做得够不够，什么时候该继续，什么时候该回滚。AI 提高了局部产出的上限，也放大了流程混乱的代价。\n所以说到底，AI 编程不是“让模型替你写代码”，而是让你用模型持续推进任务，直到真的交付结果。\n这就是 GSD。\n","permalink":"https://leochu.work/blog/tech/ai/ai%E7%BC%96%E7%A8%8B%E7%9A%84%E6%9C%AC%E8%B4%A8%E4%B8%8D%E6%98%AF%E5%86%99%E4%BB%A3%E7%A0%81%E8%80%8C%E6%98%AFgsd/","summary":"\u003cp\u003e很多人谈 AI 编程，谈到最后都会滑向两个方向：要么研究 prompt 话术，要么研究工具排行榜。可真正把项目做下来之后会发现，决定结果的往往不是模型有多强，而是任务有没有被稳定推进。\u003c/p\u003e\n\u003cp\u003e所以我越来越确信，AI 编程的本质不是写代码，而是 \u003cstrong\u003eGSD: Get Shit Done\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e这不是一句鸡血口号，而是一种工作判断。AI 最擅长的是局部生成，人最不可替代的是定义目标、控制边界、校验结果、决定下一步。谁负责把事情做完，谁就掌握了 AI 编程的核心。\u003c/p\u003e\n\u003ch2 id=\"什么是-gsd\"\u003e什么是 GSD\u003c/h2\u003e\n\u003cp\u003eGSD 本来就不是 AI 圈专属概念，它说的是一种执行方法：少空转，少沉迷完美方案，尽快把任务拆开、落地、验证、收口。\u003c/p\u003e\n\u003cp\u003e它在 AI 编程里尤其重要，因为 AI 把“产出代码”这件事变得太便宜了。过去写错一段代码，要付出几十分钟；现在模型可以在错误方向上连续输出三轮，还都看起来像那么回事。于是，真正稀缺的能力不再是“产出更多”，而是“识别什么值得继续推进”。\u003c/p\u003e\n\u003cp\u003e从这个角度看，AI 时代没有削弱软件工程，反而把它最硬的那一面暴露出来了：任务拆分、上下文管理、验收标准、回滚意识、重构节奏，这些东西一个都没消失。\u003c/p\u003e\n\u003ch2 id=\"ai-编程真正的问题\"\u003eAI 编程真正的问题\u003c/h2\u003e\n\u003cp\u003eAI 编程当然快，但快不等于高效。它最常见的问题有三个。\u003c/p\u003e\n\u003ch3 id=\"低效\"\u003e低效\u003c/h3\u003e\n\u003cp\u003e表面上，模型替你写了很多代码；实际上，你只是把时间从“自己写”换成了“审它、修它、补上下文”。如果生成规模大于验证能力，效率往往是下降的。\u003c/p\u003e\n\u003ch3 id=\"反复试错\"\u003e反复试错\u003c/h3\u003e\n\u003cp\u003e很多人以为自己在迭代，实际上只是陷入了“生成一版、修一版、再修一版”的循环。每一轮都像在前进，但系统并没有更接近完成状态，只是在不断清理上一轮制造的噪音。\u003c/p\u003e\n\u003ch3 id=\"上下文失真\"\u003e上下文失真\u003c/h3\u003e\n\u003cp\u003e模型对项目的理解不是稳定存在的，它只是在当前上下文里做局部推断。上下文一长、一脏、一旧，就会开始忘约定、乱覆盖、重复犯错。很多所谓“模型不靠谱”，本质上是上下文早就失控了。\u003c/p\u003e\n\u003cp\u003e所以 AI 编程的问题，从来不只是模型问题，更是工作流问题。\u003c/p\u003e\n\u003ch2 id=\"gsd-的核心原则\"\u003eGSD 的核心原则\u003c/h2\u003e\n\u003ch3 id=\"拆任务\"\u003e拆任务\u003c/h3\u003e\n\u003cp\u003e不要让 AI 一次处理一个模糊的大目标。大任务一旦没有边界，生成结果几乎注定失真。AI 适合吃小而明确的问题，不适合替你发明完整项目。\u003c/p\u003e\n\u003ch3 id=\"快速验证\"\u003e快速验证\u003c/h3\u003e\n\u003cp\u003e每走一步，都要尽快确认这一步到底有没有真的往前。能不能运行，输出对不对，页面能不能打开，接口是不是污染了别的模块。这些检查越便宜，闭环越短，任务越稳。\u003c/p\u003e\n\u003ch3 id=\"循环迭代\"\u003e循环迭代\u003c/h3\u003e\n\u003cp\u003eAI 编程最好的节奏不是一次性生成大而全，而是持续压缩闭环：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e给清晰目标\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e生成最小结果\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e立刻验证\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e根据结果继续下一轮\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eGSD 的价值，就在于它把“生成”放回“推进”之内，而不是把生成本身误当成进展。\u003c/p\u003e\n\u003ch2 id=\"一个够用的标准流程\"\u003e一个够用的标准流程\u003c/h2\u003e\n\u003cp\u003e如果要把 GSD 落到 AI 编程里，我觉得最实用的流程就是五步：\u003c/p\u003e\n\u003ch3 id=\"需求\"\u003e需求\u003c/h3\u003e\n\u003cp\u003e先把目标说清楚。不是“我想做个什么”，而是输入输出是什么、约束是什么、做到什么算完成。没有完成标准，后面的每一轮生成都会发飘。\u003c/p\u003e\n\u003ch3 id=\"prompt\"\u003ePrompt\u003c/h3\u003e\n\u003cp\u003ePrompt 的重点不是修辞，而是约束。你至少要说清背景、任务、涉及范围、禁止事项和输出形式。不给边界，模型就会自动脑补。\u003c/p\u003e","title":"AI编程的本质不是写代码，而是GSD"},{"content":"哪里有彩虹告诉我 能不能把我的愿望还给我 为什么天这么安静 所有的云都跑到我这里 有没有口罩一个给我 释怀说了太多就成真不了 也许时间是一种解药 也是我现在正服下的毒药 看不见你的笑 我怎么睡得着 你的声音这么近我却抱不到 没有地球太阳还是会绕 没有理由我也能自己走 你要离开 我知道很简单 你说依赖 是我们的阻碍 就算放开 但能不能别没收我的爱 当作我最后才明白\n","permalink":"https://leochu.work/blog/thoughts/%E5%BD%A9%E8%99%B9/","summary":"\u003cp\u003e哪里有彩虹告诉我 能不能把我的愿望还给我 为什么天这么安静 所有的云都跑到我这里 有没有口罩一个给我 释怀说了太多就成真不了 也许时间是一种解药 也是我现在正服下的毒药 看不见你的笑 我怎么睡得着 你的声音这么近我却抱不到 没有地球太阳还是会绕 没有理由我也能自己走 你要离开 我知道很简单 你说依赖 是我们的阻碍 就算放开 但能不能别没收我的爱 当作我最后才明白\u003c/p\u003e","title":"彩虹"},{"content":"你现在是一个软件工程师助手，需要与我协作完成开发任务。\n请严格遵守以下流程：\n【阶段一：分析与方案】\n不要直接修改代码 先完整理解需求和现有代码结构 给出一个详细的实现方案，包括： 修改哪些文件 每一步要做什么 可能的风险点 是否有更优方案（如有请说明） 输出清晰的分步骤计划（Step 1, Step 2, \u0026hellip;） 完成后，停止执行，等待我的确认。\n【阶段二：执行（必须等我确认后）】\n严格按照确认的步骤逐步执行 每执行一步都需要： 说明本步做了什么 展示具体代码改动（diff） 不要一次性完成所有步骤 【约束规则】\n不允许跳过方案阶段 不允许一次性修改多个步骤 不允许未经确认直接改代码 如果发现方案需要调整，必须重新提出方案并等待确认 【输出要求】\n结构清晰，步骤编号明确 解释要简洁但完整 ","permalink":"https://leochu.work/blog/tech/ai/codex%E7%B1%BBclaude%E6%8F%90%E7%A4%BA%E8%AF%8D%E7%BA%A6%E6%9D%9F/","summary":"\u003cp\u003e你现在是一个软件工程师助手，需要与我协作完成开发任务。\u003c/p\u003e\n\u003cp\u003e请严格遵守以下流程：\u003c/p\u003e\n\u003cp\u003e【阶段一：分析与方案】\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e不要直接修改代码\u003c/li\u003e\n\u003cli\u003e先完整理解需求和现有代码结构\u003c/li\u003e\n\u003cli\u003e给出一个详细的实现方案，包括：\n\u003cul\u003e\n\u003cli\u003e修改哪些文件\u003c/li\u003e\n\u003cli\u003e每一步要做什么\u003c/li\u003e\n\u003cli\u003e可能的风险点\u003c/li\u003e\n\u003cli\u003e是否有更优方案（如有请说明）\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e输出清晰的分步骤计划（Step 1, Step 2, \u0026hellip;）\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e完成后，停止执行，等待我的确认。\u003c/p\u003e\n\u003cp\u003e【阶段二：执行（必须等我确认后）】\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e严格按照确认的步骤逐步执行\u003c/li\u003e\n\u003cli\u003e每执行一步都需要：\n\u003cul\u003e\n\u003cli\u003e说明本步做了什么\u003c/li\u003e\n\u003cli\u003e展示具体代码改动（diff）\u003c/li\u003e\n\u003cli\u003e不要一次性完成所有步骤\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e【约束规则】\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e不允许跳过方案阶段\u003c/li\u003e\n\u003cli\u003e不允许一次性修改多个步骤\u003c/li\u003e\n\u003cli\u003e不允许未经确认直接改代码\u003c/li\u003e\n\u003cli\u003e如果发现方案需要调整，必须重新提出方案并等待确认\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e【输出要求】\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e结构清晰，步骤编号明确\u003c/li\u003e\n\u003cli\u003e解释要简洁但完整\u003c/li\u003e\n\u003c/ul\u003e","title":"codex类claude提示词约束"},{"content":"多重执行xcall.sh\n#!/bin/bash\r# 遍历所有节点（集群主机列表）\rfor i in cdhmaster01 cdhmaster02 cdhmaster03 cdhnode01 cdhnode02 cdhnode03\rdo\r# 打印当前正在操作的主机，方便区分输出\recho \u0026#34;--------- $i ----------\u0026#34;\r# 通过 ssh 在远程主机执行传入的命令\r# \u0026#34;$*\u0026#34; 表示把所有参数当作一个整体传过去\r# 例如：./xcall.sh pwd\r# 实际执行：ssh cdhmaster01 \u0026#34;pwd\u0026#34;\rssh $i \u0026#34;$*\u0026#34;\rdone 集群分发xsync.sh\n#!/bin/bash\rif [ $# -eq 0 ]; then\recho \u0026#34;Usage: $0 [more files_or_dirs]\u0026#34;\rexit 1\rfi\rhosts=(cdhmaster01 cdhmaster02 cdhmaster03 cdhnode01 cdhnode02 cdhnode03)\ruser=$(whoami)\rfor src in \u0026#34;$@\u0026#34;\rdo\rif [ ! -e \u0026#34;$src\u0026#34; ]; then\recho \u0026#34;Not found: $src\u0026#34;\rcontinue\rfi\rabs_src=$(readlink -f \u0026#34;$src\u0026#34;)\rname=$(basename \u0026#34;$abs_src\u0026#34;)\rparent=$(dirname \u0026#34;$abs_src\u0026#34;)\rfor host in \u0026#34;${hosts[@]}\u0026#34;\rdo\recho \u0026#34;===== $host =====\u0026#34;\rssh \u0026#34;$host\u0026#34; \u0026#34;mkdir -p \u0026#39;$parent\u0026#39;\u0026#34;\rif [ -d \u0026#34;$abs_src\u0026#34; ]; then\rrsync -av \u0026#34;$abs_src\u0026#34; \u0026#34;$user@$host:$parent/\u0026#34;\relse\rrsync -av \u0026#34;$abs_src\u0026#34; \u0026#34;$user@$host:$parent/\u0026#34;\rfi\rdone\rdone ","permalink":"https://leochu.work/blog/tech/engineering/%E9%9B%86%E7%BE%A4%E5%B8%B8%E7%94%A8%E8%84%9A%E6%9C%AC/","summary":"\u003cp\u003e多重执行xcall.sh\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#!/bin/bash\r\n\r\n# 遍历所有节点（集群主机列表）\r\nfor i in cdhmaster01 cdhmaster02 cdhmaster03 cdhnode01 cdhnode02 cdhnode03\r\ndo\r\n  # 打印当前正在操作的主机，方便区分输出\r\n  echo \u0026#34;--------- $i ----------\u0026#34;\r\n\r\n  # 通过 ssh 在远程主机执行传入的命令\r\n  # \u0026#34;$*\u0026#34; 表示把所有参数当作一个整体传过去\r\n  # 例如：./xcall.sh pwd\r\n  # 实际执行：ssh cdhmaster01 \u0026#34;pwd\u0026#34;\r\n  ssh $i \u0026#34;$*\u0026#34;\r\ndone\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e集群分发xsync.sh\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#!/bin/bash\r\n\r\nif [ $# -eq 0 ]; then\r\n  echo \u0026#34;Usage: $0  [more files_or_dirs]\u0026#34;\r\n  exit 1\r\nfi\r\n\r\nhosts=(cdhmaster01 cdhmaster02 cdhmaster03 cdhnode01 cdhnode02 cdhnode03)\r\nuser=$(whoami)\r\n\r\nfor src in \u0026#34;$@\u0026#34;\r\ndo\r\n  if [ ! -e \u0026#34;$src\u0026#34; ]; then\r\n    echo \u0026#34;Not found: $src\u0026#34;\r\n    continue\r\n  fi\r\n\r\n  abs_src=$(readlink -f \u0026#34;$src\u0026#34;)\r\n  name=$(basename \u0026#34;$abs_src\u0026#34;)\r\n  parent=$(dirname \u0026#34;$abs_src\u0026#34;)\r\n\r\n  for host in \u0026#34;${hosts[@]}\u0026#34;\r\n  do\r\n    echo \u0026#34;===== $host =====\u0026#34;\r\n\r\n    ssh \u0026#34;$host\u0026#34; \u0026#34;mkdir -p \u0026#39;$parent\u0026#39;\u0026#34;\r\n\r\n    if [ -d \u0026#34;$abs_src\u0026#34; ]; then\r\n      rsync -av \u0026#34;$abs_src\u0026#34; \u0026#34;$user@$host:$parent/\u0026#34;\r\n    else\r\n      rsync -av \u0026#34;$abs_src\u0026#34; \u0026#34;$user@$host:$parent/\u0026#34;\r\n    fi\r\n  done\r\ndone\n\u003c/code\u003e\u003c/pre\u003e","title":"集群常用脚本"},{"content":"一、设计原则 标签用于表达文章的“内容特征”，而不是分类。\n分类（categories）用于表示文章所属目录（放在哪），标签（tags）用于描述文章内容（讲什么）。\n标签统一为单层结构，不做层级划分。\n标签体系同时服务两种需求：\n浏览：快速理解文章内容 检索：精确定位知识点 二、标签分类（逻辑分层） 1. 技术类标签（核心索引） 用于标识具体技术或组件，可包含低频标签：\nJVM\nJava\nPython\nDjango\nGit\nMaven\nLinux\nHive\nSpark\nKafka\nFlink\nHadoop\nYarn\nFlume\nPresto ClickHouse\nMySQL\nMongoDB\nElasticsearch\n说明：\n技术标签允许低频存在，用于精确检索，不需要强制精简。\n2. 内容类型标签（高频 ⭐） 用于标识文章表达形式：\n速查\n总结\n原理\n教程\n配置\n调优\n源码\n笔记\n摘录\n记录\n3. 场景类标签（中频） 用于标识问题或使用场景：\n踩坑\n实战\n排错\n面试\n4. 通用认知标签（非技术内容 ⭐） 用于 reading / thoughts / ideas：\n思考\n认知\n学习\n产品\n副业\n灵感\n想法\n三、使用规范（重点） 每篇文章建议使用 2～5 个标签 建议组合： 1～2 个技术标签（可低频） 1 个类型标签（必选） 可选 1 个场景或认知标签 技术标签优先表达“具体技术点” 类型标签控制文章表达形式（避免缺失） 避免重复语义标签（如：调优 vs 优化，仅保留调优） 四、示例 示例 1：Git 命令速查 tags:\nGit 速查 示例 2：Kafka 调优 tags:\nKafka 调优 实战 示例 3：JVM 原理 tags:\nJVM 原理 示例 4：Hive 问题排查 tags:\nHive 踩坑 排错 示例 5：读书笔记 tags:\n笔记 摘录 认知 示例 6：随笔 / 思考 tags:\n思考 认知 示例 7：产品想法 tags:\n想法 灵感 产品 五、补充说明 技术标签优先使用英文（Kafka / JVM） 类型、场景、认知类标签使用中文 技术标签允许低频存在（用于检索） 标签体系保持稳定，避免频繁增删 优先通过组合表达含义，而不是新增标签 ","permalink":"https://leochu.work/blog/ideas/%E5%8D%9A%E5%AE%A2%E6%A0%87%E7%AD%BE%E8%AE%BE%E8%AE%A1/","summary":"\u003ch2 id=\"一设计原则\"\u003e一、设计原则\u003c/h2\u003e\n\u003cp\u003e标签用于表达文章的“内容特征”，而不是分类。\u003c/p\u003e\n\u003cp\u003e分类（categories）用于表示文章所属目录（放在哪），标签（tags）用于描述文章内容（讲什么）。\u003c/p\u003e\n\u003cp\u003e标签统一为单层结构，不做层级划分。\u003c/p\u003e\n\u003cp\u003e标签体系同时服务两种需求：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e浏览：快速理解文章内容\u003c/li\u003e\n\u003cli\u003e检索：精确定位知识点\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"二标签分类逻辑分层\"\u003e二、标签分类（逻辑分层）\u003c/h2\u003e\n\u003ch3 id=\"1-技术类标签核心索引\"\u003e1. 技术类标签（核心索引）\u003c/h3\u003e\n\u003cp\u003e用于标识具体技术或组件，可包含低频标签：\u003c/p\u003e\n\u003cp\u003eJVM\u003cbr\u003e\nJava\u003cbr\u003e\nPython\u003cbr\u003e\nDjango\u003cbr\u003e\nGit\u003cbr\u003e\nMaven\u003cbr\u003e\nLinux\u003c/p\u003e\n\u003cp\u003eHive\u003cbr\u003e\nSpark\u003cbr\u003e\nKafka\u003cbr\u003e\nFlink\u003cbr\u003e\nHadoop\u003cbr\u003e\nYarn\u003cbr\u003e\nFlume\u003cbr\u003e\nPresto\nClickHouse\u003c/p\u003e\n\u003cp\u003eMySQL\u003cbr\u003e\nMongoDB\u003cbr\u003e\nElasticsearch\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e说明：\u003cbr\u003e\n技术标签允许低频存在，用于精确检索，不需要强制精简。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch3 id=\"2-内容类型标签高频-\"\u003e2. 内容类型标签（高频 ⭐）\u003c/h3\u003e\n\u003cp\u003e用于标识文章表达形式：\u003c/p\u003e\n\u003cp\u003e速查\u003cbr\u003e\n总结\u003cbr\u003e\n原理\u003cbr\u003e\n教程\u003cbr\u003e\n配置\u003cbr\u003e\n调优\u003cbr\u003e\n源码\u003c/p\u003e\n\u003cp\u003e笔记\u003cbr\u003e\n摘录\u003cbr\u003e\n记录\u003c/p\u003e\n\u003chr\u003e\n\u003ch3 id=\"3-场景类标签中频\"\u003e3. 场景类标签（中频）\u003c/h3\u003e\n\u003cp\u003e用于标识问题或使用场景：\u003c/p\u003e\n\u003cp\u003e踩坑\u003cbr\u003e\n实战\u003cbr\u003e\n排错\u003cbr\u003e\n面试\u003c/p\u003e\n\u003chr\u003e\n\u003ch3 id=\"4-通用认知标签非技术内容-\"\u003e4. 通用认知标签（非技术内容 ⭐）\u003c/h3\u003e\n\u003cp\u003e用于 reading / thoughts / ideas：\u003c/p\u003e\n\u003cp\u003e思考\u003cbr\u003e\n认知\u003cbr\u003e\n学习\u003c/p\u003e\n\u003cp\u003e产品\u003cbr\u003e\n副业\u003c/p\u003e\n\u003cp\u003e灵感\u003cbr\u003e\n想法\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"三使用规范重点\"\u003e三、使用规范（重点）\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e每篇文章建议使用 2～5 个标签\u003c/li\u003e\n\u003cli\u003e建议组合：\n\u003cul\u003e\n\u003cli\u003e1～2 个技术标签（可低频）\u003c/li\u003e\n\u003cli\u003e1 个类型标签（必选）\u003c/li\u003e\n\u003cli\u003e可选 1 个场景或认知标签\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e技术标签优先表达“具体技术点”\u003c/li\u003e\n\u003cli\u003e类型标签控制文章表达形式（避免缺失）\u003c/li\u003e\n\u003cli\u003e避免重复语义标签（如：调优 vs 优化，仅保留调优）\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch2 id=\"四示例\"\u003e四、示例\u003c/h2\u003e\n\u003ch3 id=\"示例-1git-命令速查\"\u003e示例 1：Git 命令速查\u003c/h3\u003e\n\u003cp\u003etags:\u003c/p\u003e","title":"博客标签设计"},{"content":"有人说，衡量两个人关系深度的方式，是看他们能聊到多深，也能玩到多野。\nDeep talk 是那种聊到凌晨两点还不困的对话。聊彼此真正在意什么，聊对这个世界有什么判断，聊价值观里哪些东西是绝对不能妥协的。不是辩论，不是说服，是两个人把各自的三观摊开来放在一起比对——发现某些地方高度重合时，那种安心感是很难言说的。能 deep talk 的人，是真的愿意被你看见的人。\nDirty talk 是另一种坦诚。它需要放下羞耻感，需要信任，需要知道对方不会因为你说了什么奇奇怪怪的话就用异样的眼光看你。它本质上也是一种语言的亲密——用语言触碰对方，用玩笑和挑逗确认彼此之间那道边界是可以随意跨过的。\n很多人能做到其中一件，却做不到两件同时。有人聊得了人生意义，一到床上就哑口无言；有人嬉皮笑脸没个正经，却永远不让你靠近他真实的内心。两件事都能做到的人，其实是说：我在你面前，没有什么是需要表演的。\n这挺难得的。\n不是每段关系都能长出这种空间。它需要时间，需要一次次试探之后积累的安全感，需要两个人都愿意先放下一点防备。有时候是一个人先开口说了一句很傻的话，另一个人没有嘲笑，于是就又多了一点胆子。慢慢的，那个空间就有了。\n所以如果你身边有这样一个人——能跟你认真聊，也能跟你不正经。\n这不是标配，这是运气。\n(享福啦)\n","permalink":"https://leochu.work/blog/thoughts/%E4%B8%A4%E7%A7%8Dtalk/","summary":"\u003cp\u003e有人说，衡量两个人关系深度的方式，是看他们能聊到多深，也能玩到多野。\u003c/p\u003e\n\u003cp\u003eDeep talk 是那种聊到凌晨两点还不困的对话。聊彼此真正在意什么，聊对这个世界有什么判断，聊价值观里哪些东西是绝对不能妥协的。不是辩论，不是说服，是两个人把各自的三观摊开来放在一起比对——发现某些地方高度重合时，那种安心感是很难言说的。能 deep talk 的人，是真的愿意被你看见的人。\u003c/p\u003e\n\u003cp\u003eDirty talk 是另一种坦诚。它需要放下羞耻感，需要信任，需要知道对方不会因为你说了什么奇奇怪怪的话就用异样的眼光看你。它本质上也是一种语言的亲密——用语言触碰对方，用玩笑和挑逗确认彼此之间那道边界是可以随意跨过的。\u003c/p\u003e\n\u003cp\u003e很多人能做到其中一件，却做不到两件同时。有人聊得了人生意义，一到床上就哑口无言；有人嬉皮笑脸没个正经，却永远不让你靠近他真实的内心。两件事都能做到的人，其实是说：我在你面前，没有什么是需要表演的。\u003c/p\u003e\n\u003cp\u003e这挺难得的。\u003c/p\u003e\n\u003cp\u003e不是每段关系都能长出这种空间。它需要时间，需要一次次试探之后积累的安全感，需要两个人都愿意先放下一点防备。有时候是一个人先开口说了一句很傻的话，另一个人没有嘲笑，于是就又多了一点胆子。慢慢的，那个空间就有了。\u003c/p\u003e\n\u003cp\u003e所以如果你身边有这样一个人——能跟你认真聊，也能跟你不正经。\u003c/p\u003e\n\u003cp\u003e这不是标配，这是运气。\u003c/p\u003e\n\u003cp\u003e(享福啦)\u003c/p\u003e","title":"两种talk"},{"content":"20260326 HDFS更改数据目录导致NameNode无法启动 问题描述： 由于一开始硬盘还未挂载目录，安装cdh时，配置hdfs相关数据目录就用了默认的系统盘下的目录，如下\n/data/dfs/nn /data/dfs/jn /data/dfs/dn\n后面数据盘挂载在/hadoop/data1/下，固将hdfs的数据目录改为\n/hadoop/data1/dfs/nn /hadoop/data1/dfs/jn /hadoop/data1/dfs/dn\n重新启动hdfs，报错，分为三类：\nnameNode 无法格式化 丢失块信息100% Cloudera Manager 无法检测 HDFS 健康状态 解决： 停掉hdfs所有服务 清空/hadoop/data1/dfs/nn /hadoop/data1/dfs/jn /hadoop/data1/dfs/dn 启动dataNode，failoverController 等服务（除NameNode，journalNode外） 对activity NameNode执行format操作 启动所有journalNode 启动activity NameNode 对Standby NameNode执行Bootstrap Standby操作 启动Standby NameNode ssh客户端以hdfs用户执行hdfs dfs -chmod -R 777 /data/tmp 等命令,完成以下目录创建及赋权,后续通过ranger做权限收口 [hdfs@cdhmaster02 ~]$ hdfs dfs -ls Found 3 items\rdrwxrwxrwt - hdfs supergroup 0 2026-03-26 12:31 /data\rdrwxrwxrwt - hdfs supergroup 0 2026-03-26 17:25 /tmp\rdrwxr-xr-x - hdfs supergroup 0 2026-03-26 17:24 /user 注意事项： 操作顺序不可颠倒，否则可能导致 NameNode 或 JournalNode 数据不一致 不能对 Standby NameNode 执行 format，否则会破坏 HA 集群 **(重要)**清空目录操作仅限于尚未存储业务数据的集群或测试环境 如果要迁移已有数据的数据目录，则需要rsync -avh /data/dfs/nn/ /hadoop/data1/dfs/nn/类似命令 原因解释： NameNode 无法格式化 HDFS 的 HA 模式下，Active NameNode 的元数据（fsimage + edits）必须与 Quorum Journal Nodes 保持一致。 修改 HDFS 存储目录后，如果 JournalNode 目录仍保留旧数据或未初始化，NameNode 在检查 quorum 时会发现元数据不匹配，因此无法格式化。 关键点：只允许对 Active NameNode 格式化，Standby NameNode 或 JournalNode 不能直接 format，否则会破坏 HA 的一致性机制。 丢失块信息 100% DataNode 存储目录修改后，旧数据无法被 NameNode 正确识别，因为 NameNode 的 fsimage 中没有对应块信息。 NameNode 启动时会扫描 DataNode 上报的块，如果目录未初始化或数据不匹配，则所有块都被标记为丢失，从而导致 CM 显示 100% missing blocks。 关键点：HDFS 的块映射严格依赖 NameNode 的元数据，DataNode 目录未同步会造成“逻辑丢失”，即使物理数据存在。 Cloudera Manager 无法检测 HDFS CM Canary 测试依赖基本的 HDFS I/O 操作（创建、写入、读取、删除文件）。 当 NameNode 在安全模式或丢失块状态下，读写请求无法完成，Canary 测试失败，导致 CM 显示健康异常。 关键点：CM 健康检测反映的是 NameNode 和 DataNode 的可用性与一致性，而非磁盘本身状态。 操作顺序要求严格 Active NameNode 必须先格式化并启动，使元数据初始化完成。 JournalNode 启动后才能形成 Quorum，保证 HA 的 edit log 复制。 Standby NameNode 只能通过 Bootstrap Standby 从 Active NameNode 同步元数据，否则会破坏 HA 的一致性，导致集群无法启动。 关键点：HDFS HA 的核心机制是 edit log 的 quorum 写入与 NameNode 状态同步，顺序错误会导致集群不可用。 ","permalink":"https://leochu.work/blog/tech/bigdata/hdfs%E6%9B%B4%E6%94%B9%E6%95%B0%E6%8D%AE%E7%9B%AE%E5%BD%95%E5%AF%BC%E8%87%B4namenode%E6%97%A0%E6%B3%95%E5%90%AF%E5%8A%A8/","summary":"\u003ch1 id=\"20260326-hdfs更改数据目录导致namenode无法启动\"\u003e20260326 HDFS更改数据目录导致NameNode无法启动\u003c/h1\u003e\n\u003ch2 id=\"问题描述\"\u003e问题描述：\u003c/h2\u003e\n\u003cp\u003e由于一开始硬盘还未挂载目录，安装cdh时，配置hdfs相关数据目录就用了默认的系统盘下的目录，如下\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e/data/dfs/nn /data/dfs/jn /data/dfs/dn\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e后面数据盘挂载在/hadoop/data1/下，固将hdfs的数据目录改为\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003e/hadoop/data1/dfs/nn /hadoop/data1/dfs/jn /hadoop/data1/dfs/dn\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e重新启动hdfs，报错，分为三类：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003enameNode 无法格式化\u003c/li\u003e\n\u003cli\u003e丢失块信息100%\u003c/li\u003e\n\u003cli\u003eCloudera Manager 无法检测 HDFS 健康状态\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"解决\"\u003e解决：\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e停掉hdfs所有服务\u003c/li\u003e\n\u003cli\u003e清空\u003ccode\u003e/hadoop/data1/dfs/nn /hadoop/data1/dfs/jn /hadoop/data1/dfs/dn\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e启动dataNode，failoverController 等服务（除NameNode，journalNode外）\u003c/li\u003e\n\u003cli\u003e对activity NameNode执行format操作\u003cimg loading=\"lazy\" src=\"/blog/resource/019d28d7-9af8-7238-a7f4-dd472d172e28-image.png\"\u003e\u003c/li\u003e\n\u003cli\u003e启动所有journalNode\u003c/li\u003e\n\u003cli\u003e启动activity NameNode\u003c/li\u003e\n\u003cli\u003e对Standby NameNode执行Bootstrap Standby操作\u003cimg loading=\"lazy\" src=\"/blog/resource/019d28dc-56f9-7688-875c-c416075ef63c-image.png\"\u003e\u003c/li\u003e\n\u003cli\u003e启动Standby NameNode\u003c/li\u003e\n\u003cli\u003essh客户端以hdfs用户执行\u003ccode\u003ehdfs dfs -chmod -R 777 /data/tmp\u003c/code\u003e 等命令,完成以下目录创建及赋权,后续通过ranger做权限收口\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e[hdfs@cdhmaster02 ~]$ hdfs dfs -ls Found 3 items\r\ndrwxrwxrwt   - hdfs supergroup          0 2026-03-26 12:31 /data\r\ndrwxrwxrwt   - hdfs supergroup          0 2026-03-26 17:25 /tmp\r\ndrwxr-xr-x   - hdfs supergroup          0 2026-03-26 17:24 /user\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"注意事项\"\u003e注意事项：\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e操作顺序不可颠倒，否则可能导致 NameNode 或 JournalNode 数据不一致\u003c/li\u003e\n\u003cli\u003e不能对 Standby NameNode 执行 format，否则会破坏 HA 集群\u003c/li\u003e\n\u003cli\u003e**(重要)**清空目录操作仅限于尚未存储业务数据的集群或测试环境\u003c/li\u003e\n\u003cli\u003e如果要迁移已有数据的数据目录，则需要\u003ccode\u003ersync -avh /data/dfs/nn/ /hadoop/data1/dfs/nn/\u003c/code\u003e类似命令\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"原因解释\"\u003e原因解释：\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eNameNode 无法格式化\u003c/strong\u003e\n\u003cul\u003e\n\u003cli\u003eHDFS 的 HA 模式下，Active NameNode 的元数据（fsimage + edits）必须与 Quorum Journal Nodes 保持一致。\u003c/li\u003e\n\u003cli\u003e修改 HDFS 存储目录后，如果 JournalNode 目录仍保留旧数据或未初始化，NameNode 在检查 quorum 时会发现元数据不匹配，因此无法格式化。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e关键点\u003c/strong\u003e：只允许对 Active NameNode 格式化，Standby NameNode 或 JournalNode 不能直接 format，否则会破坏 HA 的一致性机制。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e丢失块信息 100%\u003c/strong\u003e\n\u003cul\u003e\n\u003cli\u003eDataNode 存储目录修改后，旧数据无法被 NameNode 正确识别，因为 NameNode 的 fsimage 中没有对应块信息。\u003c/li\u003e\n\u003cli\u003eNameNode 启动时会扫描 DataNode 上报的块，如果目录未初始化或数据不匹配，则所有块都被标记为丢失，从而导致 CM 显示 100% missing blocks。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e关键点\u003c/strong\u003e：HDFS 的块映射严格依赖 NameNode 的元数据，DataNode 目录未同步会造成“逻辑丢失”，即使物理数据存在。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCloudera Manager 无法检测 HDFS\u003c/strong\u003e\n\u003cul\u003e\n\u003cli\u003eCM Canary 测试依赖基本的 HDFS I/O 操作（创建、写入、读取、删除文件）。\u003c/li\u003e\n\u003cli\u003e当 NameNode 在安全模式或丢失块状态下，读写请求无法完成，Canary 测试失败，导致 CM 显示健康异常。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e关键点\u003c/strong\u003e：CM 健康检测反映的是 NameNode 和 DataNode 的可用性与一致性，而非磁盘本身状态。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e操作顺序要求严格\u003c/strong\u003e\n\u003cul\u003e\n\u003cli\u003eActive NameNode 必须先格式化并启动，使元数据初始化完成。\u003c/li\u003e\n\u003cli\u003eJournalNode 启动后才能形成 Quorum，保证 HA 的 edit log 复制。\u003c/li\u003e\n\u003cli\u003eStandby NameNode 只能通过 \u003cstrong\u003eBootstrap Standby\u003c/strong\u003e 从 Active NameNode 同步元数据，否则会破坏 HA 的一致性，导致集群无法启动。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e关键点\u003c/strong\u003e：HDFS HA 的核心机制是 edit log 的 quorum 写入与 NameNode 状态同步，顺序错误会导致集群不可用。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e","title":"HDFS更改数据目录导致NameNode无法启动"},{"content":" 孔飞力《叫魂》。核心观点：一场看似荒诞的民间恐慌，之所以会扩大成全国性政治事件，是因为它精准触发了国家、社会与人心深处的脆弱结构。\n核心观点 谣言的传播并不靠真实性，而靠可附着性。 一个说法只要足够符合时代焦虑，就会迅速扩散。\n国家机器会放大某些恐慌。 当权力系统把模糊恐惧上升为正式治理对象时，事情就不再只是民间流言，而会演变成制度性运动。\n底层社会的不安会借由荒诞形式表现出来。 “叫魂”不是单纯迷信，而是一种集体焦虑的出口。\n印象较深的部分 很多看起来荒唐的事件，背后都埋着真实的不安全感。 人们害怕的未必是谣言本身，而是自己无法掌控的生活处境。\n统治者对秩序失控的恐惧，与百姓对生活失控的恐惧，会在同一事件里相互放大。\n读后感 讲了一整套机制：恐慌怎么形成，怎么扩散，又怎么被权力接手。\n合上书之后，这个判断会变得更清楚：很多群体性狂热都不是偶然，它们总有适合生长的土壤。\n","permalink":"https://leochu.work/blog/reading/%E5%8F%AB%E9%AD%82/","summary":"\u003cblockquote\u003e\n\u003cp\u003e孔飞力《叫魂》。核心观点：一场看似荒诞的民间恐慌，之所以会扩大成全国性政治事件，是因为它精准触发了国家、社会与人心深处的脆弱结构。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e谣言的传播并不靠真实性，而靠可附着性。\u003c/strong\u003e 一个说法只要足够符合时代焦虑，就会迅速扩散。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e国家机器会放大某些恐慌。\u003c/strong\u003e 当权力系统把模糊恐惧上升为正式治理对象时，事情就不再只是民间流言，而会演变成制度性运动。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e底层社会的不安会借由荒诞形式表现出来。\u003c/strong\u003e “叫魂”不是单纯迷信，而是一种集体焦虑的出口。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e很多看起来荒唐的事件，背后都埋着真实的不安全感。\u003c/strong\u003e 人们害怕的未必是谣言本身，而是自己无法掌控的生活处境。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e统治者对秩序失控的恐惧，与百姓对生活失控的恐惧，会在同一事件里相互放大。\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e讲了一整套机制：恐慌怎么形成，怎么扩散，又怎么被权力接手。\u003c/p\u003e\n\u003cp\u003e合上书之后，这个判断会变得更清楚：很多群体性狂热都不是偶然，它们总有适合生长的土壤。\u003c/p\u003e","title":"叫魂"},{"content":"1.使用datax同步数据\n2.模板json(已配置hdfs ha)：\n{ \u0026#34;job\u0026#34;: { \u0026#34;content\u0026#34;: [ { \u0026#34;reader\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;postgresqlreader\u0026#34;, \u0026#34;parameter\u0026#34;: { \u0026#34;username\u0026#34;: \u0026#34;hs_sync\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;Pass2025\u0026#34;, \u0026#34;column\u0026#34;: [ \u0026#34;order_date\u0026#34;, \u0026#34;day\u0026#34;, \u0026#34;iso_day_of_week\u0026#34;, \u0026#34;weekday_cn\u0026#34;, \u0026#34;weekday_en\u0026#34;, \u0026#34;weekday_short\u0026#34;, \u0026#34;is_weekend\u0026#34;, \u0026#34;iso_week\u0026#34;, \u0026#34;month\u0026#34;, \u0026#34;month_cn\u0026#34;, \u0026#34;month_en_full\u0026#34;, \u0026#34;month_en_short\u0026#34;, \u0026#34;quarter\u0026#34;, \u0026#34;year\u0026#34; ], \u0026#34;connection\u0026#34;: [ { \u0026#34;table\u0026#34;: [ \u0026#34;dim_calendar\u0026#34; ], \u0026#34;jdbcUrl\u0026#34;: [ \u0026#34;jdbc:postgresql://100.64.0.10:25432/hs_sync_data\u0026#34; ] } ], \u0026#34;fetchSize\u0026#34;: 1000 } }, \u0026#34;writer\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;hdfswriter\u0026#34;, \u0026#34;parameter\u0026#34;: { \u0026#34;defaultFS\u0026#34;: \u0026#34;hdfs://nameservice1\u0026#34;, \u0026#34;hadoopConfig\u0026#34;: { \u0026#34;dfs.nameservices\u0026#34;: \u0026#34;nameservice1\u0026#34;, \u0026#34;dfs.ha.namenodes.nameservice1\u0026#34;: \u0026#34;nn1,nn2\u0026#34;, \u0026#34;dfs.namenode.rpc-address.nameservice1.nn1\u0026#34;: \u0026#34;192.168.33.61:8020\u0026#34;, \u0026#34;dfs.namenode.rpc-address.nameservice1.nn2\u0026#34;: \u0026#34;192.168.33.62:8020\u0026#34;, \u0026#34;dfs.client.failover.proxy.provider.nameservice1\u0026#34;: \u0026#34;org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider\u0026#34; }, \u0026#34;fileType\u0026#34;: \u0026#34;orc\u0026#34;, \u0026#34;path\u0026#34;: \u0026#34;/tmp/data/test/\u0026#34;, \u0026#34;fileName\u0026#34;: \u0026#34;dim_calendar\u0026#34;, \u0026#34;writeMode\u0026#34;: \u0026#34;truncate\u0026#34;, \u0026#34;column\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;order_date\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;date\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;day\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;smallint\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;iso_day_of_week\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;smallint\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;weekday_cn\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;weekday_en\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;weekday_short\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;is_weekend\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;boolean\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;iso_week\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;smallint\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;month\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;smallint\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;month_cn\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;month_en_full\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;month_en_short\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;quarter\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34; }, { \u0026#34;name\u0026#34;: \u0026#34;year\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;smallint\u0026#34; } ], \u0026#34;fieldDelimiter\u0026#34;: \u0026#34;\\t\u0026#34;, \u0026#34;maxFileSize\u0026#34;: 134217728, \u0026#34;encoding\u0026#34;: \u0026#34;UTF-8\u0026#34; } } } ], \u0026#34;setting\u0026#34;: { \u0026#34;speed\u0026#34;: { \u0026#34;channel\u0026#34;: 5 }, \u0026#34;errorLimit\u0026#34;: { \u0026#34;record\u0026#34;: 0, \u0026#34;percentage\u0026#34;: 0.02 }, \u0026#34;retry\u0026#34;: { \u0026#34;limit\u0026#34;: 3, \u0026#34;interval\u0026#34;: 5000 } } } } ","permalink":"https://leochu.work/blog/tech/bigdata/postgresql%E5%90%8C%E6%AD%A5hdfs/","summary":"\u003cp\u003e1.使用datax同步数据\u003c/p\u003e\n\u003cp\u003e2.模板json(已配置hdfs ha)：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;job\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;content\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;reader\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;postgresqlreader\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;parameter\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;username\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;hs_sync\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;password\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Pass2025\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;column\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;order_date\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;day\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;iso_day_of_week\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;weekday_cn\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;weekday_en\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;weekday_short\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;is_weekend\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;iso_week\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;month\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;month_cn\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;month_en_full\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;month_en_short\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;quarter\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;year\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;connection\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;table\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;dim_calendar\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;jdbcUrl\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;jdbc:postgresql://100.64.0.10:25432/hs_sync_data\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fetchSize\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;writer\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;hdfswriter\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;parameter\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;defaultFS\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;hdfs://nameservice1\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;hadoopConfig\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;dfs.nameservices\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;nameservice1\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;dfs.ha.namenodes.nameservice1\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;nn1,nn2\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;dfs.namenode.rpc-address.nameservice1.nn1\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;192.168.33.61:8020\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;dfs.namenode.rpc-address.nameservice1.nn2\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;192.168.33.62:8020\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;dfs.client.failover.proxy.provider.nameservice1\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fileType\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;orc\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;path\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/tmp/data/test/\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fileName\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;dim_calendar\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;writeMode\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;truncate\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;column\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;order_date\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;day\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;smallint\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;iso_day_of_week\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;smallint\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;weekday_cn\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;weekday_en\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;weekday_short\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;is_weekend\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;boolean\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;iso_week\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;smallint\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;month\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;smallint\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;month_cn\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;month_en_full\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;month_en_short\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;quarter\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;string\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;year\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;smallint\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fieldDelimiter\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\\t\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;maxFileSize\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e134217728\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;encoding\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;UTF-8\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;setting\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;speed\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;channel\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;errorLimit\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;record\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;percentage\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e0.02\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;retry\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;limit\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;interval\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"PostgreSQL同步HDFS"},{"content":"一、hive中文注释乱码 1、设置 hive 元数据库字符集 show create database hive;\n查看为 utf8，需变更为 latin1\nalter database hive character set latin1; 2、更改如下表字段为字符集编码为 utf8 ①修改表字段注解和表注解\nalter table COLUMNS_V2 modify column COMMENT varchar(256) character set utf8;\nalter table TABLE_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;\n② 修改分区字段注解：\nalter table PARTITION_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8 ;\nalter table PARTITION_KEYS modify column PKEY_COMMENT varchar(4000) character set utf8;\n③修改索引注解：\nalter table INDEX_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;\n3. 验证 二、Impala时区和hive不一致 1.impala时间早8小时 2.hive时间正常 3.修改hue时区： 将America/Los_Angeles改为Asia/Shanghai\n4.修改impala配置： 新增impala 命令行参数高级配置片段（安全阀）:\n-use_local_tz_for_unix_timestamp_conversions=true\n-convert_legacy_hive_parquet_utc_timestamps=true\n5.滚动重启 6.验证 三、hive配置默认计算引擎为spark 1.原默认计算引擎 2.修改配置项，重启hive服务 3.验证 四、修改数据存储目录到新加数据盘挂载目录 1.hdfs: 2.yarn 五、内存配额调优 每台机器可用内存：250G*0.8=200G（0.2预留系统和管理组件）\n项 内存 描述 impala 30G 提供即席查询服务 yarn(spark) 150G 承担主要计算任务 冗余 20G 防止JVM 抖动、OOM等 暂时按220G可用来配，后面升级配置到250G版。220G×0.8=176G\n项 内存 描述 impala 30G 提供即席查询服务 yarn(spark) 126G 承担主要计算任务 冗余 20G 防止JVM 抖动、OOM等 3.后期:\nworker节点内存均匀后，yarn(spark)配置：126G →150G\n4.配置项\nImpala Daemon Memory Limit\nyarn.nodemanager.resource.memory-mb\n六、hdfs 线程4096→65535\ndatanode和blancer堆内存1G→4G\nnamenode堆内存4G→20G\n回收站清理 1day→14day\nstale启用（避免脏读脏写）\n七、hive 小文件合并\n动态分区优化\nspark资源调优\n八、yarn 调整yarn内存，jvm内存\n调整容器最大最小内存\n九、一些目录配置的更改： hdfs上，一些目录的创建及赋权，如\nhdfs dfs -mkdir -p /user/spark/applicationHistory\rhdfs dfs -chown -R spark:spark /user/spark/applicationHistory\rhdfs dfs -chmod -R 1777 /user/spark/applicationHistory cdh界面配置，将所有/var/log → /data/var/log , /tmp → /data/tmp\n创建opt/cloudera/parcels软连接指向/data下\n十、修改默认纠删码策略为无。（保证副本策略为3） 目前datanode节点太少，纠删码不起作用，后期扩展节点可作为冷数据压缩空间的优化项，膨胀率3→1.4。\n十一、修改网卡检测项： 排除办公网vpn网卡检测 十二、系统的/var太小，导致警告 修改各组件的日志目录，/var/log → /data/var/log 。 新配置：\n/boot/efi 1G\n/boot 1G\n剩下的都给 / 230G\n数据盘： /hadoop/data1\n十三、 worker节点熵值不均 cdh报：Test of whether a host has enough entropy. Concerning : 72 entropy was available. Warning threshold: 100\n什么是 entropy（熵）？\n在 Linux 系统中，熵是用于生成随机数的“随机性来源”，主要用于：\nSSL/TLS 加密（HTTPS、Kerberos）\nSSH 连接\nHadoop / CDH 安全通信\n如果熵太低，会导致：\n加密操作变慢\n某些服务启动卡住（尤其是涉及安全认证时）\n熵值不是“配置出来的”，而是“系统实时积累的随机性”\n谁“更活跃/更有随机输入”，谁熵就高\n为什么必须统一？\n否则会出现：\n某些节点启动快（高熵）\n某些节点卡住（低熵）\n在 Hadoop 里可能导致：\nKerberos 卡死\nHDFS 启动慢\nAgent 心跳异常\n解决：所有节点统一装 haveged\nyum install -y haveged systemctl enable haveged systemctl start haveged 十四、hdfs修改数据目录后无法启动nameNode 20260326 HDFS更改数据目录导致NameNode无法启动\n十五、全组件健康图截图，纪念一下 汇总： ","permalink":"https://leochu.work/blog/tech/bigdata/cdh%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE%E5%8F%8A%E4%BC%98%E5%8C%96/","summary":"\u003ch1 id=\"一hive中文注释乱码\"\u003e一、hive中文注释乱码\u003c/h1\u003e\n\u003ch2 id=\"1设置-hive-元数据库字符集\"\u003e\u003cem\u003e1、设置 hive 元数据库字符集\u003c/em\u003e\u003c/h2\u003e\n\u003cp\u003e\u003cem\u003eshow create database hive;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/019d0a6a-72c9-7608-ac3a-0a57f498e4fd-image.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e查看为 utf8，需变更为 latin1\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003ealter database hive character set latin1; \u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/019d0a6b-d35a-7499-96cd-9281218f8e88-image.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"2更改如下表字段为字符集编码为-utf8\"\u003e\u003cem\u003e2、更改如下表字段为字符集编码为 utf8\u003c/em\u003e\u003c/h2\u003e\n\u003cp\u003e①修改表字段注解和表注解\u003cbr\u003e\nalter table COLUMNS_V2 modify column COMMENT varchar(256) character set utf8;\u003cbr\u003e\nalter table TABLE_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;\u003cbr\u003e\n② 修改分区字段注解：\u003cbr\u003e\nalter table PARTITION_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8 ;\u003cbr\u003e\nalter table PARTITION_KEYS modify column PKEY_COMMENT varchar(4000) character set utf8;\u003cbr\u003e\n③修改索引注解：\u003cbr\u003e\nalter table INDEX_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;\u003c/p\u003e","title":"CDH基础配置及优化"},{"content":"一、在cdh+cm部署时，已经做了部分系统优化 ssh双向免密\n配置时间同步服务（本次没用ntpd，用的是chrony）\n禁用透明大页、碎片整理：THP (Transparent Huge Pages)\n禁用内存交换swap（常用0或1）\n二、hadoop集群推荐内核参数： 参数名称 默认值 说明 文件系统参数 fs.file-max 6815744 系统最大文件描述符数量（所有进程可打开文件总数上限） fs.aio-max-nr 1048576 异步I/O请求的最大并发数（影响高并发场景性能） 网络核心参数 net.core.rmem_default 262144 TCP接收缓冲区默认大小（256KB） net.core.wmem_default 262144 TCP发送缓冲区默认大小（256KB） net.core.rmem_max 16777216 TCP接收缓冲区最大允许值（16MB） net.core.wmem_max 16777216 TCP发送缓冲区最大允许值（16MB） TCP协议栈参数 net.ipv4.tcp_rmem 4096 262144 16777216 接收窗口尺寸：• 最小值4KB• 默认值256KB• 最大值16MB net.ipv4.tcp_wmem 4096 262144 16777216 发送窗口尺寸：• 最小值4KB• 默认值256KB• 最大值16MB 查看当前内核配置：\nsysctl -e fs.file-max fs.aio-max-nr \\ net.core.rmem_default net.core.wmem_default \\ net.core.rmem_max net.core.wmem_max \\ net.ipv4.tcp_rmem net.ipv4.tcp_wmem 2\u0026gt;/dev/nulll 对比发现除了文件描述符数量外，其他内核参数都偏低，估所有机器都需要调整。\n三、用户级文件数限制： 增加10倍，修改为655360\n可以通过编辑 /etc/security/limits.conf 文件来执行此操作，如下所示:\nSoft nofile 655360 Hard nofile 655360 四、socket监听队列上限 此内核参数对应的具体文件路径为/proc/sys/net/core/somaxconn,它用来设置socket监听(listen)的\nbacklog上限。\n什么是backlog呢？就是Socket的监听队列，当一个请求(Request)未被处理或建立时，便会进入\nbacklog。而socket server可以一次性处理backlog中的所有请求，处理后的请求不再位于监听队列中。\n如果server处理请求较慢，以至于监听队列被填满时，那么新来的请求会被拒绝，所以必须增大这个值，\n此参数默认值为128。\n执行以下命令\necho 4096 \u0026gt;/proc/sys/net/core/somaxconn\n五、动态刷新内核设置： sysctl -p 六、内核参数优化设置分类 📊 sysctl参数汇总\n","permalink":"https://leochu.work/blog/tech/bigdata/linux%E7%B3%BB%E7%BB%9F%E7%BA%A7%E4%BC%98%E5%8C%96/","summary":"\u003ch2 id=\"一在cdhcm部署时已经做了部分系统优化\"\u003e一、在cdh+cm部署时，已经做了部分系统优化\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003essh双向免密\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e配置时间同步服务（本次没用ntpd，用的是chrony）\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e禁用透明大页、碎片整理：THP (Transparent Huge Pages)\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e禁用内存交换swap（常用0或1）\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"二hadoop集群推荐内核参数\"\u003e二、hadoop集群推荐内核参数：\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003cstrong\u003e参数名称\u003c/strong\u003e\u003c/th\u003e\n          \u003cth\u003e\u003cstrong\u003e默认值\u003c/strong\u003e\u003c/th\u003e\n          \u003cth\u003e\u003cstrong\u003e说明\u003c/strong\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e文件系统参数\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003efs.file-max\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e6815744\u003c/td\u003e\n          \u003ctd\u003e系统最大文件描述符数量（所有进程可打开文件总数上限）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003efs.aio-max-nr\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e1048576\u003c/td\u003e\n          \u003ctd\u003e异步I/O请求的最大并发数（影响高并发场景性能）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e网络核心参数\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enet.core.rmem_default\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e262144\u003c/td\u003e\n          \u003ctd\u003eTCP接收缓冲区\u003cstrong\u003e默认大小\u003c/strong\u003e（256KB）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enet.core.wmem_default\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e262144\u003c/td\u003e\n          \u003ctd\u003eTCP发送缓冲区\u003cstrong\u003e默认大小\u003c/strong\u003e（256KB）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enet.core.rmem_max\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e16777216\u003c/td\u003e\n          \u003ctd\u003eTCP接收缓冲区\u003cstrong\u003e最大允许值\u003c/strong\u003e（16MB）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enet.core.wmem_max\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e16777216\u003c/td\u003e\n          \u003ctd\u003eTCP发送缓冲区\u003cstrong\u003e最大允许值\u003c/strong\u003e（16MB）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003eTCP协议栈参数\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enet.ipv4.tcp_rmem\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e4096 262144 16777216\u003c/td\u003e\n          \u003ctd\u003e接收窗口尺寸：• 最小值4KB• 默认值256KB• 最大值16MB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003ccode\u003enet.ipv4.tcp_wmem\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e4096 262144 16777216\u003c/td\u003e\n          \u003ctd\u003e发送窗口尺寸：• 最小值4KB• 默认值256KB• 最大值16MB\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e查看当前内核配置：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esysctl -e fs.file-max fs.aio-max-nr \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e net.core.rmem_default net.core.wmem_default \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e net.core.rmem_max net.core.wmem_max \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e net.ipv4.tcp_rmem net.ipv4.tcp_wmem 2\u0026gt;/dev/nulll\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/019d1e9d-a8aa-71fa-bc02-15c30fb69345-image.png\"\u003e\u003c/p\u003e","title":"linux系统级优化"},{"content":"使用ansible做自动化部署\nCDH 集群安装指南 环境准备 项目 值 NFS 服务器 xxx.xxx.xxx.xxx:/nfs/share Inventory inventory/cdh-init.ini 安装步骤 步骤 1：配置 /etc/hosts 和挂载 NFS ansible-playbook cdh-hosts-nfs.yml -i inventory/cdh-init.ini 步骤 2：系统初始化 ansible-playbook cdh-init-raw.yaml -i inventory/cdh-init.ini 验证 # 验证 NFS 挂载 ansible cdh_all -i inventory/cdh-init.ini -m shell -a \u0026#34;ls /mnt\u0026#34; -b # 验证 Java ansible cdh_all -i inventory/cdh-init.ini -m shell -a \u0026#34;java -version\u0026#34; # 验证 SELinux ansible cdh_all -i inventory/cdh-init.ini -m shell -a \u0026#34;sestatus\u0026#34; # 验证 haveged ansible cdh_all -i inventory/cdh-init.ini -m shell -a \u0026#34;systemctl status haveged\u0026#34; 安装介质目录 /mnt/\r├── cdh/ # CDH Parcel\r├── ClouderaManager/ # CM 6.3.1\r└── mysql/ # MySQL 5.7 RPM ","permalink":"https://leochu.work/blog/tech/bigdata/cdh6.3.2%E9%9B%86%E7%BE%A4%E9%83%A8%E7%BD%B2/","summary":"\u003cp\u003e使用ansible做自动化部署\u003c/p\u003e\n\u003ch1 id=\"cdh-集群安装指南\"\u003eCDH 集群安装指南\u003c/h1\u003e\n\u003ch2 id=\"环境准备\"\u003e环境准备\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e项目\u003c/th\u003e\n          \u003cth\u003e值\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eNFS 服务器\u003c/td\u003e\n          \u003ctd\u003exxx.xxx.xxx.xxx:/nfs/share\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eInventory\u003c/td\u003e\n          \u003ctd\u003einventory/cdh-init.ini\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"安装步骤\"\u003e安装步骤\u003c/h2\u003e\n\u003ch3 id=\"步骤-1配置-etchosts-和挂载-nfs\"\u003e步骤 1：配置 /etc/hosts 和挂载 NFS\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eansible-playbook cdh-hosts-nfs.yml -i inventory/cdh-init.ini\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"步骤-2系统初始化\"\u003e步骤 2：系统初始化\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eansible-playbook cdh-init-raw.yaml -i inventory/cdh-init.ini\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003chr\u003e\n\u003ch2 id=\"验证\"\u003e验证\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 验证 NFS 挂载\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eansible cdh_all -i inventory/cdh-init.ini -m shell -a \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ls /mnt\u0026#34;\u003c/span\u003e -b\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 验证 Java\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eansible cdh_all -i inventory/cdh-init.ini -m shell -a \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;java -version\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 验证 SELinux\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eansible cdh_all -i inventory/cdh-init.ini -m shell -a \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sestatus\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 验证 haveged\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eansible cdh_all -i inventory/cdh-init.ini -m shell -a \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;systemctl status haveged\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003chr\u003e\n\u003ch2 id=\"安装介质目录\"\u003e安装介质目录\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/mnt/\r\n├── cdh/                    # CDH Parcel\r\n├── ClouderaManager/        # CM 6.3.1\r\n└── mysql/                  # MySQL 5.7 RPM\n\u003c/code\u003e\u003c/pre\u003e","title":"CDH6.3.2集群部署"},{"content":" 黄仁宇《万历十五年》。核心观点：历史的转折往往不体现在戏剧性大事件中，而体现在制度内部长期积累的失衡。\n核心观点 看似平静的一年，往往隐藏着深层结构问题。 万历十五年本身没有惊天动地的大事，但正因为如此，它更能显出帝国运转逻辑的疲态。\n个人悲剧常常是制度问题的投影。 张居正、申时行、海瑞、戚继光、李贽，这些人物的命运并不只是性格故事，而是制度张力作用到个人身上的结果。\n道德化治理会掩盖技术性治理的缺失。 一个系统如果只能不断诉诸名分、道德和姿态，而缺乏可计算、可执行、可调整的治理能力，迟早会陷入僵局。\n印象较深的部分 帝国并不是突然崩坏的。 它往往是在表面秩序仍然存在的时候，内部弹性已经一点点耗尽。\n历史里最难修复的不是一次错误，而是长期没有被正视的结构性问题。\n读后感 很多表面上的人物冲突，放大看，其实都是制度问题在借人发声。\n把它放到现实里对照着看，会更有感觉，因为很多组织衰败的逻辑其实并没变。\n","permalink":"https://leochu.work/blog/reading/%E4%B8%87%E5%8E%86%E5%8D%81%E4%BA%94%E5%B9%B4/","summary":"\u003cblockquote\u003e\n\u003cp\u003e黄仁宇《万历十五年》。核心观点：历史的转折往往不体现在戏剧性大事件中，而体现在制度内部长期积累的失衡。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e看似平静的一年，往往隐藏着深层结构问题。\u003c/strong\u003e 万历十五年本身没有惊天动地的大事，但正因为如此，它更能显出帝国运转逻辑的疲态。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e个人悲剧常常是制度问题的投影。\u003c/strong\u003e 张居正、申时行、海瑞、戚继光、李贽，这些人物的命运并不只是性格故事，而是制度张力作用到个人身上的结果。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e道德化治理会掩盖技术性治理的缺失。\u003c/strong\u003e 一个系统如果只能不断诉诸名分、道德和姿态，而缺乏可计算、可执行、可调整的治理能力，迟早会陷入僵局。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e帝国并不是突然崩坏的。\u003c/strong\u003e 它往往是在表面秩序仍然存在的时候，内部弹性已经一点点耗尽。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e历史里最难修复的不是一次错误，而是长期没有被正视的结构性问题。\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e很多表面上的人物冲突，放大看，其实都是制度问题在借人发声。\u003c/p\u003e\n\u003cp\u003e把它放到现实里对照着看，会更有感觉，因为很多组织衰败的逻辑其实并没变。\u003c/p\u003e","title":"万历十五年"},{"content":" Eric Ries《精益创业》。核心观点：创业不是把想法尽快做大，而是用最小成本持续验证“哪些假设成立”。\n核心观点 创业的核心单位不是产品，而是学习。 如果一个团队发布了很多功能，却没有获得更清楚的用户认知，本质上并没有前进。\nMVP 的重点是验证，不是简陋。 它的价值在于用最小代价测试关键假设，而不是随便做个半成品糊出去。\n增长必须建立在可重复的因果关系上。 偶然增长不等于找到模式，真正有价值的是知道“为什么用户会留下来”。\n印象较深的部分 很多团队失败不是因为不努力，而是一直在高效地做错事。 这句话几乎适用于所有产品开发。\n转向不是失败，而是认知更新。 当关键假设被验证为错误时，继续硬推才是真正的浪费。\n读后感 这本书最好的地方，是它把创业从激情叙事拉回到验证逻辑。创业不是证明自己有多投入，而是在不断回答：这个问题到底存不存在，这个解法到底成不成立。\n不只是创业者，做产品的人读这本书也很合适。\n","permalink":"https://leochu.work/blog/reading/%E7%B2%BE%E7%9B%8A%E5%88%9B%E4%B8%9A/","summary":"\u003cblockquote\u003e\n\u003cp\u003eEric Ries《精益创业》。核心观点：创业不是把想法尽快做大，而是用最小成本持续验证“哪些假设成立”。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e创业的核心单位不是产品，而是学习。\u003c/strong\u003e 如果一个团队发布了很多功能，却没有获得更清楚的用户认知，本质上并没有前进。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eMVP 的重点是验证，不是简陋。\u003c/strong\u003e 它的价值在于用最小代价测试关键假设，而不是随便做个半成品糊出去。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e增长必须建立在可重复的因果关系上。\u003c/strong\u003e 偶然增长不等于找到模式，真正有价值的是知道“为什么用户会留下来”。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e很多团队失败不是因为不努力，而是一直在高效地做错事。\u003c/strong\u003e 这句话几乎适用于所有产品开发。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e转向不是失败，而是认知更新。\u003c/strong\u003e 当关键假设被验证为错误时，继续硬推才是真正的浪费。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e这本书最好的地方，是它把创业从激情叙事拉回到验证逻辑。创业不是证明自己有多投入，而是在不断回答：这个问题到底存不存在，这个解法到底成不成立。\u003c/p\u003e\n\u003cp\u003e不只是创业者，做产品的人读这本书也很合适。\u003c/p\u003e","title":"精益创业"},{"content":" 约翰·格雷《男人来自火星，女人来自金星》。核心观点：很多两性冲突不是恶意，而是沟通语言和情感需求错位导致的误读。\n核心观点 很多争执并不是立场冲突，而是回应方式不对。 一方在表达情绪时，希望先被理解；另一方却急着给建议、讲道理、解决问题。结果问题还没处理，情绪先被放大了。\n男性和女性在压力下常见的反应不同。 书里用“洞穴”和“倾诉”来做比喻：有的人会先退回自己的空间整理情绪，有的人则会通过表达来获得安慰和连接。\n爱意是否被感受到，取决于是否击中对方在意的点。 你以为自己在付出，对方却可能没有接收到，因为形式不对、时机不对、语气也不对。\n对我有帮助的提醒 先共情，再建议。 别急着修理问题，先让对方感觉自己被听见。\n不要把差异自动解释成不在乎。 很多时候对方不是冷漠，而是处理情绪的方式跟自己不一样。\n关系里需要翻译能力。 同一句话，在不同的人耳朵里，可能完全不是一个意思。愿意多解释一步，很多误会根本不会扩大。\n不要在错误时机沟通。 一方需要空间时被追问，一方需要回应时被回避，都会让原本不大的问题迅速升级。\n读后感 这本书有时代背景，里面有些表达今天看会显得比较类型化，但它提醒了一件很实用的事：亲密关系里，“你到底想表达什么”和“对方实际听到了什么”经常不是一回事。\n如果把它当成一套绝对性别规则，价值会有限；但如果把它当成一本关于差异化沟通的入门书，反而很有启发。它提供的不是结论，而是一个解释框架：很多矛盾不是谁对谁错，而是两套系统在错位运行。理解这一点，关系至少不会越走越窄。\n","permalink":"https://leochu.work/blog/reading/%E7%94%B7%E4%BA%BA%E6%9D%A5%E8%87%AA%E7%81%AB%E6%98%9F%E5%A5%B3%E4%BA%BA%E6%9D%A5%E8%87%AA%E9%87%91%E6%98%9F/","summary":"\u003cblockquote\u003e\n\u003cp\u003e约翰·格雷《男人来自火星，女人来自金星》。核心观点：很多两性冲突不是恶意，而是沟通语言和情感需求错位导致的误读。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e很多争执并不是立场冲突，而是回应方式不对。\u003c/strong\u003e 一方在表达情绪时，希望先被理解；另一方却急着给建议、讲道理、解决问题。结果问题还没处理，情绪先被放大了。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e男性和女性在压力下常见的反应不同。\u003c/strong\u003e 书里用“洞穴”和“倾诉”来做比喻：有的人会先退回自己的空间整理情绪，有的人则会通过表达来获得安慰和连接。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e爱意是否被感受到，取决于是否击中对方在意的点。\u003c/strong\u003e 你以为自己在付出，对方却可能没有接收到，因为形式不对、时机不对、语气也不对。\u003c/p\u003e\n\u003ch2 id=\"对我有帮助的提醒\"\u003e对我有帮助的提醒\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e先共情，再建议。\u003c/strong\u003e 别急着修理问题，先让对方感觉自己被听见。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e不要把差异自动解释成不在乎。\u003c/strong\u003e 很多时候对方不是冷漠，而是处理情绪的方式跟自己不一样。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e关系里需要翻译能力。\u003c/strong\u003e 同一句话，在不同的人耳朵里，可能完全不是一个意思。愿意多解释一步，很多误会根本不会扩大。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e不要在错误时机沟通。\u003c/strong\u003e 一方需要空间时被追问，一方需要回应时被回避，都会让原本不大的问题迅速升级。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e这本书有时代背景，里面有些表达今天看会显得比较类型化，但它提醒了一件很实用的事：亲密关系里，“你到底想表达什么”和“对方实际听到了什么”经常不是一回事。\u003c/p\u003e\n\u003cp\u003e如果把它当成一套绝对性别规则，价值会有限；但如果把它当成一本关于差异化沟通的入门书，反而很有启发。它提供的不是结论，而是一个解释框架：很多矛盾不是谁对谁错，而是两套系统在错位运行。理解这一点，关系至少不会越走越窄。\u003c/p\u003e","title":"男人来自火星，女人来自金星"},{"content":" 周志明《凤凰架构》。核心观点：架构不是炫技，不是堆名词，而是在约束条件下持续支持业务演进的系统设计。\n核心观点 架构首先服务于演进。 一个系统最重要的能力，不是初版多优雅，而是变化来临时能否低成本调整。\n分布式不是目标，只是代价高昂的手段。 很多问题在单体阶段并不存在，一旦走向分布式，复杂性、调用链、事务一致性、运维成本都会急剧上升。\n架构决策必须对应具体问题。 如果没有明确的性能瓶颈、组织边界或交付约束，很多“先进架构”都只是过度设计。\n印象较深的部分 技术选型背后总有交换。 性能、可维护性、可扩展性、团队认知成本，永远不可能同时最优。\n架构能力的一部分是克制。 什么时候不拆、什么时候不分布式、什么时候保留简单方案，往往比“会多少框架”更重要。\n读后感 这本书好在它不太谈抽象的“架构之美”，而是不断把问题拉回现实：业务规模、团队能力、系统约束、演进成本。\n这本书最后落下来的判断很明确：好的架构师不只是会设计复杂系统，更重要的是知道复杂性应该从哪里开始，又该在哪停下来。\n","permalink":"https://leochu.work/blog/reading/%E5%87%A4%E5%87%B0%E6%9E%B6%E6%9E%84/","summary":"\u003cblockquote\u003e\n\u003cp\u003e周志明《凤凰架构》。核心观点：架构不是炫技，不是堆名词，而是在约束条件下持续支持业务演进的系统设计。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e架构首先服务于演进。\u003c/strong\u003e 一个系统最重要的能力，不是初版多优雅，而是变化来临时能否低成本调整。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e分布式不是目标，只是代价高昂的手段。\u003c/strong\u003e 很多问题在单体阶段并不存在，一旦走向分布式，复杂性、调用链、事务一致性、运维成本都会急剧上升。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e架构决策必须对应具体问题。\u003c/strong\u003e 如果没有明确的性能瓶颈、组织边界或交付约束，很多“先进架构”都只是过度设计。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e技术选型背后总有交换。\u003c/strong\u003e 性能、可维护性、可扩展性、团队认知成本，永远不可能同时最优。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e架构能力的一部分是克制。\u003c/strong\u003e 什么时候不拆、什么时候不分布式、什么时候保留简单方案，往往比“会多少框架”更重要。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e这本书好在它不太谈抽象的“架构之美”，而是不断把问题拉回现实：业务规模、团队能力、系统约束、演进成本。\u003c/p\u003e\n\u003cp\u003e这本书最后落下来的判断很明确：好的架构师不只是会设计复杂系统，更重要的是知道复杂性应该从哪里开始，又该在哪停下来。\u003c/p\u003e","title":"凤凰架构"},{"content":" 罗杰·道森《优势谈判》。核心观点：谈判不是零和博弈，而是让双方都觉得自己赢了的艺术。\n核心原则 永远不要接受第一次报价。 即使条件很好，接受也会让对方后悔——他们会觉得本可以要得更少。让对方经过一番拉锯，才能真正感到满足。\n开价要高于预期。 为自己留出让步空间。让步本身就是一种礼物，能让对方感到赢了。\n让步要递减，不要等额。 每次让步幅度依次缩小，暗示已经接近底线。等额让步反而会让对方期待下一次同样幅度的让步。\n常用战术 退缩（Flinch）：对报价表现出明显的惊讶，无论心里怎么想。沉默和惊讶会让对方主动补充解释或降价。\n不情愿的卖家/买家：在开始谈判前表现出勉为其难，压低对方的期望值，同时给自己争取更大的空间。\n更高权威：告知对方需要请示上级，为自己保留退路，避免当场被逼表态。这不是谎言，是谈判工具。\n逐步蚕食（Nibbling）：在达成主要协议后，再追加小要求。此时对方已经投入，更难拒绝。\n好警察/坏警察：多人谈判时分工，一人强硬，一人温和，引导对方向温和一方妥协。\n读后感 技巧本身不是重点，理解背后的心理才是。谈判双方都需要一个\u0026quot;我赢了\u0026quot;的感受，好的谈判者的工作就是在达成目标的同时，帮对方也构建这个感受。\n","permalink":"https://leochu.work/blog/reading/%E4%BC%98%E5%8A%BF%E8%B0%88%E5%88%A4/","summary":"\u003cblockquote\u003e\n\u003cp\u003e罗杰·道森《优势谈判》。核心观点：谈判不是零和博弈，而是让双方都觉得自己赢了的艺术。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心原则\"\u003e核心原则\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e永远不要接受第一次报价。\u003c/strong\u003e 即使条件很好，接受也会让对方后悔——他们会觉得本可以要得更少。让对方经过一番拉锯，才能真正感到满足。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e开价要高于预期。\u003c/strong\u003e 为自己留出让步空间。让步本身就是一种礼物，能让对方感到赢了。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e让步要递减，不要等额。\u003c/strong\u003e 每次让步幅度依次缩小，暗示已经接近底线。等额让步反而会让对方期待下一次同样幅度的让步。\u003c/p\u003e\n\u003ch2 id=\"常用战术\"\u003e常用战术\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e退缩（Flinch）\u003c/strong\u003e：对报价表现出明显的惊讶，无论心里怎么想。沉默和惊讶会让对方主动补充解释或降价。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e不情愿的卖家/买家\u003c/strong\u003e：在开始谈判前表现出勉为其难，压低对方的期望值，同时给自己争取更大的空间。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e更高权威\u003c/strong\u003e：告知对方需要请示上级，为自己保留退路，避免当场被逼表态。这不是谎言，是谈判工具。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e逐步蚕食（Nibbling）\u003c/strong\u003e：在达成主要协议后，再追加小要求。此时对方已经投入，更难拒绝。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e好警察/坏警察\u003c/strong\u003e：多人谈判时分工，一人强硬，一人温和，引导对方向温和一方妥协。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e技巧本身不是重点，理解背后的心理才是。谈判双方都需要一个\u0026quot;我赢了\u0026quot;的感受，好的谈判者的工作就是在达成目标的同时，帮对方也构建这个感受。\u003c/p\u003e","title":"优势谈判"},{"content":" 彼得·德鲁克《卓有成效的管理者》。核心观点：管理者的有效性不是天赋，而是一套可以训练的工作习惯。\n核心观点 先管理时间。 很多管理失效，本质上不是能力不足，而是时间被切碎到无法做真正重要的事。\n重视贡献而不是忙碌。 有效管理者会不断问：我这项工作最终对组织产生了什么结果，而不是我今天看起来做了多少事。\n用人要看长处。 管理的任务不是修理人的短板，而是把不同人的强项放到能产生效果的位置上。\n印象较深的部分 决策不是越多越好，而是少而关键。 真正重要的决策不会天天发生，关键在于识别哪些问题值得上升到决策层面。\n管理不是控制细节，而是让系统产出结果。 一个人如果永远沉迷于亲自处理所有问题，通常意味着管理结构出了问题。\n读后感 很多问题最后都会落回管理者自己身上：时间是不是被切碎了，事情是不是做了很多却没结果，管理是不是变成了盯细节。\n我读完最大的感受是，有效性不是更努力，而是先分清什么值得投入，什么只是看起来忙。\n","permalink":"https://leochu.work/blog/reading/%E5%8D%93%E6%9C%89%E6%88%90%E6%95%88%E7%9A%84%E7%AE%A1%E7%90%86%E8%80%85/","summary":"\u003cblockquote\u003e\n\u003cp\u003e彼得·德鲁克《卓有成效的管理者》。核心观点：管理者的有效性不是天赋，而是一套可以训练的工作习惯。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e先管理时间。\u003c/strong\u003e 很多管理失效，本质上不是能力不足，而是时间被切碎到无法做真正重要的事。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e重视贡献而不是忙碌。\u003c/strong\u003e 有效管理者会不断问：我这项工作最终对组织产生了什么结果，而不是我今天看起来做了多少事。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e用人要看长处。\u003c/strong\u003e 管理的任务不是修理人的短板，而是把不同人的强项放到能产生效果的位置上。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e决策不是越多越好，而是少而关键。\u003c/strong\u003e 真正重要的决策不会天天发生，关键在于识别哪些问题值得上升到决策层面。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e管理不是控制细节，而是让系统产出结果。\u003c/strong\u003e 一个人如果永远沉迷于亲自处理所有问题，通常意味着管理结构出了问题。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e很多问题最后都会落回管理者自己身上：时间是不是被切碎了，事情是不是做了很多却没结果，管理是不是变成了盯细节。\u003c/p\u003e\n\u003cp\u003e我读完最大的感受是，有效性不是更努力，而是先分清什么值得投入，什么只是看起来忙。\u003c/p\u003e","title":"卓有成效的管理者"},{"content":"·Xmx不要超过机器内存的50%，留下些内存供VM堆外内存和操作系统使用。 ·并且Xm不要超过32G。建议最大配置为30G。接近32G,JVM会启用压缩对象指针的功能，导致 性能下降。常见es集群。具体可以参考：A Heap of Trouble: Managing Elasticsearch\u0026rsquo;s Managed Heap | Elastic Blog\n","permalink":"https://leochu.work/blog/tech/java/jvm%E8%B0%83%E4%BC%98%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/","summary":"\u003cp\u003e·Xmx不要超过机器内存的50%，留下些内存供VM堆外内存和操作系统使用。\n·并且Xm不要超过32G。建议最大配置为30G。接近32G,JVM会启用压缩对象指针的功能，导致\n性能下降。常见es集群。具体可以参考：\u003ca href=\"https://www.elastic.co/cn/blog/a-heap-of-trouble\"\u003eA Heap of Trouble: Managing Elasticsearch\u0026rsquo;s Managed Heap | Elastic Blog\u003c/a\u003e\u003c/p\u003e","title":"JVM调优注意事项"},{"content":"JDK 8 代表的是一个大版本的更新，你可以理解成定义好了框架和实现\nJDK 8u代表的是基于JDK 8的后续小版本的迭代，里面不会有 JDK 8 标准之外的内容，只会包含一些安全性，性能等方面的修改，例如某个Class的实现优化\n观点一 一般来说，建议选用大版本下面最新的u版本，比如你要选择 JDK 8 ，那么就选择 JDK 8u281，这个是目前8这个大版本的最新版本，原因是里面会修复和优化前序版本的一些问题\n观点二 正常来说，应该使用OpenJDK8。\nOpenJDK8u是一些后期维护，一些特性并不是想要的。\n观点三 对于jdk8u\n这个最新的免费版本号，其实包括了两个，8u201和8u202，这个就是JDK版本号的命名问题了。从2014年10月发布Java SE 7 Update 71(Java SE 7u71)开始，Oracle在发布Oracle JDK关键补丁更新(CPUs：Critical Patch Updates)的同时一般会发布相应的补丁集更新(PSUs：Patch Set Updates)。其中Oracle JDK关键补丁更新(CPUs)包含安全漏洞修复和重要漏洞修复，Oracle强烈建议所有Oracle JDK用户及时升级到最新的CPU版本，Oracle JDK 关键补丁更新(CPUs)版本号采用奇数编号。Oracle JDK补丁集更新(PSUs)包含相应CPUs中的所有修复以及其他非重要修复，仅当受到Oracle JDK关键补丁更新(CPUs)版本之外的其他漏洞的影响时才应当使用相应的补丁集更新 (PSUs)，Oracle JDK补丁集更新(PSUs)版本号采用偶数编号。因此，一般情况下我们只要下载奇数编号的最新版本更新就行了。\n简单来讲，Oracle将奇数版本作为BUG修正并全部通过检验的版本，Oracle官方建议用在生产环境最好使用这个版本。Oracle会在奇数版本之后同时发布一个偶数版本，偶数版本包含了奇数版本所有的内容，以及未被验证的BUG修复，Oracle官方建议，除非你受到未验证BUG影响，急需BUG修复才使用这个版本。因此，8u201是CPUs，关键补丁更新。8u202是PSUs，补丁集更新，推荐下载8u201。\n","permalink":"https://leochu.work/blog/tech/java/jdk8%E5%92%8Cjdk8u%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/","summary":"\u003cp\u003eJDK 8 代表的是一个大版本的更新，你可以理解成定义好了框架和实现\u003c/p\u003e\n\u003cp\u003eJDK 8u代表的是基于JDK 8的后续小版本的迭代，里面不会有 JDK 8 标准之外的内容，只会包含一些安全性，性能等方面的修改，例如某个Class的实现优化\u003c/p\u003e\n\u003ch3 id=\"观点一\"\u003e观点一\u003c/h3\u003e\n\u003cp\u003e一般来说，建议选用大版本下面最新的u版本，比如你要选择 JDK 8 ，那么就选择 JDK 8u281，这个是目前8这个大版本的最新版本，原因是里面会修复和优化前序版本的一些问题\u003c/p\u003e\n\u003ch3 id=\"观点二\"\u003e观点二\u003c/h3\u003e\n\u003cp\u003e正常来说，应该使用OpenJDK8。\u003c/p\u003e\n\u003cp\u003eOpenJDK8u是一些后期维护，一些特性并不是想要的。\u003c/p\u003e\n\u003ch3 id=\"观点三\"\u003e观点三\u003c/h3\u003e\n\u003cp\u003e对于jdk8u\u003c/p\u003e\n\u003cp\u003e这个最新的免费版本号，其实包括了两个，8u201和8u202，这个就是JDK版本号的命名问题了。从2014年10月发布Java SE 7 Update 71(Java SE 7u71)开始，Oracle在发布Oracle JDK关键补丁更新(CPUs：Critical Patch Updates)的同时一般会发布相应的补丁集更新(PSUs：Patch Set Updates)。其中Oracle JDK关键补丁更新(CPUs)包含安全漏洞修复和重要漏洞修复，Oracle强烈建议所有Oracle JDK用户及时升级到最新的CPU版本，Oracle JDK 关键补丁更新(CPUs)版本号采用奇数编号。Oracle JDK补丁集更新(PSUs)包含相应CPUs中的所有修复以及其他非重要修复，仅当受到Oracle JDK关键补丁更新(CPUs)版本之外的其他漏洞的影响时才应当使用相应的补丁集更新 (PSUs)，Oracle JDK补丁集更新(PSUs)版本号采用偶数编号。因此，一般情况下我们只要下载奇数编号的最新版本更新就行了。\u003c/p\u003e\n\u003cp\u003e简单来讲，Oracle将奇数版本作为BUG修正并全部通过检验的版本，Oracle官方建议用在生产环境最好使用这个版本。Oracle会在奇数版本之后同时发布一个偶数版本，偶数版本包含了奇数版本所有的内容，以及未被验证的BUG修复，Oracle官方建议，除非你受到未验证BUG影响，急需BUG修复才使用这个版本。因此，8u201是CPUs，关键补丁更新。8u202是PSUs，补丁集更新，推荐下载8u201。\u003c/p\u003e","title":"jdk8和jdk8u有什么区别"},{"content":" 罗兰·米勒《亲密关系》。核心观点：好的关系不是“找到对的人”就自动成立，而是理解吸引、依恋、沟通与冲突如何共同塑造一段关系。\n核心观点 关系质量取决于互动是否长期稳定、可预期。 我们常常不是在和真实的人相处，而是在和自己脑中的“理想伴侣模板”相处。期待越不切实际，越容易把普通摩擦解读成严重问题。\n亲密感来自持续的自我暴露。 关系变深，不是因为一次惊天动地的表达，而是因为双方愿意不断交换真实的信息、情绪和脆弱。\n依恋模式会影响相处方式。 安全型更容易建立稳定联结；焦虑型更容易反复确认爱；回避型则会在靠近时本能撤退。很多矛盾，不是因为不爱，而是因为应对亲密的方式不同。\n印象较深的部分 吸引不只是感觉问题。 熟悉、相似、接近、回应感，这些都比“命中注定”更能解释一段关系为什么会开始。\n满意度来自比较。 人会用“我本来能得到什么”来衡量当前关系，所以幸福感并不只来自伴侣本身，也来自自己的参照系。\n回应感比付出总量更重要。 不是做了多少，而是对方是否感到被理解、被在意。很多冲突，本质上都是回应缺失。\n冲突不可避免，但处理方式有高下。 逃避、指责、翻旧账都会消耗关系；能把问题和人格分开讨论，关系才有修复空间。\n归因方式会持续影响关系温度。 对伴侣更容易做负面归因，对自己则更容易找情境理由；这种偏差会一点点侵蚀善意。\n回应循环会把小问题放大成结构性问题。 一方冷淡，另一方就会加码确认；加码又会让前者更想后退，最后形成“越追越逃、越逃越追”的负反馈。\n读后感 这本书最有价值的地方，是把很多原本被浪漫化的东西重新放回现实。爱情并不是纯凭运气，也不是只靠感觉维持。很多我们以为是“缘分不好”的问题，其实是认知偏差、沟通习惯、依恋模式和互动结构共同作用的结果。\n成熟的亲密关系，不是从不受伤，而是双方都愿意学习如何更好地靠近彼此，同时保留边界，修正误解，维持稳定回应。\n","permalink":"https://leochu.work/blog/reading/%E4%BA%B2%E5%AF%86%E5%85%B3%E7%B3%BB/","summary":"\u003cblockquote\u003e\n\u003cp\u003e罗兰·米勒《亲密关系》。核心观点：好的关系不是“找到对的人”就自动成立，而是理解吸引、依恋、沟通与冲突如何共同塑造一段关系。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e关系质量取决于互动是否长期稳定、可预期。\u003c/strong\u003e 我们常常不是在和真实的人相处，而是在和自己脑中的“理想伴侣模板”相处。期待越不切实际，越容易把普通摩擦解读成严重问题。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e亲密感来自持续的自我暴露。\u003c/strong\u003e 关系变深，不是因为一次惊天动地的表达，而是因为双方愿意不断交换真实的信息、情绪和脆弱。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e依恋模式会影响相处方式。\u003c/strong\u003e 安全型更容易建立稳定联结；焦虑型更容易反复确认爱；回避型则会在靠近时本能撤退。很多矛盾，不是因为不爱，而是因为应对亲密的方式不同。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e吸引不只是感觉问题。\u003c/strong\u003e 熟悉、相似、接近、回应感，这些都比“命中注定”更能解释一段关系为什么会开始。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e满意度来自比较。\u003c/strong\u003e 人会用“我本来能得到什么”来衡量当前关系，所以幸福感并不只来自伴侣本身，也来自自己的参照系。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e回应感比付出总量更重要。\u003c/strong\u003e 不是做了多少，而是对方是否感到被理解、被在意。很多冲突，本质上都是回应缺失。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e冲突不可避免，但处理方式有高下。\u003c/strong\u003e 逃避、指责、翻旧账都会消耗关系；能把问题和人格分开讨论，关系才有修复空间。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e归因方式会持续影响关系温度。\u003c/strong\u003e 对伴侣更容易做负面归因，对自己则更容易找情境理由；这种偏差会一点点侵蚀善意。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e回应循环会把小问题放大成结构性问题。\u003c/strong\u003e 一方冷淡，另一方就会加码确认；加码又会让前者更想后退，最后形成“越追越逃、越逃越追”的负反馈。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e这本书最有价值的地方，是把很多原本被浪漫化的东西重新放回现实。爱情并不是纯凭运气，也不是只靠感觉维持。很多我们以为是“缘分不好”的问题，其实是认知偏差、沟通习惯、依恋模式和互动结构共同作用的结果。\u003c/p\u003e\n\u003cp\u003e成熟的亲密关系，不是从不受伤，而是双方都愿意学习如何更好地靠近彼此，同时保留边界，修正误解，维持稳定回应。\u003c/p\u003e","title":"亲密关系"},{"content":"与程序计数器一样，Java虚拟机栈（Java Virtual Machine Stack）也是线程私有的，它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型：每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧（Stack Frame）用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。\n经常有人把Java内存区域笼统地划分为堆内存（Heap）和栈内存（Stack），这种划分方式直接继承自传统的C、C++程序的内存布局结构，在Java语言里就显得有些粗糙了，实际的内存区域划分要比这更复杂。不过这种划分方式的流行也间接说明了程序员最关注的、与对象内存分配关系最密切的区域是“堆”和“栈”两块。其中，“堆”在稍后笔者会专门讲述，而“栈”通常就是指这里讲的虚拟机栈，或者更多的情况下只是指虚拟机栈中局部变量表部分。\n在《Java虚拟机规范》中，对这个内存区域规定了两类异常状况：如果线程请求的栈深度大于虚拟机所允许的深度，将抛出StackOverflowError异常；如果Java虚拟机栈容量可以动态扩展，当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。\n","permalink":"https://leochu.work/blog/tech/java/java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88/","summary":"\u003cp\u003e与程序计数器一样，Java虚拟机栈（Java Virtual Machine Stack）也是线程私有的，它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型：每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧（Stack Frame）用于存储\u003ca href=\"/blog/tech/java/%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8/\"\u003e局部变量表\u003c/a\u003e、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。\u003c/p\u003e\n\u003cp\u003e经常有人把Java内存区域笼统地划分为堆内存（Heap）和栈内存（Stack），这种划分方式直接继承自传统的C、C++程序的内存布局结构，在Java语言里就显得有些粗糙了，实际的内存区域划分要比这更复杂。不过这种划分方式的流行也间接说明了程序员最关注的、与对象内存分配关系最密切的区域是“堆”和“栈”两块。其中，“堆”在稍后笔者会专门讲述，而“栈”通常就是指这里讲的虚拟机栈，或者更多的情况下只是指虚拟机栈中局部变量表部分。\u003c/p\u003e\n\u003cp\u003e在《Java虚拟机规范》中，对这个内存区域规定了两类异常状况：如果线程请求的栈深度大于虚拟机所允许的深度，将抛出StackOverflowError异常；如果Java虚拟机栈容量可以动态扩展，当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。\u003c/p\u003e","title":"Java虚拟机栈"},{"content":"Java 代码如何运行 我们写的 Java 代码是高级语言，机器肯定是读不懂的。所以我们需要将它转换成机器能读懂的机器语言 (机器码)。 转换工作主要分为以下几个步骤：\n前端编译器 javac 就是前端编译器，可以将 java 文件编译成字节码组成的 class 文件。 java 代码如下：\npublic class Info { public static void main(String[] args) { int a = 1; System.out.println(a); } } 复制代码 执行 javac Info.java 生成 Info.class 文件, 再使用 javap -c Info.class 来查看其中的字节码。\nclass 中字节码内容如下：\n解释器和即时编译器 我们通过 javac 将 java 文件编译成 class 文件，当 jvm 启动加载 class，需要逐条执行字节码指令来完成程序功能。但是程序的执行还是得在机器上，但是机器是不认识字节码的，所以我们需要将字节码转换成机器码，这样才能让机器执行程序。 什么是机器码？ 机器码就是用二进制代码表示的计算机能直接识别和执行的一种机器指令的集合。 而解释器和即时编译器 (Just In Time Compiler，JIT) 就是 JVM 中将字节码转化为机器码的工具。\n解释器 解释器是一行一行地将字节码解析成机器码，解释到哪就执行到哪，狭义地说，就是 for 循环 100 次，你就要将循环体中的代码逐行解释执行 100 次。当程序需要迅速启动和执行时，解释器可以首先发挥作用，省去编译的时间，立即执行。\n即时编译器（JIT） 即时编译器按照我的理解就是：以方法为单位，将热点代码的字节码一次性转为机器码，并在本地缓存起来的工具。避免了部分代码被解释器逐行解释执行的效率问题。 即时编译器分为两种，Client Compiler(C1 编译器) 和 Server Compiler(C2)，默认使用的是 C2，因其运行性能更高。\n什么是热点代码？\n被多次调用的方法和循环体被认定为热点代码。热点代码的判断方法有两种，一是基于采样的热点探测：周期检查每个线程栈顶，统计哪个方法出现次数多，但是不准确；二是基于计数器的热点探测：目前在用，为每个方法建立计数器，统计方法的调用次数。计数器分为方法调用计数器（默认阈值 C1 是 1500 次，C2 是 1w，到达阈值则触发即时编译）和回边计数器（统计一个方法中循环体的执行次数）。 下图为方法调用计数器的执行过程：\n目前主流的 HotSpot 虚拟机中默认是采用解释器与其中一个编译器 (C2 编译器) 直接配合的方式将字节码转换成机器码。\n运行参数 在执行 java 程序的时候，以下参数是和编译方面的运行及调试参数。\n","permalink":"https://leochu.work/blog/tech/java/java%E4%BB%A3%E7%A0%81%E7%9A%84%E8%BF%90%E8%A1%8C/","summary":"\u003ch3 id=\"java-代码如何运行\"\u003eJava 代码如何运行\u003c/h3\u003e\n\u003cp\u003e我们写的 Java 代码是高级语言，机器肯定是读不懂的。所以我们需要将它转换成机器能读懂的机器语言 (机器码)。 转换工作主要分为以下几个步骤：\u003c/p\u003e\n\u003ch4 id=\"前端编译器\"\u003e前端编译器\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003ejavac\u003c/strong\u003e 就是前端编译器，可以将 java 文件编译成字节码组成的 class 文件。 java 代码如下：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eInfo\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(String\u003cspan style=\"color:#f92672\"\u003e[]\u003c/span\u003e args) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e a \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e 1;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        System.\u003cspan style=\"color:#a6e22e\"\u003eout\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eprintln\u003c/span\u003e(a);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e复制代码\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e执行 \u003cstrong\u003ejavac Info.java\u003c/strong\u003e 生成 Info.class 文件, 再使用 \u003cstrong\u003ejavap -c Info.class 来\u003c/strong\u003e查看其中的字节码。\u003c/p\u003e\n\u003cp\u003eclass 中字节码内容如下：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230331151428.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230331151428.png\"\u003e\u003c/p\u003e\n\u003ch4 id=\"解释器和即时编译器\"\u003e解释器和即时编译器\u003c/h4\u003e\n\u003cp\u003e我们通过 javac 将 java 文件编译成 class 文件，当 jvm 启动加载 class，需要逐条执行字节码指令来完成程序功能。但是程序的执行还是得在机器上，但是机器是不认识字节码的，所以我们需要将字节码转换成机器码，这样才能让机器执行程序。 \u003cem\u003e什么是机器码？\u003c/em\u003e 机器码就是用二进制代码表示的计算机能直接识别和执行的一种机器指令的集合。 而解释器和即时编译器 (Just In Time Compiler，JIT) 就是 JVM 中将字节码转化为机器码的工具。\u003c/p\u003e\n\u003ch5 id=\"解释器\"\u003e解释器\u003c/h5\u003e\n\u003cp\u003e解释器是一行一行地将字节码解析成机器码，解释到哪就执行到哪，狭义地说，就是 for 循环 100 次，你就要将循环体中的代码逐行解释执行 100 次。当程序需要迅速启动和执行时，解释器可以首先发挥作用，省去编译的时间，立即执行。\u003c/p\u003e","title":"java代码的运行"},{"content":"·在虚拟机栈（栈帧中的本地变量表）中引用的对象，譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。\n·在方法区中类静态属性引用的对象，譬如Java类的引用类型静态变量。\n·在方法区中常量引用的对象，譬如字符串常量池（String Table）里的引用。\n·在本地方法栈中JNI（即通常所说的Native方法）引用的对象。\n·Java虚拟机内部的引用，如基本数据类型对应的Class对象，一些常驻的异常对象（比如NullPointExcepiton、OutOfMemoryError）等，还有系统类加载器。\n·所有被同步锁（synchronized关键字）持有的对象。\n·反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。\n除了这些固定的GC Roots集合以外，根据用户所选用的垃圾收集器以及当前回收的内存区域不同，还可以有其他对象“临时性”地加入，共同构成完整GC Roots集合。譬如后文将会提到的分代收集和局部回收（Partial GC），如果只针对Java堆中某一块区域发起垃圾收集时（如最典型的只针对新生代的垃圾收集），必须考虑到内存区域是虚拟机自己的实现细节（在用户视角里任何内存区域都是不可见的），更不是孤立封闭的，所以某个区域里的对象完全有可能被位于堆中其他区域的对象所引用，这时候就需要将这些关联区域的对象也一并加入GC Roots集合中去，才能保证可达性分析的正确性。\n目前最新的几款垃圾收集器 无一例外都具备了局部回收的特征，为了避免GC Roots包含过多对象而过度膨胀，它们在实现上也做出了各种优化处理。\n","permalink":"https://leochu.work/blog/tech/java/gc-roots/","summary":"\u003cp\u003e·在虚拟机栈（栈帧中的本地变量表）中引用的对象，譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。\u003c/p\u003e\n\u003cp\u003e·在方法区中类静态属性引用的对象，譬如Java类的引用类型静态变量。\u003c/p\u003e\n\u003cp\u003e·在方法区中常量引用的对象，譬如字符串常量池（String Table）里的引用。\u003c/p\u003e\n\u003cp\u003e·在本地方法栈中JNI（即通常所说的Native方法）引用的对象。\u003c/p\u003e\n\u003cp\u003e·Java虚拟机内部的引用，如基本数据类型对应的Class对象，一些常驻的异常对象（比如NullPointExcepiton、OutOfMemoryError）等，还有系统类加载器。\u003c/p\u003e\n\u003cp\u003e·所有被同步锁（synchronized关键字）持有的对象。\u003c/p\u003e\n\u003cp\u003e·反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。\u003c/p\u003e\n\u003cp\u003e除了这些固定的GC Roots集合以外，根据用户所选用的垃圾收集器以及当前回收的内存区域不同，还可以有其他对象“临时性”地加入，共同构成完整GC Roots集合。譬如后文将会提到的分代收集和局部回收（Partial GC），如果只针对Java堆中某一块区域发起垃圾收集时（如最典型的只针对新生代的垃圾收集），必须考虑到内存区域是虚拟机自己的实现细节（在用户视角里任何内存区域都是不可见的），更不是孤立封闭的，所以某个区域里的对象完全有可能被位于堆中其他区域的对象所引用，这时候就需要将这些关联区域的对象也一并加入GC Roots集合中去，才能保证可达性分析的正确性。\u003c/p\u003e\n\u003cp\u003e目前最新的几款垃圾收集器 无一例外都具备了局部回收的特征，为了避免GC Roots包含过多对象而过度膨胀，它们在实现上也做出了各种优化处理。\u003c/p\u003e","title":"GC Roots"},{"content":"有女生发帖：男朋友把她哄睡后，会打游戏到很晚来释放压力。她想知道，这种行为该怎么理解\n我试着把这件事往更严重的方向去理解。\n这是可能是她完全没有get到的一个关系中的巨大失败。\n她在对方的世界里，变成了像通勤、打卡、坐班那样的消耗，像擦屁股、撕逼那样的麻烦，像给父母报喜不报忧、明明在摸鱼却还要在同事面前演努力那样的表演——一件恼人心神、不搞定就不会自己消停、不处理就无法安生的麻烦本身。\n她把自己活成了一个关系中的被应付，被处理，被物化的对象。对方在她面前，深感压力，相当抵触，至少是回避、躲逃、推迟、早退，任何无厘头的理由都可以拿出来，换取片刻的安宁。极其抗拒与她的长时间物理绑定，因为物理绑定中的她，并不消停，一直试图推行本不可能的精神世界的强行交融。\n他们看似肉体日日共处一室，卧榻之侧香甜酣睡，实则精神深处激烈排异。\n把她哄睡了，自己好独处一下，就是他的一天结束时的精神打卡。\n她成了他时间线上一个需要被搪塞、安抚的事项之一。\n而不是一个让他依恋，让他沉迷，让他感觉环境宽松，压力归零，心有联结，愿意分享和物理捆绑的爱人。\n她以为他终于被驯服，成功被调教，每天早请示晚汇报，提供了长久的物理陪伴和充足的情绪价值。\n殊不知从这个时候开始，她再也难以进入他的精神家园了。她只是他赤脚进入自己独享的精神家园之前，扔掉的鞋子，洗掉的征尘，卸载的零碎和负重罢了。\n所谓男人的“洞穴时间”——车里那半个小时，厕所那盏不关的灯，或者深夜独自亮起的屏幕。本质未必只是独处，更可能是一段关系已经失效的证据。\n长期靠洞穴时间活着，对男人来说，不是什么本能，更像是一种悲哀。\n","permalink":"https://leochu.work/blog/thoughts/%E6%B4%9E%E7%A9%B4%E6%97%B6%E9%97%B4/","summary":"\u003cp\u003e有女生发帖：男朋友把她哄睡后，会打游戏到很晚来释放压力。她想知道，这种行为该怎么理解\u003c/p\u003e\n\u003cp\u003e我试着把这件事往更严重的方向去理解。\u003c/p\u003e\n\u003cp\u003e这是可能是她完全没有get到的一个关系中的巨大失败。\u003c/p\u003e\n\u003cp\u003e她在对方的世界里，变成了像通勤、打卡、坐班那样的消耗，像擦屁股、撕逼那样的麻烦，像给父母报喜不报忧、明明在摸鱼却还要在同事面前演努力那样的表演——一件恼人心神、不搞定就不会自己消停、不处理就无法安生的麻烦本身。\u003c/p\u003e\n\u003cp\u003e她把自己活成了一个关系中的被应付，被处理，被物化的对象。对方在她面前，深感压力，相当抵触，至少是回避、躲逃、推迟、早退，任何无厘头的理由都可以拿出来，换取片刻的安宁。极其抗拒与她的长时间物理绑定，因为物理绑定中的她，并不消停，一直试图推行本不可能的精神世界的强行交融。\u003c/p\u003e\n\u003cp\u003e他们看似肉体日日共处一室，卧榻之侧香甜酣睡，实则精神深处激烈排异。\u003c/p\u003e\n\u003cp\u003e把她哄睡了，自己好独处一下，就是他的一天结束时的精神打卡。\u003c/p\u003e\n\u003cp\u003e她成了他时间线上一个需要被搪塞、安抚的事项之一。\u003c/p\u003e\n\u003cp\u003e而不是一个让他依恋，让他沉迷，让他感觉环境宽松，压力归零，心有联结，愿意分享和物理捆绑的爱人。\u003c/p\u003e\n\u003cp\u003e她以为他终于被驯服，成功被调教，每天早请示晚汇报，提供了长久的物理陪伴和充足的情绪价值。\u003c/p\u003e\n\u003cp\u003e殊不知从这个时候开始，她再也难以进入他的精神家园了。她只是他赤脚进入自己独享的精神家园之前，扔掉的鞋子，洗掉的征尘，卸载的零碎和负重罢了。\u003c/p\u003e\n\u003cp\u003e所谓男人的“洞穴时间”——车里那半个小时，厕所那盏不关的灯，或者深夜独自亮起的屏幕。本质未必只是独处，更可能是一段关系已经失效的证据。\u003c/p\u003e\n\u003cp\u003e长期靠洞穴时间活着，对男人来说，不是什么本能，更像是一种悲哀。\u003c/p\u003e","title":"洞穴时间"},{"content":"Exact VM因它使用准确式内存管理（Exact Memory Management，也可以叫Non-Con-servative/Accurate Memory Management）而得名。\n准确式内存管理是指虚拟机可以知道内存中某个位置的数据具体是什么类型。\n譬如内存中有一个32bit的整数123456，虚拟机将有能力分辨出它到底是一个指向了123456的内存地址的引用类型还是一个数值为123456的整数，准确分辨出哪些内存是引用类型，这也是在垃圾收集时准确判断堆上的数据是否还可能被使用的前提。\n由于使用了准确式内存管理，Exact VM可以抛弃掉以前Classic VM基于句柄（Handle）的对象查找方式（原因是垃圾收集后对象将可能会被移动位置，如果地址为123456的对象移动到654321，在没有明确信息表明内存中哪些数据是引用类型的前提下，那虚拟机肯定是不敢把内存中所有为123456的值改成654321的，所以要使用句柄来保持引用值的稳定），这样每次定位对象都少了一次间接查找的开销，显著提升执行性能。\n","permalink":"https://leochu.work/blog/tech/java/exact-vm/","summary":"\u003cp\u003eExact VM因它使用准确式内存管理（Exact Memory Management，也可以叫Non-Con-servative/Accurate Memory Management）而得名。\u003c/p\u003e\n\u003cp\u003e准确式内存管理是指虚拟机可以知道内存中某个位置的数据具体是什么类型。\u003c/p\u003e\n\u003cp\u003e譬如内存中有一个32bit的整数123456，虚拟机将有能力分辨出它到底是一个指向了123456的内存地址的引用类型还是一个数值为123456的整数，准确分辨出哪些内存是引用类型，这也是在垃圾收集时准确判断堆上的数据是否还可能被使用的前提。\u003c/p\u003e\n\u003cp\u003e由于使用了准确式内存管理，Exact VM可以抛弃掉以前Classic VM基于句柄（Handle）的对象查找方式（原因是垃圾收集后对象将可能会被移动位置，如果地址为123456的对象移动到654321，在没有明确信息表明内存中哪些数据是引用类型的前提下，那虚拟机肯定是不敢把内存中所有为123456的值改成654321的，所以要使用句柄来保持引用值的稳定），这样每次定位对象都少了一次间接查找的开销，显著提升执行性能。\u003c/p\u003e","title":"Exact VM"},{"content":" Frederick P. Brooks Jr.《人月传奇》。核心观点：软件项目最大的难点不是编码本身，而是复杂性、沟通成本和不可压缩的系统设计工作。\n核心观点 向延期项目增加人手，只会让它更晚。 新人加入需要沟通、培训、同步上下文，短期内增加的是负担而不是产能。\n概念完整性比局部聪明更重要。 一个系统最宝贵的是整体一致的设计语言，而不是每个模块都由最聪明的人各自发挥。\n软件开发存在本质复杂性。 有些问题可以靠工具改善，但需求理解、系统边界、多人协作这些难题无法被简单消灭。\n印象较深的部分 人月并不能自由替换。 并不是每个任务都能线性拆分后并行推进，很多关键工作天然依赖少数人做深度思考。\n文档和接口设计是沟通成本控制器。 项目一旦变大，真正稀缺的就不是“谁会写代码”，而是“谁能让协作保持有序”。\n读后感 这本书虽然老，但读起来并不旧。今天工具多了很多，项目翻车的原因却没怎么变：目标没说清，边界没定住，沟通一乱，就开始指望“再加点人”补回来。\n核心是把软件工程从“多写点代码”拉回到“怎么组织人和系统一起工作”。\n","permalink":"https://leochu.work/blog/reading/%E4%BA%BA%E6%9C%88%E4%BC%A0%E5%A5%87/","summary":"\u003cblockquote\u003e\n\u003cp\u003eFrederick P. Brooks Jr.《人月传奇》。核心观点：软件项目最大的难点不是编码本身，而是复杂性、沟通成本和不可压缩的系统设计工作。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e向延期项目增加人手，只会让它更晚。\u003c/strong\u003e 新人加入需要沟通、培训、同步上下文，短期内增加的是负担而不是产能。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e概念完整性比局部聪明更重要。\u003c/strong\u003e 一个系统最宝贵的是整体一致的设计语言，而不是每个模块都由最聪明的人各自发挥。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e软件开发存在本质复杂性。\u003c/strong\u003e 有些问题可以靠工具改善，但需求理解、系统边界、多人协作这些难题无法被简单消灭。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e人月并不能自由替换。\u003c/strong\u003e 并不是每个任务都能线性拆分后并行推进，很多关键工作天然依赖少数人做深度思考。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e文档和接口设计是沟通成本控制器。\u003c/strong\u003e 项目一旦变大，真正稀缺的就不是“谁会写代码”，而是“谁能让协作保持有序”。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e这本书虽然老，但读起来并不旧。今天工具多了很多，项目翻车的原因却没怎么变：目标没说清，边界没定住，沟通一乱，就开始指望“再加点人”补回来。\u003c/p\u003e\n\u003cp\u003e核心是把软件工程从“多写点代码”拉回到“怎么组织人和系统一起工作”。\u003c/p\u003e","title":"人月传奇"},{"content":"\n1.什么是线程不安全？ 线程不安全也叫非线程安全，是指多线程执行中，程序的执行结果和预期的结果不符的情况就叫做线程不安全。 ​\n线程不安全的代码 SimpleDateFormat 就是一个典型的线程不安全事例，接下来我们动手来实现一下。首先我们先创建 10 个线程来格式化时间，时间格式化每次传递的待格式化时间都是不同的，所以程序如果正确执行将会打印 10 个不同的值，接下来我们来看具体的代码实现：\nimport java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleDateFormatExample { // 创建 SimpleDateFormat 对象 private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(\u0026#34;mm:ss\u0026#34;); public static void main(String[] args) { // 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); // 执行 10 次时间格式化 for (int i = 0; i \u0026lt; 10; i++) { int finalI = i; // 线程池执行任务 threadPool.execute(new Runnable() { @Override public void run() { // 创建时间对象 Date date = new Date(finalI * 1000); // 执行时间格式化并打印结果 System.out.println(simpleDateFormat.format(date)); } }); } } } 我们预期的正确结果是这样的（10 次打印的值都不同）：\n然而，以上程序的运行结果却是这样的：\n从上述结果可以看出，当在多线程中使用 SimpleDateFormat 进行时间格式化是线程不安全的。 ​\n2.解决方案 SimpleDateFormat 线程不安全的解决方案总共包含以下 5 种：\n将 SimpleDateFormat 定义为局部变量； 使用 synchronized 加锁执行； 使用 Lock 加锁执行（和解决方案 2 类似）； 使用 ThreadLocal； 使用 JDK 8 中提供的 DateTimeFormat。 接下来我们分别来看每种解决方案的具体实现。\n① 将SimpleDateFormat变为局部变量 将 SimpleDateFormat 定义为局部变量时，因为每个线程都是独享 SimpleDateFormat 对象的，相当于将多线程程序变成“单线程”程序了，所以不会有线程不安全的问题，具体实现代码如下：\nimport java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleDateFormatExample { public static void main(String[] args) { // 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); // 执行 10 次时间格式化 for (int i = 0; i \u0026lt; 10; i++) { int finalI = i; // 线程池执行任务 threadPool.execute(new Runnable() { @Override public void run() { // 创建 SimpleDateFormat 对象 SimpleDateFormat simpleDateFormat = new SimpleDateFormat(\u0026#34;mm:ss\u0026#34;); // 创建时间对象 Date date = new Date(finalI * 1000); // 执行时间格式化并打印结果 System.out.println(simpleDateFormat.format(date)); } }); } // 任务执行完之后关闭线程池 threadPool.shutdown(); } } 以上程序的执行结果为：\n当打印的结果都不相同时，表示程序的执行是正确的，从上述结果可以看出，将 SimpleDateFormat 定义为局部变量之后，就可以成功的解决线程不安全问题了。 ​\n② 使用synchronized加锁 锁是解决线程不安全问题最常用的手段，接下来我们先用 synchronized 来加锁进行时间格式化，实现代码如下：\nimport java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleDateFormatExample2 { // 创建 SimpleDateFormat 对象 private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(\u0026#34;mm:ss\u0026#34;); public static void main(String[] args) { // 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); // 执行 10 次时间格式化 for (int i = 0; i \u0026lt; 10; i++) { int finalI = i; // 线程池执行任务 threadPool.execute(new Runnable() { @Override public void run() { // 创建时间对象 Date date = new Date(finalI * 1000); // 定义格式化的结果 String result = null; synchronized (simpleDateFormat) { // 时间格式化 result = simpleDateFormat.format(date); } // 打印结果 System.out.println(result); } }); } // 任务执行完之后关闭线程池 threadPool.shutdown(); } } 以上程序的执行结果为：\n③ 使用Lock加锁 在 Java 语言中，锁的常用实现方式有两种，除了 synchronized 之外，还可以使用手动锁 Lock，接下来我们使用 Lock 来对线程不安全的代码进行改造，实现代码如下：\nimport java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Lock 解决线程不安全问题 */ public class SimpleDateFormatExample3 { // 创建 SimpleDateFormat 对象 private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(\u0026#34;mm:ss\u0026#34;); public static void main(String[] args) { // 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); // 创建 Lock 锁 Lock lock = new ReentrantLock(); // 执行 10 次时间格式化 for (int i = 0; i \u0026lt; 10; i++) { int finalI = i; // 线程池执行任务 threadPool.execute(new Runnable() { @Override public void run() { // 创建时间对象 Date date = new Date(finalI * 1000); // 定义格式化的结果 String result = null; // 加锁 lock.lock(); try { // 时间格式化 result = simpleDateFormat.format(date); } finally { // 释放锁 lock.unlock(); } // 打印结果 System.out.println(result); } }); } // 任务执行完之后关闭线程池 threadPool.shutdown(); } } 以上程序的执行结果为：\n从上述代码可以看出，手动锁的写法相比于 synchronized 要繁琐一些。\n④ 使用ThreadLocal 加锁方案虽然可以正确的解决线程不安全的问题，但同时也引入了新的问题，加锁会让程序进入排队执行的流程，从而一定程度的降低了程序的执行效率，如下图所示：\n那有没有一种方案既能解决线程不安全的问题，同时还可以避免排队执行呢？ ​\n答案是有的，可以考虑使用 ThreadLocal。ThreadLocal 翻译为中文是线程本地变量的意思，字如其人 ThreadLocal 就是用来创建线程的私有（本地）变量的，每个线程拥有自己的私有对象，这样就可以避免线程不安全的问题了，实现如下：\n知道了实现方案之后，接下来我们使用具体的代码来演示一下 ThreadLocal 的使用，实现代码如下：\nimport java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * ThreadLocal 解决线程不安全问题 */ public class SimpleDateFormatExample4 { // 创建 ThreadLocal 对象，并设置默认值（new SimpleDateFormat） private static ThreadLocal\u0026lt;SimpleDateFormat\u0026gt; threadLocal = ThreadLocal.withInitial(() -\u0026gt; new SimpleDateFormat(\u0026#34;mm:ss\u0026#34;)); public static void main(String[] args) { // 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); // 执行 10 次时间格式化 for (int i = 0; i \u0026lt; 10; i++) { int finalI = i; // 线程池执行任务 threadPool.execute(new Runnable() { @Override public void run() { // 创建时间对象 Date date = new Date(finalI * 1000); // 格式化时间 String result = threadLocal.get().format(date); // 打印结果 System.out.println(result); } }); } // 任务执行完之后关闭线程池 threadPool.shutdown(); } } 以上程序的执行结果为：\nThreadLocal和局部变量的区别 首先来说 ThreadLocal 不等于局部变量，这里的“局部变量”指的是像 2.1 示例代码中的局部变量， ThreadLocal 和局部变量最大的区别在于：ThreadLocal 属于线程的私有变量，如果使用的是线程池，那么 ThreadLocal 中的变量是可以重复使用的，而代码级别的局部变量，每次执行时都会创建新的局部变量，二者区别如下图所示：\n更多关于 ThreadLocal 的内容，可以访问ThreadLocal。\n⑤ 使用DateTimeFormatter 以上 4 种解决方案都是因为 SimpleDateFormat 是线程不安全的，所以我们需要加锁或者使用 ThreadLocal 来处理，然而，JDK 8 之后我们就有了新的选择，如果使用的是 JDK 8+ 版本，就可以直接使用 JDK 8 中新增的、安全的时间格式化工具类 DateTimeFormatter 来格式化时间了，接下来我们来具体实现一下。\n使用 DateTimeFormatter 必须要配合 JDK 8 中新增的时间对象 LocalDateTime 来使用，因此在操作之前，我们可以先将 Date 对象转换成 LocalDateTime，然后再通过 DateTimeFormatter 来格式化时间，具体实现代码如下：\nimport java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * DateTimeFormatter 解决线程不安全问题 */ public class SimpleDateFormatExample5 { // 创建 DateTimeFormatter 对象 private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(\u0026#34;mm:ss\u0026#34;); public static void main(String[] args) { // 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); // 执行 10 次时间格式化 for (int i = 0; i \u0026lt; 10; i++) { int finalI = i; // 线程池执行任务 threadPool.execute(new Runnable() { @Override public void run() { // 创建时间对象 Date date = new Date(finalI * 1000); // 将 Date 转换成 JDK 8 中的时间类型 LocalDateTime LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); // 时间格式化 String result = dateTimeFormatter.format(localDateTime); // 打印结果 System.out.println(result); } }); } // 任务执行完之后关闭线程池 threadPool.shutdown(); } } 以上程序的执行结果为：\n3.线程不安全原因分析 要了解 SimpleDateFormat 为什么是线程不安全的？我们需要查看并分析 SimpleDateFormat 的源码才行，那我们先从使用的方法 format 入手，源码如下：\nprivate StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // 注意此行代码 calendar.setTime(date); boolean useDateFormatSymbols = useDateFormatSymbols(); for (int i = 0; i \u0026lt; compiledPattern.length; ) { int tag = compiledPattern[i] \u0026gt;\u0026gt;\u0026gt; 8; int count = compiledPattern[i++] \u0026amp; 0xff; if (count == 255) { count = compiledPattern[i++] \u0026lt;\u0026lt; 16; count |= compiledPattern[i++]; } switch (tag) { case TAG_QUOTE_ASCII_CHAR: toAppendTo.append((char)count); break; case TAG_QUOTE_CHARS: toAppendTo.append(compiledPattern, i, count); i += count; break; default: subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols); break; } } return toAppendTo; } 也许是好运使然，没想到刚开始分析第一个方法就找到了线程不安全的问题所在。 ​\n从上述源码可以看出，在执行 SimpleDateFormat.format 方法时，会使用 calendar.setTime 方法将输入的时间进行转换，那么我们想象一下这样的场景：\n线程 1 执行了 calendar.setTime(date) 方法，将用户输入的时间转换成了后面格式化时所需要的时间； 线程 1 暂停执行，线程 2 得到 CPU 时间片开始执行； 线程 2 执行了 calendar.setTime(date) 方法，对时间进行了修改； 线程 2 暂停执行，线程 1 得出 CPU 时间片继续执行，因为线程 1 和线程 2 使用的是同一对象，而时间已经被线程 2 修改了，所以此时当线程 1 继续执行的时候就会出现线程安全的问题了。 正常的情况下，程序的执行是这样的：\n非线程安全的执行流程是这样的：\n在多线程执行的情况下，线程 1 的 date1 和线程 2 的 date2，因为执行顺序的问题，最终都被格式化成 date2 formatted，而非线程 1 date1 formatted 和线程 2 date2 formatted，这样就会导致线程不安全的问题。\n4.各方案优缺点总结 如果使用的是 JDK 8+ 版本，可以直接使用线程安全的 DateTimeFormatter 来进行时间格式化，如果使用的 JDK 8 以下版本或者改造老的 SimpleDateFormat 代码，可以考虑使用 synchronized 或 ThreadLocal 来解决线程不安全的问题。因为实现方案 1 局部变量的解决方案，每次执行的时候都会创建新的对象，因此不推荐使用。synchronized 的实现比较简单，而使用 ThreadLocal 可以避免加锁排队执行的问题。\n","permalink":"https://leochu.work/blog/tech/java/%E7%BA%BF%E7%A8%8B%E4%B8%8D%E5%AE%89%E5%85%A8%E7%9A%84simpledateformat/","summary":"\u003cp\u003e\u003cimg alt=\"Pasted image 20230420183112.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230420183112.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"1什么是线程不安全\"\u003e1.什么是线程不安全？\u003c/h2\u003e\n\u003cp\u003e线程不安全也叫非线程安全，是指\u003cstrong\u003e多线程执行中，程序的执行结果和预期的结果不符的情况就叫做线程不安全\u003c/strong\u003e。 ​\u003c/p\u003e\n\u003ch3 id=\"线程不安全的代码\"\u003e线程不安全的代码\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eSimpleDateFormat\u003c/code\u003e 就是一个典型的线程不安全事例，接下来我们动手来实现一下。首先我们先创建 10 个线程来格式化时间，时间格式化每次传递的待格式化时间都是不同的，所以程序如果正确执行将会打印 10 个不同的值，接下来我们来看具体的代码实现：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.text.SimpleDateFormat;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.util.Date;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.util.concurrent.ExecutorService;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e java.util.concurrent.Executors;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eSimpleDateFormatExample\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 创建 SimpleDateFormat 对象\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e SimpleDateFormat simpleDateFormat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e SimpleDateFormat(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;mm:ss\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estatic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e(String\u003cspan style=\"color:#f92672\"\u003e[]\u003c/span\u003e args) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e// 创建线程池\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        ExecutorService threadPool \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Executors.\u003cspan style=\"color:#a6e22e\"\u003enewFixedThreadPool\u003c/span\u003e(10);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#75715e\"\u003e// 执行 10 次时间格式化\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e i \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e 0; i \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u003c/span\u003e 10; i\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e finalI \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e i;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#75715e\"\u003e// 线程池执行任务\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            threadPool.\u003cspan style=\"color:#a6e22e\"\u003eexecute\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e Runnable() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erun\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#75715e\"\u003e// 创建时间对象\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    Date date \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e Date(finalI \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e 1000);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#75715e\"\u003e// 执行时间格式化并打印结果\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    System.\u003cspan style=\"color:#a6e22e\"\u003eout\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eprintln\u003c/span\u003e(simpleDateFormat.\u003cspan style=\"color:#a6e22e\"\u003eformat\u003c/span\u003e(date));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            });\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e我们预期的正确结果是这样的（10 次打印的值都不同）：\u003c/p\u003e","title":"线程不安全的simpleDateFormat"},{"content":"第一部分 走进java 第一章 走进java 1.2 java技术体系 1.3 java发展史 Jigsaw项目：虚拟机层面的模块化支持。\nDLL HELL : 模块地狱\n1.4 jvm家族 1.4.1　虚拟机始祖：Sun Classic/Exact VM 编译器/解释器/即时编译器 : java代码的运行\nExact VM 使用准确式内存管理（Exact Memory Management，也可以叫Non-Con-servative/Accurate Memory Management）\n1.4.2　武林盟主：HotSpot VM HotSpot虚拟机是Sun/OracleJDK和OpenJDK中的默认Java虚拟机，也是目前使用范围最广的Java虚拟机。\n1.6　实战：自己编译JDK 1.6.1　获取源码 OpenJDK 12地址 jdk8和jdk8u有什么区别?\n1.6.2　系统需求 推荐linux 64位和mac os 64位\n查看位数命令: getconf LONG_BIT\n第二部分　自动内存管理 第2章　Java内存区域与内存溢出异常 2.2　运行时数据区域 2.2.1　程序计数器 程序计数器（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。\n2.2.2　Java虚拟机栈 与程序计数器一样，Java虚拟机栈（Java Virtual Machine Stack）也是线程私有的，它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型：每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧 用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。\n2.2.3　本地方法栈 本地方法栈（Native Method Stacks）与虚拟机栈所发挥的作用是非常相似的，其区别只是虚拟机栈为虚拟机执行Java方法（也就是字节码）服务，而本地方法栈则是为虚拟机使用到的本地（Native）方法服务。\n2.2.4　Java堆 Java堆中存储内容的共性，无论是哪个区域，存储的都只能是对象的实例\n2.2.5　方法区 方法区（Method Area）与Java堆一样，是各个线程共享的内存区域，它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分，但是它却有一个别名叫作“非堆”（Non-Heap），目的是与Java堆区分开来。\n2.2.6　运行时常量池 运行时常量池（Runtime Constant Pool）是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外，还有一项信息是常量池表（Constant Pool Table），用于存放编译期生成的各种字面量与符号引用，这部分内容将在类加载后存放到方法区的运行时常量池中。\n运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性，Java语言并不要求常量一定只有编译期才能产生，也就是说，并非预置入Class文件中常量池的内容才能进入方法区运行时常量池，运行期间也可以将新的常量放入池中，这种特性被开发人员利用得比较多的便是String类的intern()方法。\n2.2.7　直接内存 直接内存（Direct Memory）并不是虚拟机运行时数据区的一部分,但是这部分内存也被频繁地使用，而且也可能导致OutOfMemoryError异常出现.\n在JDK 1.4中新加入了NIO（New Input/Output）类，引入了一种基于通道（Channel）与缓冲区（Buffer）的I/O方式，它可以使用Native函数库直接分配堆外内存，然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能，因为避免了在Java堆和Native堆中来回复制数据。\n就是堆外内存,0拷贝基于这个\n2.3　HotSpot虚拟机对象探秘 2.3.1　对象的创建 假设Java堆中内存是绝对规整的，所有被使用过的内存都被放在一边，空闲的内存被放在另一边，中间放着一个指针作为分界点的指示器，那所分配内存就仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的距离，这种分配方式称为“指针碰撞”（Bump The Pointer）。但如果Java堆中的内存并不是规整的，已被使用的内存和空闲的内存相互交错在一起，那就没有办法简单地进行指针碰撞了，虚拟机就必须维护一个列表，记录上哪些内存块是可用的，在分配的时候从列表中找到一块足够大的空间划分给对象实例，并更新列表上的记录，这种分配方式称为“空闲列表”（Free List）。选择哪种分配方式由Java堆是否规整决定，而Java堆是否规整又由所采用的垃圾收集器是否带有空间压缩整理（Compact）的能力决定。因此，当使用Serial、ParNew等带压缩整理过程的收集器时，系统采用的分配算法是指针碰撞，既简单又高效；而当使用CMS这种基于清除（Sweep）算法的收集器时，理论上就只能采用较为复杂的空闲列表来分配内存。\n除如何划分可用空间之外，还有另外一个需要考虑的问题：对象创建在虚拟机中是非常频繁的行为，即使仅仅修改一个指针所指向的位置，在并发情况下也并不是线程安全的，可能出现正在给对象A分配内存，指针还没来得及修改，对象B又同时使用了原来的指针来分配内存的情况。解决这个问题有两种可选方案：一种是对分配内存空间的动作进行同步处理——实际上虚拟机是采用CAS配上失败重试的方式保证更新操作的原子性；另外一种是把内存分配的动作按照线程划分在不同的空间之中进行，即每个线程在Java堆中预先分配一小块内存，称为本地线程分配缓冲（Thread Local Allocation Buffer，TLAB），哪个线程要分配内存，就在哪个线程的本地缓冲区中分配，只有本地缓冲区用完了，分配新的缓存区时才需要同步锁定。虚拟机是否使用TLAB，可以通过-XX：+/-UseTLAB参数来设定。\n2.3.2　对象的内存布局 在HotSpot虚拟机里，对象在堆内存中的存储布局可以划分为三个部分：对象头（Header）、实例数据（Instance Data）和对齐填充（Padding）。\n2.3.3　对象的访问定位 通过句柄访问对象 通过直接指针访问对象\n第3章　垃圾收集器与内存分配策略 3.2.2　可达性分析算法 可达性分析（Reachability Analysis）算法来判定对象是否存活的。这个算法的基本思路就是通过一系列称为GC Roots的根对象作为起始节点集，从这些节点开始，根据引用关系向下搜索，搜索过程所走过的路径称为“引用链”（Reference Chain），如果某个对象到GC Roots间没有任何引用链相连，或者用图论的话来说就是从GC Roots到这个对象不可达时，则证明此对象是不可能再被使用的。\n第4章　虚拟机性能监控、故障处理工具 第5章　调优案例分析与实战 ","permalink":"https://leochu.work/blog/reading/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3jvm/","summary":"\u003ch2 id=\"第一部分-走进java\"\u003e第一部分 走进java\u003c/h2\u003e\n\u003ch3 id=\"第一章-走进java\"\u003e第一章 走进java\u003c/h3\u003e\n\u003ch4 id=\"12-java技术体系\"\u003e1.2 java技术体系\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230331145417.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230331145417.png\"\u003e\u003c/p\u003e\n\u003ch4 id=\"13-java发展史\"\u003e1.3 java发展史\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cp\u003eJigsaw项目：虚拟机层面的模块化支持。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003eDLL HELL :  \u003ca href=\"/blog/tech/engineering/%E6%A8%A1%E5%9D%97%E5%9C%B0%E7%8B%B1/\"\u003e模块地狱\u003c/a\u003e\u003c/strong\u003e\u003c/p\u003e\n\u003ch4 id=\"14-jvm家族\"\u003e1.4 jvm家族\u003c/h4\u003e\n\u003ch5 id=\"141虚拟机始祖sun-classicexact-vm\"\u003e1.4.1　虚拟机始祖：Sun Classic/Exact VM\u003c/h5\u003e\n\u003cp\u003e编译器/解释器/即时编译器 : \u003ca href=\"/blog/tech/java/java%E4%BB%A3%E7%A0%81%E7%9A%84%E8%BF%90%E8%A1%8C/\"\u003ejava代码的运行\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/blog/tech/java/exact-vm/\"\u003eExact VM\u003c/a\u003e 使用准确式内存管理（Exact Memory Management，也可以叫Non-Con-servative/Accurate Memory Management）\u003c/p\u003e\n\u003ch5 id=\"142武林盟主hotspot-vm\"\u003e1.4.2　武林盟主：HotSpot VM\u003c/h5\u003e\n\u003cp\u003eHotSpot虚拟机是Sun/OracleJDK和OpenJDK中的默认Java虚拟机，也是目前使用范围最广的Java虚拟机。\u003c/p\u003e\n\u003ch4 id=\"16实战自己编译jdk\"\u003e1.6　实战：自己编译JDK\u003c/h4\u003e\n\u003ch5 id=\"161获取源码\"\u003e1.6.1　获取源码\u003c/h5\u003e\n\u003cp\u003e\u003ca href=\"https://hg.openjdk.java.net/jdk/jdk12/\"\u003eOpenJDK 12地址\u003c/a\u003e\n\u003cimg alt=\"Pasted image 20230331162843.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230331162843.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/blog/tech/java/jdk8%E5%92%8Cjdk8u%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/\"\u003ejdk8和jdk8u有什么区别\u003c/a\u003e?\u003c/p\u003e\n\u003ch5 id=\"162系统需求\"\u003e1.6.2　系统需求\u003c/h5\u003e\n\u003cp\u003e推荐linux 64位和mac os 64位\u003c/p\u003e\n\u003cp\u003e查看位数命令:\n\u003ccode\u003egetconf LONG_BIT\u003c/code\u003e\u003c/p\u003e\n\u003ch2 id=\"第二部分自动内存管理\"\u003e第二部分　自动内存管理\u003c/h2\u003e\n\u003ch3 id=\"第2章java内存区域与内存溢出异常\"\u003e第2章　Java内存区域与内存溢出异常\u003c/h3\u003e\n\u003ch4 id=\"22运行时数据区域\"\u003e2.2　运行时数据区域\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230331171948.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230331171948.png\"\u003e\u003c/p\u003e\n\u003ch5 id=\"221程序计数器\"\u003e2.2.1　程序计数器\u003c/h5\u003e\n\u003cp\u003e\u003ca href=\"/blog/tech/java/%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8/\"\u003e程序计数器\u003c/a\u003e（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。\u003c/p\u003e\n\u003ch5 id=\"222java虚拟机栈\"\u003e2.2.2　Java虚拟机栈\u003c/h5\u003e\n\u003cp\u003e与程序计数器一样，\u003ca href=\"/blog/tech/java/java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88/\"\u003eJava虚拟机栈\u003c/a\u003e（Java Virtual Machine Stack）也是线程私有的，它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型：每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧 用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。\u003c/p\u003e","title":"深入理解jvm"},{"content":"局部变量表存放了编译期可知的各种Java虚拟机基本数据类型（boolean、byte、char、short、int、float、long、double）、对象引用（reference类型，它并不等同于对象本身，可能是一个指向对象起始地址的引用指针，也可能是指向一个代表对象的句柄或者其他与此对象相关的位置）和returnAddress类型（指向了一条字节码指令的地址）。\n这些数据类型在局部变量表中的存储空间以局部变量槽（Slot）来表示，其中64位长度的long和double类型的数据会占用两个变量槽，其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配，当进入一个方法时，这个方法需要在栈帧中分配多大的局部变量空间是完全确定的，在方法运行期间不会改变局部变量表的大小。请读者注意，这里说的“大小”是指变量槽的数量，虚拟机真正使用多大的内存空间（譬如按照1个变量槽占用32个比特、64个比特，或者更多）来实现一个变量槽，这是完全由具体的虚拟机实现自行决定的事情。\n","permalink":"https://leochu.work/blog/tech/java/%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8/","summary":"\u003cp\u003e局部变量表存放了编译期可知的各种Java虚拟机基本数据类型（boolean、byte、char、short、int、float、long、double）、对象引用（reference类型，它并不等同于对象本身，可能是一个指向对象起始地址的引用指针，也可能是指向一个代表对象的句柄或者其他与此对象相关的位置）和returnAddress类型（指向了一条字节码指令的地址）。\u003c/p\u003e\n\u003cp\u003e这些数据类型在局部变量表中的存储空间以局部变量槽（Slot）来表示，其中64位长度的long和double类型的数据会占用两个变量槽，其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配，当进入一个方法时，这个方法需要在栈帧中分配多大的局部变量空间是完全确定的，在方法运行期间不会改变局部变量表的大小。请读者注意，这里说的“大小”是指变量槽的数量，虚拟机真正使用多大的内存空间（譬如按照1个变量槽占用32个比特、64个比特，或者更多）来实现一个变量槽，这是完全由具体的虚拟机实现自行决定的事情。\u003c/p\u003e","title":"局部变量表"},{"content":"程序计数器（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里，字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令，它是程序控制流的指示器.\n由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的，在任何一个确定的时刻，一个处理器（对于多核处理器来说是一个内核）都只会执行一条线程中的指令。因此，为了线程切换后能恢复到正确的执行位置，每条线程都需要有一个独立的程序计数器，各条线程之间计数器互不影响，独立存储，我们称这类内存区域为“线程私有”的内存。\n如果线程正在执行的是一个Java方法，这个计数器记录的是正在执行的虚拟机字节码指令的地址；如果正在执行的是本地（Native）方法，这个计数器值则应为空（Undefined）。此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。\n","permalink":"https://leochu.work/blog/tech/java/%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8/","summary":"\u003cp\u003e程序计数器（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里，字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令，它是程序控制流的指示器.\u003c/p\u003e\n\u003cp\u003e由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的，在任何一个确定的时刻，一个处理器（对于多核处理器来说是一个内核）都只会执行一条线程中的指令。因此，为了线程切换后能恢复到正确的执行位置，每条线程都需要有一个独立的程序计数器，各条线程之间计数器互不影响，独立存储，我们称这类内存区域为“线程私有”的内存。\u003c/p\u003e\n\u003cp\u003e如果线程正在执行的是一个Java方法，这个计数器记录的是正在执行的虚拟机字节码指令的地址；如果正在执行的是本地（Native）方法，这个计数器值则应为空（Undefined）。此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。\u003c/p\u003e","title":"程序计数器"},{"content":"问题： spark-submit --master yarn --conf spark.default.parallelism=100 \\ --deploy-mode cluster --driver-memory 4G --executor-memory 4G \\ --num-executors 40 --executor-cores 2 \\ --conf spark.yarn.executor.memoryOverhead=5g \\ --class com.lz.hbase.CompanyInfo /tmp/test_langzi/original-spark_hbase01-1.0-SNAPSHOT.jar 以上提交参数中的\u0026ndash;num-executors 40没有生效，executors 大于40并且占满yarn资源，导致后来的yarn任务阻塞\n原因： 官方参数解释\n\u0026ndash;num-executors NUM Number of executors to launch (Default: 2). If dynamic allocation is enabled, the initial number of executors will be at least NUM.\n当开启动态分配时，num-executors成为了最小executors 数，而cdh中spark默认开启dynamic allocation，所以当yarn队列资源空闲时，真正的excutor数会大于设置的num-executors\n解决方案： 提交参数添加--conf spark.dynamicAllocation.maxExecutors=40 限制最大excutor数\n附：spark提交任务模板 spark-submit --master yarn --conf spark.default.parallelism=100 \\ --conf spark.dynamicAllocation.maxExecutors=40\\ --deploy-mode cluster --driver-memory 4G --executor-memory 4G \\ --num-executors 40 --executor-cores 3 \\ --conf spark.yarn.executor.memoryOverhead=4G \\ --class com.lz.hbase.CompanyInfo /tmp/test_langzi/original-spark_hbase01-1.0-SNAPSHOT.jar ","permalink":"https://leochu.work/blog/tech/bigdata/spark%E5%8F%82%E6%95%B0num-executors%E6%9C%AA%E7%94%9F%E6%95%88/","summary":"\u003ch3 id=\"问题\"\u003e问题：\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003espark-submit --master yarn --conf spark.default.parallelism\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--deploy-mode cluster --driver-memory 4G --executor-memory 4G \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--num-executors \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e --executor-cores \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--conf spark.yarn.executor.memoryOverhead\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e5g \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--class com.lz.hbase.CompanyInfo /tmp/test_langzi/original-spark_hbase01-1.0-SNAPSHOT.jar\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e以上提交参数中的\u0026ndash;num-executors 40没有生效，executors 大于40并且占满yarn资源，导致后来的yarn任务阻塞\u003c/p\u003e\n\u003ch3 id=\"原因\"\u003e原因：\u003c/h3\u003e\n\u003cp\u003e官方参数解释\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u0026ndash;num-executors NUM\nNumber of executors to launch (Default: 2).\nIf dynamic allocation is enabled, the initial number of executors will be at least NUM.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e当开启动态分配时，num-executors成为了最小executors 数，而cdh中spark默认开启dynamic allocation，所以当yarn队列资源空闲时，真正的excutor数会大于设置的num-executors\u003c/p\u003e\n\u003ch3 id=\"解决方案\"\u003e解决方案：\u003c/h3\u003e\n\u003cp\u003e提交参数添加\u003ccode\u003e--conf spark.dynamicAllocation.maxExecutors=40\u003c/code\u003e 限制最大excutor数\u003c/p\u003e\n\u003ch3 id=\"附spark提交任务模板\"\u003e附：spark提交任务模板\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003espark-submit --master yarn --conf spark.default.parallelism\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--conf spark.dynamicAllocation.maxExecutors\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e40\u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--deploy-mode cluster --driver-memory 4G --executor-memory 4G \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--num-executors \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e --executor-cores \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--conf spark.yarn.executor.memoryOverhead\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e4G \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--class com.lz.hbase.CompanyInfo /tmp/test_langzi/original-spark_hbase01-1.0-SNAPSHOT.jar\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"spark参数num-executors未生效"},{"content":" 标签 #maven 场景 工作 时间 2023年2月14日17:58:09 一、问题描述 每次打包的时候，项目目录会多出一个莫名的文件dependency-reduced-pom.xml\n二、导致原因 maven打包插件maven-shade-plugin打包时自动生成，createDependencyReducedPom默认为true。\ndependency-reduced-pom.xml 删除了已经在你的着色 jar 中的传递依赖项。这可以防止消费者两次拉他们，从而避免无用的重复。\n三、解决方案 添加以下配置\n\u0026lt;configuration\u0026gt; \u0026lt;createDependencyReducedPom\u0026gt;false\u0026lt;/createDependencyReducedPom\u0026gt; \u0026lt;/configuration\u0026gt; 如：\n\u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.apache.maven.plugins\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;maven-shade-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;3.1.1\u0026lt;/version\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;createDependencyReducedPom\u0026gt;false\u0026lt;/createDependencyReducedPom\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;executions\u0026gt; \u0026lt;execution\u0026gt; \u0026lt;phase\u0026gt;package\u0026lt;/phase\u0026gt; \u0026lt;goals\u0026gt; \u0026lt;goal\u0026gt;shade\u0026lt;/goal\u0026gt; \u0026lt;/goals\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;artifactSet\u0026gt; \u0026lt;excludes\u0026gt; \u0026lt;exclude\u0026gt;org.slf4j:*\u0026lt;/exclude\u0026gt; \u0026lt;exclude\u0026gt;log4j:*\u0026lt;/exclude\u0026gt; \u0026lt;exclude\u0026gt;ch.qos.logback:*\u0026lt;/exclude\u0026gt; \u0026lt;exclude\u0026gt;com.google.code.findbugs:jsr305\u0026lt;/exclude\u0026gt; \u0026lt;/excludes\u0026gt; \u0026lt;/artifactSet\u0026gt; \u0026lt;filters\u0026gt; \u0026lt;filter\u0026gt; \u0026lt;!-- Do not copy the signatures in the META-INF folder. Otherwise, this might cause SecurityExceptions when using the JAR. --\u0026gt; \u0026lt;artifact\u0026gt;*:*\u0026lt;/artifact\u0026gt; \u0026lt;excludes\u0026gt; \u0026lt;exclude\u0026gt;META-INF/*.SF\u0026lt;/exclude\u0026gt; \u0026lt;exclude\u0026gt;META-INF/*.DSA\u0026lt;/exclude\u0026gt; \u0026lt;exclude\u0026gt;META-INF/*.RSA\u0026lt;/exclude\u0026gt; \u0026lt;/excludes\u0026gt; \u0026lt;/filter\u0026gt; \u0026lt;/filters\u0026gt; \u0026lt;transformers\u0026gt; \u0026lt;transformer implementation=\u0026#34;org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\u0026#34;\u0026gt; \u0026lt;!-- Replace this with the main class of your job --\u0026gt; \u0026lt;mainClass\u0026gt;my.programs.main.clazz\u0026lt;/mainClass\u0026gt; \u0026lt;/transformer\u0026gt; \u0026lt;transformer implementation=\u0026#34;org.apache.maven.plugins.shade.resource.ServicesResourceTransformer\u0026#34;/\u0026gt; \u0026lt;/transformers\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/execution\u0026gt; \u0026lt;/executions\u0026gt; \u0026lt;/plugin\u0026gt; ","permalink":"https://leochu.work/blog/tech/bigdata/maven%E6%89%93%E5%8C%85%E5%8E%BB%E9%99%A4dependency-reduced-pom.xml%E6%96%87%E4%BB%B6/","summary":"\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e标签\u003c/th\u003e\n          \u003cth\u003e#maven\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e场景\u003c/td\u003e\n          \u003ctd\u003e工作\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e时间\u003c/td\u003e\n          \u003ctd\u003e2023年2月14日17:58:09\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"一问题描述\"\u003e一、问题描述\u003c/h3\u003e\n\u003cp\u003e每次打包的时候，项目目录会多出一个莫名的文件\u003cstrong\u003edependency-reduced-pom.xml\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image-20230214175954392\" loading=\"lazy\" src=\"/blog/resource/image-20230214175954392.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"二导致原因\"\u003e二、导致原因\u003c/h3\u003e\n\u003cp\u003emaven打包插件\u003cstrong\u003emaven-shade-plugin\u003c/strong\u003e打包时自动生成，\u003ccode\u003ecreateDependencyReducedPom\u003c/code\u003e默认为\u003ccode\u003etrue\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003edependency-reduced-pom.xml\u003c/strong\u003e 删除了已经在你的着色 jar 中的传递依赖项。这可以防止消费者两次拉他们，从而避免无用的重复。\u003c/p\u003e\n\u003ch3 id=\"三解决方案\"\u003e三、解决方案\u003c/h3\u003e\n\u003cp\u003e添加以下配置\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;configuration\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026lt;createDependencyReducedPom\u0026gt;\u003c/span\u003efalse\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/createDependencyReducedPom\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/configuration\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e如：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;plugin\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;groupId\u0026gt;\u003c/span\u003eorg.apache.maven.plugins\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/groupId\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;artifactId\u0026gt;\u003c/span\u003emaven-shade-plugin\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/artifactId\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;version\u0026gt;\u003c/span\u003e3.1.1\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/version\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;configuration\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;createDependencyReducedPom\u0026gt;\u003c/span\u003efalse\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/createDependencyReducedPom\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/configuration\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;executions\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;execution\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026lt;phase\u0026gt;\u003c/span\u003epackage\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/phase\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026lt;goals\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026lt;goal\u0026gt;\u003c/span\u003eshade\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/goal\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/goals\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026lt;configuration\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026lt;artifactSet\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;excludes\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;exclude\u0026gt;\u003c/span\u003eorg.slf4j:*\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/exclude\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;exclude\u0026gt;\u003c/span\u003elog4j:*\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/exclude\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;exclude\u0026gt;\u003c/span\u003ech.qos.logback:*\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/exclude\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;exclude\u0026gt;\u003c/span\u003ecom.google.code.findbugs:jsr305\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/exclude\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/excludes\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/artifactSet\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026lt;filters\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;filter\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Do not copy the signatures in the META-INF folder.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e                        Otherwise, this might cause SecurityExceptions when using the JAR. --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;artifact\u0026gt;\u003c/span\u003e*:*\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/artifact\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;excludes\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#f92672\"\u003e\u0026lt;exclude\u0026gt;\u003c/span\u003eMETA-INF/*.SF\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/exclude\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#f92672\"\u003e\u0026lt;exclude\u0026gt;\u003c/span\u003eMETA-INF/*.DSA\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/exclude\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#f92672\"\u003e\u0026lt;exclude\u0026gt;\u003c/span\u003eMETA-INF/*.RSA\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/exclude\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/excludes\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/filter\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/filters\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026lt;transformers\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;transformer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#a6e22e\"\u003eimplementation=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#75715e\"\u003e\u0026lt;!-- Replace this with the main class of your job --\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;mainClass\u0026gt;\u003c/span\u003emy.programs.main.clazz\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/mainClass\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/transformer\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;transformer\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                            \u003cspan style=\"color:#a6e22e\"\u003eimplementation=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;org.apache.maven.plugins.shade.resource.ServicesResourceTransformer\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/transformers\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/configuration\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/execution\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/executions\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/plugin\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"maven打包去除dependency-reduced-pom.xml文件"},{"content":"1.现象 由于zookeeper挂掉，造成kafka出现：There are 60 offline partitions。 2.造成的原因 经过排查发现，由于kafka之前Topic在zookeeper中的数据还在，再重新建立会产生冲突导致失败。\n3.解决方案 进入Zookeeper中将之前的脏数据删掉再重启kafka。\n#1.进入zookeeper sh /opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/zookeeper/bin/zkCli.sh #2.删除掉脏数据 deleteall /brokers/topics ","permalink":"https://leochu.work/blog/tech/bigdata/kafka%E9%9B%86%E7%BE%A4%E8%BF%90%E8%A1%8C%E8%8A%82%E7%82%B9%E8%BF%90%E8%A1%8C%E4%B8%8D%E6%88%90%E5%8A%9F/","summary":"\u003ch3 id=\"1现象\"\u003e1.现象\u003c/h3\u003e\n\u003cp\u003e由于zookeeper挂掉，造成kafka出现：There are 60 offline partitions。\n\u003cimg alt=\"Pasted image 20230327102327.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327102327.png\"\u003e\n\u003cimg loading=\"lazy\" src=\"http://pmd.71360.com/download/attachments/12159442/image2021-11-25_11-12-28.png?version=1\u0026modificationDate=1637809890000\u0026api=v2\"\u003e\u003c/p\u003e\n\u003ch3 id=\"2造成的原因\"\u003e2.造成的原因\u003c/h3\u003e\n\u003cp\u003e经过排查发现，由于kafka之前Topic在zookeeper中的数据还在，再重新建立会产生冲突导致失败。\u003c/p\u003e\n\u003ch3 id=\"3解决方案\"\u003e3.解决方案\u003c/h3\u003e\n\u003cp\u003e进入Zookeeper中将之前的脏数据删掉再重启kafka。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#1.进入zookeeper\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esh /opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/zookeeper/bin/zkCli.sh\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#2.删除掉脏数据\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeleteall /brokers/topics\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"kafka集群运行节点运行不成功"},{"content":"1.现象 2.原因 集群资源使用率过高时可能会导致Hive On Spark查询失败-查询超时。\n从hive on spark的架构看出超时的位置：\n3.解决 修改以下参数，重启集群\n### 其他可设置的参考参数 # 在Hive client和远程Spark driver通信过程中，随机生成密码的比特数。最好设置成8的倍数。　hive.spark.client.secret.bits # 远程Spark drive用于处理RPC事件所用的最大线程数，默认是8。　hive.spark.client.rpc.threads # Hive client和远程Spark driver通信最大的消息大小（单位：byte）,默认是50MB。　hive.spark.client.rpc.max.size # 远程Spark driver的通道日志级别，必须是DEBUG, ERROR, INFO, TRACE, WARN中的一个。　hive.spark.client.channel.log.level # 用于身份验证的SASL机制的名称。 hive.spark.client.rpc.sasl.mechanisms #生产集群设置的相应参数： hive.spark.client.future.timeout=360s # Hive client请求Spark driver的超时时间，如果没有指定时间单位，默认就是秒。 hive.metastore.client.socket.timeout=360s # 客户端socket超时时间，默认20秒。 hive.spark.client.connect.timeout=360000ms # Spark driver连接Hive client的超时时间，如果没有指定时间单位，默认就是毫秒。 hive.spark.client.server.connect.timeout=360000ms # Hive client和远程Spark driver握手时的超时时间，这个会在两边都检查的，如果没有指定时间单位，默认就是毫秒。 hive.spark.job.monitor.timeout=180s # Job监控获取Spark作业状态的超时时间，如果没有指定时间单位，默认就是秒。 ","permalink":"https://leochu.work/blog/tech/bigdata/hiveonspark%E5%AE%A2%E6%88%B7%E7%AB%AFremotesparkdriver%E8%B6%85%E6%97%B6/","summary":"\u003ch2 id=\"1现象\"\u003e1.现象\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230327113600.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327113600.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"2原因\"\u003e2.原因\u003c/h2\u003e\n\u003cp\u003e集群资源使用率过高时可能会导致Hive On Spark查询失败-查询超时。\u003c/p\u003e\n\u003cp\u003e从hive on spark的架构看出超时的位置：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230327113614.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327113614.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"3解决\"\u003e3.解决\u003c/h2\u003e\n\u003cp\u003e修改以下参数，重启集群\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e### 其他可设置的参考参数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 在Hive client和远程Spark driver通信过程中，随机生成密码的比特数。最好设置成8的倍数。　\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.client.secret.bits\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 远程Spark drive用于处理RPC事件所用的最大线程数，默认是8。　\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.client.rpc.threads\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Hive client和远程Spark driver通信最大的消息大小（单位：byte）,默认是50MB。　\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.client.rpc.max.size\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 远程Spark driver的通道日志级别，必须是DEBUG, ERROR, INFO, TRACE, WARN中的一个。　\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.client.channel.log.level\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 用于身份验证的SASL机制的名称。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.client.rpc.sasl.mechanisms\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#生产集群设置的相应参数：\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.client.future.timeout\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e360s \u003cspan style=\"color:#75715e\"\u003e# Hive client请求Spark driver的超时时间，如果没有指定时间单位，默认就是秒。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.metastore.client.socket.timeout\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e360s \u003cspan style=\"color:#75715e\"\u003e# 客户端socket超时时间，默认20秒。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.client.connect.timeout\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e360000ms \u003cspan style=\"color:#75715e\"\u003e# Spark driver连接Hive client的超时时间，如果没有指定时间单位，默认就是毫秒。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.client.server.connect.timeout\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e360000ms \u003cspan style=\"color:#75715e\"\u003e# Hive client和远程Spark driver握手时的超时时间，这个会在两边都检查的，如果没有指定时间单位，默认就是毫秒。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive.spark.job.monitor.timeout\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e180s \u003cspan style=\"color:#75715e\"\u003e# Job监控获取Spark作业状态的超时时间，如果没有指定时间单位，默认就是秒。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"hiveOnSpak客户端RemoteSparkDriver超时"},{"content":"问题描述： 使用hive load hdfs文件时报错：\nFAILED: Execution Error, return code 3 from org.apache.hadoop.hive.ql.exec.spark.SparkTask. Spark job failed due to task failures: Cannot obtain block length for LocatedBlock{BP-1984322900-192.168.102.3-1594185446267:blk_1180034904_106295094; getBlockSize()=4179; corrupt=false; offset=0; locs=[DatanodeInfoWithStorage[192.168.102.11:9866,DS-cb5a2e07-20e9-45fd-869b-5d8b4ad170a4,DISK], DatanodeInfoWithStorage[192.168.102.9:9866,DS-74706bce-bb23-4aaf-a6eb-ceaa9bdbf38c,DISK], DatanodeInfoWithStorage[192.168.102.5:9866,DS-57f122fb-b6ca-437c-a52e-5f81efdd239c,DISK]]} 22/02/23 16:31:17 ERROR ql.Driver: FAILED: Execution Error, return code 3 from org.apache.hadoop.hive.ql.exec.spark.SparkTask. Spark job failed due to task failures: Cannot obtain block length for LocatedBlock{BP-1984322900-192.168.102.3-1594185446267:blk_1180034904_106295094; getBlockSize()=4179; corrupt=false; offset=0; locs=[DatanodeInfoWithStorage[192.168.102.11:9866,DS-cb5a2e07-20e9-45fd-869b-5d8b4ad170a4,DISK], DatanodeInfoWithStorage[192.168.102.9:9866,DS-74706bce-bb23-4aaf-a6eb-ceaa9bdbf38c,DISK], DatanodeInfoWithStorage[192.168.102.5:9866,DS-57f122fb-b6ca-437c-a52e-5f81efdd239c,DISK]]} 分析问题： 可得知hdfs文件块出现异常，Cannot obtain block length for LocatedBlock，无法获取块文件长度信息\n猜测是因为昨日yarn重启导致hdfs文件未关闭写状态\n解决问题： 对hive load hdfs文件的地址执行检查命令\nhdfs fsck /flume/ex_trade/date=20220222 -openforwrite\nConnecting to namenode via http://master1.zd.prod:9870/fsck?ugi=jay\u0026amp;openforwrite=1\u0026amp;path=%2Fflume%2Fex_trade%2Fdate%3D20220222 FSCK started by jay (auth:SIMPLE) from /192.168.102.11 for path /flume/ex_trade/date=20220222 at Wed Feb 23 16:37:10 CST 2022 /flume/ex_trade/date=20220222/ex_trade_.1645508080614 1494 bytes, replicated: replication=3, 1 block(s), OPENFORWRITE: /flume/ex_trade/date=20220222/ex_trade_.1645519019856 4179 bytes, replicated: replication=3, 1 block(s), OPENFORWRITE: Status: HEALTHY Number of data-nodes: 6 Number of racks: 1 Total dirs: 1 Total symlinks: 0 Replicated Blocks: Total size: 51340251 B Total files: 7 Total blocks (validated): 7 (avg. block size 7334321 B) Minimally replicated blocks: 5 (71.42857 %) Over-replicated blocks: 0 (0.0 %) Under-replicated blocks: 0 (0.0 %) Mis-replicated blocks: 0 (0.0 %) Default replication factor: 3 Average block replication: 2.142857 Missing blocks: 0 Corrupt blocks: 0 Missing replicas: 0 (0.0 %) Blocks queued for replication: 0 Erasure Coded Block Groups: Total size: 0 B Total files: 0 Total block groups (validated): 0 Minimally erasure-coded block groups: 0 Over-erasure-coded block groups: 0 Under-erasure-coded block groups: 0 Unsatisfactory placement block groups: 0 Average block group size: 0.0 Missing block groups: 0 Corrupt block groups: 0 Missing internal blocks: 0 Blocks queued for replication: 0 FSCK ended at Wed Feb 23 16:37:10 CST 2022 in 2 milliseconds The filesystem under path \u0026#39;/flume/ex_trade/date=20220222\u0026#39; is HEALTHY 发现文件 ex_trade.1645508080614和 ex_trade.1645519019856未关闭\n执行修复文件命令\nhdfs debug recoverLease -path /flume/ex_trade/date=20220222/ex_trade_.1645508080614 -retries 3\nhdfs debug recoverLease -path /flume/ex_trade/date=20220222/ex_trade_.1645519019856 -retries 3\n重新使用hive load hdfs文件，解决问题。\n","permalink":"https://leochu.work/blog/tech/bigdata/hdfs%E6%96%87%E4%BB%B6%E6%9C%AA%E5%85%B3%E9%97%AD/","summary":"\u003ch3 id=\"问题描述\"\u003e问题描述：\u003c/h3\u003e\n\u003cp\u003e使用hive load hdfs文件时报错：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-apl\" data-lang=\"apl\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eFAILED: Execution Error\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e return code \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e from org\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eapache\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003ehadoop\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003ehive\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eql\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eexec\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003espark\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eSparkTask\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003e Spark job failed due to task failures: Cannot obtain block length for LocatedBlock\u003cspan style=\"color:#66d9ef\"\u003e{\u003c/span\u003eBP\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1984322900\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1594185446267\u003c/span\u003e:blk_1180034904_106295094\u003cspan style=\"color:#e6db74\"\u003e;\u003c/span\u003e getBlockSize()\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4179\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e;\u003c/span\u003e corrupt\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003efalse\u003cspan style=\"color:#e6db74\"\u003e;\u003c/span\u003e offset\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e;\u003c/span\u003e locs\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003eDatanodeInfoWithStorage\u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.11\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9866\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ecb5a2e07\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e20e9\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e45\u003c/span\u003efd\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e869\u003c/span\u003eb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003ed8b4ad170a4\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDISK\u003cspan style=\"color:#e6db74\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e DatanodeInfoWithStorage\u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.9\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9866\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e74706\u003c/span\u003ebce\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ebb23\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003eaaf\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ea6eb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eceaa9bdbf38c\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDISK\u003cspan style=\"color:#e6db74\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e DatanodeInfoWithStorage\u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.5\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9866\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e57\u003c/span\u003ef122fb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eb6ca\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e437\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ea52e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003ef81efdd239c\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDISK\u003cspan style=\"color:#e6db74\"\u003e]]\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#ae81ff\"\u003e22\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e02\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e23\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e16\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e31\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e17\u003c/span\u003e ERROR ql\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eDriver: FAILED: Execution Error\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e return code \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e from org\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eapache\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003ehadoop\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003ehive\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eql\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eexec\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003espark\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003eSparkTask\u003cspan style=\"color:#a6e22e\"\u003e.\u003c/span\u003e Spark job failed due to task failures: Cannot obtain block length for LocatedBlock\u003cspan style=\"color:#66d9ef\"\u003e{\u003c/span\u003eBP\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1984322900\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1594185446267\u003c/span\u003e:blk_1180034904_106295094\u003cspan style=\"color:#e6db74\"\u003e;\u003c/span\u003e getBlockSize()\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4179\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e;\u003c/span\u003e corrupt\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003efalse\u003cspan style=\"color:#e6db74\"\u003e;\u003c/span\u003e offset\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e;\u003c/span\u003e locs\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003eDatanodeInfoWithStorage\u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.11\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9866\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ecb5a2e07\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e20e9\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e45\u003c/span\u003efd\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e869\u003c/span\u003eb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003ed8b4ad170a4\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDISK\u003cspan style=\"color:#e6db74\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e DatanodeInfoWithStorage\u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.9\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9866\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e74706\u003c/span\u003ebce\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ebb23\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003eaaf\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ea6eb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eceaa9bdbf38c\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDISK\u003cspan style=\"color:#e6db74\"\u003e]\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003e DatanodeInfoWithStorage\u003cspan style=\"color:#e6db74\"\u003e[\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.5\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9866\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e57\u003c/span\u003ef122fb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eb6ca\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e437\u003c/span\u003ec\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ea52e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003ef81efdd239c\u003cspan style=\"color:#f92672\"\u003e,\u003c/span\u003eDISK\u003cspan style=\"color:#e6db74\"\u003e]]\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"分析问题\"\u003e分析问题：\u003c/h3\u003e\n\u003cp\u003e可得知hdfs文件块出现异常，Cannot obtain block length for LocatedBlock，无法获取块文件长度信息\u003c/p\u003e\n\u003cp\u003e猜测是因为昨日yarn重启导致hdfs文件未关闭写状态\u003c/p\u003e\n\u003ch3 id=\"解决问题\"\u003e解决问题：\u003c/h3\u003e\n\u003cp\u003e对hive load hdfs文件的地址执行检查命令\u003c/p\u003e","title":"hdfs文件未关闭"},{"content":"问题描述： 使用hive load hdfs文件时报错：\nFAILED: Execution Error, return code 3 from org.apache.hadoop.hive.ql.exec.spark.SparkTask. Spark job failed due to task failures: Cannot obtain block length for LocatedBlock{BP-1984322900-192.168.102.3-1594185446267:blk1180034904106295094; getBlockSize()=4179; corrupt=false; offset=0; locs=[DatanodeInfoWithStorage[192.168.102.11:9866,DS-cb5a2e07-20e9-45fd-869b-5d8b4ad170a4,DISK], DatanodeInfoWithStorage[192.168.102.9:9866,DS-74706bce-bb23-4aaf-a6eb-ceaa9bdbf38c,DISK], DatanodeInfoWithStorage[192.168.102.5:9866,DS-57f122fb-b6ca-437c-a52e-5f81efdd239c,DISK]]} 22/02/23 16:31:17 ERROR ql.Driver: FAILED: Execution Error, return code 3 from org.apache.hadoop.hive.ql.exec.spark.SparkTask. Spark job failed due to task failures: Cannot obtain block length for LocatedBlock{BP-1984322900-192.168.102.3-1594185446267:blk1180034904106295094; getBlockSize()=4179; corrupt=false; offset=0; locs=[DatanodeInfoWithStorage[192.168.102.11:9866,DS-cb5a2e07-20e9-45fd-869b-5d8b4ad170a4,DISK], DatanodeInfoWithStorage[192.168.102.9:9866,DS-74706bce-bb23-4aaf-a6eb-ceaa9bdbf38c,DISK], DatanodeInfoWithStorage[192.168.102.5:9866,DS-57f122fb-b6ca-437c-a52e-5f81efdd239c,DISK]]} 分析问题： 可得知hdfs文件块出现异常，Cannot obtain block length for LocatedBlock，无法获取块文件长度信息\n因为昨日CDH重启导致hdfs文件未关闭写状态\n解决问题： 对hive load hdfs文件的地址执行检查命令\nhdfs fsck /flume/ex_trade/date=20220222 -openforwrite\nConnecting to namenode via http://master1.zd.prod:9870/fsck?ugi=jay\u0026amp;openforwrite=1\u0026amp;path=%2Fflume%2Fex_trade%2Fdate%3D20220222 FSCK started by jay (auth:SIMPLE) from /192.168.102.11 for path /flume/ex_trade/date=20220222 at Wed Feb 23 16:37:10 CST 2022 /flume/ex_trade/date=20220222/ex_trade_.1645508080614 1494 bytes, replicated: replication=3, 1 block(s), OPENFORWRITE: /flume/ex_trade/date=20220222/ex_trade_.1645519019856 4179 bytes, replicated: replication=3, 1 block(s), OPENFORWRITE: Status: HEALTHY Number of data-nodes: 6 Number of racks: 1 Total dirs: 1 Total symlinks: 0 Replicated Blocks: Total size: 51340251 B Total files: 7 Total blocks (validated): 7 (avg. block size 7334321 B) Minimally replicated blocks: 5 (71.42857 %) Over-replicated blocks: 0 (0.0 %) Under-replicated blocks: 0 (0.0 %) Mis-replicated blocks: 0 (0.0 %) Default replication factor: 3 Average block replication: 2.142857 Missing blocks: 0 Corrupt blocks: 0 Missing replicas: 0 (0.0 %) Blocks queued for replication: 0 Erasure Coded Block Groups: Total size: 0 B Total files: 0 Total block groups (validated): 0 Minimally erasure-coded block groups: 0 Over-erasure-coded block groups: 0 Under-erasure-coded block groups: 0 Unsatisfactory placement block groups: 0 Average block group size: 0.0 Missing block groups: 0 Corrupt block groups: 0 Missing internal blocks: 0 Blocks queued for replication: 0 FSCK ended at Wed Feb 23 16:37:10 CST 2022 in 2 milliseconds The filesystem under path \u0026#39;/flume/ex_trade/date=20220222\u0026#39; is HEALTHY 发现文件 ex_trade.1645508080614和 ex_trade.1645519019856未关闭\n执行修复文件命令\nhdfs debug recoverLease -path /flume/ex_trade/date=20220222/ex_trade_.1645508080614 -retries 3\nhdfs debug recoverLease -path /flume/ex_trade/date=20220222/ex_trade_.1645519019856 -retries 3\n重新使用hive load hdfs文件，解决问题。\n","permalink":"https://leochu.work/blog/tech/bigdata/hdfs%E6%96%87%E4%BB%B6%E5%9D%97%E5%BC%82%E5%B8%B8/","summary":"\u003ch3 id=\"问题描述\"\u003e问题描述：\u003c/h3\u003e\n\u003cp\u003e使用hive load hdfs文件时报错：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eFAILED: Execution Error, \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e code 3 from org.\u003cspan style=\"color:#a6e22e\"\u003eapache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehadoop\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehive\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eql\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eexec\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003espark\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSparkTask\u003c/span\u003e. Spark job failed due to task failures: Cannot obtain block length \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e LocatedBlock{BP\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e1984322900\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e192.\u003cspan style=\"color:#a6e22e\"\u003e168\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e102\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e1594185446267:blk1180034904106295094; getBlockSize()\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e4179; corrupt\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e; offset\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e0; locs\u003cspan style=\"color:#f92672\"\u003e=[\u003c/span\u003eDatanodeInfoWithStorage\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e192.\u003cspan style=\"color:#a6e22e\"\u003e168\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e102\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e11\u003c/span\u003e:9866,DS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ecb5a2e07\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e20e9\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e45fd\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e869b\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e5d8b4ad170a4,DISK\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e, DatanodeInfoWithStorage\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e192.\u003cspan style=\"color:#a6e22e\"\u003e168\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e102\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e9\u003c/span\u003e:9866,DS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e74706bce\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ebb23\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e4aaf\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ea6eb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eceaa9bdbf38c,DISK\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e, DatanodeInfoWithStorage\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e192.\u003cspan style=\"color:#a6e22e\"\u003e168\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e102\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e5\u003c/span\u003e:9866,DS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e57f122fb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eb6ca\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e437c\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ea52e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e5f81efdd239c,DISK\u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e 22\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e02\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e23 16:31:17 ERROR ql.\u003cspan style=\"color:#a6e22e\"\u003eDriver\u003c/span\u003e: FAILED: Execution Error, \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e code 3 from org.\u003cspan style=\"color:#a6e22e\"\u003eapache\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehadoop\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ehive\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eql\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eexec\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003espark\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSparkTask\u003c/span\u003e. Spark job failed due to task failures: Cannot obtain block length \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e LocatedBlock{BP\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e1984322900\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e192.\u003cspan style=\"color:#a6e22e\"\u003e168\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e102\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e3\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e1594185446267:blk1180034904106295094; getBlockSize()\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e4179; corrupt\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e; offset\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e0; locs\u003cspan style=\"color:#f92672\"\u003e=[\u003c/span\u003eDatanodeInfoWithStorage\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e192.\u003cspan style=\"color:#a6e22e\"\u003e168\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e102\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e11\u003c/span\u003e:9866,DS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ecb5a2e07\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e20e9\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e45fd\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e869b\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e5d8b4ad170a4,DISK\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e, DatanodeInfoWithStorage\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e192.\u003cspan style=\"color:#a6e22e\"\u003e168\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e102\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e9\u003c/span\u003e:9866,DS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e74706bce\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ebb23\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e4aaf\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ea6eb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eceaa9bdbf38c,DISK\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e, DatanodeInfoWithStorage\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e192.\u003cspan style=\"color:#a6e22e\"\u003e168\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e102\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003e5\u003c/span\u003e:9866,DS\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e57f122fb\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eb6ca\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e437c\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ea52e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e5f81efdd239c,DISK\u003cspan style=\"color:#f92672\"\u003e]]\u003c/span\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3\u003e\u003c/h3\u003e\n\u003ch3 id=\"分析问题\"\u003e分析问题：\u003c/h3\u003e\n\u003cp\u003e可得知hdfs文件块出现异常，Cannot obtain block length for LocatedBlock，无法获取块文件长度信息\u003c/p\u003e\n\u003cp\u003e因为昨日CDH重启导致hdfs文件未关闭写状态\u003c/p\u003e\n\u003ch3 id=\"解决问题\"\u003e解决问题：\u003c/h3\u003e\n\u003cp\u003e对hive load hdfs文件的地址执行检查命令\u003c/p\u003e","title":"hdfs文件块异常"},{"content":" Robert C. Martin《代码整洁之道》。核心观点：整洁代码不是美学偏好，而是长期可维护性的基础。\n核心观点 代码首先是写给人读的。 机器最终当然会执行，但程序员大部分时间都花在理解和修改已有代码上。可读性不是附加价值，而是主价值。\n函数要短，职责要单一。 一个函数一旦同时承担多层抽象、多种意图，理解成本就会迅速上升。短函数不是教条，单一意图才是关键。\n命名是第一层设计。 好命名能直接省掉解释成本；坏命名会让系统像永远开着一层雾。\n印象较深的部分 重复不只是代码味道，更是未来修改成本。 逻辑复制一次看似省事，后面会以一致性风险的形式成倍偿还。\n错误处理和正常流程应当分离。 把异常分支塞进主逻辑里，会让代码阅读路径被打断，结构也变得混浊。\n整洁不是一次性整理，而是持续的小修正。 童子军军规式的“离开时让它比来时更干净一点”，比大规模重构更现实。\n读后感 代码不会在项目后期突然变整洁，它只会在每次提交里慢慢变好，或者慢慢变烂。\n很多工程问题到最后都不是技术突破不了，而是前面欠下的整洁债开始一起算账。\n","permalink":"https://leochu.work/blog/reading/%E4%BB%A3%E7%A0%81%E6%95%B4%E6%B4%81%E4%B9%8B%E9%81%93/","summary":"\u003cblockquote\u003e\n\u003cp\u003eRobert C. Martin《代码整洁之道》。核心观点：整洁代码不是美学偏好，而是长期可维护性的基础。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e代码首先是写给人读的。\u003c/strong\u003e 机器最终当然会执行，但程序员大部分时间都花在理解和修改已有代码上。可读性不是附加价值，而是主价值。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e函数要短，职责要单一。\u003c/strong\u003e 一个函数一旦同时承担多层抽象、多种意图，理解成本就会迅速上升。短函数不是教条，单一意图才是关键。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e命名是第一层设计。\u003c/strong\u003e 好命名能直接省掉解释成本；坏命名会让系统像永远开着一层雾。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e重复不只是代码味道，更是未来修改成本。\u003c/strong\u003e 逻辑复制一次看似省事，后面会以一致性风险的形式成倍偿还。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e错误处理和正常流程应当分离。\u003c/strong\u003e 把异常分支塞进主逻辑里，会让代码阅读路径被打断，结构也变得混浊。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e整洁不是一次性整理，而是持续的小修正。\u003c/strong\u003e 童子军军规式的“离开时让它比来时更干净一点”，比大规模重构更现实。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e代码不会在项目后期突然变整洁，它只会在每次提交里慢慢变好，或者慢慢变烂。\u003c/p\u003e\n\u003cp\u003e很多工程问题到最后都不是技术突破不了，而是前面欠下的整洁债开始一起算账。\u003c/p\u003e","title":"代码整洁之道"},{"content":"问题：agent启动后跑到一半报错卡死 解决：修改flume_ng启动脚本中jvm参数： vi /opt/cloudera/parcels/CDH/lib/flume-ng/bin/flume-ng\n把 JAVA_OPTS=\u0026quot;-Xmx20m\u0026quot; 改为 JAVA_OPTS=\u0026quot;-Xmx2048m\u0026quot;\n重启agent，顺畅running ","permalink":"https://leochu.work/blog/tech/bigdata/flume%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA%E5%8D%A1%E6%AD%BB/","summary":"\u003ch3 id=\"问题agent启动后跑到一半报错卡死\"\u003e问题：agent启动后跑到一半报错卡死\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230327115742.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327115742.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"解决修改flume_ng启动脚本中jvm参数\"\u003e解决：修改flume_ng启动脚本中jvm参数：\u003c/h3\u003e\n\u003cp\u003evi /opt/cloudera/parcels/CDH/lib/flume-ng/bin/flume-ng\u003c/p\u003e\n\u003cp\u003e把 \u003cem\u003eJAVA_OPTS=\u0026quot;-Xmx20m\u003c/em\u003e\u0026quot; 改为 \u003cem\u003eJAVA_OPTS=\u0026quot;-Xmx2048m\u0026quot;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230327115750.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327115750.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"重启agent顺畅running\"\u003e重启agent，顺畅running\u003c/h3\u003e","title":"Flume内存溢出卡死"},{"content":"问题描述：flume采集到hdfs上的文件一直不关闭，有tmp后缀，hive读不到 原配置： # 当前文件写入达到该值时间后触发滚动创建新文件，单位：秒，设置为24小时防止产生小文件\nex_trade_agent.sinks.k1.hdfs.rollInterval = 86400\n# 当前文件写入达到该大小后触发滚动创建新文件，单位：字节，设置为128M\nex_trade_agent.sinks.k1.hdfs.rollSize = 134217700\n# 向 HDFS 写入内容时每次批量操作的 Event 数量\nex_trade_agent.sinks.k1.hdfs.batchSize = 2000\n# 不根据 Event 数量来分割文件\nex_trade_agent.sinks.k1.hdfs.rollCount = 0\n可以看到只按照128m和24小时来判断是否写新文件，如果两者都不满足那就不关闭临时文件\n解决方案： # 当前文件写入达到该值时间后触发滚动创建新文件，单位：秒，设置为4小时防止产生小文件\nex_trade_agent.sinks.k1.hdfs.rollInterval = 14400\n# 当非活动文件超过4小时，关闭该文件\nex_trade_agent.sinks.k1.hdfs.idleTimeout = 14400\n","permalink":"https://leochu.work/blog/tech/bigdata/flume%E4%B8%8D%E5%85%B3%E9%97%AD%E4%B8%B4%E6%97%B6%E6%96%87%E4%BB%B6/","summary":"\u003ch4 id=\"问题描述flume采集到hdfs上的文件一直不关闭有tmp后缀hive读不到\"\u003e问题描述：flume采集到hdfs上的文件一直不关闭，有tmp后缀，hive读不到\u003c/h4\u003e\n\u003ch4 id=\"原配置\"\u003e原配置：\u003c/h4\u003e\n\u003cp\u003e # 当前文件写入达到该值时间后触发滚动创建新文件，单位：秒，设置为24小时防止产生小文件\u003cbr\u003e\n ex_trade_agent.sinks.k1.hdfs.rollInterval = 86400\u003cbr\u003e\n # 当前文件写入达到该大小后触发滚动创建新文件，单位：字节，设置为128M\u003cbr\u003e\n ex_trade_agent.sinks.k1.hdfs.rollSize = 134217700\u003cbr\u003e\n # 向 HDFS 写入内容时每次批量操作的 Event 数量\u003cbr\u003e\n ex_trade_agent.sinks.k1.hdfs.batchSize = 2000\u003cbr\u003e\n # 不根据 Event 数量来分割文件\u003cbr\u003e\n ex_trade_agent.sinks.k1.hdfs.rollCount = 0\u003c/p\u003e\n\u003cp\u003e可以看到只按照128m和24小时来判断是否写新文件，如果两者都不满足那就不关闭临时文件\u003c/p\u003e\n\u003ch4 id=\"解决方案\"\u003e解决方案：\u003c/h4\u003e\n\u003cp\u003e # 当前文件写入达到该值时间后触发滚动创建新文件，单位：秒，设置为4小时防止产生小文件\u003cbr\u003e\n ex_trade_agent.sinks.k1.hdfs.rollInterval = 14400\u003cbr\u003e\n # 当非活动文件超过4小时，关闭该文件\u003cbr\u003e\n ex_trade_agent.sinks.k1.hdfs.idleTimeout = 14400\u003c/p\u003e","title":"flume不关闭临时文件"},{"content":"1.现象 flink任务提交任务虚拟内存不足导致的失败\nContainer [pid=3007,containerID=container_1599018748796_0004_01_000004] is running 342252032B beyond the \u0026#39;VIRTUAL\u0026#39; memory limit. Current usage: 416.0 MB of 1 GB physical memory used; 2.4 GB of 2.1 GB virtual memory used. Killing container. 2.原因 因为yarn强制检查虚拟内存是否符合配置导致的，当我们的服务器或者虚拟机的内存达不到配置要求，可能就会报这个错误 。\n3.解决 修改检查虚拟内存的属性为false\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.nodemanager.vmem-check-enabled\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;false\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; ","permalink":"https://leochu.work/blog/tech/bigdata/flink%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98%E4%B8%8D%E8%B6%B3/","summary":"\u003ch2 id=\"1现象\"\u003e1.现象\u003c/h2\u003e\n\u003cp\u003eflink任务提交任务虚拟内存不足导致的失败\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eContainer \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003epid\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e3007,containerID\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003econtainer_1599018748796_0004_01_000004\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e is  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erunning 342252032B beyond the \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;VIRTUAL\u0026#39;\u003c/span\u003e memory limit. Current usage: 416.0 MB  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eof \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e GB physical memory used; 2.4 GB of 2.1 GB virtual memory used.  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eKilling container.\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"2原因\"\u003e2.原因\u003c/h2\u003e\n\u003cp\u003e因为yarn强制检查虚拟内存是否符合配置导致的，当我们的服务器或者虚拟机的内存达不到配置要求，可能就会报这个错误 。\u003c/p\u003e\n\u003ch2 id=\"3解决\"\u003e3.解决\u003c/h2\u003e\n\u003cp\u003e修改检查虚拟内存的属性为false\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;property\u0026gt;\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026lt;name\u0026gt;\u003c/span\u003eyarn.nodemanager.vmem-check-enabled\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/name\u0026gt;\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#f92672\"\u003e\u0026lt;value\u0026gt;\u003c/span\u003efalse\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/value\u0026gt;\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/property\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"flink虚拟内存不足"},{"content":"问题: flink的web管理界面在日志过大时无法加载且无法下载\n解决: 复制该任务task manager的container编号 container_e01_1675618892497_0264_01_000003\n远程登录该task manager所在服务器\n使用命令查询进程 ps -ef | grep taskmanager | grep -v \u0026quot;bash -c\u0026quot; |grep container_e01_1675618892497_0264_01_000003 进入该目录\n各文件用途 taskmanager.out：业务侧的输出文件，该文件中的内容一般是业务代码中，使用算子的print()方法或者使用java的System.out.print()产生的内容，与前台的taskmanager输出打印内容一致\ntaskmanager.log就是运行中的taskmanager的日志\ntaskmanager.err ：taskmanager中出错时候的异常信息\ngc.log.x.current：taskmanager的gc日志\n","permalink":"https://leochu.work/blog/tech/bigdata/flink%E6%9F%A5%E7%9C%8Bstdout%E6%97%A5%E5%BF%97/","summary":"\u003ch4 id=\"问题\"\u003e问题:\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003eflink的web管理界面在日志过大时无法加载且无法下载\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/image-20230209180331732.png\"\u003e\u003c/p\u003e\n\u003ch4 id=\"解决\"\u003e解决:\u003c/h4\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/image-20230209180433316.png\"\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e复制该任务task manager的container编号 container_e01_1675618892497_0264_01_000003\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e远程登录该task manager所在服务器\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e使用命令查询进程\u003c/strong\u003e \u003ccode\u003eps -ef | grep taskmanager | grep -v \u0026quot;bash -c\u0026quot; |grep container_e01_1675618892497_0264_01_000003\u003c/code\u003e \u003cimg loading=\"lazy\" src=\"/blog/resource/image-20230209180903878.png\"\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e进入该目录\u003c/strong\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e各文件用途\u003c/strong\u003e taskmanager.out：业务侧的输出文件，该文件中的内容一般是业务代码中，使用算子的print()方法或者使用java的System.out.print()产生的内容，与前台的taskmanager输出打印内容一致\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003etaskmanager.log就是运行中的taskmanager的日志\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003etaskmanager.err ：taskmanager中出错时候的异常信息\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003egc.log.x.current：taskmanager的gc日志\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"flink查看stdout日志"},{"content":"在es中数据类型为date： \u0026#34;addTime\u0026#34;: { \u0026#34;format\u0026#34;: \u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;date\u0026#34; } 在hive建映射表 CREATE EXTERNAL TABLE hive_es.cty_test1( addTime date ) STORED BY \u0026#39;org.elasticsearch.hadoop.hive.EsStorageHandler\u0026#39; TBLPROPERTIES(\u0026#39;es.resource\u0026#39; = \u0026#39;cty_test/cty_test\u0026#39;, \u0026#39;es.nodes\u0026#39;=\u0026#39;172.16.98.113,172.16.98.149,172.16.98.150,172.16.98.151,172.16.98.152\u0026#39;, \u0026#39;es.port\u0026#39;=\u0026#39;9200\u0026#39;, \u0026#39;es.mapping.names\u0026#39;= \u0026#39;addTime:addTime\u0026#39;, \u0026#39;es.date.format\u0026#39;=\u0026#39;yyyy-MM-dd HH:mm:ss\u0026#39;, \u0026#39;es.index.auto.create\u0026#39;=\u0026#39;false\u0026#39;, ) 查询报错： 更改hive表数据类型为string CREATE EXTERNAL TABLE hive_es.cty_test5( addTime string ) STORED BY \u0026#39;org.elasticsearch.hadoop.hive.EsStorageHandler\u0026#39; TBLPROPERTIES(\u0026#39;es.resource\u0026#39; = \u0026#39;cty_test/cty_test\u0026#39;, \u0026#39;es.nodes\u0026#39;=\u0026#39;172.16.98.113,172.16.98.149,172.16.98.150,172.16.98.151,172.16.98.152\u0026#39;, \u0026#39;es.port\u0026#39;=\u0026#39;9200\u0026#39;, \u0026#39;es.mapping.names\u0026#39;= \u0026#39;addTime:addTime\u0026#39;, \u0026#39;es.date.format\u0026#39;=\u0026#39;yyyy-MM-dd HH:mm:ss\u0026#39;, \u0026#39;es.index.auto.create\u0026#39;=\u0026#39;false\u0026#39;, ) 查询继续报错： 查阅资料： elasticsearch-hadoop中用于将ES中的日期转换为Hive中的日期格式的类为org.elasticsearch.hadoop.hive.HiveValueReader，通过查看该类的源码，其实现的用户日期转换的方法为：\n@Override protected Object parseDate(String value, boolean richDate) { return (richDate ? new TimestampWritable(new Timestamp(DatatypeConverter.parseDateTime(value).getTimeInMillis())) : parseString(value)); } 可以看到它是通过javax.xml.bind.DatatypeConverter.parseDateTime(String)方法将对应的日期字符串转换为日期的，该方法不支持的日期字符串格式为“yyyy-MM-dd HH:mm:ss”的字符串，它支持的日期字符串的格式为“yyyy-MM-ddTHH:mm:ss”这样的。\n解决方案： 在建表时设置参数\u0026rsquo;es.mapping.date.rich\u0026rsquo;=\u0026lsquo;false\u0026rsquo;，然后hive字段类型设为string。\n官方解释： Whether to create a rich Date like object for Date fields in Elasticsearch or returned them as primitives (String or long). By default this is true. The actual object type is based on the library used; noteable exception being Map/Reduce which provides no built-in Date object and as such LongWritable and Text are returned regardless of this setting.\n其他解决方案： https://blog.csdn.net/fenglibing/article/details/80626728\n","permalink":"https://leochu.work/blog/tech/bigdata/es%E6%98%A0%E5%B0%84hive%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8Bdate%E6%97%A0%E6%B3%95%E8%A7%A3%E6%9E%90/","summary":"\u003ch3 id=\"在es中数据类型为date\"\u003e在es中数据类型为date：\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;addTime\u0026#34;\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e:\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;format\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"在hive建映射表\"\u003e在hive建映射表\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eEXTERNAL\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e hive_es.cty_test1(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaddTime date\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSTORED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;org.elasticsearch.hadoop.hive.EsStorageHandler\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTBLPROPERTIES(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.resource\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;cty_test/cty_test\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.nodes\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;172.16.98.113,172.16.98.149,172.16.98.150,172.16.98.151,172.16.98.152\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.port\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;9200\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.mapping.names\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;addTime:addTime\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.date.format\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;yyyy-MM-dd HH:mm:ss\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.index.auto.create\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;false\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"查询报错\"\u003e查询报错：\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230327120417.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327120417.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"更改hive表数据类型为string\"\u003e更改hive表数据类型为string\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eEXTERNAL\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e hive_es.cty_test5(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaddTime string\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSTORED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;org.elasticsearch.hadoop.hive.EsStorageHandler\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTBLPROPERTIES(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.resource\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;cty_test/cty_test\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.nodes\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;172.16.98.113,172.16.98.149,172.16.98.150,172.16.98.151,172.16.98.152\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.port\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;9200\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.mapping.names\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;addTime:addTime\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.date.format\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;yyyy-MM-dd HH:mm:ss\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.index.auto.create\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;false\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"查询继续报错\"\u003e查询继续报错：\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230327120434.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327120434.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"查阅资料\"\u003e查阅资料：\u003c/h3\u003e\n\u003cp\u003eelasticsearch-hadoop中用于将ES中的日期转换为Hive中的日期格式的类为org.elasticsearch.hadoop.hive.HiveValueReader，通过查看该类的源码，其实现的用户日期转换的方法为：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eprotected\u003c/span\u003e Object \u003cspan style=\"color:#a6e22e\"\u003eparseDate\u003c/span\u003e(String value, \u003cspan style=\"color:#66d9ef\"\u003eboolean\u003c/span\u003e richDate) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e (richDate \u003cspan style=\"color:#f92672\"\u003e?\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e TimestampWritable(\u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e Timestamp(DatatypeConverter.\u003cspan style=\"color:#a6e22e\"\u003eparseDateTime\u003c/span\u003e(value).\u003cspan style=\"color:#a6e22e\"\u003egetTimeInMillis\u003c/span\u003e())) : parseString(value));\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e可以看到它是通过javax.xml.bind.DatatypeConverter.parseDateTime(String)方法将对应的日期字符串转换为日期的，该方法不支持的日期字符串格式为“yyyy-MM-dd HH:mm:ss”的字符串，它支持的日期字符串的格式为“yyyy-MM-ddTHH:mm:ss”这样的。\u003c/p\u003e\n\u003ch3 id=\"解决方案\"\u003e解决方案：\u003c/h3\u003e\n\u003cp\u003e在建表时设置参数\u0026rsquo;es.mapping.date.rich\u0026rsquo;=\u0026lsquo;false\u0026rsquo;，然后hive字段类型设为string。\u003c/p\u003e\n\u003ch3 id=\"官方解释\"\u003e官方解释：\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003eWhether to create a rich Date like object for Date fields in Elasticsearch or returned them as primitives (String or long). By default this is true. The actual object type is based on the library used; noteable exception being Map/Reduce which provides no built-in Date object and as such LongWritable and Text are returned regardless of this setting.\u003c/p\u003e","title":"ES映射hive数据类型date无法解析"},{"content":" 一个sql节点只能写一条sql且结尾不能写分号 使用sql节点时由于dolohin写了hive的预编译，某些时候变量不能正确传入，比如 location ‘xxxxx${变量名}’,在预编译过程中会直接变成 location ‘xxxxx?’ **解决方案：**把sql节点替换成shell节点用hive -e \u0026lsquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.. location ‘xxxxx${变量名}’来实现变量传参 节点开启非严格模式，从节点偶尔会失效 **解决方案：**每个需要动态分区的sql都加上设置非严格模式的前置sql sql节点无法使用hive永久注册的udf **解决方案：**在dolphin资源中心上传udf并创建（会注册成临时udf） sql节点使用 REPLACE函数会有bug，导致任务流无法保存 **解决方案：**使用REGEXP_REPLACE函数 sql节点添加扩展jar包，无法使用本地路径 **解决方案：**需要先上传jar到hdfs，然后加上hdfs://nameservice1/前缀 ","permalink":"https://leochu.work/blog/tech/bigdata/dolphinscheduler%E8%B8%A9%E5%9D%91/","summary":"\u003cul\u003e\n\u003cli\u003e一个sql节点只能写一条sql且结尾不能写分号\u003c/li\u003e\n\u003cli\u003e使用sql节点时由于dolohin写了hive的预编译，某些时候变量不能正确传入，比如 location ‘xxxxx${变量名}’,在预编译过程中会直接变成 location ‘xxxxx?’\n**解决方案：**把sql节点替换成shell节点用hive -e \u0026lsquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.. location ‘xxxxx${变量名}’来实现变量传参\u003c/li\u003e\n\u003cli\u003e节点开启非严格模式，从节点偶尔会失效\n**解决方案：**每个需要动态分区的sql都加上设置非严格模式的前置sql\u003c/li\u003e\n\u003cli\u003esql节点无法使用hive永久注册的udf\n**解决方案：**在dolphin资源中心上传udf并创建（会注册成临时udf）\u003c/li\u003e\n\u003cli\u003esql节点使用 REPLACE函数会有bug，导致任务流无法保存\n**解决方案：**使用REGEXP_REPLACE函数\u003c/li\u003e\n\u003cli\u003esql节点添加扩展jar包，无法使用本地路径\n**解决方案：**需要先上传jar到hdfs，然后加上hdfs://nameservice1/前缀\u003c/li\u003e\n\u003c/ul\u003e","title":"dolphin踩坑"},{"content":"1.zookeeper刚启动时会报错，晾它两分钟会就好了\n2.更改或迁移服务时一定要切换到维护模式并停止当前服务！！！\n3.cdh01的hadoop组件起不来，原因是一些文件夹的权限问题，更改权限并且更改拥有者及组（参照其他服务器）\n4.内存不足主要因为NodeManager内存调的太高，调小即可（个人经验，5台机器可用内存和为30*4+20=160G，可同时对20G的数据做处理，感觉够用了）\n5.oozie报错：Failed to install Oozie ShareLib，这个问题是cdh6.2的通病，只要安装oozie就会 出现(无论是升级，还是新装。\n解决：\ncd /opt/cloudera/parcels/CDH/lib/oozie/libtools\nln -s ../../../jars/logredactor-2.0.7.jar logredactor-2.0.7.jar\n执行完后重启oozie。\n6.oozie端口号11000冲突，改为11002\n7.从节点不要提前安装agent服务！！！直接在web界面安装！\n8.把CM和CDH装好后面就轻松多了\n9.==主机名不要出现下划线!==\n","permalink":"https://leochu.work/blog/tech/bigdata/%E9%83%A8%E7%BD%B2cdh%E8%B8%A9%E5%9D%91%E6%80%BB%E7%BB%93/","summary":"\u003cp\u003e1.zookeeper刚启动时会报错，晾它两分钟会就好了\u003c/p\u003e\n\u003cp\u003e2.更改或迁移服务时一定要切换到维护模式并停止当前服务！！！\u003c/p\u003e\n\u003cp\u003e3.cdh01的hadoop组件起不来，原因是一些文件夹的权限问题，更改权限并且更改拥有者及组（参照其他服务器）\u003c/p\u003e\n\u003cp\u003e4.内存不足主要因为NodeManager内存调的太高，调小即可（个人经验，5台机器可用内存和为30*4+20=160G，可同时对20G的数据做处理，感觉够用了）\u003c/p\u003e\n\u003cp\u003e5.oozie报错：Failed to install Oozie ShareLib，这个问题是cdh6.2的通病，只要安装oozie就会 出现(无论是升级，还是新装。\u003c/p\u003e\n\u003cp\u003e解决：\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ecd /opt/cloudera/parcels/CDH/lib/oozie/libtools\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eln -s ../../../jars/logredactor-2.0.7.jar logredactor-2.0.7.jar\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e执行完后重启oozie。\u003c/p\u003e\n\u003cp\u003e6.oozie端口号11000冲突，改为11002\u003c/p\u003e\n\u003cp\u003e7.从节点不要提前安装agent服务！！！直接在web界面安装！\u003c/p\u003e\n\u003cp\u003e8.把CM和CDH装好后面就轻松多了\u003c/p\u003e\n\u003cp\u003e9.==主机名不要出现下划线!==\u003c/p\u003e","title":"部署CDH踩坑总结"},{"content":" 罗伯特·摩尔《国王，武士，祭司，诗人》。核心观点：成熟男性人格不是单一特质，而是四种原型之间的平衡。\n核心观点 国王代表秩序与祝福。 健康的国王型人格会建立边界、分配资源、让周围人各得其所；失衡时则会滑向专断或软弱。\n武士代表行动与纪律。 武士的价值不在攻击性，而在执行力、方向感和承担痛苦的能力。没有武士，很多判断最终都落不了地。\n祭司代表洞察与理解。 他对应的是解释世界、提炼模式、理解因果的能力。这个部分越弱，人越容易陷入情绪化和短视。\n诗人代表感受与想象。 诗人型让人保有柔软、审美、象征感和与内心世界的连接；但过度时也可能滑向沉溺和虚无。\n印象较深的部分 很多人的问题不是没有力量，而是力量结构失衡。 有人只有武士，没有国王，所以执行力强但总在打仗；有人只有祭司，没有武士，所以理解很多但始终不行动。\n成熟不是压抑某一面，而是让不同部分各归其位。 这本书最有启发的一点，就是它不把“强硬”“敏感”“理性”“秩序”对立起来，而是把它们看作一整套人格结构。\n读后感 这本书原型心理学的味道很重，不是那种拿来就能用的工具书。但它提供了一个很好用的观察框架：很多人并不是简单地“性格有问题”，而是人格结构失了衡。\n把它当镜子看自己，比把它当结论去套别人，要有用得多。\n","permalink":"https://leochu.work/blog/reading/%E5%9B%BD%E7%8E%8B%E6%AD%A6%E5%A3%AB%E7%A5%AD%E5%8F%B8%E8%AF%97%E4%BA%BA/","summary":"\u003cblockquote\u003e\n\u003cp\u003e罗伯特·摩尔《国王，武士，祭司，诗人》。核心观点：成熟男性人格不是单一特质，而是四种原型之间的平衡。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"核心观点\"\u003e核心观点\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e国王代表秩序与祝福。\u003c/strong\u003e 健康的国王型人格会建立边界、分配资源、让周围人各得其所；失衡时则会滑向专断或软弱。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e武士代表行动与纪律。\u003c/strong\u003e 武士的价值不在攻击性，而在执行力、方向感和承担痛苦的能力。没有武士，很多判断最终都落不了地。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e祭司代表洞察与理解。\u003c/strong\u003e 他对应的是解释世界、提炼模式、理解因果的能力。这个部分越弱，人越容易陷入情绪化和短视。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e诗人代表感受与想象。\u003c/strong\u003e 诗人型让人保有柔软、审美、象征感和与内心世界的连接；但过度时也可能滑向沉溺和虚无。\u003c/p\u003e\n\u003ch2 id=\"印象较深的部分\"\u003e印象较深的部分\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e很多人的问题不是没有力量，而是力量结构失衡。\u003c/strong\u003e 有人只有武士，没有国王，所以执行力强但总在打仗；有人只有祭司，没有武士，所以理解很多但始终不行动。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e成熟不是压抑某一面，而是让不同部分各归其位。\u003c/strong\u003e 这本书最有启发的一点，就是它不把“强硬”“敏感”“理性”“秩序”对立起来，而是把它们看作一整套人格结构。\u003c/p\u003e\n\u003ch2 id=\"读后感\"\u003e读后感\u003c/h2\u003e\n\u003cp\u003e这本书原型心理学的味道很重，不是那种拿来就能用的工具书。但它提供了一个很好用的观察框架：很多人并不是简单地“性格有问题”，而是人格结构失了衡。\u003c/p\u003e\n\u003cp\u003e把它当镜子看自己，比把它当结论去套别人，要有用得多。\u003c/p\u003e","title":"国王，武士，祭司，诗人"},{"content":"1.指标字典 指标字典，是业务数据标准化的基础，目的是对指标进行统一管理，方便共享达成对业务指标的共识，并且统一修改和维护。指标字典可以更新在 Excel或者Wiki中。如果有足够多的资源，那么开发指标管理模块可以放在数据管理系统中再配合血缘关系，就可以很方便追踪数据流转了。\n1.1设计指标字典的主要目的： (1)规范维度和量度命名，命名规则要尽量做到明确、通用、易懂。\n(2)对维度或量度统一计算口径，避免岐义。\n(3)涵盖尽可能多的关注的核心维度和量度，以此为基础推动数据建设，确保指标字典里覆盖的维度都可区分、指标都可统计\n(4)基于指标字典，将核心维度和量度注入元数据中心，接入指标提取工具，后续实现不需要写QL语句即可完成自査询及分析需求\n指标字典的建立，是搭建数据平台的基础。\n1.2指标、量度和维度的相关概念 1.2.1 指标: 定义：衡量目标的方法\n构成要素：维度+汇总方式+量度\n（1）维度=哪些角度去看问题\n（2）汇总方式=哪些方法衡量问题\n（3）量度=目标是什么问题\n1.2.2 维度： 定义：看问题的角度和方向，例如我要从年份看毛衣的销量，那么年份就是我看问题的角度\n1.2.3 量度： 定义：对一个物理量的测定，通常数字+计算单位表示。例如，金额，次数，率\n1.3 指标定义规范 1.3.1怎么定义一个合格的指标字典？需要遵守什么规范？ 一个指标一经录入，它的命名和所有下钻维度的口径都已确定(默认口径)，这称为指标的一义性。\n例如，“交易额”这个指标默认的时间口径是：支付时间，默认的城市口径是：下单所在城市等。\n如果需要按下单时间口径看订单金额，我们定义了一个新的指标“下单交易额”。一个在某些维度上口径不确定的“指标”是不能被使用的，在业务场景中是毫无意义的。\n1.3.2指标一般分为基础指标、普通指标和计算指标三类。 1.3.2.1基础指标 例如，“交易额”作为一个基于单纯实体的属性的简单计算，它没有更上游的指标，即它的父指标是它自身。我们称这样的指标为基础指标。\n1.3.2.2.普通指标 所谓普通指标，即在单一父指标的基础上通过一些维度上的取值限定可以定义的指标。\n例如，对于购买中PC端首次购买用户数，限制条件为首次购买用户中下单平台＝PC。\n1.3.2.3.计算指标 可以在若干个注册指标之上通过四则运算、排序、累计或汇总定义出的指标称为计算指标。\n1.4量度和维度都考虑好了，在构建一个指标字典时我们应该考虑哪些要素呢? 1.4.1指标字典要素如下： 1.4.2通常指标字典包含指标维度和指标量度两个部分，如下： 总结： 通过上面的步骤和方法，根据自身业务情况，建立一个指标字典。\n指标字典在建立知乎，要经过各个业务产品经理的评审，纠正错误不明或者有歧义的指标，在达成一致后，由数据产品推广，共大家参考使用。\n好的指标字典就像二叉树一样，从单维度，粗糙维度分析，再细拆维度。看问题需要：由大到小，由内到外。\n","permalink":"https://leochu.work/blog/tech/bigdata/%E6%8C%87%E6%A0%87%E5%AD%97%E5%85%B8/","summary":"\u003ch2 id=\"1指标字典\"\u003e\u003cstrong\u003e1.指标字典\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003e指标字典，是业务数据标准化的基础，目的是对指标进行统一管理，方便共享达成对业务指标的共识，并且统一修改和维护。指标字典可以更新在 Excel或者Wiki中。如果有足够多的资源，那么开发指标管理模块可以放在数据管理系统中再配合血缘关系，就可以很方便追踪数据流转了。\u003c/p\u003e\n\u003ch3 id=\"11设计指标字典的主要目的\"\u003e\u003cstrong\u003e1.1设计指标字典的主要目的：\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e(1)规范维度和量度命名，命名规则要尽量做到明确、通用、易懂。\u003c/p\u003e\n\u003cp\u003e(2)对维度或量度统一计算口径，避免岐义。\u003c/p\u003e\n\u003cp\u003e(3)涵盖尽可能多的关注的核心维度和量度，以此为基础推动数据建设，确保指标字典里覆盖的维度都可区分、指标都可统计\u003c/p\u003e\n\u003cp\u003e(4)基于指标字典，将核心维度和量度注入元数据中心，接入指标提取工具，后续实现不需要写QL语句即可完成自査询及分析需求\u003c/p\u003e\n\u003cp\u003e指标字典的建立，是搭建数据平台的基础。\u003c/p\u003e\n\u003ch3 id=\"12指标量度和维度的相关概念\"\u003e\u003cstrong\u003e1.2指标、量度和维度的相关概念\u003c/strong\u003e\u003c/h3\u003e\n\u003ch4 id=\"121-指标\"\u003e\u003cstrong\u003e1.2.1 指标:\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e定义：衡量目标的方法\u003c/p\u003e\n\u003cp\u003e构成要素：维度+汇总方式+量度\u003c/p\u003e\n\u003cp\u003e（1）维度=哪些角度去看问题\u003c/p\u003e\n\u003cp\u003e（2）汇总方式=哪些方法衡量问题\u003c/p\u003e\n\u003cp\u003e（3）量度=目标是什么问题\u003c/p\u003e\n\u003ch4 id=\"122-维度\"\u003e\u003cstrong\u003e1.2.2 维度：\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e定义：看问题的角度和方向，例如我要从年份看毛衣的销量，那么年份就是我看问题的角度\u003c/p\u003e\n\u003ch4 id=\"123-量度\"\u003e\u003cstrong\u003e1.2.3 量度：\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e定义：对一个物理量的测定，通常数字+计算单位表示。例如，金额，次数，率\u003c/p\u003e\n\u003ch3 id=\"13-指标定义规范\"\u003e\u003cstrong\u003e1.3 指标定义规范\u003c/strong\u003e\u003c/h3\u003e\n\u003ch4 id=\"131怎么定义一个合格的指标字典需要遵守什么规范\"\u003e\u003cstrong\u003e1.3.1怎么定义一个合格的指标字典？需要遵守什么规范？\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e一个指标一经录入，它的命名和所有下钻维度的口径都已确定(默认口径)，这称为指标的一义性。\u003cbr\u003e\n例如，“交易额”这个指标默认的时间口径是：支付时间，默认的城市口径是：下单所在城市等。\u003cbr\u003e\n如果需要按下单时间口径看订单金额，我们定义了一个新的指标“下单交易额”。一个在某些维度上口径不确定的“指标”是不能被使用的，在业务场景中是毫无意义的。\u003c/p\u003e\n\u003ch4 id=\"132指标一般分为基础指标普通指标和计算指标三类\"\u003e\u003cstrong\u003e1.3.2指标一般分为基础指标、普通指标和计算指标三类。\u003c/strong\u003e\u003c/h4\u003e\n\u003ch5 id=\"1321基础指标\"\u003e\u003cstrong\u003e1.3.2.1基础指标\u003c/strong\u003e\u003c/h5\u003e\n\u003cp\u003e例如，“交易额”作为一个基于单纯实体的属性的简单计算，它没有更上游的指标，即它的父指标是它自身。我们称这样的指标为基础指标。\u003c/p\u003e\n\u003ch5 id=\"1322普通指标\"\u003e\u003cstrong\u003e1.3.2.2.普通指标\u003c/strong\u003e\u003c/h5\u003e\n\u003cp\u003e所谓普通指标，即在单一父指标的基础上通过一些维度上的取值限定可以定义的指标。\u003cbr\u003e\n例如，对于购买中PC端首次购买用户数，限制条件为首次购买用户中下单平台＝PC。\u003c/p\u003e\n\u003ch5 id=\"1323计算指标\"\u003e\u003cstrong\u003e1.3.2.3.计算指标\u003c/strong\u003e\u003c/h5\u003e\n\u003cp\u003e可以在若干个注册指标之上通过四则运算、排序、累计或汇总定义出的指标称为计算指标。\u003c/p\u003e\n\u003ch3 id=\"14量度和维度都考虑好了在构建一个指标字典时我们应该考虑哪些要素呢\"\u003e\u003cstrong\u003e1.4量度和维度都考虑好了，在构建一个指标字典时我们应该考虑哪些要素呢?\u003c/strong\u003e\u003c/h3\u003e\n\u003ch4 id=\"141指标字典要素如下\"\u003e\u003cstrong\u003e1.4.1指标字典要素如下：\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20260323095931.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020260323095931.png\"\u003e\u003c/p\u003e\n\u003ch4 id=\"142通常指标字典包含指标维度和指标量度两个部分如下\"\u003e\u003cstrong\u003e1.4.2通常指标字典包含指标维度和指标量度两个部分，如下：\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20260323095936.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020260323095936.png\"\u003e\n\u003cimg alt=\"Pasted image 20260323095940.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020260323095940.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"总结\"\u003e\u003cstrong\u003e总结：\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003e通过上面的步骤和方法，根据自身业务情况，建立一个指标字典。\u003c/p\u003e\n\u003cp\u003e指标字典在建立知乎，要经过各个业务产品经理的评审，纠正错误不明或者有歧义的指标，在达成一致后，由数据产品推广，共大家参考使用。\u003c/p\u003e\n\u003cp\u003e好的指标字典就像二叉树一样，从单维度，粗糙维度分析，再细拆维度。看问题需要：由大到小，由内到外。\u003c/p\u003e","title":"指标字典"},{"content":"一、什么是 REST ？（一种软件架构风格） 缩写：REST （不是\u0026quot;rest\u0026quot;这个单词） 外文名：Representational State Transfer，简称REST。 中文名：表现层状态转移。\n提出时间：2000年。 属性：一种软件架构风格。（以Web为平台的。web服务的架构风格，前后端接口时候用到。）\nREST之所以晦涩难懂，是因为前面主语（Resource ）被去掉了。 全称是： Resource Representational State Transfer。 通俗来讲就是：资源在网络中以某种表现形式进行状态转移。\n分解开来讲解: Resource：资源，即数据（这是网络的核心）； Representational：某种表现形式，比如用JSON，XML，JPEG等； State Transfer：状态变化。通过HTTP的动词（get查询、post新增、put修改、delete删除）实现。\n一句话描述 REST 实质：\nURL 中只使用名词来定位资源，用 HTTP 协议里的动词（GET、POST、PUT、DELETE）来实现资源的增删改查操作。\n什么意思呢？\n比如，我们有一个 friends 接口，对于 “朋友” 我们有增删改查四种操作，怎么定义 REST 接口？\n增加一个朋友，uri: generalcode.cn/v1/friends 接口类型：POST\n删除一个朋友，uri: generalcode.cn/va/friends 接口类型：DELETE（在 http 的 parameter 指定好友 id）\n修改一个朋友，uri: generalcode.cn/va/friends 接口类型：PUT（在 http 的 parameter 指定好友 id）\n查找一个朋友，uri: generalcode.cn/va/friends 接口类型：GET\n上面我们定义的四个接口就是符合 REST 协议的，请注意，这几个接口都没有动词，只有名词 friends，都是通过 Http 请求的接口类型来判断是什么业务操作。\n举个反例： generalcode.cn/va/deleteFriends 该接口用来表示删除朋友，这就是不符合REST协议的接口。 不能用deleteFriends ，而应该就用friends + http请求的delete方式。\n一般接口的返回值绝大部分是 JSON 类型的。\n总结：\n看 Url 就知道要什么\n看 http method 就知道干什么\n看 http status code 就知道结果如何\n二、什么是 RESTFUL ？ 从上面的定义中，我们可以发现 REST 其实是一种组织 Web 服务的架构，\n并不是实现 Web 服务的一种技术（注意：不是一种技术！！！也不是一种标准！！！），\n其目标是为了创建具有良好扩展性的分布式系统。\n反过来，作为一种架构，其提出了一系列架构级约束。这些约束有：\n使用客户 / 服务器 (b/s、 c/s) 模型。客户和服务器之间通过一个统一的接口来互相通讯。 层次化的系统。在一个 REST 系统中，客户端并不会固定地与一个服务器打交道。 无状态。在一个 REST 系统中，服务端并不会保存有关客户的任何状态。也就是说，客户端自身负责用户状态的维持，并在每次发送请求时都需要提供足够的信息。 可缓存。REST 系统需要能够恰当地缓存请求，以尽量减少服务端和客户端之间的信息传输，以提高性能。 统一的接口。一个 REST 系统需要使用一个统一的接口来完成子系统之间以及服务与用户之间的交互。这使得 REST 系统中的各个子系统可以独自完成演化。 如果一个系统满足了上面所列出的五条约束，那么该系统就被称为是 RESTful 的。\n三、REST 风格好处 ？ rest 风格的接口有什么好处呢:\n前后端分离。\n前端拿到数据只负责展示和渲染，不对数据做任何处理。\n后端处理数据并以 JSON 格式传输出去，定义这样一套统一的接口，在 web，ios，android 三端都可以用相同的接口，是不是很爽？！（因为不需要写三次代码，一次代码可以公用给三端；另外，修改代码只要修改一次，三端都同步访问新代码，不需要修改三次代码。）\n四、REST 风格缺点 ？ 无状态约束。是他的一个缺点。\nAuthentication\n无状态约束给 REST 实现带来的麻烦：用户的状态是需要全部保存在客户端的。当用户需要执行某个操作的时候，其需要将所有的执行该请求所需要的信息添加到请求中。该请求将可能被 REST 服务集群中的任意服务器处理，而不需要担心该服务器中是否存有用户相关的状态。\n但是在现有的各种基于 HTTP 的 Web 服务中，我们常常使用会话来管理用户状态，至少是用户的登陆状态。因此，REST 系统的无状态约束实际上并不是一个对传统用户登录功能友好的约束：在传统登陆过程中，其本身就是通过用户所提供的用户名和密码等在服务端创建一个用户的登陆状态，而 REST 的无状态约束为了横向扩展性却不想要这种状态。而这也就是为基于 HTTP 的 REST 服务添加身份验证功能的困难之处。\n为了解决该问题，最为经典也最符合 REST 规范的实现是在每次发送请求的时候都将用户的用户名和密码都发送给服务器。而服务器将根据请求中的用户名和密码调用登陆服务，以从该服务中得到用户所对应的 Identity 和其所具有的权限。接下来，在 REST 服务中根据用户的权限来访问资源。\n这里有一个问题就是登陆的性能。随着系统当前的加密算法越来越复杂，登陆已经不再是一个轻量级的操作。因此用户所发送的每次请求都要求一次登陆对于整个系统而言就是一个巨大的瓶颈。\n在当前，解决该问题的方法主要是一个独立的缓存系统，如整个集群唯一的登陆服务器。但是缓存系统本身所存储的仍然是用户的登陆状态。因此该解决方案将仍然轻微地违反了 REST 的无状态约束。\n还有一个类似的方法是通过添加一个代理来完成的。该代理会完成用户的登陆并获得该用户所拥有的权限。接下来，该代理会将与状态有关的信息从请求中删除，并添加用户的权限信息。在经过了这种处理之后，这些请求就可以转发到其后的各个服务器上了。转发目的地所在的服务器则会假设所有传入的请求都是合法的并直接对这些请求进行处理。\n可以看到，无论是一个独立的登陆服务器还是为整个集群添加一个代理，系统中都将有一个地方保留了用户的登陆状态。这实际上和在集群中对会话集中进行管理并没有什么不同。也就是说，我们所尝试的通过禁止使用会话来达成完全的无状态并不现实。因此在一个基于 HTTP 的 REST 服务中，为登陆功能使用集中管理的会话是合理的。\n既然我们放松了对 REST 系统的无状态约束，那么一个 REST 系统所可以使用的登陆机制将主要分为以下两种：\n基于 HTTPS 的 Basic Access Authentication 其好处是其易于实现，而且主流的浏览器都提供了对该功能的支持。但是由于登陆窗口都是由浏览器所提供的，因此其与产品外观有很大不同。除此之外，浏览器都没有提供登出的功能，也没有提供找回密码等功能。\n基于 Cookie 及 Session 的管理 在使用 Cookie 来管理用户的注册状态的时候，其实际上就是将服务端所返回的 Cookie 在每次发送请求的时候添加到请求中。虽然说这个 Cookie 并非存储了用户应用的状态，但是其实际存储了用户的登陆状态。因此客户端的角度来讲，由服务端管理的 Session 并不符合 REST 所倡导的无状态的要求。\n可以说，上面的两种方法各有优劣。可能第二种方法从客户端的角度看来并不是 RESTful 的，但是其优势则在于很多类库都直接提供了对该功能的支持，从而简化了会话管理服务器的实现。\n在这里顺便提一句，如果项目足够大，将一些 SSO 产品集成到服务中也是不错的选择。\n","permalink":"https://leochu.work/blog/tech/engineering/restful%E9%A3%8E%E6%A0%BC/","summary":"\u003ch3 id=\"一什么是-rest-一种软件架构风格\"\u003e一、什么是 REST ？（一种软件架构风格）\u003c/h3\u003e\n\u003cp\u003e缩写：REST （不是\u0026quot;rest\u0026quot;这个单词）\n外文名：Representational State Transfer，简称REST。\n中文名：表现层状态转移。\u003c/p\u003e\n\u003cp\u003e提出时间：2000年。\n属性：一种软件架构风格。（以Web为平台的。web服务的架构风格，前后端接口时候用到。）\u003c/p\u003e\n\u003cp\u003eREST之所以晦涩难懂，是因为前面主语（Resource ）被去掉了。\n全称是： Resource Representational State Transfer。\n通俗来讲就是：资源在网络中以某种表现形式进行状态转移。\u003c/p\u003e\n\u003cp\u003e分解开来讲解:\nResource：资源，即数据（这是网络的核心）；\nRepresentational：某种表现形式，比如用JSON，XML，JPEG等；\nState Transfer：状态变化。通过HTTP的动词（get查询、post新增、put修改、delete删除）实现。\u003c/p\u003e\n\u003cp\u003e一句话描述 REST 实质：\u003cbr\u003e\nURL 中只使用名词来定位资源，用 HTTP 协议里的动词（GET、POST、PUT、DELETE）来实现资源的增删改查操作。\u003c/p\u003e\n\u003cp\u003e什么意思呢？\u003c/p\u003e\n\u003cp\u003e比如，我们有一个 friends 接口，对于 “朋友” 我们有增删改查四种操作，怎么定义 REST 接口？\u003c/p\u003e\n\u003cp\u003e增加一个朋友，uri: generalcode.cn/v1/friends 接口类型：POST\u003cbr\u003e\n删除一个朋友，uri: generalcode.cn/va/friends 接口类型：DELETE（在 http 的 parameter 指定好友 id）\u003cbr\u003e\n修改一个朋友，uri: generalcode.cn/va/friends 接口类型：PUT（在 http 的 parameter 指定好友 id）\u003cbr\u003e\n查找一个朋友，uri: generalcode.cn/va/friends 接口类型：GET\u003c/p\u003e\n\u003cp\u003e上面我们定义的四个接口就是符合 REST 协议的，请注意，这几个接口都没有动词，只有名词 friends，都是通过 Http 请求的接口类型来判断是什么业务操作。\u003c/p\u003e\n\u003cp\u003e举个反例：\ngeneralcode.cn/va/deleteFriends 该接口用来表示删除朋友，这就是不符合REST协议的接口。\n不能用deleteFriends ，而应该就用friends + http请求的delete方式。\u003c/p\u003e","title":"restful风格"},{"content":"1. 直接打开就读 with open(\u0026#39;filepath\u0026#39;,\u0026#39;r\u0026#39;) as f:\rfor line in f:\rprint（line）\rprint(\u0026#39;一行数据\u0026#39;) 虽然 f 是一个文件实例，但可以通过以上方式对每一行进行循环处理了，处理时每一行是一个字符串 str, 而且这个是速度最快最简洁的方法\n2. 用 read（）打开 with open(\u0026#39;filepath\u0026#39;,\u0026#39;r\u0026#39;) as f:\rff=f.read() 这个函数把文件全部内容一次性读到一个字符串中。就是一坨的那种，如果把 ff 用循环读取的方式输出，会是一个一个字符，因为 ff 是字符串，本质上是 tuple\n3. 用 readlines（） with open(\u0026#39;filepath\u0026#39;,\u0026#39;r\u0026#39;) as f:\rlines=f.readlines()\rfor line in lines:\rprint(line) 这个函数将文件所用内容以行为区分读到一个列表中 ，列表中的每一个元素是一行；lines 是 list，line 是 str\n4. 用 readline() with open(\u0026#39;filepath\u0026#39;,\u0026#39;r\u0026#39;) as f:\rline =f.readline()\rwhile line:\rprint(line)\rline=f.readline() 这种方式是一行一行的读，非常的省内存，当文件巨大的情况下是有好处的\n注 如果不用 with open 可以用以下代码来打开关闭文件\nf=open(path,\u0026#39;r\u0026#39;)\rf.close() ","permalink":"https://leochu.work/blog/tech/python/python%E8%AF%BB%E6%96%87%E4%BB%B6/","summary":"\u003ch2 id=\"1-直接打开就读\"\u003e1. 直接打开就读\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewith open(\u0026#39;filepath\u0026#39;,\u0026#39;r\u0026#39;) as f:\r\n    for line in f:\r\n        print（line）\r\n        print(\u0026#39;一行数据\u0026#39;)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e虽然 f 是一个文件实例，但可以通过以上方式对每一行进行循环处理了，处理时每一行是一个字符串 str, 而且这个是速度最快最简洁的方法\u003c/p\u003e\n\u003ch2 id=\"2-用-read打开\"\u003e2. 用 read（）打开\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewith open(\u0026#39;filepath\u0026#39;,\u0026#39;r\u0026#39;) as f:\r\n    ff=f.read()\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e这个函数把文件全部内容一次性读到一个字符串中。就是一坨的那种，如果把 ff 用循环读取的方式输出，会是一个一个字符，因为 ff 是字符串，本质上是 tuple\u003c/p\u003e\n\u003ch2 id=\"3-用-readlines\"\u003e3. 用 readlines（）\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewith open(\u0026#39;filepath\u0026#39;,\u0026#39;r\u0026#39;) as f:\r\n    lines=f.readlines()\r\n    for line in lines:\r\n        print(line)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e这个函数将文件所用内容以行为区分读到一个列表中 ，列表中的每一个元素是一行；lines 是 list，line 是 str\u003c/p\u003e\n\u003ch2 id=\"4-用-readline\"\u003e4. 用 readline()\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewith open(\u0026#39;filepath\u0026#39;,\u0026#39;r\u0026#39;) as f:\r\n    line =f.readline()\r\n    while line:\r\n        print(line)\r\n        line=f.readline()\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e这种方式是一行一行的读，非常的省内存，当文件巨大的情况下是有好处的\u003c/p\u003e\n\u003ch2 id=\"注\"\u003e注\u003c/h2\u003e\n\u003cp\u003e如果不用 with open 可以用以下代码来打开关闭文件\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ef=open(path,\u0026#39;r\u0026#39;)\r\nf.close()\n\u003c/code\u003e\u003c/pre\u003e","title":"python读文件"},{"content":"ORM常用操作 Django开发过程中对表(model)的增删改查是最常用的功能之一，本文介绍笔者在使用model 操作过程中遇到的一些操作。\nmodel update常规用法 假如我们的表结构是这样的\nclass User(models.Model):\rusername = models.CharField(max_length=255, unique=True, verbose_name=\u0026#39;用户名\u0026#39;)\ris_active = models.BooleanField(default=False, verbose_name=\u0026#39;激活状态\u0026#39;) 那么我们修改用户名和状态可以使用如下两种方法：\n方法一：\nUser.objects.filter(id=1).update(username=\u0026#39;nick\u0026#39;,is_active=True) 方法二：\n_t = User.objects.get(id=1)\r_t.username=\u0026#39;nick\u0026#39;\r_t.is_active=True\r_t.save() 方法一适合更新一批数据，类似于mysql语句update user set username='nick' where id = 1\n方法二适合更新一条数据，也只能更新一条数据，当只有一条数据更新时推荐使用此方法，另外此方法还有一个好处，我们接着往下看\n具有auto_now属性字段的更新 我们通常会给表添加三个默认字段\n自增ID，这个django已经默认加了，就像上边的建表语句，虽然只写了username和is_active两个字段，但表建好后也会有一个默认的自增id字段 创建时间，用来标识这条记录的创建时间，具有auto_now_add属性，创建记录时会自动填充当前时间到此字段 修改时间，用来标识这条记录最后一次的修改时间，具有auto_now属性，当记录发生变化时填充当前时间到此字段 就像下边这样的表结构\nclass User(models.Model):\rcreate_time = models.DateTimeField(auto_now_add=True, verbose_name=\u0026#39;创建时间\u0026#39;)\rupdate_time = models.DateTimeField(auto_now=True, verbose_name=\u0026#39;更新时间\u0026#39;)\rusername = models.CharField(max_length=255, unique=True, verbose_name=\u0026#39;用户名\u0026#39;)\ris_active = models.BooleanField(default=False, verbose_name=\u0026#39;激活状态\u0026#39;) 当表有字段具有auto_now属性且你希望他能自动更新时，必须使用上边方法二的更新，不然auto_now字段不会更新，也就是：\n_t = User.objects.get(id=1)\r_t.username=\u0026#39;nick\u0026#39;\r_t.is_active=True\r_t.save() json/dict类型数据更新字段 目前主流的web开放方式都讲究前后端分离，分离之后前后端交互的数据格式大都用通用的json型，那么如何用最少的代码方便的更新json格式数据到数据库呢？同样可以使用如下两种方法：\n方法一：\ndata = {\u0026#39;username\u0026#39;:\u0026#39;nick\u0026#39;,\u0026#39;is_active\u0026#39;:\u0026#39;0\u0026#39;}\rUser.objects.filter(id=1).update(**data) 同样这种方法不能自动更新具有auto_now属性字段的值 通常我们再变量前加一个星号(*)表示这个变量是元组/列表，加两个星号表示这个参数是字典 方法二：\ndata = {\u0026#39;username\u0026#39;:\u0026#39;nick\u0026#39;,\u0026#39;is_active\u0026#39;:\u0026#39;0\u0026#39;}\r_t = User.objects.get(id=1)\r_t.__dict__.update(**data)\r_t.save() 方法二和方法一同样无法自动更新auto_now字段的值 注意这里使用到了一个**dict**方法 方法三：\n_t = User.objects.get(id=1)\r_t.role=Role.objects.get(id=3)\r_t.save() ForeignKey字段更新 假如我们的表中有Foreignkey外键时，该如何更新呢？\nclass User(models.Model):\rcreate_time = models.DateTimeField(auto_now_add=True, verbose_name=\u0026#39;创建时间\u0026#39;)\rupdate_time = models.DateTimeField(auto_now=True, verbose_name=\u0026#39;更新时间\u0026#39;)\rusername = models.CharField(max_length=255, unique=True, verbose_name=\u0026#39;用户名\u0026#39;)\ris_active = models.BooleanField(default=False, verbose_name=\u0026#39;激活状态\u0026#39;)\rrole = models.ForeignKey(Role, on_delete=models.CASCADE, null=True, verbose_name=\u0026#39;角色\u0026#39;) 方法一：\nUser.objects.filter(id=1).update(role=2) 最简单的方法，直接让给role字段设置为一个id即可 当然也可以用dict作为参数更新： User.objects.filter(id=1).update(**{\u0026#39;username\u0026#39;:\u0026#39;nick\u0026#39;,\u0026#39;role\u0026#39;:3}) 方法二：\n_role = Role.objects.get(id=2)\rUser.objects.filter(id=1).update(role=_role) 也可以赋值一个实例给role 当然也可以用dict作为参数更新： _role = Role.objects.get(id=1)\rUser.objects.filter(id=1).update(**{\u0026#39;username\u0026#39;:\u0026#39;nick\u0026#39;,\u0026#39;role\u0026#39;:_role}) 方法三：\n_t = User.objects.get(id=1)\r_t.role=Role.objects.get(id=3)\r_t.save() 注意：这里的role必须赋值为一个对象，不能写id，不然会报错\u0026quot;User.role\u0026quot; must be a \u0026quot;Role\u0026quot; instance 当使用dict作为参数更新时又有一点不同，如下代码： _t = User.objects.get(id=1)\r_t.__dict__.update(**{\u0026#39;username\u0026#39;:\u0026#39;nick\u0026#39;,\u0026#39;role_id\u0026#39;:2})\r_t.save() Foreignkey外键必须加上_id，例如：{\u0026lsquo;role_id\u0026rsquo;:3} role_id后边必须跟一个id（int或str类型都可），不能跟role实例 ManyToManyField字段更新 假如我们的表中有ManyToManyField字段时更新又有什么影响呢？\nclass User(models.Model):\rcreate_time = models.DateTimeField(auto_now_add=True, verbose_name=\u0026#39;创建时间\u0026#39;)\rupdate_time = models.DateTimeField(auto_now=True, verbose_name=\u0026#39;更新时间\u0026#39;)\rusername = models.CharField(max_length=255, unique=True, verbose_name=\u0026#39;用户名\u0026#39;)\ris_active = models.BooleanField(default=False, verbose_name=\u0026#39;激活状态\u0026#39;)\rrole = models.ForeignKey(Role, on_delete=models.CASCADE, null=True, verbose_name=\u0026#39;角色\u0026#39;)\rgroups = models.ManyToManyField(Group, null=True, verbose_name=\u0026#39;组\u0026#39;) m2m更新：m2m字段没有直接更新的方法，只能通过清空再添加的方法更新了\n_t = User.objects.get(id=1)\r_t.groups.clear()\r_t.groups.add(*[1,3,5])\r_t.save() add()：m2m字段添加一个值，当有多个值的时候可用列表，参照上边例子\n_t.groups.add(2) _t.groups.add(Group.objects.get(id=2)) remove()：m2m字段移除一个值，，当有多个值的时候可用列表，参照上边例子\n_t.groups.remove(2) _t.groups.remove(Group.objects.get(id=2)) clear()：清空m2m字段的值\nDjango model select的各种用法详解 基本操作 # 获取所有数据，对应SQL：select * from User\rUser.objects.all()\r# 匹配，对应SQL：select * from User where name = \u0026#39;运维咖啡吧\u0026#39;\rUser.objects.filter(name=\u0026#39;运维咖啡吧\u0026#39;)\r# 不匹配，对应SQL：select * from User where name != \u0026#39;运维咖啡吧\u0026#39;\rUser.objects.exclude(name=\u0026#39;运维咖啡吧\u0026#39;)\r# 获取单条数据（有且仅有一条，id唯一），对应SQL：select * from User where id = 724\rUser.objects.get(id=123) 常用操作 # 获取总数，对应SQL：select count(1) from User\rUser.objects.count()\r# 获取总数，对应SQL：select count(1) from User where name = \u0026#39;运维咖啡吧\u0026#39;\rUser.objects.filter(name=\u0026#39;运维咖啡吧\u0026#39;).count()\r# 大于，\u0026gt;，对应SQL：select * from User where id \u0026gt; 724\rUser.objects.filter(id__gt=724)\r# 大于等于，\u0026gt;=，对应SQL：select * from User where id \u0026gt;= 724\rUser.objects.filter(id__gte=724)\r# 小于，\u0026lt;，对应SQL：select * from User where id \u0026lt; 724\rUser.objects.filter(id__lt=724)\r# 小于等于，\u0026lt;=，对应SQL：select * from User where id \u0026lt;= 724\rUser.objects.filter(id__lte=724)\r# 同时大于和小于， 1 \u0026lt; id \u0026lt; 10，对应SQL：select * from User where id \u0026gt; 1 and id \u0026lt; 10\rUser.objects.filter(id__gt=1, id__lt=10)\r# 包含，in，对应SQL：select * from User where id in (11,22,33)\rUser.objects.filter(id__in=[11, 22, 33])\r# 不包含，not in，对应SQL：select * from User where id not in (11,22,33)\rUser.objects.exclude(id__in=[11, 22, 33])\r# 为空：isnull=True，对应SQL：select * from User where pub_date is null\rUser.objects.filter(pub_date__isnull=True)\r# 不为空：isnull=False，对应SQL：select * from User where pub_date is not null\rUser.objects.filter(pub_date__isnull=True)\r# 匹配，like，大小写敏感，对应SQL：select * from User where name like \u0026#39;%sre%\u0026#39;，SQL中大小写不敏感\rUser.objects.filter(name__contains=\u0026#34;sre\u0026#34;)\r# 匹配，like，大小写不敏感，对应SQL：select * from User where name like \u0026#39;%sre%\u0026#39;，SQL中大小写不敏感\rUser.objects.filter(name__icontains=\u0026#34;sre\u0026#34;)\r# 不匹配，大小写敏感，对应SQL：select * from User where name not like \u0026#39;%sre%\u0026#39;，SQL中大小写不敏感\rUser.objects.exclude(name__contains=\u0026#34;sre\u0026#34;)\r# 不匹配，大小写不敏感，对应SQL：select * from User where name not like \u0026#39;%sre%\u0026#39;，SQL中大小写不敏感\rUser.objects.exclude(name__icontains=\u0026#34;sre\u0026#34;)\r# 范围，between and，对应SQL：select * from User where id between 3 and 8\rUser.objects.filter(id__range=[3, 8])\r# 以什么开头，大小写敏感，对应SQL：select * from User where name like \u0026#39;sh%\u0026#39;，SQL中大小写不敏感\rUser.objects.filter(name__startswith=\u0026#39;sre\u0026#39;)\r# 以什么开头，大小写不敏感，对应SQL：select * from User where name like \u0026#39;sh%\u0026#39;，SQL中大小写不敏感\rUser.objects.filter(name__istartswith=\u0026#39;sre\u0026#39;)\r# 以什么结尾，大小写敏感，对应SQL：select * from User where name like \u0026#39;%sre\u0026#39;，SQL中大小写不敏感\rUser.objects.filter(name__endswith=\u0026#39;sre\u0026#39;)\r# 以什么结尾，大小写不敏感，对应SQL：select * from User where name like \u0026#39;%sre\u0026#39;，SQL中大小写不敏感\rUser.objects.filter(name__iendswith=\u0026#39;sre\u0026#39;)\r# 排序，order by，正序，对应SQL：select * from User where name = \u0026#39;运维咖啡吧\u0026#39; order by id\rUser.objects.filter(name=\u0026#39;运维咖啡吧\u0026#39;).order_by(\u0026#39;id\u0026#39;)\r# 多级排序，order by，先按name进行正序排列，如果name一致则再按照id倒叙排列\rUser.objects.filter(name=\u0026#39;运维咖啡吧\u0026#39;).order_by(\u0026#39;name\u0026#39;,\u0026#39;-id\u0026#39;)\r# 排序，order by，倒序，对应SQL：select * from User where name = \u0026#39;运维咖啡吧\u0026#39; order by id desc\rUser.objects.filter(name=\u0026#39;运维咖啡吧\u0026#39;).order_by(\u0026#39;-id\u0026#39;) 进阶操作 # limit，对应SQL：select * from User limit 3;\rUser.objects.all()[:3]\r# limit，取第三条以后的数据，没有对应的SQL，类似的如：select * from User limit 3,10000000，从第3条开始取数据，取10000000条（10000000大于表中数据条数）\rUser.objects.all()[3:]\r# offset，取出结果的第10-20条数据（不包含10，包含20）,也没有对应SQL，参考上边的SQL写法\rUser.objects.all()[10:20]\r# 分组，group by，对应SQL：select username,count(1) from User group by username;\rfrom django.db.models import Count\rUser.objects.values_list(\u0026#39;username\u0026#39;).annotate(Count(\u0026#39;id\u0026#39;))\r# 去重distinct，对应SQL：select distinct(username) from User\rUser.objects.values(\u0026#39;username\u0026#39;).distinct().count()\r# filter多列、查询多列，对应SQL：select username,fullname from accounts_user\rUser.objects.values_list(\u0026#39;username\u0026#39;, \u0026#39;fullname\u0026#39;)\r# filter单列、查询单列，正常values_list给出的结果是个列表，里边里边的每条数据对应一个元组，当只查询一列时，可以使用flat标签去掉元组，将每条数据的结果以字符串的形式存储在列表中，从而避免解析元组的麻烦\rUser.objects.values_list(\u0026#39;username\u0026#39;, flat=True)\r# int字段取最大值、最小值、综合、平均数\rfrom django.db.models import Sum,Count,Max,Min,Avg\rUser.objects.aggregate(Count(‘id’))\rUser.objects.aggregate(Sum(‘age’)) 时间字段 # 匹配日期，date\rUser.objects.filter(create_time__date=datetime.date(2018, 8, 1))\rUser.objects.filter(create_time__date__gt=datetime.date(2018, 8, 2))\r# 匹配年，year\rUser.objects.filter(create_time__year=2018)\rUser.objects.filter(create_time__year__gte=2018)\r# 匹配月，month\rUser.objects.filter(create_time__month__gt=7)\rUser.objects.filter(create_time__month__gte=7)\r# 匹配日，day\rUser.objects.filter(create_time__day=8)\rUser.objects.filter(create_time__day__gte=8)\r# 匹配周，week_day\rUser.objects.filter(create_time__week_day=2)\rUser.objects.filter(create_time__week_day__gte=2)\r# 匹配时，hour\rUser.objects.filter(create_time__hour=9)\rUser.objects.filter(create_time__hour__gte=9)\r# 匹配分，minute\rUser.objects.filter(create_time__minute=15)\rUser.objects.filter(create_time__minute_gt=15)\r# 匹配秒，second\rUser.objects.filter(create_time__second=15)\rUser.objects.filter(create_time__second__gte=15)\r# 按天统计归档\rtoday = datetime.date.today()\rselect = {\u0026#39;day\u0026#39;: connection.ops.date_trunc_sql(\u0026#39;day\u0026#39;, \u0026#39;create_time\u0026#39;)}\rdeploy_date_count = Task.objects.filter(\rcreate_time__range=(today - datetime.timedelta(days=7), today)\r).extra(select=select).values(\u0026#39;day\u0026#39;).annotate(number=Count(\u0026#39;id\u0026#39;)) Q 的使用 Q对象可以对关键字参数进行封装，从而更好的应用多个查询，可以组合\u0026amp;(and)、|(or)、~(not)操作符。\n例如下边的语句\nfrom django.db.models import Q\rUser.objects.filter(\rQ(role__startswith=\u0026#39;sre_\u0026#39;),\rQ(name=\u0026#39;公众号\u0026#39;) | Q(name=\u0026#39;运维咖啡吧\u0026#39;)\r) 转换成SQL语句如下：\nselect * from User where role like \u0026#39;sre_%\u0026#39; and (name=\u0026#39;公众号\u0026#39; or name=\u0026#39;运维咖啡吧\u0026#39;) 通常更多的时候我们用Q来做搜索逻辑，比如前台搜索框输入一个字符，后台去数据库中检索标题或内容中是否包含\n_s = request.GET.get(\u0026#39;search\u0026#39;)\r_t = Blog.objects.all()\rif _s:\r_t = _t.filter(\rQ(title__icontains=_s) |\rQ(content__icontains=_s)\r)\rreturn _t 外键：ForeignKey 表结构： class Role(models.Model):\rname = models.CharField(max_length=16, unique=True)\rclass User(models.Model):\rusername = models.EmailField(max_length=255, unique=True)\rrole = models.ForeignKey(Role, on_delete=models.CASCADE) 正向查询: # 查询用户的角色名\r_t = User.objects.get(username=\u0026#39;运维咖啡吧\u0026#39;)\r_t.role.name 反向查询： # 查询角色下包含的所有用户\r_t = Role.objects.get(name=\u0026#39;Role03\u0026#39;)\r_t.user_set.all() 另一种反向查询的方法： _t = Role.objects.get(name=\u0026#39;Role03\u0026#39;)\r# 这种方法比上一种_set的方法查询速度要快\rUser.objects.filter(role=_t) 第三种反向查询的方法： 如果外键字段有related_name属性，例如models如下：\nclass User(models.Model):\rusername = models.EmailField(max_length=255, unique=True)\rrole = models.ForeignKey(Role, on_delete=models.CASCADE,related_name=\u0026#39;roleUsers\u0026#39;) 那么可以直接用related_name属性取到某角色的所有用户\n_t = Role.objects.get(name = \u0026#39;Role03\u0026#39;)\r_t.roleUsers.all() M2M：ManyToManyField 表结构： class Group(models.Model):\rname = models.CharField(max_length=16, unique=True)\rclass User(models.Model):\rusername = models.CharField(max_length=255, unique=True)\rgroups = models.ManyToManyField(Group, related_name=\u0026#39;groupUsers\u0026#39;) 正向查询: # 查询用户隶属组\r_t = User.objects.get(username = \u0026#39;运维咖啡吧\u0026#39;)\r_t.groups.all() 反向查询： # 查询组包含用户\r_t = Group.objects.get(name = \u0026#39;groupC\u0026#39;)\r_t.user_set.all() 同样M2M字段如果有related_name属性，那么可以直接用下边的方式反查\n_t = Group.objects.get(name = \u0026#39;groupC\u0026#39;)\r_t.groupUsers.all() get_object_or_404 正常如果我们要去数据库里搜索某一条数据时，通常使用下边的方法：\n_t = User.objects.get(id=734) 但当id=724的数据不存在时，程序将会抛出一个错误\nabcer.models.DoesNotExist: User matching query does not exist. 为了程序兼容和异常判断，我们可以使用下边两种方式:\n方式一：get改为filter _t = User.objects.filter(id=724)\r# 取出_t之后再去判断_t是否存在 方式二：使用get_object_or_404 from django.shortcuts import get_object_or_404\r_t = get_object_or_404(User, id=724)\r# get_object_or_404方法，它会先调用django的get方法，如果查询的对象不存在的话，则抛出一个Http404的异常 实现方法类似于下边这样：\nfrom django.http import Http404\rtry:\r_t = User.objects.get(id=724)\rexcept User.DoesNotExist:\rraise Http404 get_or_create 顾名思义，查找一个对象如果不存在则创建，如下：\nobject, created = User.objects.get_or_create(username=\u0026#39;运维咖啡吧\u0026#39;) 返回一个由object和created组成的元组，其中object就是一个查询到的或者是被创建的对象，created是一个表示是否创建了新对象的布尔值\n实现方式类似于下边这样：\ntry:\robject = User.objects.get(username=\u0026#39;运维咖啡吧\u0026#39;)\rcreated = False\rexception User.DoesNoExist:\robject = User(username=\u0026#39;运维咖啡吧\u0026#39;)\robject.save()\rcreated = True\rreturen object, created 执行原生SQL Django中能用ORM的就用它ORM吧，不建议执行原生SQL，可能会有一些安全问题，如果实在是SQL太复杂ORM实现不了，那就看看下边执行原生SQL的方法，跟直接使用pymysql基本一致了\nfrom django.db import connection\rwith connection.cursor() as cursor:\rcursor.execute(\u0026#39;select * from accounts_User\u0026#39;)\rrow = cursor.fetchall()\rreturn row 注意这里表名字要用app名+下划线+model名的方式\n","permalink":"https://leochu.work/blog/tech/python/django/orm%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/","summary":"\u003ch1 id=\"orm常用操作\"\u003eORM常用操作\u003c/h1\u003e\n\u003cp\u003eDjango开发过程中对表(model)的增删改查是最常用的功能之一，本文介绍笔者在使用model 操作过程中遇到的一些操作。\u003c/p\u003e\n\u003ch1 id=\"model-update常规用法\"\u003emodel update常规用法\u003c/h1\u003e\n\u003cp\u003e假如我们的表结构是这样的\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eclass User(models.Model):\r\n    username = models.CharField(max_length=255, unique=True, verbose_name=\u0026#39;用户名\u0026#39;)\r\n    is_active = models.BooleanField(default=False, verbose_name=\u0026#39;激活状态\u0026#39;)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e那么我们修改用户名和状态可以使用如下两种方法：\u003c/p\u003e\n\u003cp\u003e方法一：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eUser.objects.filter(id=1).update(username=\u0026#39;nick\u0026#39;,is_active=True)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e方法二：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e_t = User.objects.get(id=1)\r\n_t.username=\u0026#39;nick\u0026#39;\r\n_t.is_active=True\r\n_t.save()\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e方法一适合更新一批数据，类似于mysql语句\u003ccode\u003eupdate user set username='nick' where id = 1\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e方法二适合更新一条数据，也只能更新一条数据，当只有一条数据更新时推荐使用此方法，另外此方法还有一个好处，我们接着往下看\u003c/p\u003e\n\u003ch1 id=\"具有auto_now属性字段的更新\"\u003e具有auto_now属性字段的更新\u003c/h1\u003e\n\u003cp\u003e我们通常会给表添加三个默认字段\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e自增ID，这个django已经默认加了，就像上边的建表语句，虽然只写了username和is_active两个字段，但表建好后也会有一个默认的自增id字段\u003c/li\u003e\n\u003cli\u003e创建时间，用来标识这条记录的创建时间，具有\u003ccode\u003eauto_now_add\u003c/code\u003e属性，创建记录时会自动填充当前时间到此字段\u003c/li\u003e\n\u003cli\u003e修改时间，用来标识这条记录最后一次的修改时间，具有\u003ccode\u003eauto_now\u003c/code\u003e属性，当记录发生变化时填充当前时间到此字段\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e就像下边这样的表结构\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eclass User(models.Model):\r\n    create_time = models.DateTimeField(auto_now_add=True, verbose_name=\u0026#39;创建时间\u0026#39;)\r\n    update_time = models.DateTimeField(auto_now=True, verbose_name=\u0026#39;更新时间\u0026#39;)\r\n    username = models.CharField(max_length=255, unique=True, verbose_name=\u0026#39;用户名\u0026#39;)\r\n    is_active = models.BooleanField(default=False, verbose_name=\u0026#39;激活状态\u0026#39;)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003e当表有字段具有\u003ccode\u003eauto_now\u003c/code\u003e属性且你希望他能自动更新时，必须使用上边方法二的更新，不然auto_now字段不会更新\u003c/strong\u003e，也就是：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e_t = User.objects.get(id=1)\r\n_t.username=\u0026#39;nick\u0026#39;\r\n_t.is_active=True\r\n_t.save()\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"jsondict类型数据更新字段\"\u003ejson/dict类型数据更新字段\u003c/h1\u003e\n\u003cp\u003e目前主流的web开放方式都讲究前后端分离，分离之后前后端交互的数据格式大都用通用的json型，那么如何用最少的代码方便的更新json格式数据到数据库呢？同样可以使用如下两种方法：\u003c/p\u003e\n\u003cp\u003e方法一：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edata = {\u0026#39;username\u0026#39;:\u0026#39;nick\u0026#39;,\u0026#39;is_active\u0026#39;:\u0026#39;0\u0026#39;}\r\nUser.objects.filter(id=1).update(**data)\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e同样这种方法不能自动更新具有\u003ccode\u003eauto_now\u003c/code\u003e属性字段的值\u003c/li\u003e\n\u003cli\u003e通常我们再变量前加一个星号(*)表示这个变量是元组/列表，加两个星号表示这个参数是字典\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e方法二：\u003c/p\u003e","title":"ORM常用操作"},{"content":" IDEA（IntelliJ IDEA）是业界公认的最好开发工具之一，当然好不好也因人而异，也不要刻意去争谁强谁弱，高手往往不在乎手中的兵器是什么的。个人而言，更高效的工具适合每个开发人员，在编写代码的时候花的时间少，就有更多的时间去设计\u0026hellip;\n目录 [toc]\n一、概述 IDEA（IntelliJ IDEA）是业界公认的最好开发工具之一，当然好不好也因人而异，也不要刻意去争谁强谁弱，高手往往不在乎手中的兵器是什么的。个人而言，更高效的工具适合每个开发人员，在编写代码的时候花的时间少，就有更多的时间去设计代码，当然，使用一个陌生的 IDE，效率肯定是比不上自己熟练的 IDE 的，所以为了节省诸君的学习成本，也为了更快地去接纳和熟练 IDEA，所以周末之余，故作此文。\u0026mdash;- 一品江南\n准备\n所有的电脑软件都有着相同的 UI 框架、菜单结构，对于每一个开发人员来说，基本的文件操作、项目操作、工作空间操作不成问题，不会讲到。 涉及的其他工具（Maven、Git 等）不会详细讲，只会讲到如何操作，想要深入学习，关注其他博文。 二、配置 在安装软件（JDK、Maven、Git、IDEA）搭建好环境后，要对 IDEA 进行一番配置，才能开始开发，每个人都有每个人的配置习惯，可以自己研究自己喜欢的配置，进入 IDEA 的配置 File | Settings（Ctrl+Alt+S）开始配置。\n选择菜单或设置步骤使用 “|” 分开，后面操作一样\n1. 主题与行为 选择自己喜欢的主题：Appearance \u0026amp; Behavior | Appearance | Theme\n配置骚气的背景图片：Appearance \u0026amp; Behavior | Appearance | UI Options | Background Image…\n在配置中选择合适的窗口选项：Appearance \u0026amp; Behavior | Appearance | Window Options\nAnimate windows：关闭动画效果 Show memory indicator：显示内存使用情况 Show tool window bars：关闭工具栏显示，按两次Alt键可以显示 Small labels in editor tabs：编辑器显示小标签 配置自己想看到的菜单项和工具栏：Appearance \u0026amp; Behavior | Menus and Toolbars\n配置启动退出和保存同步：Appearance \u0026amp; Behavior | System Settings\n2. 快捷键与编辑器 配置快捷键：Keymap\n配置 Git 的 Pull 操作快捷键：Version Control Systems | Git | Repository | Pull 添加Alt+P\n配置 Maven 窗口显示快捷键：Tool Windows | Maven 添加 Alt+3\n优化导包：Editor | General | Auto Import | Java\nInsert imports on paste: All：粘贴的时候导入全部包\nAdd unambiguous imports on the fly Optimize imports on the fly (for current project) 配置显示行号和方法分隔线：Editor | General | Appearance\nShow line numbers Show method separators 配置代码折叠规则：Editor | General | Code Folding\n配置编辑器标签页：Editor | General | Editor Tabs\nShow tabs in one row：多行显示标签 配置高效的智能键：Editor | General | Smart Keys\nInsert paired brackets (), [], \u0026lt;\u0026gt;：成双成对输入 Reformat block on typing ‘}’：输入’}\u0026lsquo;后格式化代码 Use “CamelHumps” words：使用驼峰词，使用Ctrl+Righ/Left可以词内移动\nJump outside closing bracket/quote with Tab：使用 Tab 键跳到括号 / 引号外面 配置牛逼的字体：Editor | Font\nEnable font ligatures：使用连字符 推荐字段：Fira Code，使用连字符输入\u0026gt;=,\u0026lt;=,==,!=,=\u0026gt;等符号有惊喜。\n配置文件和代码模板：Editor | File and Code Templates\n配置文件编码：Editor | File Encodings\n建议全部配置成 UTF-8\n配置代码片段：Editor | Live Templates\n添加高效的代码片段可以提高效率\n3. 编译构建 配置 JDK 环境：File | Other Settings | Structure for New Projects… | Project | Project SDK\n配置 Maven 构建：Build, Execution, Deployment | Maven\n配置 Maven 主目录：D:/apache-maven-3.5.4\n配置用户 Maven 设置文件：D:\\apache-maven-3.5.4\\conf\\settings.xml\n配置本地仓库：D:\\apache-maven-3.5.4\\repo\nAlways update snapshots：构建时总是更新依赖快照版本\n4. 版本管理 配置 Git 管理：Version Control | Git\n配置 Git 路径：D:\\Program Files\\Git\\bin\\git.exe 5. 运行内存 编辑 IDEA 安装目录下 bin 目录：D:\\IntelliJ IDEA\\bin\\idea64.exe.vmoptions文件\n-Xms750m\r-Xmx750m 运行内存配置可以配置 IDEA 初时内存大小和最大内存数，根据自己电脑配置优化\n三、视图 好的界面布局和主题，会让开发者更舒适。快速地能看到想要的信息，会让开发者事半功倍。\n1. 工具 常用的工具栏默认位于 IDEA 界面的左、右、下三侧，也可以用鼠标悬停界面左下角图标弹出所有工具栏，你如果在前面主题与行为中配置隐藏工具栏显示，可以按两次 Alt 键显示。按键Esc可以帮助你从任何激活的工具栏返回到代码编辑。每个工具栏窗口顶部都会有一个 Toolbar，可快速实现某些常用功能以及配置。\n推荐使用快捷键弹出想要使用的工具栏，不建议修改默认快捷键，但常用的工具栏如果没有快捷键，应当自定义：\nAlt+1：Project Alt+2：Favorites Alt+3：Maven（自定义） Alt+4：Run Alt+5：Debug Alt+6：TODO Alt+7：Structure Alt+9：Version Control Alt+F12：Terminal 1.1 Project Project 工具栏主要是管理项目文件和目录，所有的项目资源都能在些找到，而且它可以有多种管理形式，可以更好聚焦于当前工作。\n管理形式\nProject：显示项目所有资源 Packages：显示项目所有包，适合开发模式 Tests：显示项目所有测试资源，适合测试模式 Problems：显示项目所有问题，所有存在问题的代码都会列举 Changed Files：显示项目所有修改资源，VCS 中修改未提交的代码\nToolbar： Scroll form source：从当前打开的文件定位文件树位置 Collapse All：折叠所有目录（Ctrl+-） Setting：打开工具栏设置 Flatten Packages：平铺展示所有包 Compact Middle Packages：折叠包名显示 Hide：隐藏工具栏（Shift+Esc） 由于所有的工具栏都会有 Setting 和 Hide，所以后面不会再说，在 Setting 里面会挑选几个常用的设置说。在 Setting 中的 View Mode 配置工具栏的显示模式：\nDock Pinned：钉住停靠显示 Dock Unpinned：不钉信停靠显示，激活其他工具栏隐藏 Undock：不停靠显示，类似 Dock Unpinned Float：浮动显示 Window：窗口模式显示，在全屏查看日志时非常有用 技巧\n在展开的文件树中，可以直接输入字母对类或包进行模糊搜索，快速定位目标。\n在很多的窗口都可以这样进行模糊搜索，可以自己大胆尝试，效率大大地提高，不过注意要使用英文输入法。\n使用 Left/Right 键可以快速折叠 / 展开当前目录 使用 Alt+Home 可以导航当前项目 1.2 Favorites Favorites 可以用来管理开发人员高频使用的目录或文件，可以是包和类，或者是类中的某一行，它分为三类：\n项目：在 Project 工具栏中对包或类右击选择 Add Favorites 添加\n书签：在 Project 工具栏中对包或类或者代码某行，按 F11 添加书签\n断点：打断点就会创建\n在很多地方使用 F4 快捷键可以快速跳转到源码或目录位置。比如选中 Favorites 中的某个书签或断点，选中 VCS 中的某个记录文件。\n1.3. Maven Maven 是管理 Maven 项目的工具集合，可以执行 Maven 的生命周期和插件，可以查看依赖等。\nToolbar：\nReimport All Maven Projects：重新导入 Maven 项目和依赖 Generate Sources and Update Folders For All Projects：创建源码更新目录 Download Sources and/or Documentation：下载源码或文档 Add Maven Projects：添加 Maven 项目 Run Maven Build：运行 Maven 生命周期或插件 Goal Execute Maven Goal：运行 Maven 插件 Goal Toggle Offline Mode：切换离线模式 Toggle ‘Skip Tests’ Mode：是否跳过测试 Setting\nGroup Modules：Maven 模块分组展示 Always Show ArtifactId：总是展示项目 ArtifactId Profiles\n选择激活 Maven 配置的 Profile\n1.4 TODO TODO 是管理项目代码中所有含有 // TODO 注释以及 // FIXME 注释的代码，这类特殊注释可以帮助开发人员处理事务。\n1.5 Structure Structure 可以管理文件结构，包括类、接口等源文件的属性、方法等。\nToolbar：\nSort by Visibility：按可见性排序 Sort Alphabetically：按字母排序 Group Methods by Defining Type：方法按类型分组 Show Properties：显示属性 Show Fields：显示字段 Show non-public：显示非公共的 Show Inherited：显示继承的 Show Anonymous Classes：显示匿名类 Show Lambdas：显示 Lambda Autoscroll to Source：自动定位到源码 Autoscroll from Source：自动从源码定位 1.6 Version Control Version Control 是版本管理工具，这里使用 Git 为例，它一般会有两个固定标签页 Local Changes 和 Log，用来管理本地修改和提交日志。\nToolbar：\nRefresh：刷新本地修改列表 Commit：提交（Ctrl+K） Revert：撤消选中的修改（Ctrl+Alt+Z） Show Diff：查看修改源文件的 Diff（Ctrl+D） Changelists：把修改源文件分组 Shelve Silently：搁置修改源文件（Ctrl+Alt+H） Group By：分组方式：Directory、Repository、Module Ignored Files：忽略的文件 Expand All：展开所有目录（Ctrl++） Collapse All：折叠所有目录（Ctrl+-） Preview Diff：预览修改源文件的 Diff 技巧\n查看 Diff 或者合并冲突的时候使用 F7/Shift+F7 查看下一个 / 上一个修改或冲突。 在提交代码的时候，可以查看历史的 Commit 信息。\n在提交代码的时候可以配置提交方式和提交前置事件。 提交方式：\nAmend commit：和上一次提交合并，等于命令 git commit --amend Sign-off commit：在提交信息后面追加用户签名 提交前置事件：\nAlibaba Code Guidelines：阿里开发规约检测（插件） Reformat code：格式化代码 Optimize imports：优化包的导入 Perform code analyses：代码分析 Check TODO（Show All）：检查 TODO 前置事件非常有用，在提交代码前会自动优化代码的格式以及检查出不合格的代码。\n在推送（Ctrl+Shift+K）代码到远程仓库的时候，可以配置推送 Tag 以及强推。 在 IDEA 状态栏右边可以操作 Git 仓库的分支，包括本地仓库和远程仓库。\n1.7 Event Log Event Log 查看 IDEA 全局操作的所有日志，当某些操作异常了，可以在此找到答案。\n1.8 Spring Spring 可以根据当前的 Spring 框架管理 Bean 和 MVC，对于使用 Spring 框架的开发人员来说，非常方便。\n技巧\n在 Spring 项目中，在自己创建的 properties 配置文件，在添加配置的时候，只有默认的application.properties文件才会有上下文提示，可以在项目右击菜单中 Open Module Settings （F4）中将 properties 文件添加到 Spring 上下文环境中。\n1.9 Terminal Terminal 是 IDEA 的集成终端，方便执行命令行。\n技巧\nWindows 系统的开发人员会觉得 CMD 的功能太弱鸡，所以会使用 Git 软件自带的 Bash，可以将 Bash 集成到 IDEA 中。 IDEA 集成终端配置 Bash：File | Settings | Tools | Terminal | Application settings\nShell path：D:\\Program Files\\Git\\bin\\bash.exe\nNOTE：如果终端出现乱码问题，可以百度解决，很简单。\n2.0 Web Web 工具栏和 Java Enterprise 工具栏有点类似，在 Web 项目中，会管理 Servlet、Filter、Listener，当然，如果我们的项目中只用到 SpringMVC，而不是自己编写 Servlet 的话，这工具似乎没有太多的作用。\n2. 代码 在代码视图中，主要是使用快捷键快速浏览一些代码的信息，这些快捷键在开发过程中也是大有用处的。\n查看当前变量、方法、类等的定义：Ctrl+Shift+I 查看当前变量、方法、类等的文档注释：Ctrl+Q 查看当前方法的参数信息：Ctrl+P 查看当前输入表达式信息：Ctrl+Shift+P 查看当前上下文信息：Alt+Q（可以查看当前类文件类定义信息）\n技巧\n使用Ctrl+Shift+I快速查看当前枚举、常量的信息 调用方法的时候使用Ctrl+P查看当前参数输入情况，由其是重载方法的情况 使用Ctrl+Shift+P快速修改某段代码 3. 文件 在文件视图中，可以导航最近修改的文件或位置。\n查看最近修改的文件：Ctrl+E 查看最近修改的位置：Ctrl+Shift+E\n4. 窗口 窗口视图可以设置 IDEA 主界面的显示方式。\n演示模式：View | Enter Presentation Mode 专注模式：View | Enter Distraction Mode 全屏模式：View | Enter Full Mode 四、导航 想去哪儿就去哪儿，定位问题与查阅代码都很方便。\n1. 文件 类全局查找：Ctrl+N 文件全局查找：Ctrl+Shift+N 符号全局查找：Ctrl+Alt+Shift+N 行 / 列定位：Ctrl+G 退回上 / 下一个位置：Ctrl+Alt + Left/Right 跳转到文件上 / 下一个修改的位置：Ctrl+Alt+Shift + Top/Bottom 切换文件标签页：Ctrl+Tab和Ctrl+Shift+Tab 2. 代码 跳转到属性、方法、类定义的位置：Ctrl+B 跳转到接口、方法实现的位置：Ctrl+Alt+B 跳转到类型定义的位置：Ctrl+Shift+B 跳转到父类，父方法的位置：Ctrl+U 跳转类测试类、方法的位置：Ctrl+Shift+T 跳转到下一个错误位置：F2 跳转到上一个错误位置：Shift+F2 跳转到上 / 下一个方法位置：Alt + Top/Bottom 跳转到大括号开头 / 结尾：Ctrl + [/] 在代码编辑器的左侧会存在 Gutter 图标，在设置 File | Settings | Editor | General | Gutter Icons 中我们可以看到这些图标的含义，单击图标也可以跳转到对应的位置。\n3. 结构 查看文件结构：Ctrl+F12 查看文件路径：Ctrl+Alt+F12 查看类型层次结构：Ctrl+H 查看方法层次结构：Ctrl+Shift+H 查看调用层次结构：Ctrl+Alt+H 技巧\n在查找类、文件的时候可以通过特殊符号定位到类中的某个方法或某行。 这个方法在定位问题的时候大有用处，而且只需简单的规则，就可以形成代码的坐标。\n使用#标记类或文件中的某个方法或属性：HttpProxy#get 使用:标记类或文件中的某行某列：HttpProxy:37:10 在团队之间发送代码位置的时候，使用右击菜单中 Copy Reference（Ctrl+Alt+Shift+C）来复制某个类、方法、属性等的唯一坐标，然后开发人员可以使用Ctrl+N输入坐标来定位代码。\n查看类型层次结构的时候可以使用 Ctrl+Alt+U 来显示类的 UML 图。 在 Project 文件树中也可以选择类使用 Ctrl+Alt+U生成 UML 图。\n五、代码 高效编辑代码，节省苦力活，缩短开发时间，今天不加班。\n1. 插入 重写父类方法：Ctrl+O 实现父类接口：Ctrl+I 生成代理方法：Code | Delegate Methods 生成多种代码：Alt+Insert Constructor：构造方法 Getter：Get 方法 Setter：Set 方法 Getter and Setter：Get 方法和 Set 方法 equals() and hashCode()：equals 方法和 hashCode 方法 toString()：toString 方法 Test：测试类或测试方法 Copyright：版权信息 @Autowired Dependency：注入 Spring Bean 插入包围代码：Ctrl+Alt+T if / else / while / for try / catch / finally synchronized / Runnable region...endregion Comments 去除包围代码：Ctrl+Shift+Delete 补全当前词语：Alt+/或Alt+Shift+/ 当前行前面插入一行：Ctrl+Alt+Enter 当前行后面插入一行：Shift+Enter 删除当前行或选中行：Ctrl+Y 重复插入当前行或选中行：Ctrl+D 在编写代码的时候，经常会要记录一些非代码文件，可以使用 Ctrl+Alt+Shift+Insert 来新建草稿文件，支持多种格式，新建好的草稿文件会保存在 Project 文件树的/Scratches and Consoles/Scratches目录下。\n2. 选择 选择当前光标所在区域：Ctrl+W，重复使用扩大选择范围 缩小当前选择范围：Ctrl+Shift+W 选择当前光标所在表达式：Ctrl+Shift+P 3. 格式 插入行注释：Ctrl+/ 插入块注释：Ctrl+Shift+/ 格式化代码：Ctrl+Alt+L 自动缩进对齐：Ctrl+Alt+I 生动优化导包：Ctrl+Alt+O 列自动对齐：Code | Align to Columns 代码折叠 折叠 / 展开当前代码段：Ctrl + -/+ 折叠 / 展开全部代码：Ctrl+Shift + -/+ 折叠全部文档注释：Code | Folding | Collapse doc comments 展开全部文档注释：Code | Folding | Expand doc comments 4. 移动 代码语句上 / 下移动：Ctrl+Shift + Up/Bottom，可以简单移动方法的位置 代码行上 / 下移动：Alt+Shift + Up/Bottom 5. 片段 在快捷键与编辑器配置中说到了如何配置代码片段，代码片段可以明显地提高编辑代码的速度，由其插入高频代码。\n智能片段：可以直接输入，也可以使用Ctrl+J来选择\n包围片段：可以选中一段代码使用Ctrl+Alt+J插入，也可以使用Ctrl+Alt+T\n6. 纠错 在代码中存在红色波浪线的代码都是有问题的代码 右侧滚动条上方图标不为绿色的对钩时表示该文件中存在有问题的代码 可以点击滚动条的彩色条来定位有问题的代码 也可以使用F2和Shift+F2来快速定位有问题的代码 滚动条的彩色条颜色决定着问题的严重性，例：警告、错误等 光标置于错误代码的时候，左侧会出现电灯泡，单击它有解决办法 使用快捷键 Alt+Enter 和单击电灯泡的功能一样 不管代码有什么问题，使用 Alt+Enter 总能找到解决办法。\n不要轻易关闭代码的错误提示，级别较低的警告可以酌情处理\n网上有许多坑人的教程，解决报红的错误的办法就是把错误提示关掉，万不可取。\n技巧\n在接口名称AE选择 Implement interface 快速添加接口的实现类。 在抽象类名称AE选择 Implement abstract class 快速添加抽象类的实现类。 在类名称AE选择 Create subclass 快速添加该类的子类。 在接口、抽象类、类名称AE选择 Create Test 快速添加类的测试类。 在方法名称AE选择 Generate missed test methods 创建测试方法。 在方法名称AE选择 Generate overloaded method with default parameter values 创建当前方法的重载方法。 在属性、方法、类名称AE选择 Add Javadoc 快速添加文档注释。 在正则表达式字符串AE选择 Check RegExp 校验正则表达式。 AE表示使用Alt+Enter快捷键\n在光标拖动选择的时候按住Alt键可以垂直选择进行多行编辑。 按住 Alt+Shift 键使用光标单击可以进行多个位置编辑。 在使用Ctrl+F的时候，想要编辑所有的结果可以使用Ctrl+Alt+Shift+J。 六、分析 分析项目，分析代码，才能分析设计。\n1. 代码 检查代码：Analyze | Inspect Code 检查代码并修复：Analyze | Code Cleanup 2. 依赖 分析依赖信息：Analyze | Analyze Dependencies 分析被依赖信息：Analyze | Analyze Backward Dependencies 分析 Module 依赖信息：Analyze | Analyze Module Dependencies 分析依赖矩阵：Analyze | Analyze Dependency Matrix 3. 数据 分析传递到当前的数据流：Analyze | Analyze Data Flow to Here 分析从当前开始传递的数据流：Analyze | Analyze Data Flow from Here 分析异常堆栈信息：Analyze | Analyze Stack Trace 技巧\n分析数据流可以追踪方法调用关系。\n可以使用分析异常堆栈信息来处理异常日志。\n七、重构 珍惜每一次重构的机会，尽量把项目设计的更好。\n1. 重构 重构当前代码：Ctrl+Alt+Shift+T 重命名：Shift+F6 修改类或方法签名：Ctrl+F6 修改定义类型：Ctrl+Shift+F6 修改成静态定义：Refactor | Make Static 修改成实例方法：Refactor | Convert To Instance Method 移动 / 复制：F6/F5（可以移动静态变量或方法） 安全删除：Alt+Delete 提取方法生成代理类：Refactor | Extract | Delegate 提取方法生成接口：Refactor | Extract | Interface 提取方法生成父类：Refactor | Extract | Superclass 2. 优化 对当前表达式提取并封装：Refactor | Extract 提取为变量：Ctrl+Alt+V 提取为常量：Ctrl+Alt+C 提取为类属性：Ctrl+Alt+F 提取为方法参数：Ctrl+Alt+P 提取为函数式参数：Ctrl+Alt+Shift+P 提取为函数式变量：Refactor | Extract | Functional Variable 提取方法的多个参数封装为对象：Refactor | Extract | Parameter Object 提取为方法：Ctrl+Alt+M 提取为方法并封装成对象：Refactor | Extract | Method Object 对当前表达式去除封装：Refactor | Inline 查找并封装重复代码：Refactor | Find and Replace Code Duplicates 倒置布尔类型：Refactor | Invert Boolean 去除中介调用：Refactor | Remove Middleman 包装方法返回结果：Refactor | Wrap Method Return Value 使用方法概括属性：Refactor | Encapsulate Fields 变量的初始化提取为方法：Refactor | Replace Temp with Query 使用工厂方法替换构造方法：Refactor | Replace Constructor with Factory Method 使用构建器替换构造方法：Refactor | Replace Constructor with Builder 使用安全的类型填充泛型：Refactor | Generify 3. 继承 将类成员推送到父类：Refactor | Pull Members Up 将类成员拉取到子类：Refactor | Push Members Dowm 使用接口替换类定义：Refactor | Use Interface Where Possible 使用代理来替换继承：Refactor | Replace Inheritance with Delegation 匿名类替换成内部类：Refactor | Convert Anonymous to Inner 技巧\n在 Controller 方法中，使用 Refactor | Extract | Parameter Object 把多个接收表单的参数封装成 VO 类。\n灵活运用好重构技能，编码速度可以提高几个档。\n八、构建 蛋变成鸡的一步，怎样处理好一个蛋，一窝蛋？\n1. 构建 构建项目：Build | Build Project （Ctrl+F9） 重新构建项目：Build | Rebuild Project （Ctrl+Shift+F9） 构建 Module：Build | Build Module 2. 运行 在菜单 Run 下面会有相关运行的操作，可以在 Run | Edit Configurations 中管理配置各种运行方式。\n运行 / 调试：Shift + F10/F9 运行 / 调试列表：Alt+Shift + F10/F9 停止 / 重启：Ctrl + F2/F5 2.1 Main 如果代码中存在 Main 方法，并以 Main 方法形式启动，打开 Main 方法所有类，除了使用 Alt+F10 或 Alt+Shift+F10 启动之外，也可以单击类名或方法名所在的运行 Gutter 图标。\n如果想要全屏或者分屏显示运行日志，可以把运行工具窗口的 View Mode 设置成 Window 模式。\n2.2 Tomcat 如果 Web 项目要使用 Tomcat 启动，则先要配置 Tomcat Server 运行方式，在 Run | Edit Configurations 添加该运行方式，在添加列表中选择 Tomcat Server | Local，配置好后即可运行。\n在配置之前需要在本地下载 Tomcat，在 Edit Configurations 配置中可以不仅可以添加运行方式，还可以对每个运行方式进行其他配置，比如：VM Options 等。\n3. 调试 使用 Debug 启动可以快速定位问题，打断点步进调试是每个开发人员必备的技能，这里不多说，常用快捷键：\n每行执行：F8 进入方法：F7 跳出方法：Shift+F8 执行到下一个断点：F9 跳转到当前执行的断点位置：Alt+F10 计算表达式：Alt+F8 技巧\n在断点停止的时候可以使用计算表达式来调试断点前的数据。\n不小心跳过了自己想要调试的断点，可以使用 Drop Frame 来删除当前栈帧，来重新执行方法。\n在栈帧窗口（Frames）右击，可以使用 Force Return 来强制返回当前方法，或使用 Throw Exception 来抛出异常。 九、工具 IDEA 不仅仅只是一个写代码的软件，还拥有开发过程中经常使用的工具。\n1. 数据库 在视图工具栏 View | Tool Windows | Database 中可以找到 Database 工具，点击 Toolbar 上的加号可以添加数据源，配置好服务器和驱动后就可以使用了，打开 Console 输入 SQL，使用 Ctrl+Enter 执行。\n2. 服务器管理 在工具栏 Tools | Deployment | Browse Remote Host 菜单打开 Remote Host 工具，点击 … 配置服务器，配置好了之后就可以操作 Remote Host 的文件树了。\n3. HTTP 客户端 在任何目录下都可以右击新建文件的时候创建 New HTTP Request 文件来编写 HTTP 报文来请求和测试 HTTP 接口，推荐在test目录下创建。创建测试文件如下：\nGET http://localhost:80/api/item?id=99\rAccept: application/json 点击每个请求报文左侧的运行 Gutter 图标就可以发送请求，同样创建 POST 请求：\nPOST http://localhost:80/api/item?id=99\rContent-Type: application/x-www-form-urlencoded\rid=1\u0026amp;name=2\u0026amp;gender=1\u0026amp;mobile=1413131212 可以在请求报文中使用环境变量，使用{{var}}来引用变量，变量可以在配置文件（http-client.env.json或rest-client.env.json）中定义：\n{\r\u0026#34;local\u0026#34;: {\r\u0026#34;host\u0026#34;: \u0026#34;http://localhost:8080\u0026#34;,\r\u0026#34;x_token\u0026#34;: \u0026#34;token\u0026#34;\r},\r\u0026#34;test\u0026#34;: {\r\u0026#34;host\u0026#34;: \u0026#34;http://10.250.1.122:8087\u0026#34;,\r\u0026#34;x_token\u0026#34;: \u0026#34;token\u0026#34;\r}\r} 在运行的时候选择不同的环境使用不同的变量：\nPOST {{host}}/api/item\rContent-Type: application/json\r{\r\u0026#34;name\u0026#34;: \u0026#34;ajn\u0026#34;,\r\u0026#34;age\u0026#34;: 24\r} 变量也可以在响应脚本中使用client.global.set设置和使用client.global.get获取。\n可以使用响应脚本来测试 HTTP 请求，脚本使用 Javascript 语言编写：\nGET https://httpbin.org/status/404\rAccept: application/json\r\u0026gt; {%\rclient.test(\u0026#34;Request executed successfully\u0026#34;, function() {\rclient.assert(response.status === 200, \u0026#34;Response status is not 200\u0026#34;);\r});\r%} 响应脚本 API 详解：\nclient：客户端对象 global：全局变量属性 set(string, object)：设置变量 get(string)：获取变量 isEmpty()：判断全局变量是否为空 clear(string)：清除某个变量 clearAll()：清除所有变量 test(string, function)：测试函数 assert(boolean, string)：断言函数 log(string)：输出日志函数 response：响应对象 body：响应体 headers：响应头 status：响应状态码 contentType：响应的 ContentType 头 4. SSH 会话 在工具栏 Tool | Start SSH session 菜单可以新建 SSH 会话，在前面服务器管理中如果创建了服务器，会自动添加该服务器的 SSH 会话。\n十、插件 强大的功能，还远远不够，一个合格的软件，都应支持功能自由地扩展。\n1. Lombok Lombok 插件支持代码中使用 Lombok 注解编译生成 Get 和 Set 等方法，该插件不仅支持注解，它在菜单中添加了两个功能：\nRefactor | Lombok：插入 Lombok 注解 Refactor | Delombok：将 Lombok 注解生成代码 更多设置：File | Settings | Other Settings | Lombok plugin\n2. Alibaba Java Coding Guidelines 该插件是结合阿里巴巴 Java 开发规约而出的，主要功能用于检测代码是否条例阿里开发规范，它结合了 IDEA 的纠错功能可以快速定位问题和修复问题，在菜单中添加的功能有：\nTool | 阿里编码规约 阿里规约插件提示的问题也可以使用 Alt+Enter 来寻找解决办法。\n技巧\n根据阿里规约，快速分析代码并一键修复。\n3. CamelCase CamelCase 添加一个快捷键 Alt+Shift+U来修改标识符的下划线、小驼峰、大驼峰等格式，并支持批量转换。\n按住 Alt+Shift 使用鼠标单击可以选择多选编辑。\n4. Easy Code Easy Code 插件可以根据数据库表和模板来生成代码，可以解决编写重复底层代码的问题。添加的设置项有：\n配置编码和作者名称：Other Settings | Easy Code 配置类型映射：Other Settings | Easy Code | Type Mapper 配置模板：Other Settings | Easy Code | Template Setting 配置全局模板：Other Settings | Easy Code | Global Config 只需在 Database 工具栏选中一个或多个表，右击 EasyCode | Generate Code 就可以生成代码，针对单表配置类型映射可以右击 EasyCode | Config Table 来配置。\n5. GenerateAllSetter 该插件可以一键调用对象的所有 Setter 方法，在新建对象实例后使用 Alt+Enter 弹出面板中菜单：\n创建似有 Setter 不带默认值：Generate all setter no default value 创建似有 Setter 带默认值：Generate all setter with default value\n6. Maven Helper 该插件用于 Maven 管理，可以分析 Maven 依赖结构和执行 Maven 生命周期，添加快捷键 Ctrl+Alt+R 来运行 Maven 生命周期，可以在 File | Settings | Other Settings | Maven Helper 中进行设置，另外一个功能就是在pom.xml文件下方添加了一个 Dependency Analyzer 标签，可以用来分析 Maven 依赖，可以查看和解决 Maven 依赖冲突。\n7. MyBatisX MyBatisX 插件主要增强了 IDEA 对 MyBatis 框架的支持，可以在 File | Settings | Other Settings | Mybatis 进行设置，它添加的功能有：\n检验 Mapper 层的代码逻辑 添加 Mapper 层接口与 XML 文件跳转功能的 Gutter 图标 快速根据接口生成 XML 标签 接口参数生成 @Param 注解\n8. RestfulToolkit 该插件是管理 Restful 接口的工具集，它提供的功能有：\n使用快捷键 Ctrl+\\ 来根据 URL 来跳转到方法定义 提供了一个接口的树形窗口 提供了一个简单的 HTTP 请求工具 在请求方法上生成特殊字符串并复制的功能 复制 K-V 形式的 QueryString：Generate \u0026amp; Copy Query Param (Key value) 复制 JSON 形式的请求体：Generate \u0026amp; Copy RequestBody (JSON) 复制相对路径 URL：Generate \u0026amp; Copy Relation URL 复制全路径 URL：Generate \u0026amp; Copy Full URL 在 Java 类上生成 JSON 并复制的功能 复制压缩格式的 JSON：Convert to JSON(Compressed) 复制 JSON：Convert to JSON\n参考文献\nIDEA 快捷键手册：Help | Keymap Reference IDEA 2019.1 官方文档：https://www.jetbrains.com/help/idea/2019.1/ ","permalink":"https://leochu.work/blog/tech/engineering/idea%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/","summary":"\u003cblockquote\u003e\n\u003cp\u003eIDEA（IntelliJ IDEA）是业界公认的最好开发工具之一，当然好不好也因人而异，也不要刻意去争谁强谁弱，高手往往不在乎手中的兵器是什么的。个人而言，更高效的工具适合每个开发人员，在编写代码的时候花的时间少，就有更多的时间去设计\u0026hellip;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"目录\"\u003e目录\u003c/h2\u003e\n\u003cp\u003e[toc]\u003c/p\u003e\n\u003ch2 id=\"一概述\"\u003e一、概述\u003c/h2\u003e\n\u003cp\u003eIDEA（IntelliJ IDEA）是业界公认的最好开发工具之一，当然好不好也因人而异，也不要刻意去争谁强谁弱，高手往往不在乎手中的兵器是什么的。个人而言，更高效的工具适合每个开发人员，在编写代码的时候花的时间少，就有更多的时间去设计代码，当然，使用一个陌生的 IDE，效率肯定是比不上自己熟练的 IDE 的，所以为了节省诸君的学习成本，也为了更快地去接纳和熟练 IDEA，所以周末之余，故作此文。\u0026mdash;- 一品江南\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e准备\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e所有的电脑软件都有着相同的 UI 框架、菜单结构，对于每一个开发人员来说，基本的文件操作、项目操作、工作空间操作不成问题，不会讲到。\u003c/li\u003e\n\u003cli\u003e涉及的其他工具（Maven、Git 等）不会详细讲，只会讲到如何操作，想要深入学习，关注其他博文。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"二配置\"\u003e二、配置\u003c/h2\u003e\n\u003cp\u003e在安装软件（JDK、Maven、Git、IDEA）搭建好环境后，要对 IDEA 进行一番配置，才能开始开发，每个人都有每个人的配置习惯，可以自己研究自己喜欢的配置，进入 IDEA 的配置 File | Settings（\u003ccode\u003eCtrl+Alt+S\u003c/code\u003e）开始配置。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e选择菜单或设置步骤使用 “|” 分开，后面操作一样\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"1-主题与行为\"\u003e1. 主题与行为\u003c/h3\u003e\n\u003cp\u003e选择自己喜欢的主题：Appearance \u0026amp; Behavior | Appearance | Theme\u003c/p\u003e\n\u003cp\u003e配置骚气的背景图片：Appearance \u0026amp; Behavior | Appearance | UI Options | Background Image…\u003c/p\u003e\n\u003cp\u003e在配置中选择合适的窗口选项：Appearance \u0026amp; Behavior | Appearance | Window Options\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cinput disabled=\"\" type=\"checkbox\"\u003e Animate windows：关闭动画效果\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e Show memory indicator：显示内存使用情况\u003c/li\u003e\n\u003cli\u003e\u003cinput disabled=\"\" type=\"checkbox\"\u003e Show tool window bars：关闭工具栏显示，按两次\u003ccode\u003eAlt\u003c/code\u003e键可以显示\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e Small labels in editor tabs：编辑器显示小标签\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e配置自己想看到的菜单项和工具栏：Appearance \u0026amp; Behavior | Menus and Toolbars\u003c/p\u003e","title":"IDEA常用操作"},{"content":" 本文由 简悦 SimpRead 转码， 原文地址 blog.csdn.net\n初次安装 gitgit 需要配置用户名和邮箱，否则 git 会提示：please tell me who you are.\n你需要运行命令来配置你的用户名和邮箱：\n$ git config \u0026ndash;global user.name “liuhanxia”\n$ git config \u0026ndash;global user.email \u0026ldquo;liuhanxia@51faguanggao.com\u0026rdquo;\n注意：（引号内请输入你自己设置的名字，和你自己的邮箱）此用户名和邮箱是 git 提交代码时用来显示你身份和联系方式的，并不是 github 用户名和邮箱\ngit 使用 ssh 密钥\ngit 支持 https 和 git 两种传输协议，github 分享链接时会有两种协议可选：git 协议链接图例、https 协议链接图例\ngit 使用 https 协议，每次 pull, push 都会提示要输入密码，\n使用 git 协议，然后使用 ssh 密钥，这样免去每次都输密码的麻烦\n初次使用 git 的用户要使用 git 协议大概需要三个步骤：\n一、生成密钥对\n二、设置远程仓库（本文以 github 为例）上的公钥\n三、把 git 的 remote url 修改为 git 协议（以上两个步骤初次设置过以后，以后使用都不需要再次设置，此步骤视以后项目的 remote url 而定，如果以后其他项目的协议为 https 则需要此步骤）\n一、生成密钥对\n大多数 Git 服务器都会选择使用 SSH 公钥来进行授权。系统中的每个用户都必须提供一个公钥用于授权，没有的话就要生成一个。生成公钥的过程在所有操作系统上都差不多。首先你要确认一下本机是否已经有一个公钥。\nSSH 公钥默认储存在账户的主目录下的 ~/.ssh 目录。进去看看：\n$ cd ~/.ssh\n$ ls\nauthorized_keys2 id_dsa known_hosts config id_dsa.pub\n看一下有没有 id_rsa 和 id_rsa.pub(或者是 id_dsa 和 id_dsa.pub 之类成对的文件)，有 .pub 后缀的文件就是公钥，另一个文件则是密钥。\n假如没有这些文件，甚至连 .ssh 目录都没有，可以用 ssh-keygen 来创建。该程序在 Linux/Mac 系统上由 SSH 包提供，而在 Windows 上则包含在 MSysGit 包里：\n$ ssh-keygen -t rsa -C \u0026ldquo;your_email@youremail.com\u0026rdquo;\nCreates a new ssh key using the provided email # Generating public/private rsa key pair.\nEnter file in which to save the key (/home/you/.ssh/id_rsa):\n直接按 Enter 就行。然后，会提示你输入密码，如下 (建议输一个，安全一点，当然不输也行，应该不会有人闲的无聊冒充你去修改你的代码)：\nEnter same passphrase again: [Type passphrase again]\n完了之后，大概是这样：\nYour public key has been saved in /home/you/.ssh/id_rsa.pub.\nThe key fingerprint is: # 01:0f:f4:3b:ca:85:d6:17:a1:7d:f0:68:9d:f0:a2:db your_email@youremail.com\n到此为止，你本地的密钥对就生成了。\n二、添加公钥到你的远程仓库（github）\n1、查看你生成的公钥：\n$ cat ~/.ssh/id_rsa.pub\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0X6L1zLL4VHuvGb8aJH3ippTozmReSUzgntvk434aJ/v7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8vR3c8E7CjZN733f5AL8uEYJA+YZevY5UCvEg+umT7PHghKYaJwaCxV7sjYP7Z6V79OMCEAGDNXC26IBMdMgOluQjp6o6j2KAdtRBdCDS/QIU5THQDxJ9lBXjk1fiq9tITo/aXBvjZeD+gH/Apkh/0GbO8VQLiYYmNfqqAHHeXdltORn8N7C9lOa/UW3KM7QdXo6J0GFlBVQeTE/IGqhMS5PMln3 admin@admin-PC\n2、登陆你的 github 帐户。点击你的头像，然后 Settings -\u0026gt; 左栏点击 SSH and GPG keys -\u0026gt; 点击 New SSH key\n3、然后你复制上面的公钥内容，粘贴进 “Key” 文本域内。 title 域，自己随便起个名字。\n4、点击 Add key。\n完成以后，验证下这个 key 是不是正常工作：\n$ ssh -T git@github.com\nAttempts to ssh to github\n如果，看到：\nHi xxx! You’ve successfully authenticated, but GitHub does not # provide shell access.\n恭喜你，你的设置已经成功了。\n三、修改 git 的 remote url\n使用命令 git remote -v 查看你当前的 remote url\n$ git remote -v\norigin https://github.com/someaccount/someproject.git (fetch)\norigin https://github.com/someaccount/someproject.git (push)\n如果是以上的结果那么说明此项目是使用 https 协议进行访问的（如果地址是 git 开头则表示是 git 协议）\n你可以登陆你的 github，就像本文开头的图例，你在上面可以看到你的 ssh 协议相应的 url，类似：\n复制此 ssh 链接，然后使用命令 git remote set-url 来调整你的 url。\ngit remote set-url origin git@github.com:someaccount/someproject.git\n然后你可以再用命令 git remote -v 查看一下，url 是否已经变成了 ssh 地址。\n然后你就可以愉快的使用 git fetch, git pull , git push，再也不用输入烦人的密码了\n","permalink":"https://leochu.work/blog/tech/engineering/git%E9%85%8D%E7%BD%AEssh%E5%AF%86%E9%92%A5/","summary":"\u003cblockquote\u003e\n\u003cp\u003e本文由 \u003ca href=\"http://ksria.com/simpread/\"\u003e简悦 SimpRead\u003c/a\u003e 转码， 原文地址 \u003ca href=\"https://blog.csdn.net/weixin_41990913/article/details/91373362\"\u003eblog.csdn.net\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e初次安装 gitgit 需要配置用户名和邮箱，否则 git 会提示：please tell me who you are.\u003c/p\u003e\n\u003cp\u003e你需要运行命令来配置你的用户名和邮箱：\u003cbr\u003e\n$ git config \u0026ndash;\u003ca href=\"https://so.csdn.net/so/search?q=global\u0026amp;spm=1001.2101.3001.7020\"\u003eglobal\u003c/a\u003e \u003ca href=\"http://user.name\"\u003euser.name\u003c/a\u003e “liuhanxia”\u003cbr\u003e\n$ git config \u0026ndash;global user.email \u003ca href=\"mailto:%22liuhanxia@51faguanggao.com\"\u003e\u0026ldquo;liuhanxia@51faguanggao.com\u003c/a\u003e\u0026rdquo;\u003cbr\u003e\n注意：（引号内请输入你自己设置的名字，和你自己的邮箱）此用户名和邮箱是 git 提交代码时用来显示你身份和联系方式的，并不是 \u003ca href=\"https://so.csdn.net/so/search?q=github\u0026amp;spm=1001.2101.3001.7020\"\u003egithub\u003c/a\u003e 用户名和邮箱\u003cbr\u003e\n\u003cimg loading=\"lazy\" src=\"/blog/resource/git_ssh_01.png\"\u003e\u003cbr\u003e\ngit 使用 ssh 密钥\u003cbr\u003e\ngit 支持 https 和 git 两种传输协议，github 分享链接时会有两种协议可选：git 协议链接图例、https 协议链接图例\u003c/p\u003e\n\u003cp\u003egit 使用 https 协议，每次 pull, push 都会提示要输入密码，\u003cbr\u003e\n使用 git 协议，然后使用 ssh 密钥，这样免去每次都输密码的麻烦\u003c/p\u003e\n\u003cp\u003e初次使用 git 的用户要使用 git 协议大概需要三个步骤：\u003cbr\u003e\n一、生成密钥对\u003cbr\u003e\n二、设置远程仓库（本文以 github 为例）上的公钥\u003cbr\u003e\n三、把 git 的 remote url 修改为 git 协议（以上两个步骤初次设置过以后，以后使用都不需要再次设置，此步骤视以后项目的 remote url 而定，如果以后其他项目的协议为 https 则需要此步骤）\u003c/p\u003e","title":"Git配置SSH密钥"},{"content":"\n本地仓库链接远程仓库 git remote add origin \u0026lt;server\u0026gt;git@github.com:yourName/yourRepo.git\n创建一个叫做“feature_x”的分支，并切换过去 git checkout -b feature_x\n再把新建的分支删掉： git branch -d feature_x\n预览差异： git diff \u0026lt;source_branch\u0026gt; \u0026lt;target_branch\u0026gt;\n创建一个叫做 1.0.0 的标签： git tag 1.0.0 1b2e1d63ff\n想commit自动来提交本地修改，我们可以使用-a标识 git commit -a -m \u0026quot;Changed some files\u0026quot; git commit 命令的-a选项可将所有被修改或者已删除的且已经被git管理的文档提交到仓库中 千万注意，-a不会造成新文件被提交，只能修改。\n内建的图形化 git： gitk\n彩色的 git 输出： git config color.ui true\n使用git gui创建sshkey及查看： 命令行创建ssh key： ssh-keygen -t rsa -C \u0026quot;your_email@youremail.com\u0026quot; 后面的your_email@youremail.com改为你在github上注册的邮箱，之后会要求确认路径和输入密码，我们这使用默认的一路回车就行。成功的话会在~/下生成.ssh文件夹，进去，打开id_rsa.pub，复制里面的key。\n验证是否成功，在git bash下输入： ssh -T git@github.com\n如果要查看指定文件的修改记录可以使用 git blame 命令，格式如下： git blame \u0026lt;file\u0026gt;\n命令行可视化树图 git log --oneline --decorate --graph\n拉取代码并rebase git pull --rebase rebase 会将两个分支进行合并，同时合并之前的 commit 历史。如果出现冲突，解决冲突后执行以下命令即可： git add git rebase --continue 如果你想要一个干净的，没有 merge commit 的线性历史树，那么你应该选择 git rebase 如果你想保留完整的历史记录，并且想要避免重写 commit history 的风险，你应该选择使用 git merge\n","permalink":"https://leochu.work/blog/tech/engineering/git%E5%91%BD%E4%BB%A4%E8%A1%8C/","summary":"\u003cp\u003e\u003cimg alt=\"img\" loading=\"lazy\" src=\"/blog/resource/git-command.jpg\"\u003e\u003c/p\u003e\n\u003ch5 id=\"本地仓库链接远程仓库\"\u003e本地仓库链接远程仓库\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egit remote add origin \u0026lt;server\u0026gt;git@github.com:yourName/yourRepo.git\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"创建一个叫做feature_x的分支并切换过去\"\u003e创建一个叫做“feature_x”的分支，并切换过去\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egit checkout -b feature_x\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"再把新建的分支删掉\"\u003e再把新建的分支删掉：\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egit branch -d feature_x\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"预览差异\"\u003e预览差异：\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egit diff \u0026lt;source_branch\u0026gt; \u0026lt;target_branch\u0026gt;\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"创建一个叫做-100-的标签\"\u003e创建一个叫做 \u003cem\u003e1.0.0\u003c/em\u003e 的标签：\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egit tag 1.0.0 1b2e1d63ff\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"想commit自动来提交本地修改我们可以使用-a标识\"\u003e想commit自动来提交本地修改，我们可以使用-a标识\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egit commit -a -m \u0026quot;Changed some files\u0026quot;\u003c/code\u003e\n\u003cem\u003egit commit 命令的-a选项可将所有\u003cstrong\u003e被修改或者已删除的且已经被git管理的文档\u003c/strong\u003e提交到仓库中\u003c/em\u003e\n\u003cem\u003e千万注意，-a不会造成新文件被提交，只能修改。\u003c/em\u003e\u003c/p\u003e\n\u003ch5 id=\"内建的图形化-git\"\u003e内建的图形化 git：\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egitk\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"彩色的-git-输出\"\u003e彩色的 git 输出：\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egit config color.ui true\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"使用git-gui创建sshkey及查看\"\u003e使用git gui创建sshkey及查看：\u003c/h5\u003e\n\u003cp\u003e\u003cimg alt=\"image-20230213164607434\" loading=\"lazy\" src=\"/blog/resource/image-20230213164607434.png\"\u003e\u003c/p\u003e\n\u003ch5 id=\"命令行创建ssh-key\"\u003e命令行创建ssh key：\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003essh-keygen -t rsa -C \u0026quot;your_email@youremail.com\u0026quot;\u003c/code\u003e\n\u003cem\u003e后面的\u003ccode\u003eyour_email@youremail.com\u003c/code\u003e改为你在github上注册的邮箱，之后会要求确认路径和输入密码，我们这使用默认的一路回车就行。成功的话会在\u003ccode\u003e~/\u003c/code\u003e下生成\u003ccode\u003e.ssh\u003c/code\u003e文件夹，进去，打开\u003ccode\u003eid_rsa.pub\u003c/code\u003e，复制里面的\u003ccode\u003ekey\u003c/code\u003e。\u003c/em\u003e\u003c/p\u003e\n\u003ch5 id=\"验证是否成功在git-bash下输入\"\u003e验证是否成功，在git bash下输入：\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003essh -T git@github.com\u003c/code\u003e\u003c/p\u003e\n\u003ch5 id=\"如果要查看指定文件的修改记录可以使用-git-blame-命令格式如下\"\u003e如果要查看指定文件的修改记录可以使用 git blame 命令，格式如下：\u003c/h5\u003e\n\u003cp\u003e\u003ccode\u003egit blame \u0026lt;file\u0026gt;\u003c/code\u003e\u003c/p\u003e","title":"Git命令行"},{"content":"上一章节中我们远程仓库使用了 Github，Github 公开的项目是免费的，2019 年开始 Github 私有存储库也可以无限制使用。\n这当然我们也可以自己搭建一台 Git 服务器作为私有仓库使用。\n接下来我们将以 Centos 为例搭建 Git 服务器。\n1、安装Git $ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel\r$ yum install git 接下来我们 创建一个git用户组和用户，用来运行git服务：\n$ groupadd git\r$ useradd git -g git 2、创建证书登录 收集所有需要登录的用户的公钥，公钥位于id_rsa.pub文件中，把我们的公钥导入到/home/git/.ssh/authorized_keys文件里，一行一个。\n如果没有该文件创建它：\n$ cd /home/git/\r$ mkdir .ssh\r$ chmod 755 .ssh\r$ touch .ssh/authorized_keys\r$ chmod 644 .ssh/authorized_keys 3、初始化Git仓库 首先我们选定一个目录作为Git仓库，假定是/home/gitrepo/runoob.git，在/home/gitrepo目录下输入命令：\n$ cd /home\r$ mkdir gitrepo\r$ chown git:git gitrepo/\r$ cd gitrepo\r$ git init --bare runoob.git\rInitialized empty Git repository in /home/gitrepo/runoob.git/ 以上命令Git创建一个空仓库，服务器上的Git仓库通常都以.git结尾。然后，把仓库所属用户改为git：\n$ chown -R git:git runoob.git 4、克隆仓库 $ git clone git@192.168.45.4:/home/gitrepo/runoob.git\rCloning into \u0026#39;runoob\u0026#39;...\rwarning: You appear to have cloned an empty repository.\rChecking connectivity... done. 192.168.45.4 为 Git 所在服务器 ip ，你需要将其修改为你自己的 Git 服务 ip。\n这样我们的 Git 服务器安装就完成。\n","permalink":"https://leochu.work/blog/tech/engineering/git%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BA/","summary":"\u003cp\u003e上一章节中我们远程仓库使用了 Github，Github 公开的项目是免费的，2019 年开始 Github 私有存储库也可以无限制使用。\u003c/p\u003e\n\u003cp\u003e这当然我们也可以自己搭建一台 Git 服务器作为私有仓库使用。\u003c/p\u003e\n\u003cp\u003e接下来我们将以 Centos 为例搭建 Git 服务器。\u003c/p\u003e\n\u003ch3 id=\"1安装git\"\u003e1、安装Git\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel\r\n$ yum install git\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e接下来我们 创建一个git用户组和用户，用来运行git服务：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ groupadd git\r\n$ useradd git -g git\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2创建证书登录\"\u003e2、创建证书登录\u003c/h3\u003e\n\u003cp\u003e收集所有需要登录的用户的公钥，公钥位于id_rsa.pub文件中，把我们的公钥导入到/home/git/.ssh/authorized_keys文件里，一行一个。\u003c/p\u003e\n\u003cp\u003e如果没有该文件创建它：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ cd /home/git/\r\n$ mkdir .ssh\r\n$ chmod 755 .ssh\r\n$ touch .ssh/authorized_keys\r\n$ chmod 644 .ssh/authorized_keys\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"3初始化git仓库\"\u003e3、初始化Git仓库\u003c/h3\u003e\n\u003cp\u003e首先我们选定一个目录作为Git仓库，假定是/home/gitrepo/runoob.git，在/home/gitrepo目录下输入命令：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ cd /home\r\n$ mkdir gitrepo\r\n$ chown git:git gitrepo/\r\n$ cd gitrepo\r\n\r\n$ git init --bare runoob.git\r\nInitialized empty Git repository in /home/gitrepo/runoob.git/\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e以上命令Git创建一个空仓库，服务器上的Git仓库通常都以.git结尾。然后，把仓库所属用户改为git：\u003c/p\u003e","title":"Git服务器搭建"},{"content":" 任务类型 工具 / 模型 说明 / 能力边界 成本 厂商 文档整理 / 项目重构 / 架构设计 Claude Code（基于 Claude Opus） 高质量理解长上下文，支持跨文件重构和工程分析；生成方案/代码，但需要外部环境执行 pro订阅 claude 写代码 / 单文件实现 OpenAI Codex（GPT-Code 系列） 自动生成可运行代码，适合函数、模块、脚本任务；执行依赖你的环境或接口 plus订阅 openAi 实时代码辅助 / IDE 提示 GitHub Copilot IDE 插件，提供智能补全和片段建议，不提供 API 对话 / 问答 /策略讨论 ChatGPT Plus 快速交互、概念解释、方案讨论 plus订阅 openAi 机械重复 / 简单批处理任务 国内轻量模型 低成本处理大量重复操作或简单格式化任务 miniMax 自动化执行 / 跨渠道智能代理 OpenClaw 可自托管的 AI Agent 框架；整合多模型、消息渠道和技能；能持续管理任务、执行脚本、调用 API 和操作工具；适合自动化工作流和多通道触发，需配置和监管 ","permalink":"https://leochu.work/blog/tech/ai/ai%E6%8A%80%E6%9C%AF%E6%A0%88/","summary":"\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e任务类型\u003c/th\u003e\n          \u003cth\u003e工具 / 模型\u003c/th\u003e\n          \u003cth\u003e说明 / 能力边界\u003c/th\u003e\n          \u003cth\u003e成本\u003c/th\u003e\n          \u003cth\u003e厂商\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e文档整理 / 项目重构 / 架构设计\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eClaude Code（基于 Claude Opus）\u003c/td\u003e\n          \u003ctd\u003e高质量理解长上下文，支持跨文件重构和工程分析；生成方案/代码，但需要外部环境执行\u003c/td\u003e\n          \u003ctd\u003epro订阅\u003c/td\u003e\n          \u003ctd\u003eclaude\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e写代码 / 单文件实现\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eOpenAI Codex（GPT-Code 系列）\u003c/td\u003e\n          \u003ctd\u003e自动生成可运行代码，适合函数、模块、脚本任务；执行依赖你的环境或接口\u003c/td\u003e\n          \u003ctd\u003eplus订阅\u003c/td\u003e\n          \u003ctd\u003eopenAi\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e实时代码辅助 / IDE 提示\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eGitHub Copilot\u003c/td\u003e\n          \u003ctd\u003eIDE 插件，提供智能补全和片段建议，不提供 API\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e对话 / 问答 /策略讨论\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eChatGPT Plus\u003c/td\u003e\n          \u003ctd\u003e快速交互、概念解释、方案讨论\u003c/td\u003e\n          \u003ctd\u003eplus订阅\u003c/td\u003e\n          \u003ctd\u003eopenAi\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e机械重复 / 简单批处理任务\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003e国内轻量模型\u003c/td\u003e\n          \u003ctd\u003e低成本处理大量重复操作或简单格式化任务\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003eminiMax\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003cstrong\u003e自动化执行 / 跨渠道智能代理\u003c/strong\u003e\u003c/td\u003e\n          \u003ctd\u003eOpenClaw\u003c/td\u003e\n          \u003ctd\u003e可自托管的 AI Agent 框架；整合多模型、消息渠道和技能；能持续管理任务、执行脚本、调用 API 和操作工具；适合自动化工作流和多通道触发，需配置和监管\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e","title":"ai栈"},{"content":"数据模型字段及属性详解 在设计数据模型的时候，我们需要根据不同的需求，设计不同的表和不同的字段。不同的字段也可以设置不同的参数。\n一个模型（model）就是一个单独的、确定的数据的信息源，包含了数据的字段和操作方法。Django是通过Model操作数据库，不管你数据库的类型是MySql或者Sqlite，Django它自动帮你生成相应数据库类型的SQL语句，所以不需要关注SQL语句和类型，对数据的操作Django帮我们自动完成。只要回写Model就可以了！\ndjango根据代码中定义的类来自动生成数据库表。我们写的类表示数据库的表，如果根据这个类创建的对象是数据库表里的一行数据，对象.id 对象.value是每一行里的数据。\n基本的原则如下： 每个模型在Django中的存在形式为一个Python类 每个模型都是django.db.models.Model的子类 模型里的每个类代表数据库中的一个表 模型的每个字段（属性）代表数据表的某一列 Django将自动为你生成数据库访问API\nDjango这种操作数据库的方式，我们把它叫做：关系对象映射（Object Relational Mapping，简称ORM）。\n我们之前有在管理后台与model模型这文章里简单的接触过。里面的models代码：\nclass Category(models.Model):\rname = models.CharField(\u0026#39;分类\u0026#39;,max_length=100)\rclass Tags(models.Model):\rname = models.CharField(\u0026#39;标签\u0026#39;,max_length=100)\rclass Article(models.Model):\rtitle = models.CharField(\u0026#39;标题\u0026#39;,max_length=70)\rintro = models.TextField(\u0026#39;摘要\u0026#39;, max_length=200, blank=True)\rcategory = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name=\u0026#39;分类\u0026#39;, default=\u0026#39;1\u0026#39;)\rtags = models.ManyToManyField(Tags, blank=True)\rbody = models.TextField()\ruser = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=\u0026#39;作者\u0026#39;)\rcreated_time = models.DateTimeField(\u0026#39;发布时间\u0026#39;,auto_now_add=True) 字段是模型中最重要的内容之一，也是唯一必须的部分。字段在Python中表现为一个类属性，体现了数据表中的一个列。Django不允许下面两种字段名：1、与Python关键字冲突。2、字段名中不能有两个以上下划线在一起，因为两个下划线是Django的查询语法。也不要使用clean、save、delete等Django内置的模型API名字，防止命名冲突。\n每一个字段都是一个类属性，每个类属性表示数据表中的一个列。上面那个models生成数据后，在数据库里是这样的：\n表名由Django自动生成，例如我们的Article类会自动生成为blog_Article，默认格式为“应用名称+下划线+小写类名”。如果你不指定主键，Django默认自动创建自增主键id。每个APP应用都有独立属于自己的模型，创建了APP之后，在使用它之前，你需要先在settings文件中的INSTALLED_APPS 处，注册models.py文件所在的app名称。看清楚了，是注册app，不是模型，也不是models.py。关于settings，我们前面也有讲解过，具体可查看文章：全局配置settings详解。例如我们创建了名为blog的应用APP则在settings这样写：\nINSTALLED_APPS = [\r#...\r\u0026#39;blog\u0026#39;, #我们要添加的应用名\r#...\r] 当你每次对模型进行增、删、修改时，我们都需要执行python manage.py makemigrations请务必执命令，然后再执行：python manage.py migrate，让操作实际应用到数据库上。\n下面我把常用的字段和常用的参数列出来，供大家参考。\n一、常用字段：\n1、AutoField \u0026mdash;自增列 = int(11) 如果没有的话，默认会生成一个名称为 id 的列，如果要显示的自定义一个自增列，必须将给列设置为主键 primary_key=True。 2、CharField \u0026mdash;字符串字段 单行输入，用于较短的字符串，如要保存大量文本, 使用 TextField。必须 max_length 参数，django会根据这个参数在数据库层和校验层限制该字段所允许的最大字符数。 3、BooleanField \u0026mdash;布尔类型=tinyint(1) 不能为空，Blank=True 4、ComaSeparatedIntegerField \u0026mdash;用逗号分割的数字=varchar 继承CharField，所以必须 max_lenght 参数， 5、DateField \u0026mdash;日期类型 date 对于参数，auto_now = True 则每次更新都会更新这个时间；auto_now_add 则只是第一次创建添加，之后的更新不再改变。 6、DateTimeField \u0026mdash;日期类型 datetime 同DateField的参数 7、Decimal \u0026mdash;十进制小数类型 = decimal 必须指定整数位max_digits和小数位decimal_places 8、EmailField \u0026mdash;字符串类型（正则表达式邮箱） =varchar 对字符串进行正则表达式 一个带有检查 Email 合法性的 CharField，不接受 maxlength 参数。 9、FloatField \u0026mdash;浮点类型 = double 浮点型字段。 必须提供两个 参数， 参数描述： max_digits：总位数(不包括小数点和符号） decimal_places：小数位数。如：要保存最大值为 999 (小数点后保存2位)，你要这样定义字段：FloatField(…，max_digits=5， decimal_places=2)，要保存最大值一百万(小数点后保存10位)的话，你要这样定义：FloatField(…，max_digits=19， decimal_places=10) 10、IntegerField \u0026mdash;整形 用于保存一个整数 11、BigIntegerField \u0026mdash;长整形\ninteger_field_ranges = {\r\u0026#39;SmallIntegerField\u0026#39;: (-32768, 32767),\r\u0026#39;IntegerField\u0026#39;: (-2147483648, 2147483647),\r\u0026#39;BigIntegerField\u0026#39;: (-9223372036854775808, 9223372036854775807),\r\u0026#39;PositiveSmallIntegerField\u0026#39;: (0, 32767),\r\u0026#39;PositiveIntegerField\u0026#39;: (0, 2147483647),\r} 12、IPAddressField \u0026mdash;字符串类型（ip4正则表达式） 一个字符串形式的 IP 地址， (如 “202.1241.30″)。 13、GenericIPAddressField \u0026mdash;字符串类型（ip4和ip6是可选的） 参数protocol可以是：both、ipv4、ipv6 验证时，会根据设置报错 14、NullBooleanField \u0026mdash;允许为空的布尔类型 类似 BooleanField， 不过允许 NULL 作为其中一个选项。 推荐使用这个字段而不要用 BooleanField 加 null=True 选项。 admin 用一个选择框 (三个可选择的值： “Unknown”， “Yes” 和 “No” ) 来表示这种字段数据。 15、PositiveIntegerField \u0026mdash;正Integer 类似 IntegerField， 但取值范围为非负整数（这个字段应该是允许0值的…可以理解为无符号整数） 16、PositiveSmallIntegerField \u0026mdash;正smallInteger 正小整型字段，类似 PositiveIntegerField， 取值范围较小(数据库相关)SlugField“Slug” 是一个报纸术语。 slug 是某个东西的小小标记(短签)， 只包　含字母，数字，下划线和连字符。它们通常用于URLs。 若你使用 Django 开发版本，你可以指定 maxlength。 若 maxlength 未指定， Django 会使用默认长度： 50，它接受一个额外的参数： prepopulate_from: 来源于slug的自动预置列表 17、SlugField \u0026mdash;减号、下划线、字母、数字 它们通常用于URLs。 18、SmallIntegerField \u0026mdash;数字 数据库中的字段有：tinyint、smallint、int、bigint. 类似 IntegerField， 不过只允许某个取值范围内的整数。(依赖数据库) 19、TextField \u0026mdash;字符串=longtext ，一个容量很大的文本字段， admin 管理界面用 多行编辑框表示该字段数据。 \u003cstrong\u003e20、TimeField\u003c/strong\u003e \u0026mdash;时间 HH:MM[:ss[.uuuuuu]] 时间字段，类似于 DateField 和 DateTimeField。 \u003cstrong\u003e21、URLField\u003c/strong\u003e \u0026mdash;字符串，地址正则表达式 用于保存URL。若 verify_exists 参数为 True (默认)， 给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应). \u003cstrong\u003e22、BinaryField\u003c/strong\u003e \u0026mdash;二进制 \u003cstrong\u003e23、ImageField\u003c/strong\u003e \u0026mdash;图片 类似 FileField， 不过要校验上传对象是否是一个合法图片。用于保存图像文件的字段。其基本用法和特性与FileField一样，只不过多了两个属性height和width。默认情况下，该字段在HTML中表现为一个ClearableFileInput标签。在数据库内，我们实际保存的是一个字符串类型，默认最大长度100，可以通过max_length参数自定义。真实的图片是保存在服务器的文件系统内的。 **height_field参数：**保存有图片高度信息的模型字段名。width_field参数：保存有图片宽度信息的模型字段名。 使用Django的ImageField需要提前安装pillow模块，pip install pillow即可。 \u003cstrong\u003e使用FileField或者ImageField字段的步骤：\u003c/strong\u003e 在settings文件中，配置MEDIA_ROOT，作为你上传文件在服务器中的基本路径（为了性能考虑，这些文件不会被储存在数据库中）。再配置个MEDIA_URL，作为公用URL，指向上传文件的基本路径。请确保Web服务器的用户账号对该目录具有写的权限。 添加FileField或者ImageField字段到你的模型中，定义好upload_to参数，文件最终会放在MEDIA_ROOT目录的“upload_to”子目录中。 所有真正被保存在数据库中的，只是指向你上传文件路径的字符串而已。可以通过url属性，在Django的模板中方便的访问这些文件。例如，假设你有一个ImageField字段，名叫mug_shot，那么在Django模板的HTML文件中，可以使用{{object.mug_shot.url}}来获取该文件。其中的object用你具体的对象名称代替。 可以通过name和size属性，获取文件的名称和大小信息。\n\u003cp\u003e\u003cstrong\u003e24、FilePathField\u003c/strong\u003e \u0026mdash;选择指定目录按限制规则选择文件，有三个参数可选， 其中”path”必需的，这三个参数可以同时使用， 参数描述： path：必需参数，一个目录的绝对文件系统路径。 FilePathField 据此得到可选项目。 Example： “/home/images”； match：可选参数， 一个正则表达式， 作为一个字符串， FilePathField 将使用它过滤文件名。 注意这个正则表达式只会应用到 base filename 而不是路径全名。 Example： “foo。\u003cem\u003e\\。txt^”， 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif； recursive：可选参数， 是否包括 path 下全部子目录，True 或 False，默认值为 False。 match 仅应用于 base filename， 而不是路径全名。 如：FilePathField(path=”/home/images”， match=”foo.\u003c/em\u003e”， recursive=True)…会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif \u003cstrong\u003e25、FileField\u003c/strong\u003e \u0026mdash;文件上传字段。 要求一个必须有的参数： upload_to， 一个用于保存上载文件的本地文件系统路径。 这个路径必须包含 strftime formatting， 该格式将被上载文件的 date/time 替换(so that uploaded files don’t fill up the given directory)。在一个 model 中使用 FileField 或 ImageField 需要以下步骤：在你的 settings 文件中， 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件。 (出于性能考虑，这些文件并不保存到数据库。) 定义 MEDIA_URL 作为该目录的公共 URL。 要确保该目录对 WEB 服务器用户帐号是可写的。在你的 model 中添加 FileField 或 ImageField， 并确保定义了 upload_to 选项，以告诉 Django 使用 MEDIA_ROOT 的哪个子目录保存上传文件。你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT)。 出于习惯你一定很想使用 Django 提供的 get_\u003cfieldname\u003e_url 函数。举例来说，如果你的 ImageField 叫作 mug_shot， 你就可以在模板中以 {{ object。get_mug_shot_url }} 这样的方式得到图像的绝对路径。 \u003cstrong\u003e26、PhoneNumberField\u003c/strong\u003e \u0026mdash;一个带有合法美国风格电话号码校验的 CharField(格式：XXX-XXX-XXXX) \u003cstrong\u003e27、USStateField\u003c/strong\u003e \u0026mdash;美国州名缩写，由两个字母组成（天朝人民无视）。 \u003cstrong\u003e28、XMLField\u003c/strong\u003e \u0026mdash;XML字符字段，校验值是否为合法XML的 TextField，必须提供参数： schema_path：校验文本的 RelaxNG schema 的文件系统路径。\n\u003cp\u003e\u003cstrong\u003e二、常用选项参数意义\u003c/strong\u003e\n\u003cp\u003e\u003cstrong\u003e1、null\u003c/strong\u003e 数据库中字段是否可以为空（null=True） \u003cstrong\u003e2、db_column\u003c/strong\u003e 数据库中字段的列名(db_column=\u0026ldquo;test\u0026rdquo;) \u003cstrong\u003e3、db_tablespace\u003c/strong\u003e \u003cstrong\u003e4、default\u003c/strong\u003e 数据库中字段的默认值 \u003cstrong\u003e5、primary_key\u003c/strong\u003e 数据库中字段是否为主键(primary_key=True) \u003cstrong\u003e6、db_index\u003c/strong\u003e 数据库中字段是否可以建立索引(db_index=True) \u003cstrong\u003e7、unique\u003c/strong\u003e 数据库中字段是否可以建立唯一索引(unique=True) \u003cstrong\u003e8、unique_for_date\u003c/strong\u003e 数据库中字段【日期】部分是否可以建立唯一索引 \u003cstrong\u003e9、unique_for_month\u003c/strong\u003e 数据库中字段【月】部分是否可以建立唯一索引 \u003cstrong\u003e10、unique_for_year\u003c/strong\u003e 数据库中字段【年】部分是否可以建立唯一索引 \u003cstrong\u003e11、auto_now\u003c/strong\u003e 更新时自动更新当前时间 \u003cstrong\u003e12、auto_now_add\u003c/strong\u003e 创建时自动更新当前时间 \u003cstrong\u003e13、verbose_name\u003c/strong\u003e Admin中显示的字段名称 \u003cstrong\u003e14、blankAdmin\u003c/strong\u003e 中是否允许用户输入为空表单提交时可以为空 \u003cstrong\u003e15、editableAdmin\u003c/strong\u003e 中是否可以编辑 \u003cstrong\u003e16、help_textAdmin\u003c/strong\u003e 中该字段的提示信息 \u003cstrong\u003e17choicesAdmin\u003c/strong\u003e 中显示选择框的内容，用不变动的数据放在内存中从而避免跨表操作 如：\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esex=models.IntegerField(choices=[(0,\u0026#39;男\u0026#39;),(1,\u0026#39;女\u0026#39;),],default=1) \u003c/code\u003e\u003c/pre\u003e\u003cp\u003eerror_messages自定义错误信息（字典类型），从而定制想要显示的错误信息； 字典健：null,blank,invalid,invalid_choice,unique,andunique_for_date 如：{\u0026rsquo;null\u0026rsquo;:\u0026ldquo;不能为空.\u0026rdquo;,\u0026lsquo;invalid\u0026rsquo;:\u0026lsquo;格式错误\u0026rsquo;} \u003cstrong\u003e18、validators\u003c/strong\u003e 自定义错误验证（列表类型），从而定制想要的验证规则\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efrom django.core.validators import RegexValidator\rfrom django.core.validators import EmailValidator,URLValidator,DecimalValidator,\rMaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator\r如：\rtest = models.CharField(\rmax_length=32,\rerror_messages={\r\u0026#39;c1\u0026#39;: \u0026#39;优先错信息1\u0026#39;,\r\u0026#39;c2\u0026#39;: \u0026#39;优先错信息2\u0026#39;,\r\u0026#39;c3\u0026#39;: \u0026#39;优先错信息3\u0026#39;,\r},\rvalidators=[\rRegexValidator(regex=\u0026#39;root_\\d+\u0026#39;, message=\u0026#39;错误了\u0026#39;, code=\u0026#39;c1\u0026#39;),\rRegexValidator(regex=\u0026#39;root_112233\\d+\u0026#39;, message=\u0026#39;又错误了\u0026#39;, code=\u0026#39;c2\u0026#39;),\rEmailValidator(message=\u0026#39;又错误了\u0026#39;, code=\u0026#39;c3\u0026#39;), ]\r) \u003c/code\u003e\u003c/pre\u003e","permalink":"https://leochu.work/blog/tech/python/django/%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B%E5%AD%97%E6%AE%B5%E5%8F%8A%E5%B1%9E%E6%80%A7%E8%AF%A6%E8%A7%A3/","summary":"\u003ch1 id=\"数据模型字段及属性详解\"\u003e数据模型字段及属性详解\u003c/h1\u003e\n\u003cp\u003e在设计数据模型的时候，我们需要根据不同的需求，设计不同的表和不同的字段。不同的字段也可以设置不同的参数。\u003c/p\u003e\n\u003cp\u003e一个模型（model）就是一个单独的、确定的数据的信息源，包含了数据的字段和操作方法。Django是通过Model操作数据库，不管你数据库的类型是MySql或者Sqlite，Django它自动帮你生成相应数据库类型的SQL语句，所以不需要关注SQL语句和类型，对数据的操作Django帮我们自动完成。只要回写Model就可以了！\u003c/p\u003e\n\u003cp\u003edjango根据代码中定义的类来自动生成数据库表。我们写的类表示数据库的表，如果根据这个类创建的对象是数据库表里的一行数据，对象.id 对象.value是每一行里的数据。\u003c/p\u003e\n\u003cp\u003e基本的原则如下：\n每个模型在Django中的存在形式为一个Python类\n每个模型都是django.db.models.Model的子类\n模型里的每个类代表数据库中的一个表\n模型的每个字段（属性）代表数据表的某一列\nDjango将自动为你生成数据库访问API\u003c/p\u003e\n\u003cp\u003eDjango这种操作数据库的方式，我们把它叫做：\u003cstrong\u003e关系对象映射（Object Relational Mapping，简称ORM）。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e我们之前有在\u003ca href=\"https://www.django.cn/course/show-11.html\"\u003e管理后台与model模型\u003c/a\u003e这文章里简单的接触过。里面的models代码：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eclass Category(models.Model):\r\n    name = models.CharField(\u0026#39;分类\u0026#39;,max_length=100)\r\n    \r\nclass Tags(models.Model):\r\n    name = models.CharField(\u0026#39;标签\u0026#39;,max_length=100)\r\n    \r\nclass Article(models.Model):\r\n    title = models.CharField(\u0026#39;标题\u0026#39;,max_length=70)\r\n    intro = models.TextField(\u0026#39;摘要\u0026#39;, max_length=200, blank=True)\r\n    category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name=\u0026#39;分类\u0026#39;, default=\u0026#39;1\u0026#39;)\r\n    tags = models.ManyToManyField(Tags, blank=True)\r\n    body = models.TextField()\r\n    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=\u0026#39;作者\u0026#39;)\r\n    created_time = models.DateTimeField(\u0026#39;发布时间\u0026#39;,auto_now_add=True)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e字段是模型中最重要的内容之一，也是唯一必须的部分。字段在Python中表现为一个类属性，体现了数据表中的一个列。Django不允许下面两种字段名：1、与Python关键字冲突。2、字段名中不能有两个以上下划线在一起，因为两个下划线是Django的查询语法。也不要使用clean、save、delete等Django内置的模型API名字，防止命名冲突。\u003c/p\u003e\n\u003cp\u003e每一个字段都是一个类属性，每个类属性表示数据表中的一个列。上面那个models生成数据后，在数据库里是这样的：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"1.jpg\" loading=\"lazy\" src=\"https://www.django.cn/media/upimg/1_20180727113014_518.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e表名由Django自动生成，例如我们的Article类会自动生成为blog_Article，默认格式为“应用名称+下划线+小写类名”。如果你不指定主键，Django默认自动创建自增主键id。每个APP应用都有独立属于自己的模型，创建了APP之后，在使用它之前，你需要先在settings文件中的INSTALLED_APPS 处，注册models.py文件所在的app名称。看清楚了，是注册app，不是模型，也不是models.py。关于settings，我们前面也有讲解过，具体可查看文章：\u003ca href=\"https://www.django.cn/course/show-10.html\"\u003e全局配置settings详解\u003c/a\u003e。例如我们创建了名为blog的应用APP则在settings这样写：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eINSTALLED_APPS = [\r\n#...\r\n\u0026#39;blog\u0026#39;,  #我们要添加的应用名\r\n#...\r\n]\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e当你每次对模型进行增、删、修改时，我们都需要执行python manage.py makemigrations请务必执命令，然后再执行：python manage.py migrate，让操作实际应用到数据库上。\u003c/p\u003e","title":"数据模型字段及属性详解"},{"content":"个人理解: 有点类似依赖冲突,循环依赖\n传递性依赖 遮蔽 版本冲突 复杂的类加载 ","permalink":"https://leochu.work/blog/tech/engineering/%E6%A8%A1%E5%9D%97%E5%9C%B0%E7%8B%B1/","summary":"\u003cp\u003e个人理解: 有点类似依赖冲突,循环依赖\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e传递性依赖\u003c/li\u003e\n\u003cli\u003e遮蔽\u003c/li\u003e\n\u003cli\u003e版本冲突\u003c/li\u003e\n\u003cli\u003e复杂的类加载\u003c/li\u003e\n\u003c/ul\u003e","title":"模块地狱"},{"content":"\n","permalink":"https://leochu.work/blog/tech/engineering/%E5%BC%80%E6%BA%90%E8%AE%B8%E5%8F%AF%E8%AF%81/","summary":"\u003cp\u003e\u003cimg alt=\"Pasted image 20230426182936.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230426182936.png\"\u003e\u003c/p\u003e","title":"开源许可证"},{"content":"1. 开发流程 **说明：**此开发流程符合大部分需求场景，少数需求可能流程会有所增减。\n2. 流程说明 2.1 需求评审 详细了解本次需求的背景和细节，并讨论需求的可行性。\n比如： 当前需求是否明确? 新需求是什么?\n需求背景及解决什么问题?\n如果需求复杂程度大，提出是否需要分阶段实施?\n需求完成的期望指标是什么? 如何评判? 如何交付?\n技术能否实现、逻辑是否有问题？\n进入开发阶段测试，遇到问题与谁对接，来协调解决问题?\n如果需求复杂请让需求方拆解需求去描述要达到的目的。\n如果需求未做到目的明确，清晰合理，请让需求方解决疑问点。\n需求对接完，请邮件告知。\n2.2 技术评审 阐述详细的技术实现方案，评估一下是否有不合理之处，比如表结构如何设计、接口怎么定义、有没有技术难点等。\n2.3 开发 \u0026amp; 测试排期 评估具体的工作量，根据工作量安排各个步骤要完成的截止日期。\n2.4 输出开发设计文档 此文档放在 confluence 上，大致包含需求背景、开发设计（技术方案）、排期计划、开发流程的具体内容、附录等。需求背景、开发设计（技术方案）、排期计划要在开发测试开始前书写完成，其他部分可以根据进度补充完善。\n2.5 开发 \u0026amp; 测试 根据技术方案和排期，具体实现。\n2.6 需求方验收 验收阶段，开发如有 bug 修改 bug，可以提前提供部分样例结果进行预验收。\n2.7 生产部署 根据具体环境部署。\n2.8 需求复盘总结 复盘一下问题主要出在哪里，以后如何规避，哪些优点可以以后借鉴等。\n3. 其他注意事项 3.1 关于对接和验收阶段 首版需求已评审过的前提下，在此阶段如果不可避免出现需求变更频繁和很多不确定的时候，要明确告知需求方批量提供需求点或完善点，避免随时出现一个接一个，导致此需求一直完不成。\n最好补充一次批量提完，如果第两个及以上批次，建议需求当需求变更处理。\n开发要合理评估，尽量避免开发不合理需求。\n4. 附件 Java 开发手册:!泰山版.pdf\n","permalink":"https://leochu.work/blog/tech/engineering/%E5%BC%80%E5%8F%91%E6%B5%81%E7%A8%8B/","summary":"\u003ch2 id=\"1-开发流程\"\u003e1. 开发流程\u003c/h2\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230327103333.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327103333.png\"\u003e\u003c/p\u003e\n\u003cp\u003e**说明：**此开发流程符合大部分需求场景，少数需求可能流程会有所增减。\u003c/p\u003e\n\u003ch2 id=\"2-流程说明\"\u003e2. 流程说明\u003c/h2\u003e\n\u003chr\u003e\n\u003ch4 id=\"21-需求评审\"\u003e2.1 需求评审\u003c/h4\u003e\n\u003cp\u003e详细了解本次需求的背景和细节，并讨论需求的可行性。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e比如：\u003c/strong\u003e     \u003c/p\u003e\n\u003cp\u003e当前需求是否明确? 新需求是什么?\u003cbr\u003e\n需求背景及解决什么问题?\u003cbr\u003e\n如果需求复杂程度大，提出是否需要分阶段实施?\u003cbr\u003e\n需求完成的期望指标是什么? 如何评判? 如何交付?\u003cbr\u003e\n技术能否实现、逻辑是否有问题？\u003cbr\u003e\n进入开发阶段测试，遇到问题与谁对接，来协调解决问题?\u003cbr\u003e\n如果需求复杂请让需求方拆解需求去描述要达到的目的。\u003cbr\u003e\n如果需求未做到目的明确，清晰合理，请让需求方解决疑问点。\u003cbr\u003e\n需求对接完，请邮件告知。\u003c/p\u003e\n\u003ch4 id=\"22-技术评审\"\u003e2.2 技术评审\u003c/h4\u003e\n\u003cp\u003e阐述详细的技术实现方案，评估一下是否有不合理之处，比如表结构如何设计、接口怎么定义、有没有技术难点等。\u003c/p\u003e\n\u003ch4 id=\"23-开发--测试排期\"\u003e2.3 开发 \u0026amp; 测试排期\u003c/h4\u003e\n\u003cp\u003e评估具体的工作量，根据工作量安排各个步骤要完成的截止日期。\u003c/p\u003e\n\u003ch4 id=\"24-输出开发设计文档\"\u003e2.4 输出开发设计文档\u003c/h4\u003e\n\u003cp\u003e此文档放在 confluence 上，大致包含需求背景、开发设计（技术方案）、排期计划、开发流程的具体内容、附录等。\u003cstrong\u003e需求背景、开发设计（技术方案）、排期计划要在开发测试开始前书写完成，其他部分可以根据进度补充完善。\u003c/strong\u003e\u003c/p\u003e\n\u003ch4 id=\"25-开发--测试\"\u003e2.5 开发 \u0026amp; 测试\u003c/h4\u003e\n\u003cp\u003e根据技术方案和排期，具体实现。\u003c/p\u003e\n\u003ch4 id=\"26-需求方验收\"\u003e2.6 需求方验收\u003c/h4\u003e\n\u003cp\u003e验收阶段，开发如有 bug 修改 bug，可以提前提供部分样例结果进行预验收。\u003c/p\u003e\n\u003ch4 id=\"27-生产部署\"\u003e2.7 生产部署\u003c/h4\u003e\n\u003cp\u003e根据具体环境部署。\u003c/p\u003e\n\u003ch4 id=\"28-需求复盘总结\"\u003e2.8 需求复盘总结\u003c/h4\u003e\n\u003cp\u003e复盘一下问题主要出在哪里，以后如何规避，哪些优点可以以后借鉴等。\u003c/p\u003e\n\u003ch2 id=\"3-其他注意事项\"\u003e3. 其他注意事项\u003c/h2\u003e\n\u003chr\u003e\n\u003ch4 id=\"31-关于对接和验收阶段\"\u003e3.1 关于对接和验收阶段\u003c/h4\u003e\n\u003cp\u003e首版需求已评审过的前提下，在此阶段如果不可避免出现需求变更频繁和很多不确定的时候，要明确告知需求方批量提供需求点或完善点，避免随时出现一个接一个，导致此需求一直完不成。\u003c/p\u003e\n\u003cp\u003e最好补充一次批量提完，如果第两个及以上批次，建议需求当需求变更处理。\u003c/p\u003e\n\u003cp\u003e开发要合理评估，尽量避免开发不合理需求。\u003c/p\u003e\n\u003ch2 id=\"4-附件\"\u003e4. 附件\u003c/h2\u003e\n\u003chr\u003e\n\u003cp\u003eJava 开发手册:!泰山版.pdf\u003c/p\u003e","title":"开发流程"},{"content":"关于技术债务的文章，尽管实践中会堆积技术债，但这个概念并不在我们的工作中频繁出现。这篇文章就系统性讲讲技术债，让大家避免知其然，不知其所以然。\n一、技术债是什么\n技术负债（英语：Technical debt），又译技术债，也称为设计负债（design debt）、代码负债（code debt），是编程及软件工程中的借鉴了财务债务的系统隐喻。指开发人员为了加速软件开发，在应该采用最佳方案时进行了妥协，改用了短期内能加速软件开发的方案，从而在未来给自己带来的额外开发负担。这种技术上的选择，就像一笔债务一样，虽然眼前看起来可以得到好处，但必须在未来偿还。软件工程师必须付出额外的时间和精力持续修复之前的妥协所造成的问题及副作用，或是进行重构，把架构改善为最佳实现方式。\n1992 年，沃德 · 坎宁安首次将技术的复杂比作为负债。第一次发布代码，就好比借了一笔钱。只要通过不断重写来偿还债务，小额负债便可以加速开发。但久未偿还债务会引发危险。复用马马虎虎的代码，类似于负债的利息。整个部门有可能因为松散的实现，不完全的面向对象的设计或其他诸如此类的负债而陷入窘境。\n二、技术债表现\n技术债与其他债务本身一样，是一种透支行为，通过牺牲未来来满足当下的一些需求。也跟其他债务一样，技术债务也有利息，而且随着时间利滚利，会成为埋在项目里的定时炸弹。如果产品长期的可持续的发展，那么技术债的重要性是毋庸置疑的。\n技术债务的本质是产品的结构阻碍了进步，表现出来的症状有：无法轻易重构产品以满足市场需求；组件之间的依赖性过多，体系结构不良；缺陷太多，结构不良；难以理解，难以改变。\n技术债务的后果有偿还技术债务造成时间浪费，员工满意度降低带来士气低落，因解决遗留代码问题而错过优质项目造成人才流失，产品质量降低造成客户满意度下降，技术债务限制创新能力、扼杀创造性等诸多问题。\n技术债不单单是技术债，它就像一个垃圾堆，久而久之不处理，慢慢周围就会产生更多的垃圾，因此产生的 “破窗效应” 更加是会对未来的项目环境造成很大的影响，大家也会逐渐丧失维护环境的信心。所以在讨论技术债的时候不仅仅是讨论技术债本身，技术债对团队追求质量的信心、对大家维护环境整洁的积极性都会造成很大的影响。\nMartinFowler 把技术债分为四个象限，如下图所示：\n三、技术债产生的原因\n●业务压力：为了满足业务的快速要求，在必要的修改并没有完成时就匆匆发布，这些未完成的修改就形成了技术负债。\n●缺少过程和理解：业务人员不清楚不理解技术负债的概念，在决策时就不会考虑到其带来的影响。\n●模块之间解耦不够：功能没有模块化，软件柔性不够，不足适应业务变化的要求。\n●缺少配套的自动化测试：导致鼓励快速而风险很大的 “创可贴” 式的 BUG 修复。\n●缺少必要文档：需求和代码都没有必要的支撑性文档或注释。\n●缺少协作：组织中的知识共享和业务效率较低，或者初级开发者缺少必要的指导。\n●重构延迟：在开发的过程中，某些部分的代码会变得难以控制，这时候就需要进行重构，以适应将来的需求变化。重构越是推迟，这些已有的代码被使用的越多，形成的技术负债就越多，直到重构完成。\n●不遵循标准或最佳实践：忽略了已有的业界标准、框架、技术和最佳实践。\n●缺少相关技能：开发人员有时候技能缺失，并不知道如何编写优雅的代码。\n四、如何 “还债”？\n技术债可视化 尽可能公开技术债，一开始就与团队，利益相关方一起权衡利弊，并明确告知影响与解决方案。平等沟通，相互理解。让技术债在业务层面、技术层面可见。\n可以在组织资产负债表的财产债中新增两列：短期技术债和长期技术债。还可以用用跟踪开发速率的方式体现技术债对于产品的影响。\n不同的债要对症下药 技术债的状态可以分类为偶然技术债、已知技术债和目标技术债。\n偿还技术债时应遵循如下原则：\n1）确定已知技术债必须还。\n2）发现偶然技术债，立即还。\n3）每个冲刺确定一定数量的已知技术债作为目标技术债，在当前冲刺中偿还。\n4）无需偿还的技术债是行将就木的产品、一次性原型和短命产品。\n五、如何避免 “欠债”\n与其后期吭哧吭哧还债填坑，不如从一开始就尽量避免欠下技术债务。\n避免使用过时的技术 遗留应用程序、过时的技术以及不同的平台和流程可能会使组织陷入沉重的技术债务，迫使其推迟基本的现代化计划。DNS 和流量管理技术提供商 NS1 的联合创始人兼首席执行官 Kris Beevers 说：“技术债务将大量金钱和宝贵的时间浪费在系统和应用程序上，而这些系统和应用程序并不是为现代企业所需的规模和速度而打造的。”\n旧资产和老方法也往往充斥着安全漏洞，难以集成和自动化，并且很可能不再更新。 Beevers 指出：“寻找人才来管理基于复杂或过时的代码构建的遗留应用程序也是一个日益严峻的难题。坚持采用过时技术不仅会消耗宝贵的预算，而且还会阻碍公司创新和保持竞争力的能力。”\n参考敏捷实践 有越来越多的组织渐渐接受敏捷软件开发，这是将方法交给协作、自行组织的团队和跨职能团队的一系列方法和实践。如果这种方法得到严格应用，敏捷开发使组织可以避免技术债务，其方法是快速且以迭代的方式创建和发布新产品。Dodd 说：“这一过程将新产品和新功能尽快并逐步地交到用户手中。” 随着新版本的交付，各种改进和问题都得到了解决，这使技术债务的积累不太可能产生。\n敏捷方法认识到项目在生命周期中从未真正完成过，并且也从来都不是完美的。“同时，敏捷方法专注于…… 针对能力和质量的简化了的开发”，Dodd 说。重要功能往往要频繁地开发，测试并投入生产。敏捷团队可能不会发布软件的 “全面（Big Bang）” 方法，而是每年发布几次重大升级。Dodd 指出：“这可以使产品保持相当平稳的发展，还可以帮助用户避免重大的中断事件。”\n遵循代码规范 是否遵守了编码规范，是否遵循最佳实践也是影响技术债的一个方面。代码规范在研发项目团队中有着重要作用，团队统一代码规范，有助于提升代码可读性以及工作效率。统一的\n代码规范是代码集体所有权的基础，会让结对编程更容易实行，对团队来说更易内部轮岗、获得晋升。代码规范和代码质量工具有助于发现代码质量方面的技术债务。\n亡羊补牢，为时未晚。从现在开始把偿还技术债务纳入 backlog，把避免产生技债务作为工作准则，相信不会出现被技术债务压垮崩溃的情况。\n","permalink":"https://leochu.work/blog/tech/engineering/%E6%8A%80%E6%9C%AF%E5%80%BA/","summary":"\u003cp\u003e关于技术债务的文章，尽管实践中会堆积技术债，但这个概念并不在我们的工作中频繁出现。这篇文章就系统性讲讲技术债，让大家避免知其然，不知其所以然。\u003c/p\u003e\n\u003cp\u003e一、技术债是什么\u003c/p\u003e\n\u003cp\u003e技术负债（英语：Technical debt），又译技术债，也称为设计负债（design debt）、代码负债（code debt），是编程及软件工程中的借鉴了财务债务的系统隐喻。指开发人员为了加速软件开发，在应该采用最佳方案时进行了妥协，改用了短期内能加速软件开发的方案，从而在未来给自己带来的额外开发负担。这种技术上的选择，就像一笔债务一样，虽然眼前看起来可以得到好处，但必须在未来偿还。软件工程师必须付出额外的时间和精力持续修复之前的妥协所造成的问题及副作用，或是进行重构，把架构改善为最佳实现方式。\u003c/p\u003e\n\u003cp\u003e1992 年，沃德 · 坎宁安首次将技术的复杂比作为负债。第一次发布代码，就好比借了一笔钱。只要通过不断重写来偿还债务，小额负债便可以加速开发。但久未偿还债务会引发危险。复用马马虎虎的代码，类似于负债的利息。整个部门有可能因为松散的实现，不完全的面向对象的设计或其他诸如此类的负债而陷入窘境。\u003c/p\u003e\n\u003cp\u003e二、技术债表现\u003c/p\u003e\n\u003cp\u003e技术债与其他债务本身一样，是一种透支行为，通过牺牲未来来满足当下的一些需求。也跟其他债务一样，技术债务也有利息，而且随着时间利滚利，会成为埋在项目里的定时炸弹。如果产品长期的可持续的发展，那么技术债的重要性是毋庸置疑的。\u003c/p\u003e\n\u003cp\u003e技术债务的本质是产品的结构阻碍了进步，表现出来的症状有：无法轻易重构产品以满足市场需求；组件之间的依赖性过多，体系结构不良；缺陷太多，结构不良；难以理解，难以改变。\u003c/p\u003e\n\u003cp\u003e技术债务的后果有偿还技术债务造成时间浪费，员工满意度降低带来士气低落，因解决遗留代码问题而错过优质项目造成人才流失，产品质量降低造成客户满意度下降，技术债务限制创新能力、扼杀创造性等诸多问题。\u003c/p\u003e\n\u003cp\u003e技术债不单单是技术债，它就像一个垃圾堆，久而久之不处理，慢慢周围就会产生更多的垃圾，因此产生的 “破窗效应” 更加是会对未来的项目环境造成很大的影响，大家也会逐渐丧失维护环境的信心。所以在讨论技术债的时候不仅仅是讨论技术债本身，技术债对团队追求质量的信心、对大家维护环境整洁的积极性都会造成很大的影响。\u003c/p\u003e\n\u003cp\u003eMartinFowler 把技术债分为四个象限，如下图所示：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/eac4b74543a98226442e205e821997094b90eb7b.jpeg\"\u003e\u003c/p\u003e\n\u003cp\u003e三、技术债产生的原因\u003c/p\u003e\n\u003cp\u003e●业务压力：为了满足业务的快速要求，在必要的修改并没有完成时就匆匆发布，这些未完成的修改就形成了技术负债。\u003c/p\u003e\n\u003cp\u003e●缺少过程和理解：业务人员不清楚不理解技术负债的概念，在决策时就不会考虑到其带来的影响。\u003c/p\u003e\n\u003cp\u003e●模块之间解耦不够：功能没有模块化，软件柔性不够，不足适应业务变化的要求。\u003c/p\u003e\n\u003cp\u003e●缺少配套的自动化测试：导致鼓励快速而风险很大的 “创可贴” 式的 BUG 修复。\u003c/p\u003e\n\u003cp\u003e●缺少必要文档：需求和代码都没有必要的支撑性文档或注释。\u003c/p\u003e\n\u003cp\u003e●缺少协作：组织中的知识共享和业务效率较低，或者初级开发者缺少必要的指导。\u003c/p\u003e\n\u003cp\u003e●重构延迟：在开发的过程中，某些部分的代码会变得难以控制，这时候就需要进行重构，以适应将来的需求变化。重构越是推迟，这些已有的代码被使用的越多，形成的技术负债就越多，直到重构完成。\u003c/p\u003e\n\u003cp\u003e●不遵循标准或最佳实践：忽略了已有的业界标准、框架、技术和最佳实践。\u003c/p\u003e\n\u003cp\u003e●缺少相关技能：开发人员有时候技能缺失，并不知道如何编写优雅的代码。\u003c/p\u003e\n\u003cp\u003e四、如何 “还债”？\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e技术债可视化\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e尽可能公开技术债，一开始就与团队，利益相关方一起权衡利弊，并明确告知影响与解决方案。平等沟通，相互理解。让技术债在业务层面、技术层面可见。\u003c/p\u003e\n\u003cp\u003e可以在组织资产负债表的财产债中新增两列：短期技术债和长期技术债。还可以用用跟踪开发速率的方式体现技术债对于产品的影响。\u003c/p\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e不同的债要对症下药\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e技术债的状态可以分类为偶然技术债、已知技术债和目标技术债。\u003c/p\u003e\n\u003cp\u003e偿还技术债时应遵循如下原则：\u003c/p\u003e\n\u003cp\u003e1）确定已知技术债必须还。\u003c/p\u003e\n\u003cp\u003e2）发现偶然技术债，立即还。\u003c/p\u003e\n\u003cp\u003e3）每个冲刺确定一定数量的已知技术债作为目标技术债，在当前冲刺中偿还。\u003c/p\u003e\n\u003cp\u003e4）无需偿还的技术债是行将就木的产品、一次性原型和短命产品。\u003c/p\u003e\n\u003cp\u003e五、如何避免 “欠债”\u003c/p\u003e\n\u003cp\u003e与其后期吭哧吭哧还债填坑，不如从一开始就尽量避免欠下技术债务。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e避免使用过时的技术\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e遗留应用程序、过时的技术以及不同的平台和流程可能会使组织陷入沉重的技术债务，迫使其推迟基本的现代化计划。DNS 和流量管理技术提供商 NS1 的联合创始人兼首席执行官 Kris Beevers 说：“技术债务将大量金钱和宝贵的时间浪费在系统和应用程序上，而这些系统和应用程序并不是为现代企业所需的规模和速度而打造的。”\u003c/p\u003e\n\u003cp\u003e旧资产和老方法也往往充斥着安全漏洞，难以集成和自动化，并且很可能不再更新。 Beevers 指出：“寻找人才来管理基于复杂或过时的代码构建的遗留应用程序也是一个日益严峻的难题。坚持采用过时技术不仅会消耗宝贵的预算，而且还会阻碍公司创新和保持竞争力的能力。”\u003c/p\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e参考敏捷实践\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e有越来越多的组织渐渐接受敏捷软件开发，这是将方法交给协作、自行组织的团队和跨职能团队的一系列方法和实践。如果这种方法得到严格应用，敏捷开发使组织可以避免技术债务，其方法是快速且以迭代的方式创建和发布新产品。Dodd 说：“这一过程将新产品和新功能尽快并逐步地交到用户手中。” 随着新版本的交付，各种改进和问题都得到了解决，这使技术债务的积累不太可能产生。\u003c/p\u003e\n\u003cp\u003e敏捷方法认识到项目在生命周期中从未真正完成过，并且也从来都不是完美的。“同时，敏捷方法专注于…… 针对能力和质量的简化了的开发”，Dodd 说。重要功能往往要频繁地开发，测试并投入生产。敏捷团队可能不会发布软件的 “全面（Big Bang）” 方法，而是每年发布几次重大升级。Dodd 指出：“这可以使产品保持相当平稳的发展，还可以帮助用户避免重大的中断事件。”\u003c/p\u003e","title":"技术债"},{"content":"把项目的配置文件按运行环境做一下区分，比如开发环境和线上环境使用的使用不同的配置文件，这里我们基于项目的 resoures/ 目录来实现：\n# resoures 目录\r.\r├── env\r│ ├── config.dev.properties\r│ └── config.prod.properties\r└── config.properties 我们创建了一个 config.properties 文件来配置项目的常用配置数据，如， kafaka 、 redis 等连接配置等。\n然后创建一个 env 子目录，并创建两个环境对应的配置文件，我们希望不同的环境当中使用不同的配置。当然，光是创建这些文件，是无法让文件实现自动按照运行环境自动实现文件匹配的，我们还需要配置 pom.xml 文件：\n首先，找到 \u0026lt;build\u0026gt; 配置字段，添加文件路径：\n\u0026lt;build\u0026gt;\r\u0026lt;!-- Loading all ${} --\u0026gt;\r\u0026lt;filters\u0026gt;\r\u0026lt;filter\u0026gt;src/main/resources/env/config.${env}.properties\u0026lt;/filter\u0026gt;\r\u0026lt;/filters\u0026gt;\r\u0026lt;!-- Map ${} into resources --\u0026gt;\r\u0026lt;resources\u0026gt;\r\u0026lt;resource\u0026gt;\r\u0026lt;directory\u0026gt;src/main/resources\u0026lt;/directory\u0026gt;\r\u0026lt;filtering\u0026gt;true\u0026lt;/filtering\u0026gt;\r\u0026lt;includes\u0026gt;\r\u0026lt;include\u0026gt;*.properties\u0026lt;/include\u0026gt;\r\u0026lt;/includes\u0026gt;\r\u0026lt;/resource\u0026gt;\r\u0026lt;/resources\u0026gt;\r\u0026lt;!-- 省略之后若干行 --\u0026gt;\r\u0026lt;/build\u0026gt; 上述代码目的是告诉构建工具，在构建的时候需要加载 resoures 目录的配置文件参与构建，并且使用 ${env} 环境变量来决定具体加载的名称。\n因此，我们还需要指定 ${env} 的环境变量配置，在 \u0026lt;profiles\u0026gt; 标签下，移除默认的 \u0026lt;profile\u0026gt; 配置内容，新建两份环境配置 dev 和 prod 的 profile 文件配置：\n\u0026lt;profiles\u0026gt;\r\u0026lt;profile\u0026gt;\r\u0026lt;id\u0026gt;dev\u0026lt;/id\u0026gt;\r\u0026lt;properties\u0026gt;\r\u0026lt;env\u0026gt;dev\u0026lt;/env\u0026gt;\r\u0026lt;/properties\u0026gt; \u0026lt;!-- 省略若干 --\u0026gt;\r\u0026lt;/profile\u0026gt;\r\u0026lt;profile\u0026gt;\r\u0026lt;id\u0026gt;prod\u0026lt;/id\u0026gt;\r\u0026lt;properties\u0026gt;\r\u0026lt;env\u0026gt;prod\u0026lt;/env\u0026gt;\r\u0026lt;/properties\u0026gt;\r\u0026lt;!-- 省略若干 --\u0026gt;\r\u0026lt;/profile\u0026gt;\r\u0026lt;/profiles\u0026gt; 如此，当我们运行项目时，可以根据不同的环境选择不同的配置文件，以保证开发和线上环境彻底分开，当然你也可以举一反三的创建测试环境、预上线环境等等。\n","permalink":"https://leochu.work/blog/tech/engineering/maven%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/","summary":"\u003cp\u003e把项目的配置文件按运行环境做一下区分，比如开发环境和线上环境使用的使用不同的配置文件，这里我们基于项目的 \u003ccode\u003eresoures/\u003c/code\u003e 目录来实现：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# resoures 目录\r\n.\r\n├── env\r\n│   ├── config.dev.properties\r\n│   └── config.prod.properties\r\n└── config.properties\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e我们创建了一个 \u003ccode\u003econfig.properties\u003c/code\u003e 文件来配置项目的常用配置数据，如， \u003ccode\u003ekafaka\u003c/code\u003e 、 \u003ccode\u003eredis\u003c/code\u003e 等连接配置等。\u003c/p\u003e\n\u003cp\u003e然后创建一个 \u003ccode\u003eenv\u003c/code\u003e 子目录，并创建两个环境对应的配置文件，我们希望不同的环境当中使用不同的配置。当然，光是创建这些文件，是无法让文件实现自动按照运行环境自动实现文件匹配的，我们还需要配置 \u003ccode\u003epom.xml\u003c/code\u003e 文件：\u003c/p\u003e\n\u003cp\u003e首先，找到 \u003ccode\u003e\u0026lt;build\u0026gt;\u003c/code\u003e 配置字段，添加文件路径：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026lt;build\u0026gt;\r\n    \u0026lt;!-- Loading all ${} --\u0026gt;\r\n    \u0026lt;filters\u0026gt;\r\n        \u0026lt;filter\u0026gt;src/main/resources/env/config.${env}.properties\u0026lt;/filter\u0026gt;\r\n    \u0026lt;/filters\u0026gt;\r\n    \u0026lt;!-- Map ${} into resources --\u0026gt;\r\n    \u0026lt;resources\u0026gt;\r\n        \u0026lt;resource\u0026gt;\r\n            \u0026lt;directory\u0026gt;src/main/resources\u0026lt;/directory\u0026gt;\r\n            \u0026lt;filtering\u0026gt;true\u0026lt;/filtering\u0026gt;\r\n            \u0026lt;includes\u0026gt;\r\n                \u0026lt;include\u0026gt;*.properties\u0026lt;/include\u0026gt;\r\n            \u0026lt;/includes\u0026gt;\r\n        \u0026lt;/resource\u0026gt;\r\n    \u0026lt;/resources\u0026gt;\r\n    \u0026lt;!-- 省略之后若干行 --\u0026gt;\r\n\u0026lt;/build\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e上述代码目的是告诉构建工具，在构建的时候需要加载 \u003ccode\u003eresoures\u003c/code\u003e 目录的配置文件参与构建，并且使用 \u003ccode\u003e${env}\u003c/code\u003e 环境变量来决定具体加载的名称。\u003c/p\u003e\n\u003cp\u003e因此，我们还需要指定 \u003ccode\u003e${env}\u003c/code\u003e 的环境变量配置，在 \u003ccode\u003e\u0026lt;profiles\u0026gt;\u003c/code\u003e 标签下，移除默认的 \u003ccode\u003e\u0026lt;profile\u0026gt;\u003c/code\u003e 配置内容，新建两份环境配置 \u003ccode\u003edev\u003c/code\u003e 和 \u003ccode\u003eprod\u003c/code\u003e 的 \u003ccode\u003eprofile\u003c/code\u003e 文件配置：\u003c/p\u003e","title":"关于maven项目的配置文件"},{"content":"【策略模式】（Strategy Pattern） 其中的Context是一个所谓的“上下文”，不一定非得是个类，也可以只是一个函数/方法。最关键的是，我们其实根本不需要ConcreteStrategyX类，也不需要它们的对象。我们要的只是一个execute函数而已，我们甚至连execute这个函数名都不需要，只要能执行它就行。\n看看函数式编程是怎么玩的（这里以大家都熟悉的JavaScript为例）：\nfunction context(func) { // 做些什么... var result = func() // 有需要的话可以传参 // 再做些什么... } context(function() { // 做些什么... return 123 // 是否需要返回值看需求 }) 一个匿名函数参数func搞定。\nJava能搞出这种类图来，全都是因为Java没有一等公民的函数，所以函数必须依附于类（静态方法）或者对象（非静态方法）。而玩静态方法又没法玩多态，而且类不能当成参数传递给其他函数/方法，所以只能用对象携带方法。而对象的方法必须有个名称，为了统一，就叫execute。由于需要多态，所以我们必须提一个接口出来，在接口里声明execute的方法签名。所有这一切说白了都是为了讨好Java编译器，否则它会给你颜色（red）看。\n当然，自从Java有了函数式接口和lambda后一定程度上也能玩函数式编程了。\n【观察者模式】（Observer Pattern） 这个在JS里大家已经熟悉到不能再熟悉了：\nsomeButton.onclick = function(event) { // 处理点击事件 } 又是一个匿名函数搞定！上图里的Subject#attach在这里就是直接赋值。Subject#detach就是赋空值。Subject#notify就是调用一下匿名函数而已。而Observer#update就是匿名函数本身。\n【访问者模式】（Visitor Pattern） 能整成这样我也是服了。说白了不就是访问者需要判断一下元素类型嘛。直接套用策略模式，在匿名函数里if-else一下不就行了？（当然还有其他方式，比如引入一个工厂。有模式匹配的函数式编程语言如Haskell、Erlang、Elixir等玩起来更简单）。\n【装饰器模式】（Decorator Pattern） 这才是函数式编程的魅力所在！\nfunction core1(arg1, arg2) { // 做些啥 return 123 } function core2(arg) { // 做些啥 return 456 } function decorate(core) { // 做些准备工作 return function() { // 做些啥，甚至可以改变参数 var ret = core(...arguments) // 做些啥，甚至可以改变返回值 return ret } } var decorated1 = decorate(core1) var decorated2 = decorate(core2) 酷吧？又是匿名函数搞定！只不过这次的匿名函数是返回值。\n小结 很多设计模式，尤其是行为模式，都是为了弥补语言本身没有一等公民的函数而存在的。如果一个语言有一等公民的函数（比如JavaScript），可以砍掉大概一半的设计模式。\n题外话 设计模式，尤其是行为模式的核心思想其实是“依赖注入”。\n一说到依赖注入，很多人的第一反应是Spring，但其实不然。“依赖注入”用一句简单的话说，就是**“我需要但我不知道怎么造的东西，我找你要”**。而这个“东西”不一定是对象（当然，在Java这种语言里只能是对象），也可以是函数。至于“你怎么把这个东西给我”，就有很多方式了。比如在Spring里，可以通过暴露一个set方法给你调用，也可以通过构造方法的参数接收，甚至可以公开一个成员变量等你把东西塞过来（顺带说一句，这里就可以看出来为什么很多DI框架提倡使用构造方法参数注入，因为只有这种方式才能保证依赖别人的对象在正式提供服务之前，依赖已经注入了，而且不会导致cyclic dependency的问题），而在函数式编程里，还有办法就是直接通过函数的参数接收。要对依赖定一个规范也不一定非要用接口，可以只是用文档或示例代码来展示依赖的特征（有些规范甚至没法用接口来限制，比如“我要一个Class类的实例，它代表的类必须有一个public的无参构造函数”，这个规范在Java里就无法用接口来表达）。更高层次的依赖注入就是框架（比如你在Servlet框架里写的每个Servlet实现类都是框架的一个非必须的依赖），再高层次的就是架构级别的了（比如服务发现需要一个能给我服务访问方式列表的机制，这个机制就是我的依赖，但我不知道怎么造，所以你要给我配一个（例如etcd，consul之类的））。\n只要理解了依赖注入，你完全就可以不看设计模式了，因为你凭直觉写出来的代码就是符合设计模式的，甚至比设计模式更简洁，且一样的松耦合。\n设计模式本质上是对于开发中的常见问题的封装。请注意：设计模式不是代码 而是类似于如何解读一个问题，然后如何解决一个问题的解释方法。\n在工作中我经常碰到的场景是这样的，很多在大学中即便是计算机科学专业，或者其他与IT领域相关的专业毕业的人们。即便他们在学校期间学习过设计模式，但从未受到足够的训练来使用它们。在开始工作之后，大多数人都会忘记设计模式，或者感觉在实际的开发中并无用处。\n然而，大多数初级开发者无法认识到的是，理解设计模式的确是开发者必须具备的核心能力。对于开发人员来说，如果希望开发水平不断提升，那么一定会在更高级的开发任务中与设计模式不期而遇。如果在构建完整的架构 / 系统 / 解决方案时，无法识别或者应用设计模式来解决问题，且不说失去了提升自己的机会，项目本身在投入生产之后也可能会迅速变得岌岌可危。\n设计模式为什么会于你有益的几点原因 长话短说：设计模式会让任务变得简单。\n举个例子\n有一个需求是要求一个类的所有属性都必须是私有的，并且保证不能被其他类获取。面对这样的需求时，是否要为每一个属性都加上private关键字？能不能使用创建者模式来保证实例变量无法被外界获取？\n另一个原因\n设计模式本身其实是一种对于解决方案的共识，通过所有人都认可的词汇来表示。这样你和其他同事之间进行沟通就会更加有效率。你可以说：“这种情况下实现一个单例模式就行了”。然后其他人就都会明白你建议背后的含义。如果没有这种共识，你将需要向大家解释所谓单例模式背后的含义，以及好处和使用场景。\n在本文中，会通过应用我个人认为_最常用的三种设计模式_，向读者们展示设计模式是如何让具体代码变的更好的。\n1. 策略模式 这是我最喜爱的一种模式。\n使用设计模式允许你在运行时切换具体实现，而且不会影响客户端。对于需要处理很多条件的处理方式的场景来说这句有两个“处理”，大部分人的第一反应是if-elif-elif-else的处理方式。但是相比于直接实现一个单一方法，使用策略模式可以在代码运行时选择具体的处理逻辑。也就是说对于不同的处理逻辑，我们可以建立不同的类，从而将分发逻辑和处理逻辑解耦。\n从开放-关闭原则中我们知道，代码应该对扩展开放，而对修改关闭。这一原则是我在刚刚成为编程新手时就学到的一个原则，我相信大多数人都至少听说过它。\n但在具体的开发工作中应用开闭原则，最常见的场景就是在需要应用策略模式的时候。\n具体来说，当多种处理逻辑需要统一对外接口的场景时，这种模式就非常趁手。实现策略模式大致涉及4个元素，包括客户端：\n— 客户端 -\u0026gt; 发起上下文的地方\n— 上下文 -\u0026gt; 客户端希望处理的场景\n— 接口 -\u0026gt; 分发策略的模块\n— 算法 -\u0026gt; 真实的处理逻辑\n一起来看个例子\n想象你有一个神级创业产品，最开始的MVP版本先从附近的邻居开始。因为客户都在周边，所以应用的第一版就只有使用自行车送货这一种选项，发布当日即获得100万的日活，很明显你的业务要起飞了。但不久后你的业务拓展到周边的城市，这时候还仅有自行车一种选项，听起来很难雇到合适的快递小哥。\n于是，事情发生了变化：业务要求至少来个通过汽车配送的选项吧。作为首席架构师你应该知道：这只是一个开始。\n在这之后各种交通工具你方唱罢我登场，铁路、飞机轮流排期。PackageDelivery这个类随着运输逻辑的增加变得越来越臃肿，越来越难以维护。任何一个小的缺陷都可能威胁整个类，于是即便只是修改了关于自行车运输的处理逻辑，也不得不引入全量回归，来测试整个运输流程。\n作为一个有追求的架构师，你终于引入策略模式来解救这场灾难。于是你对所有开发者声明：每一个具体运输逻辑应该有自己的类，这些类我们称之为“策略”。这样一来，下次再需要引入“通过海运传输”这一新选项时，就不用再去修改主流程的代码了。\n下面的伪代码展示了主类是如何应用这些策略，以及根据包裹类型分发给不同的策略\ninterface process PackageStrategy has method processPackage(package); ​ //These strategies implement the algorithm by implementing the interface above. class SendByRail implements process PackageStrategy has method processPackage(package) { //process the package that will be sent by Rail and return something } class SendByBike implements process PackageStrategy has method processPackage(package) { // process and return something } //strategies used by the context class class Context { private strategy: processPackageStrategy method setStrategy(processPackageStrategy Strategy) does this.strategy= strategy; ​ method executeStrategy(Package package) does return strategy.processPackage(); } ​ // read that strategy from user (UI or Api) class App { create a new context instance; get package info; read the desired user choice if (choice is rail) then context.setStrategy(new SendByRail()) ​ if (choice is bike) then context.setStrategy(new SendByBike()) ​ response = context.executeStrategy(package) //do something with response. 2. 单例模式 一个类只有一个实例\n单例模式适用于对于一个类仅仅需要其唯一实例的场景。限制类的实例化的主要动机在于希望能够统一维护和控制共享资源，比如数据库、存储或者文件。凭借单例模式的技巧，我们可以构建唯一的类实例，然后让其可在全局访问。\n比如现代前端框架中对于统一状态管理的工具，通常都会使用单例模式生成全局唯一的实例来管理当前应用的状态。这句好像和上面的英文原文不一致\n下面的JavaScript片段可以当作一个小小的实例，展示如何实现一个单例\nclass Store { static instance; constructor() { if (!Store.instance) { this._state = []; Store.instance = this; } return Store.instance; } add(stuff) { this._state.push(stuff); } } ​ const instance = new Store(); Object.freeze(instance); ​ export default instance; ​ // In other files const a = new Store(); a.add(\u0026#34;phone\u0026#34;); ​ const b = new Store(); console.log(b._state) // outputs [\u0026#34;phone\u0026#34;] - shared state 很多人应该在工作中多多少少都会碰到类似场景：我们只想要全局唯一的一个实例。也许甚至直到看到这篇文章是才知道这是一种设计模式。\n这再次回应了前文所说的，设计模式是开发人员在实践中总结出来的对于特定类型的问题专用解决方案、最佳实践。抛开那些看起来很高大上的名字，设计模式本身解决的问题都是很接地气的。\n3. 观察者模式 观察者模式存在的土壤就是需要解决大量对象之间一对多关系的问题。观察者模式可以用于设置订阅机制，以便在一个实体上发生某种你感兴趣的行为时，将每次这种行为发生的事件通知给你。\nKafka, RabbitMQ都是在真实世界中通过实现pub/sub（一种观察者模式的变体）模式创造出的发布订阅系统。\n下面是一些示例\n— 在web开发领域中，尤其是React生态中，你一定听说过使用Redux来管理应用的状态。Redux就是一种观察者模式的实现。当你通过action更新了store中的状态，监听这一改变的组件会根据变更的状态调整自身的显示\n— 一旦代码被推送到远程Git仓库，CI环境会监视到这一事件，并执行构建\n— 事件驱动的过程式编程用于模拟观察者模式。与其他模式类似，这一模式的应用让整个系统的各个组件通过松散的关系各自链接。由此特性我们便可以开发并维护模块化的系统，以及在事件驱动系统中实现不同actor之间的明显分割\n设计模式总是好的吗？ 虽然当我们谈到设计模式的时候，总是讨论它广为人知的好的一面。然而当设计模式被滥用时，也会产生不好的副作用。\n因此，开发者应当思考在项目中使用设计模式是否是有效并且恰当的，以及查找其他替代方案，并有能力比较替代方案和设计模式。\nI’m not sure what’s wrong with people: they don’t learn by comprehending; they learn by rote or something else.” Their understanding is so weak! — Feynman, Richard P.\n我在工作中以及技术交流中见过很多工程师，我经常会谈起关于设计模式的话题。我发现有一些工程师很擅长记住这些设计模式，然后就很有信心的认为自己已经掌握了设计模式的咒语可以随便施展。理解设计模式可不是仅仅记住那些炫酷的名字。\n这就是为什么在理解每个设计模式之前，要了解它的固有限制以及应用它的时候需要进行权衡，是至关重要的。\n","permalink":"https://leochu.work/blog/tech/engineering/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E6%A6%82%E8%BF%B0/","summary":"\u003ch2 id=\"策略模式strategy-pattern\"\u003e【策略模式】（Strategy Pattern）\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230420121518.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230420121518.png\"\u003e\u003c/p\u003e\n\u003cp\u003e其中的Context是一个所谓的“上下文”，不一定非得是个类，也可以只是一个函数/方法。最关键的是，我们其实根本不需要ConcreteStrategyX类，也不需要它们的对象。我们要的只是一个execute函数而已，我们甚至连execute这个函数名都不需要，只要能执行它就行。\u003c/p\u003e\n\u003cp\u003e看看函数式编程是怎么玩的（这里以大家都熟悉的JavaScript为例）：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econtext\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003efunc\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// 做些什么...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eresult\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efunc\u003c/span\u003e()  \u003cspan style=\"color:#75715e\"\u003e// 有需要的话可以传参\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// 再做些什么...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003econtext\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// 做些什么...\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e123\u003c/span\u003e  \u003cspan style=\"color:#75715e\"\u003e// 是否需要返回值看需求\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e})\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e一个匿名函数参数\u003ccode\u003efunc\u003c/code\u003e搞定。\u003c/p\u003e\n\u003cp\u003eJava能搞出这种类图来，全都是因为Java没有一等公民的函数，所以函数必须依附于类（静态方法）或者对象（非静态方法）。而玩静态方法又没法玩多态，而且类不能当成参数传递给其他函数/方法，所以只能用对象携带方法。而对象的方法必须有个名称，为了统一，就叫execute。由于需要多态，所以我们必须提一个接口出来，在接口里声明execute的方法签名。所有这一切说白了都是为了讨好Java编译器，否则它会给你颜色（red）看。\u003c/p\u003e\n\u003cp\u003e当然，自从Java有了函数式接口和lambda后一定程度上也能玩函数式编程了。\u003c/p\u003e\n\u003ch2 id=\"观察者模式observer-pattern\"\u003e【观察者模式】（Observer Pattern）\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230420121526.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230420121526.png\"\u003e\u003c/p\u003e\n\u003cp\u003e这个在JS里大家已经熟悉到不能再熟悉了：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esomeButton\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eonclick\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eevent\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// 处理点击事件\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e又是一个匿名函数搞定！上图里的\u003ccode\u003eSubject#attach\u003c/code\u003e在这里就是直接赋值。\u003ccode\u003eSubject#detach\u003c/code\u003e就是赋空值。\u003ccode\u003eSubject#notify\u003c/code\u003e就是调用一下匿名函数而已。而\u003ccode\u003eObserver#update\u003c/code\u003e就是匿名函数本身。\u003c/p\u003e\n\u003ch2 id=\"访问者模式visitor-pattern\"\u003e【访问者模式】（Visitor Pattern）\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230420121532.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230420121532.png\"\u003e\u003c/p\u003e\n\u003cp\u003e能整成这样我也是服了。说白了不就是访问者需要判断一下元素类型嘛。直接套用策略模式，在匿名函数里if-else一下不就行了？（当然还有其他方式，比如引入一个工厂。有模式匹配的函数式编程语言如Haskell、Erlang、Elixir等玩起来更简单）。\u003c/p\u003e\n\u003ch2 id=\"装饰器模式decorator-pattern\"\u003e【装饰器模式】（Decorator Pattern）\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230420121538.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230420121538.png\"\u003e\u003c/p\u003e\n\u003cp\u003e这才是函数式编程的魅力所在！\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecore1\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003earg1\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003earg2\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// 做些啥\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e123\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecore2\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003earg\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// 做些啥\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e456\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorate\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecore\u003c/span\u003e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#75715e\"\u003e// 做些准备工作\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 做些啥，甚至可以改变参数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eret\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ecore\u003c/span\u003e(...\u003cspan style=\"color:#a6e22e\"\u003earguments\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#75715e\"\u003e// 做些啥，甚至可以改变返回值\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eret\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorated1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorate\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecore1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorated2\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edecorate\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecore2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e酷吧？又是匿名函数搞定！只不过这次的匿名函数是返回值。\u003c/p\u003e","title":"关于设计模式"},{"content":"分页 Paginator 分页功能是几乎所有的网站上都需要提供的功能，当你要展示的条目比较多时，必须进行分页，不但能减小数据库读取数据压力，也有利于用户浏览。\nDjango又很贴心的为我们提供了一个Paginator分页工具，但是不幸的是，这个工具功能差了点，不好添加CSS样式，所以前端的展示效果比较丑。如果你能力够，自己编写一个分页器，然后提交给Django官方吧，争取替代掉这个当前的分页器，我看好你哦！\n但不管怎么样，当前的Paginator分页器，还是要学一下用一下的。\n一、实例展示 分页功能位于django.core.paginator模块。\n向Paginator提供包含一些对象的列表，以及你想每一页显示几条，比如每页5条、10条、20条、100条等等，它就会为你提供访问的一系列API方法，示例如下：\n\u0026gt;\u0026gt;\u0026gt; from django.core.paginator import Paginator\r\u0026gt;\u0026gt;\u0026gt; objects = [\u0026#39;john\u0026#39;, \u0026#39;paul\u0026#39;, \u0026#39;george\u0026#39;, \u0026#39;ringo\u0026#39;]\r\u0026gt;\u0026gt;\u0026gt; p = Paginator(objects, 2) # 对objects进行分页，虽然objects只是个字符串列表，但没关系，一样用。每页显示2条。\r\u0026gt;\u0026gt;\u0026gt; p.count # 对象个数\r4\r\u0026gt;\u0026gt;\u0026gt; p.num_pages # 总共几页\r2\r\u0026gt;\u0026gt;\u0026gt; type(p.page_range) # `\u0026lt;type \u0026#39;rangeiterator\u0026#39;\u0026gt;` in Python 2.\r\u0026lt;class \u0026#39;range_iterator\u0026#39;\u0026gt;\r\u0026gt;\u0026gt;\u0026gt; p.page_range # 分页范围\rrange(1, 3)\r\u0026gt;\u0026gt;\u0026gt; page1 = p.page(1) # 获取第一页\r\u0026gt;\u0026gt;\u0026gt; page1\r\u0026lt;Page 1 of 2\u0026gt;\r\u0026gt;\u0026gt;\u0026gt; page1.object_list # 获取第一页的对象\r[\u0026#39;john\u0026#39;, \u0026#39;paul\u0026#39;]\r\u0026gt;\u0026gt;\u0026gt; page2 = p.page(2)\r\u0026gt;\u0026gt;\u0026gt; page2.object_list\r[\u0026#39;george\u0026#39;, \u0026#39;ringo\u0026#39;]\r\u0026gt;\u0026gt;\u0026gt; page2.has_next() # 判断是否有下一页\rFalse\r\u0026gt;\u0026gt;\u0026gt; page2.has_previous()# 判断是否有上一页\rTrue\r\u0026gt;\u0026gt;\u0026gt; page2.has_other_pages() # 判断是否有其它页\rTrue\r\u0026gt;\u0026gt;\u0026gt; page2.next_page_number() # 获取下一页的页码\rTraceback (most recent call last):\r...\rEmptyPage: That page contains no results\r\u0026gt;\u0026gt;\u0026gt; page2.previous_page_number() # 获取上一页的页码\r1\r\u0026gt;\u0026gt;\u0026gt; page2.start_index() # 从1开始计数的当前页的第一个对象\r3\r\u0026gt;\u0026gt;\u0026gt; page2.end_index() # 从1开始计数的当前页最后1个对象\r4\r\u0026gt;\u0026gt;\u0026gt; p.page(0) # 访问不存在的页面\rTraceback (most recent call last):\r...\rEmptyPage: That page number is less than 1\r\u0026gt;\u0026gt;\u0026gt; p.page(3) # 访问不存在的页面\rTraceback (most recent call last):\r...\rEmptyPage: That page contains no results 简单地说，使用Paginator分四步走：\n使用任何方法，获取要展示的对象列表QuerySet； 将对象列表和每页展示数量传递给Paginator，返回一个分页对象； 调用该对象的各种方法，获取各种分页信息； 在HTML模板中，使用上面的分页信息构建分页栏。. 二、在视图中使用Paginator 下面的例子假设你拥有一个Contacts模型。\n在函数视图中使用Paginator，参考下面的代码：\nfrom django.core.paginator import Paginator, EmptyPage, PageNotAnInteger\rfrom django.shortcuts import render\rfrom myapp.models import Contact\rdef listing(request):\rcontact_list = Contacts.objects.all()\rpaginator = Paginator(contact_list, 25) # 每页显示25条\rpage = request.GET.get(\u0026#39;page\u0026#39;)\rcontacts = paginator.get_page(page)\rreturn render(request, \u0026#39;list.html\u0026#39;, {\u0026#39;contacts\u0026#39;: contacts}) 在list.html模板中，使用下面的范例来展示每个要显示的contact，以及最后的一个分页栏：\n{% for contact in contacts %}\r{# 每个\u0026#34;contact\u0026#34;都是一个Contact模型对象. #}\r{{ contact.full_name|upper }}\u0026lt;br /\u0026gt;\r...\r{% endfor %}\r{# 分页标签的HTML代码 #}\r\u0026lt;div class=\u0026#34;pagination\u0026#34;\u0026gt;\r\u0026lt;span class=\u0026#34;step-links\u0026#34;\u0026gt;\r{% if contacts.has_previous %}\r\u0026lt;a href=\u0026#34;?page={{ contacts.previous_page_number }}\u0026#34;\u0026gt;previous\u0026lt;/a\u0026gt;\r{% endif %}\r\u0026lt;span class=\u0026#34;current\u0026#34;\u0026gt;\rPage {{ contacts.number }} of {{ contacts.paginator.num_pages }}.\r\u0026lt;/span\u0026gt;\r{% if contacts.has_next %}\r\u0026lt;a href=\u0026#34;?page={{ contacts.next_page_number }}\u0026#34;\u0026gt;next\u0026lt;/a\u0026gt;\r{% endif %}\r\u0026lt;/span\u0026gt;\r\u0026lt;/div\u0026gt; 对于Django内置的通用列表视图类django.views.generic.list.ListView，它和paginator结合得比较好，只需要简单的提供一个属性即可，如下所示：\nfrom django.views.generic import ListView\rfrom myapp.models import Contact\rclass ContactList(ListView):\rpaginate_by = 2\rmodel = Contact paginate_by指示每页显示的联系人数量，并将page_obj和paginator对象添加到context上下文中，供模板渲染使用。下面是一个典型的模板页面：\n{% for contact in page_obj %}\r{# Each \u0026#34;contact\u0026#34; is a Contact model object. #}\r{{ contact.full_name|upper }}\u0026lt;br\u0026gt;\r...\r{% endfor %}\r\u0026lt;div class=\u0026#34;pagination\u0026#34;\u0026gt;\r\u0026lt;span class=\u0026#34;step-links\u0026#34;\u0026gt;\r{% if page_obj.has_previous %}\r\u0026lt;a href=\u0026#34;?page=1\u0026#34;\u0026gt;\u0026amp;laquo; first\u0026lt;/a\u0026gt;\r\u0026lt;a href=\u0026#34;?page={{ page_obj.previous_page_number }}\u0026#34;\u0026gt;previous\u0026lt;/a\u0026gt;\r{% endif %}\r\u0026lt;span class=\u0026#34;current\u0026#34;\u0026gt;\rPage {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.\r\u0026lt;/span\u0026gt;\r{% if page_obj.has_next %}\r\u0026lt;a href=\u0026#34;?page={{ page_obj.next_page_number }}\u0026#34;\u0026gt;next\u0026lt;/a\u0026gt;\r\u0026lt;a href=\u0026#34;?page={{ page_obj.paginator.num_pages }}\u0026#34;\u0026gt;last \u0026amp;raquo;\u0026lt;/a\u0026gt;\r{% endif %}\r\u0026lt;/span\u0026gt;\r\u0026lt;/div\u0026gt; 三、Paginator对象 Paginator类拥有以下主要方法和属性：\n方法：\nPaginator.page(number)\n返回指定页面的对象列表，比如第7页的所有内容，下标以1开始。如果提供的页码不存在，抛出InvalidPage异常。\nPaginator.get_page(number)\n上面方法的安全版本，不会弹出异常。如果输入的参数不是数字，返回第一页。如果输入的数字大于最大页码，返回最后一页。\n属性：\nPaginator.count：对象总数。 Paginator.num_pages：页面总数。 Paginator.page_range：基于1的页数范围迭代器。比如：[1, 2, 3, 4] 四、Page对象 Paginator.page(num)将返回一个Page对象，我们主要的操作都是基于Page对象的，它具有下面的方法和属性：\n方法：\nPage.has_next()：如果有下一页，则返回True。 Page.has_previous()：如果有上一页，返回 True。 Page.has_other_pages()：如果有上一页或下一页，返回True。 Page.next_page_number()：返回下一页的页码。如果下一页不存在，抛出InvalidPage异常。 Page.previous_page_number()：返回上一页的页码。如果上一页不存在，抛出InvalidPage异常。 Page.start_index()：返回当前页上的第一个对象，相对于分页列表的所有对象的序号，从1开始计数。 比如，将五个对象的列表分为每页两个对象，第二页的start_index()会返回3。 Page.end_index():返回当前页上的最后一个对象，相对于分页列表的所有对象的序号，从1开始。 比如，将五个对象的列表分为每页两个对象，第二页的end_index()会返回4。 属性:\nPage.object_list:当前页上所有对象的列表。 Page.number:当前页的序号，从1开始计数。 Page.paginator：当前Page对象所属的Paginator对象。 五、处理异常 在实际使用中，可能恶意也可能不小心，用户请求的页面，可能千奇百怪。正常我们希望是个合法的1，2，3之类，但请求的可能是‘apple’，‘1000000’，‘#’，这就有可能导致异常，需要特别处理。Django为我们内置了下面几个，Paginator相关异常。\nexception InvalidPage：异常的基类，当paginator传入一个无效的页码时抛出。 exception PageNotAnInteger：当向page()提供一个不是整数的值时抛出。 exception EmptyPage：当向page()提供一个有效值，但是那个页面上没有任何对象时抛出。 后面两个异常都是InvalidPage的子类，所以你可以通过简单的except InvalidPage来处理它们。\n","permalink":"https://leochu.work/blog/tech/python/django/%E5%88%86%E9%A1%B5/","summary":"\u003ch1 id=\"分页-paginator\"\u003e分页 Paginator\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e分页功能是几乎所有的网站上都需要提供的功能，当你要展示的条目比较多时，必须进行分页，不但能减小数据库读取数据压力，也有利于用户浏览。\u003c/p\u003e\n\u003cp\u003eDjango又很贴心的为我们提供了一个Paginator分页工具，但是不幸的是，这个工具功能差了点，不好添加CSS样式，所以前端的展示效果比较丑。如果你能力够，自己编写一个分页器，然后提交给Django官方吧，争取替代掉这个当前的分页器，我看好你哦！\u003c/p\u003e\n\u003cp\u003e但不管怎么样，当前的Paginator分页器，还是要学一下用一下的。\u003c/p\u003e\n\u003ch2 id=\"一实例展示\"\u003e一、实例展示\u003c/h2\u003e\n\u003cp\u003e分页功能位于\u003ccode\u003edjango.core.paginator\u003c/code\u003e模块。\u003c/p\u003e\n\u003cp\u003e向\u003ccode\u003ePaginator\u003c/code\u003e提供包含一些对象的列表，以及你想每一页显示几条，比如每页5条、10条、20条、100条等等，它就会为你提供访问的一系列API方法，示例如下：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026gt;\u0026gt;\u0026gt; from django.core.paginator import Paginator\r\n\u0026gt;\u0026gt;\u0026gt; objects = [\u0026#39;john\u0026#39;, \u0026#39;paul\u0026#39;, \u0026#39;george\u0026#39;, \u0026#39;ringo\u0026#39;]\r\n\u0026gt;\u0026gt;\u0026gt; p = Paginator(objects, 2)  # 对objects进行分页，虽然objects只是个字符串列表，但没关系，一样用。每页显示2条。\r\n\r\n\u0026gt;\u0026gt;\u0026gt; p.count   # 对象个数\r\n4\r\n\u0026gt;\u0026gt;\u0026gt; p.num_pages  # 总共几页\r\n2\r\n\u0026gt;\u0026gt;\u0026gt; type(p.page_range)  # `\u0026lt;type \u0026#39;rangeiterator\u0026#39;\u0026gt;` in Python 2.\r\n\u0026lt;class \u0026#39;range_iterator\u0026#39;\u0026gt;\r\n\u0026gt;\u0026gt;\u0026gt; p.page_range  # 分页范围\r\nrange(1, 3)\r\n\r\n\u0026gt;\u0026gt;\u0026gt; page1 = p.page(1) # 获取第一页\r\n\u0026gt;\u0026gt;\u0026gt; page1\r\n\u0026lt;Page 1 of 2\u0026gt;\r\n\u0026gt;\u0026gt;\u0026gt; page1.object_list # 获取第一页的对象\r\n[\u0026#39;john\u0026#39;, \u0026#39;paul\u0026#39;]\r\n\r\n\u0026gt;\u0026gt;\u0026gt; page2 = p.page(2)\r\n\u0026gt;\u0026gt;\u0026gt; page2.object_list\r\n[\u0026#39;george\u0026#39;, \u0026#39;ringo\u0026#39;]\r\n\u0026gt;\u0026gt;\u0026gt; page2.has_next()  # 判断是否有下一页\r\nFalse\r\n\u0026gt;\u0026gt;\u0026gt; page2.has_previous()# 判断是否有上一页\r\nTrue\r\n\u0026gt;\u0026gt;\u0026gt; page2.has_other_pages() # 判断是否有其它页\r\nTrue\r\n\u0026gt;\u0026gt;\u0026gt; page2.next_page_number() # 获取下一页的页码\r\nTraceback (most recent call last):\r\n...\r\nEmptyPage: That page contains no results\r\n\u0026gt;\u0026gt;\u0026gt; page2.previous_page_number() # 获取上一页的页码\r\n1\r\n\u0026gt;\u0026gt;\u0026gt; page2.start_index() # 从1开始计数的当前页的第一个对象\r\n3\r\n\u0026gt;\u0026gt;\u0026gt; page2.end_index() # 从1开始计数的当前页最后1个对象\r\n4\r\n\r\n\u0026gt;\u0026gt;\u0026gt; p.page(0)  # 访问不存在的页面\r\nTraceback (most recent call last):\r\n...\r\nEmptyPage: That page number is less than 1\r\n\u0026gt;\u0026gt;\u0026gt; p.page(3) # 访问不存在的页面\r\nTraceback (most recent call last):\r\n...\r\nEmptyPage: That page contains no results\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e简单地说，使用Paginator分四步走：\u003c/p\u003e","title":"分页"},{"content":"在管理后台与model模型文章里，我们曾经有一个操作，就是在admin.py里注册blog应用，这样blog应用才会在后台显示出来，我们才能在后台对这个应用进行管理。这就是Django自带的后台管理的特色之一，它可以让我们快速便捷管理数据，后台管理可以在各个app的admin.py文件中进行控制。\n想要对APP应用进行管理，最基本的前提是要先在settings里对其进行注册，就是在INSTALLED_APPS里把APP名添加进去，更多的可以查看文章：全局配置settings详解\n下面我们以一个blog应用来举例，向大家介绍一些常用的自定制admin的方法。如下为blog的models的Article表的内容:\nclass Article(models.Model):\rtitle = models.CharField(\u0026#39;标题\u0026#39;, max_length=70)\rkeywords = models.CharField(\u0026#39;文章关键词\u0026#39;, max_length=120, blank=True, null=True)\rexcerpt = models.TextField(\u0026#39;摘要\u0026#39;, max_length=200, blank=True)\rcategory = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name=\u0026#39;分类\u0026#39;, blank=True, null=True)\rbody = models.TextField(\u0026#39;内容\u0026#39;)\ruser = models.ForeignKey(Userinfo, on_delete=models.CASCADE, verbose_name=\u0026#39;作者\u0026#39;)\rviews = models.PositiveIntegerField(\u0026#39;阅读量\u0026#39;, default=0)\rtop = models.IntegerField(choices=[(0, \u0026#39;否\u0026#39;), (1, \u0026#39;是\u0026#39;), ], default=0, verbose_name=\u0026#39;是否推荐\u0026#39;)\rcreated_time = models.DateTimeField(\u0026#39;发布时间\u0026#39;, auto_now_add=True)\rmodified_time = models.DateTimeField(\u0026#39;修改时间\u0026#39;, auto_now=True)\rclass Meta:\rverbose_name = \u0026#39;文章\u0026#39;verbose_name_plural = \u0026#39;文章\u0026#39;def __str__(self):\rreturn self.title 一、管理后台注册需要管理的应用\n只有注册了，我们才能在管理后台看到这个APP应用，才能对其进行管理，这个注册有两种方式：\n1、装饰器注册\nfrom django.contrib import admin\rfrom .models import Article\r@admin.register(Article)\rclass ArticleAdmin(admin.ModelAdmin):\rlist_display=(\u0026#39;id\u0026#39;, \u0026#39;category\u0026#39;, \u0026#39;title\u0026#39;, \u0026#39;user\u0026#39;,\u0026#39;views\u0026#39;,\u0026#39;created_time\u0026#39;) 2、注册参数\nfrom django.contrib import admin\rfrom .models import Article\rclass ArticleAdmin(admin.ModelAdmin):\rlist_display=(\u0026#39;id\u0026#39;, \u0026#39;category\u0026#39;, \u0026#39;title\u0026#39;, \u0026#39;user\u0026#39;,\u0026#39;views\u0026#39;,\u0026#39;created_time\u0026#39;)\radmin.site.register(Article,ArticleAdmin) 注册参数将管理器和注册语句分开，有时容易忘记写注册语句，或者模型很多，不容易对应。所以一般推荐第一种方法。\n注意：我们注册的时候，先需要在头部把需要注册的类导入。如：from .models import Article\n注册好之后，我们就能在后台看到我们的注册的应用：\n二、应用类的列表管理界面设置\n什么是应用类的列表？我们都知道每个应用里有很多个类，每个类都对应着数据库里的一个表，每个表里都有着多个字段。点击刚才注册的blog应用，点击文章，进入文章列表管理界面：\n1、类列表的基本设置\n这里面我们用得比较多的设置是：显示字段、每页记录数和排序等。点击列头可以进行升序或降序排列。\nfrom django.contrib import admin\rfrom .models import Article\r@admin.register(Article)\rclass ArticleAdmin(admin.ModelAdmin):\r#listdisplay设置要显示在列表中的字段（id字段是Django模型的默认主键）\rlist_display=(\u0026#39;id\u0026#39;, \u0026#39;title\u0026#39;, \u0026#39;user\u0026#39;,\u0026#39;views\u0026#39;,\u0026#39;created_time\u0026#39;)\r#list_per_page设置每页显示多少条记录，默认是100条\rlist_per_page = 50\r#ordering设置默认排序字段，负号表示降序排序\rordering = (\u0026#39;-created_time\u0026#39;,)\r#list_editable 设置默认可编辑字段，在列表里就可以编辑\rlist_editable = [\u0026#39;title\u0026#39;, \u0026#39;user\u0026#39;]\r#fk_fields 设置显示外键字段\rfk_fields = [\u0026#39;category\u0026#39;] 2、进入编辑界面\n默认的情况下，我们点击第一个字段，就能进入编辑界面。\n这里默认的是ID这个字段，我们可以设置其他字段进入编辑界面，比如标题：\nfrom django.contrib import admin\rfrom .models import Category, Article\r@admin.register(Article)\rclass ArticleAdmin(admin.ModelAdmin):\rlist_display = (\u0026#39;id\u0026#39;, \u0026#39;category\u0026#39;,\u0026#39;title\u0026#39;, \u0026#39;user\u0026#39;,\u0026#39;views\u0026#39;,\u0026#39;created_time\u0026#39;,\u0026#39;modified_time\u0026#39;)\rlist_per_page = 50ordering = (\u0026#39;-created_time\u0026#39;,)\rlist_display_links = (\u0026#39;id\u0026#39;, \u0026#39;title\u0026#39;) 现在我们点击ID和标题也能进入编辑界面\n3、\u0026ldquo;操作选项\u0026quot;的设置\n操作选项，如上图标记所示。\n#列表顶部，设置为False不在顶部显示，默认为True。\ractions_on_top=True\r#列表底部，设置为False不在底部显示，默认为False。\ractions_on_bottom=False 数据较多的话，建议顶部和底部都加上，操作的时候能节省点时间。\n如果我们想定制自己的行为的话，可以用下面的方法：\n# 定制Action行为具体方法\rdef func(self, request, queryset):\rqueryset.update(created_time=\u0026#39;2018-09-28\u0026#39;)\r#批量更新我们的created_time字段的值为2018-09-28\rfunc.short_description = \u0026#34;中文显示自定义Actions\u0026#34;\ractions = [func, ] 4、将方法作为列\n列可以是模型字段，还可以是模型方法，要求方法有返回值。 通过设置short_description属性，可以设置在admin站点中显示的列名。\nblog/models.py\rclass Article(models.Model):\r...\r...\rdef riqi(self):\rreturn self.created_time.strftime(\u0026#34;%b %d %Y %H:%M:%S\u0026#34;)\r# 设置方法字段在admin中显示的标题\rriqi.short_description = \u0026#39;发布日期\u0026#39; 然后在admin.py里把我们定义的方法加在list_display里：\nblog/admin.py\rclass ArticleAdmin(admin.ModelAdmin):\rlist_display = (\u0026#39;id\u0026#39;, \u0026#39;category\u0026#39;,\u0026#39;title\u0026#39;, \u0026#39;user\u0026#39;,\u0026#39;views\u0026#39;,\u0026#39;created_time\u0026#39;,\u0026#39;riqi\u0026#39;) 效果如下：\n红色的是我们在models里定义的方法返回的效果。\n注意：方法列是不能排序的，如果需要排序需要为方法指定排序依据。\nadmin_order_field=\u0026#39;模型类字段\u0026#39;\rblog/models.py\rclass Article(models.Model):\r...\r...\rdef riqi(self):\rreturn self.created_time.strftime(\u0026#34;%b %d %Y %H:%M:%S\u0026#34;)\r# 设置方法字段在admin中显示的标题\rriqi.short_description = \u0026#39;发布日期\u0026#39;\rriqi.admin_order_field=\u0026#39;created_time\u0026#39; 效果如下：\n5 关联对象\n无法直接访问关联对象的属性或方法，可以在模型类中封装方法，访问关联对象的成员。例如，blog模型里有一个文章分类，分类里有一个分类排序字段\u0026rsquo;index\u0026rsquo;，我们需要把这个关联进来。\nblog/models.py\rclass Article(models.Model):\r....\r....\rdef paixu(self):\rreturn self.category.index\rpaixu.short_description = \u0026#39;分类排序\u0026#39; 然后在admin.py里把我们定义的方法加在list_display里：\nblog/admin.py\rclass ArticleAdmin(admin.ModelAdmin):\rlist_display = (\u0026#39;id\u0026#39;, \u0026#39;category\u0026#39;,\u0026#39;title\u0026#39;, \u0026#39;user\u0026#39;,\u0026#39;views\u0026#39;,\u0026#39;created_time\u0026#39;,\u0026#39;paixu\u0026#39;) 刷新浏览器，效果如下：\n6、搜索框\n属性如下，用于对指定字段的值进行搜索，支持模糊查询。列表类型，表示在这些字段上进行搜索。\nsearch_fields=[] 在admin.py里添加search_fields，指定标题title做为搜索字段：\nblog/admin.py\rclass ArticleAdmin(admin.ModelAdmin):\r...\rsearch_fields=[\u0026#39;title\u0026#39;] 刷新得到如下的效果：\n** **\n**7、右侧栏过滤器和日期筛选 **\n右侧栏过滤器属性如下，只能接收字段，会将对应字段的值列出来，用于快速过滤。一般用于有重复值的字段。\nlist_filter=[] 日期筛选属性：\ndate_hierarchy = \u0026#39;\u0026#39; 然在admin.py里添加list_filter，指定作者user做为搜索字段：：\nblog/admin.py\rclass ArticleAdmin(admin.ModelAdmin):\r...\rlist_filter=[\u0026#39;user\u0026#39;] #右侧栏过滤器，按作者进行筛选\rdate_hierarchy = \u0026#39;created_time\u0026#39; # 详细时间分层筛选　效果如下：\n注意：\n一般ManyToManyField多对多字段用过滤器；标题等文本字段用搜索框；日期时间用分层筛选。过滤器如果是外键需要遵循这样的语法：本表字段__外键表要显示的字段。如：“user__user_name”。\n二、修改后台管理页面头部显示内容和页面标题\nblog/admin.py\radmin.site.site_header = \u0026#39;Django中文网管理后台\u0026#39;\radmin.site.site_title = \u0026#39;Django中文网\u0026#39; 效果如下：\n三、重写模板\n这个可治任何不服。想要什么效果，就直接自己写。\n1）在templates/目录下创建admin目录\n2）打开当前虚拟环境中Django包的目录，再向下找到admin的模板。不知道目录在哪，可以使用下面的方法：\n进入python终端\nroot@server-zc:/home/x/mysite# python\r\u0026gt;\u0026gt;\u0026gt; import django\r\u0026gt;\u0026gt;\u0026gt; django.__file__\r\u0026#39;/usr/local/lib/python3/dist-packages/django/__init__.pyc\u0026#39; 找到包的目录之后，复制它，我们需要的模板目录为\n/usr/local/lib/python3/dist-packages/django/contrib/admin/ 3）将需要更改文件拷贝到（在项目的templates文件夹里面创建admin文件夹）里，此处以base_site.html为例。\n{% extends \u0026#34;admin/base.html\u0026#34; %}\r{% block title %}\r{{ title }} | {{ site_title|default:_(\u0026#39;Django site admin\u0026#39;) }}\r{% endblock %}\r{% block branding %}\r\u0026lt;h1 id=\u0026#34;site-name\u0026#34;\u0026gt;\r\u0026lt;a href=\u0026#34;{% url \u0026#39;admin:index\u0026#39; %}\u0026#34;\u0026gt;{{ site_header|default:_(\u0026#39;Django administration\u0026#39;) }}\u0026lt;/a\u0026gt;\r\u0026lt;/h1\u0026gt;\r\u0026lt;hr\u0026gt;\r\u0026lt;h1\u0026gt;自定义的管理页模板\u0026lt;/h1\u0026gt;\r\u0026lt;hr\u0026gt;\r{% endblock %}\r{% block nav-global %}{% endblock %} 4）刷新即可看到效果 其它后台的模板可以按照相同的方式进行修改。\n","permalink":"https://leochu.work/blog/tech/python/django/%E5%AE%9A%E5%88%B6admin%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0/","summary":"\u003cp\u003e在\u003ca href=\"https://www.django.cn/course/show-11.html\"\u003e管理后台与model模型\u003c/a\u003e文章里，我们曾经有一个操作，就是在admin.py里注册blog应用，这样blog应用才会在后台显示出来，我们才能在后台对这个应用进行管理。这就是Django自带的后台管理的特色之一，它可以让我们快速便捷管理数据，后台管理可以在各个app的admin.py文件中进行控制。\u003c/p\u003e\n\u003cp\u003e想要对APP应用进行管理，最基本的前提是要先在settings里对其进行注册，就是在INSTALLED_APPS里把APP名添加进去，更多的可以查看文章：\u003ca href=\"https://www.django.cn/course/show-10.html\"\u003e全局配置settings详解\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e下面我们以一个blog应用来举例，向大家介绍一些常用的自定制admin的方法。如下为blog的models的Article表的内容:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eclass Article(models.Model):\r\n    title = models.CharField(\u0026#39;标题\u0026#39;, max_length=70)\r\n    keywords = models.CharField(\u0026#39;文章关键词\u0026#39;, max_length=120, blank=True, null=True)\r\n    excerpt = models.TextField(\u0026#39;摘要\u0026#39;, max_length=200, blank=True)\r\n    category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name=\u0026#39;分类\u0026#39;, blank=True, null=True)\r\n    body = models.TextField(\u0026#39;内容\u0026#39;)\r\n    user = models.ForeignKey(Userinfo, on_delete=models.CASCADE, verbose_name=\u0026#39;作者\u0026#39;)\r\n    views = models.PositiveIntegerField(\u0026#39;阅读量\u0026#39;, default=0)\r\n    top = models.IntegerField(choices=[(0, \u0026#39;否\u0026#39;), (1, \u0026#39;是\u0026#39;), ], default=0, verbose_name=\u0026#39;是否推荐\u0026#39;)\r\n    created_time = models.DateTimeField(\u0026#39;发布时间\u0026#39;, auto_now_add=True)\r\n    modified_time = models.DateTimeField(\u0026#39;修改时间\u0026#39;, auto_now=True)\r\n    class Meta:\r\n        verbose_name = \u0026#39;文章\u0026#39;verbose_name_plural = \u0026#39;文章\u0026#39;def __str__(self):\r\n        return self.title\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003e一、管理后台注册需要管理的应用\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e只有注册了，我们才能在管理后台看到这个APP应用，才能对其进行管理，这个注册有两种方式：\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1、装饰器注册\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efrom django.contrib import admin\r\nfrom .models import Article\r\n  \r\n@admin.register(Article)\r\nclass ArticleAdmin(admin.ModelAdmin):\r\n    list_display=(\u0026#39;id\u0026#39;, \u0026#39;category\u0026#39;, \u0026#39;title\u0026#39;, \u0026#39;user\u0026#39;,\u0026#39;views\u0026#39;,\u0026#39;created_time\u0026#39;)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003e2、注册参数\u003c/strong\u003e\u003c/p\u003e","title":"定制Admin管理后台"},{"content":"命名风格\n【强制】抽象类命名使用 Abstract 或 Base 开头; 异常类命名使用 Exception 结尾; 测试类 命名以它要测试的类的名称开始，以 Test 结尾。\n【强制】POJO 类中布尔类型变量都不要加 is 前缀，否则部分框架解析会引起序列化错误。\n【强制】包名统一使用小写，点分隔符之间有且仅有一个自然语义的英语单词。包名统一使 用单数形式，但是类名如果有复数含义，类名可以使用复数形式。\n正例: 应用工具类包名为 com.alibaba.ai.util、类名为 MessageUtils\n【推荐】在常量与变量的命名时，表示类型的名词放在词尾，以提升辨识度。 正例: startTime / workQueue / nameList / TERMINATED_THREAD_COUNT\n17.【参考】枚举类名带上 Enum 后缀，枚举成员名称需要全大写，单词间用下划线隔开。\nB) 领域模型命名规约\n数据对象: xxxDO，xxx 即为数据表名。\n数据传输对象: xxxDTO，xxx 为业务领域相关的名称。\n展示对象: xxxVO，xxx 一般为网页名称。\nPOJO 是 DO/DTO/BO/VO 的统称，禁止命名成 xxxPOJO。\n常量定义\n【推荐】不要使用一个常量类维护所有常量，要按常量功能进行归类，分开维护。\n【推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。\nSPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);\n代码格式\n【强制】采用 4 个空格缩进，禁止使用 tab 字符。 IDEA 设置 tab 为 4 个空格时，请勿勾选 Use tab character\n【强制】注释的双斜线与注释内容之间有且仅有一个空格。 // 注释\n【强制】IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式，不 要使用 Windows 格式。\n11.【推荐】单个方法的总行数不超过 80 行\n(四) OOP 规约\n【强制】外部正在调用或者二方库依赖的接口，不允许修改方法签名，避免对接口调用方产生影响。接口过时必须加 @Deprecated 注解，并清晰地说明采用的新接口或者新服务是什 么。\n【强制】不能使用过时的类或方法。\n说明: java.net.URLDecoder 中的方法 decode(String encodeStr) 这个方法已经过时，应该使用双参数 decode(String source, String encode)。接口提供方既然明确是过时接口，那么有义务同时提供新的接 口; 作为调用方来说，有义务去考证过时方法的新实现是什么。\n【强制】Object 的 equals 方法容易抛空指针异常，应使用常量或确定有值的对象来调用 equals。\n【强制】所有整型包装类对象之间值的比较，全部使用 equals 方法比较。\n【强制】浮点数之间的等值判断，基本数据类型不能用 == 来比较，包装数据类型不能用 equals 来判断。\n正例:\n(1) 指定一个误差范围，两个浮点数的差值在此范围之内，则认为是相等的。\n(2) 使用 BigDecimal 来定义值，再进行浮点数的运算操作。\n10.【强制】为了防止精度损失，禁止使用构造方法 BigDecimal(double) 的方式把 double 值转 化为 BigDecimal 对象。\n正例: 优先推荐入参为 String 的构造方法，或使用 BigDecimal 的 valueOf 方法，此方法内部其实执行了 Double 的 toString，而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。\n关于基本数据类型与包装数据类型的使用标准如下: 【强制】所有的 POJO 类属性必须使用包装数据类型。\n【强制】RPC 方法的返回值和参数必须使用包装数据类型。\n【推荐】所有的局部变量使用基本数据类型。\n12.【强制】定义 DO/DTO/VO 等 POJO 类时，不要设定任何属性默认值。\n13.【强制】序列化类新增属性时，请不要修改 serialVersionUID 字段，避免反序列失败; 如果 完全不兼容升级，避免反序列化混乱，那么请修改 serialVersionUID 值。\n14.【强制】构造方法里面禁止加入任何业务逻辑，如果有初始化逻辑，请放在 init 方法中。\n15.【强制】POJO 类必须写 toString 方法。使用 IDE 中的工具: source\u0026gt; generate toString 时，如果继承了另一个 POJO 类，注意在前面加一下 super.toString。\n19.【推荐】 类内方法定义的顺序依次是: 公有方法或保护方法 \u0026gt; 私有方法 \u0026gt; getter / setter 方法。\n说明: 公有方法是类的调用者和维护者最关心的方法，首屏展示最好; 保护方法虽然只是子类关心，也可能是 “模板设计模式” 下的核心方法; 而私有方法外部一般不需要特别关心，是一个黑盒实现; 因为承载的信息价值较低，所有 Service 和 DAO 的 getter/setter 方法放在类体最后。\n21.【推荐】循环体内，字符串的连接方式，使用 StringBuilder 的 append 方法进行扩展。\n22.【推荐】final 可以声明类、成员变量、方法、以及本地变量，下列情况使用 final 关键字:\n不允许被继承的类，如: String 类。\n不允许修改引用的域对象。\n不允许被覆写的方法，如: POJO 类的 setter 方法。\n不允许运行过程中重新赋值的局部变量。\n避免上下文重复使用一个变量，使用 final 可以强制重新定义一个变量，方便更好地进行重构。\n23.【推荐】慎用 Object 的 clone 方法来拷贝对象。\n说明: 对象 clone 方法默认是浅拷贝，若想实现深拷贝需覆写 clone 方法实现域对象的深度遍历式拷贝。\n【推荐】类成员与方法访问控制从严: 如果不允许外部直接通过 new 来创建对象，那么构造方法必须是 private。\n工具类不允许有 public 或 default 构造方法。\n类非 static 成员变量并且与子类共享，必须是 protected。\n类非 static 成员变量并且仅在本类使用，必须是 private。\n类 static 成员变量如果仅在本类使用，必须是 private。\n若是 static 成员变量，考虑是否为 final。\n类成员方法只供类内部调用，必须是 private。\n类成员方法只对继承类公开，那么限制为 protected。\n一个 private 的方法，想删除就删除，可是一个 public 的 service 成员方法或成员变量，删除一下，不得手心冒点汗吗?\n(五) 集合处理\n【强制】关于 hashCode 和 equals 的处理，遵循如下规则: 只要覆写 equals，就必须覆写 hashCode。\n因为 Set 存储的是不重复的对象，依据 hashCode 和 equals 进行判断，所以 Set 存储的对象必须覆 写这两个方法。\n如果自定义对象作为 Map 的键，那么必须覆写 hashCode 和 equals。\n【强制】Collections 类返回的对象，如: emptyList()/singletonList() 等都是 immutable list，不可对其进行添加或者删除元素的操作。\n【强制】在 subList 场景中，高度注意对原集合元素的增加或删除，均会导致子列表的遍 历、增加、删除产生 ConcurrentModificationException 异常。\n【强制】使用集合转数组的方法，必须使用集合的 toArray(T[]array)，传入的是类型完全一 致、长度为 0 的空数组。\n正例:\nList list = new ArrayList\u0026lt;\u0026gt;(2);\nlist.add(\u0026ldquo;guan\u0026rdquo;);\nlist.add(\u0026ldquo;bao\u0026rdquo;);\nString[] array = list.toArray(new String[0]);\n说明: 使用 toArray 带参方法，数组空间大小的 length:\n等于 0，动态创建与 size 相同的数组，性能最好。 【强制】在使用 Collection 接口任何实现类的 addAll() 方法时，都要对输入的集合参数进行 NPE 判断。\n【强制】使用工具类 Arrays.asList() 把数组转换成集合时，不能使用其修改集合相关的方 法，它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。\n说明: asList 的返回对象是一个 Arrays 内部类，并没有实现集合的修改方法。Arrays.asList 体现的是适 配器模式，只是转换接口，后台的数据仍是数组。\nString[] str = new String[] { \u0026ldquo;yang\u0026rdquo;, \u0026ldquo;hao\u0026rdquo; };\nList list = Arrays.asList(str);\n第一种情况: list.add(\u0026ldquo;yangguanbao\u0026rdquo;); 运行时异常。\n第二种情况: str[0] = \u0026ldquo;changed\u0026rdquo;; 也会随之修改，反之亦然。\n【强制】泛型通配符 \u0026lt;?extendsT\u0026gt; 来接收返回的数据，此写法的泛型集合不能使用 add 方法，而 \u0026lt;? super T \u0026gt; 不能使用 get 方法，作为接口调用赋值时易出错。 说明: 扩展说一下 PECS(Producer Extends Consumer Super)\n原则:\n第一、频繁往外读取内容的，适合 用 \u0026lt;? extends T\u0026gt;。\n第二、经常往里插入的，适合用 \u0026lt;? super T\u0026gt;\n【强制】在无泛型限制定义的集合赋值给泛型限制的集合时，在使用集合元素时，需要进行 instanceof 判断，避免抛出 ClassCastException 异常。 11.【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式，如果并发操作，需要对 Iterator 对象加锁。\n12.【强制】在 JDK7 版本及以上，Comparator 实现类要满足如下三个条件，不然 Arrays.sort， Collections.sort 会抛 IllegalArgumentException 异常。\n说明: 三个条件如下\nx，y 的比较结果和 y，x 的比较结果相反。\nx\u0026gt;y，y\u0026gt;z，则 x\u0026gt;z。\nx=y，则 x，z 比较结果和 y，z 比较结果相同。\n反例: 下例中没有处理相等的情况，交换两个对象判断结果并不互反，不符合第一个条件，在实际使用中 可能会出现异常。\nnew Comparator() {\n@Override\npublic int compare(Student o1, Student o2) {\nreturn o1.getId()\u0026gt; o2.getId() ? 1 : -1; }\n};\n【推荐】集合初始化时，指定集合初始值大小。 说明: HashMap 使用 HashMap(int initialCapacity) 初始化。\n正例: initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子 (即 loader factor) 默认 为 0.75，如果暂时无法确定初始值大小，请设置为 16(即默认值)。\n15.【推荐】使用 entrySet 遍历 Map 类集合 KV，而不是 keySet 方式进行遍历。如果是 JDK8， 使用 Map.forEach 方法。\n16.【推荐】高度注意 Map 类集合 K/V 能不能存储 null 值的情况，如下表格:\n￼\n【参考】合理利用好集合的有序性 (sort) 和稳定性 (order)，避免集合的无序性(unsort) 和不稳 定性 (unorder) 带来的负面影响。 说明: 有序性是指遍历的结果是按某种比较规则依次排列的。稳定性指集合每次遍历的元素次序是一定 的。如: ArrayList 是 order/unsort;HashMap 是 unorder/unsort;TreeSet 是 order/sort。\n(六) 并发处理\n【强制】获取单例对象需要保证线程安全，其中的方法也要保证线程安全。\n【强制】创建线程或线程池时请指定有意义的线程名称，方便出错时回溯。\n【强制】线程资源必须通过线程池提供，不允许在应用中自行显式创建线程。\n【强制】线程池不允许使用 Executors 去创建，而是通过 ThreadPoolExecutor 的方式，这 样的处理方式让写的同学更加明确线程池的运行规则，规避资源耗尽的风险。\n说明: Executors 返回的线程池对象的弊端如下:\nFixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE，可能会堆积大量的请求，从而导致 OOM。\nCachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE，可能会创建大量的线程，从而导致 OOM。\n【强制】SimpleDateFormat 是线程不安全的类，一般不要定义为 static 变量，如果定义为 static，必须加锁，或者使用 DateUtils 工具类。 说明: 如果是 JDK8 的应用，可以使用 Instant 代替 Date，LocalDateTime 代替 Calendar， DateTimeFormatter 代替 SimpleDateFormat。\n【强制】必须回收自定义的 ThreadLocal 变量，尤其在线程池场景下，线程经常会被复用， 如果不清理自定义的 ThreadLocal 变量，可能会影响后续业务逻辑和造成内存泄露等问题。 尽量在代理中使用 try-finally 块进行回收。 正例:\nobjectThreadLocal.set(userInfo);\ntry {\n// \u0026hellip;\n} finally {objectThreadLocal.remove();\n}\n【强制】高并发时，同步调用应该去考量锁的性能损耗。能用无锁数据结构，就不要用锁; 能锁区块，就不要锁整个方法体; 能用对象锁，就不要用类锁。 说明: 尽可能使加锁的代码块工作量尽可能的小，避免在锁代码块中调用 RPC 方法。\n【强制】在使用阻塞等待获取锁的方式中，必须在 try 代码块之外，并且在加锁方法与 try 代 码块之间没有任何可能抛出异常的方法调用，避免加锁成功后，在 finally 中无法解锁。 正例:\nLock lock = new XxxLock();\n// \u0026hellip;\nlock.lock();\ntry {\ndoSomething();\ndoOthers();}\nfinally {\nlock.unlock();\n}\n【强制】在使用尝试机制来获取锁的方式中，进入业务代码块之前，必须先判断当前线程是否持有锁。锁的释放规则与锁的阻塞等待方式相同。 boolean isLocked = lock.tryLock();\nif (isLocked) {\ntry {\ndoSomething();\ndoOthers();}\nfinally {\nlock.unlock();}\n}\n【强制】并发修改同一记录时，避免更新丢失，需要加锁。要么在应用层加锁，要么在缓存 加锁，要么在数据库层使用乐观锁，使用 version 作为更新依据。 说明: 如果每次访问冲突概率小于 20%，推荐使用乐观锁，否则使用悲观锁。乐观锁的重试次数不得小于 3 次。 12.【强制】多线程并行处理定时任务时，Timer 运行多个 TimeTask 时，只要其中之一没有捕获 抛出的异常，其它任务便会自动终止运行，如果在处理定时任务时使用 ScheduledExecutorService 则没有这个问题。\n13.【推荐】资金相关的金融敏感信息，使用悲观锁策略。\n16.【推荐】在并发场景下，通过双重检查锁 (double-checked locking) 实现延迟初始化的优化 问题隐患(可参考 The \u0026ldquo;Double-Checked Locking is Broken\u0026rdquo; Declaration)，推荐解决方案中较为 简单一种(适用于 JDK5 及以上版本)，将目标属性声明为 volatile 型。\n17.【参考】volatile 解决多线程内存不可见问题。对于一写多读，是可以解决变量同步问题，但 是如果多写，同样无法解决线程安全问题。\n说明: 如果是 count++ 操作，使用如下类实现: AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8，推荐使用 LongAdder 对象，比 AtomicLong 性能更好 (减少乐观 锁的重试次数)。\n(七) 控制语句\n【强制】当 switch 括号内的变量类型为 String 并且此变量为外部参数时，必须先进行 null 判断。\n【强制】在 if/else/for/while/do 语句中必须使用大括号。\n4.【强制】在高并发场景中，避免使用” 等于” 判断作为中断或退出的条件。\n说明: 如果并发控制没有处理好，容易产生等值判断被 “击穿” 的情况，使用大于等于或小于等于的区间判断条件来代替。\n反例: 判断剩余奖品数量等于 0 时，终止发放奖品，但因为并发处理错误导致奖品数量瞬间变成了负数，这样，活动无法终止。\n【推荐】表达异常的分支时，少用 if-else 方式。 正例: 超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现，其中卫语句 即代码逻辑先考虑失败、异常、中断、退出等直接返回的情况，以方法多个出口的方式，解决代码中判断 分支嵌套的问题，这是逆向思维的体现。\n9.【推荐】避免采用取反逻辑运算符。\n【推荐】接口入参保护，这种场景常见的是用作批量操作的接口。 (八) 注释规约\n【强制】类、类属性、类方法的注释必须使用 Javadoc 规范，使用 /** 内容 */ 格式，不得使用 // xxx 方式。\n【强制】所有的抽象方法 (包括接口中的方法) 必须要用 Javadoc 注释、除了返回值、参数、 异常说明外，还必须指出该方法做什么事情，实现什么功能。\n【强制】所有的枚举类型字段必须要有注释，说明每个数据项的用途。\n【推荐】与其 “半吊子” 英文来注释，不如用中文注释把问题说清楚。专有名词与关键字保 持英文原文即可。\n【参考】谨慎注释掉代码。在上方详细说明，而不是简单地注释掉。如果无用，则删除。 说明: 代码被注释掉有两种可能性: 1) 后续会恢复此段代码逻辑。2) 永久不用。前者如果没有备注信 息，难以知晓注释动机。后者建议直接删掉 (代码仓库已然保存了历史代码)。\n【参考】对于注释的要求:\n第一、能够准确反映设计思想和代码逻辑；\n第二、能够描述业务 含义，使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者 形同天书，注释是给自己看的，即使隔很长时间，也能清晰理解当时的思路; 注释也是给继 任者看的，使其能够快速接替自己的工作。\n【参考】好的命名、代码结构是自解释的，注释力求精简准确、表达到位。\n【参考】特殊注释标记，请注明标记人与标记时间。\n待办事宜 (TODO):(标记人，标记时间，[预计处理时间])\n错误，不能工作 (FIXME):(标记人，标记时间，[预计处理时间])\n(九) 其它\n【强制】注意 Math.random() 这个方法返回是 double 类型，注意取值的范围 0≤x\u0026lt;1(能够 取到零值，注意除零异常)，如果想获取整数类型的随机数，不要将 x 放大 10 的若干倍然后 取整，直接使用 Random 对象的 nextInt 或者 nextLong 方法。\n【强制】获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();\n说明: 如果想获取更加精确的纳秒级时间值，使用 System.nanoTime() 的方式。在 JDK8 中，针对统计时 间等场景，推荐使用 Instant 类。\n【推荐】不要在视图模板中加入任何复杂的逻辑。\n【推荐】任何数据结构的构造或初始化，都应指定大小，避免数据结构无限增长吃光内存。\n【推荐】及时清理不再使用的代码段或配置信息。\n二、异常日志\n(一) 异常处理\n【强制】catch 时请分清稳定代码和非稳定代码，稳定代码指的是无论如何不会出错的代码。 对于非稳定代码的 catch 尽可能进行区分异常类型，再做对应的异常处理。\n【强制】捕获异常是为了处理它，不要捕获了却什么都不处理而抛弃之，如果不想处理它， 请将该异常抛给它的调用者。最外层的业务使用者，必须处理异常，将其转化为用户可以理 解的内容。\n【强制】有 try 块放到了事务代码中，catch 异常后，如果需要回滚事务，一定要注意手动回 滚事务。\n【强制】不要在 finally 块中使用 return。\n说明: try 块中的 return 语句执行成功后，并不马上返回，而是继续执行 finally 块中的语句，如果此处存在 return 语句，则在此直接返回，无情丢弃掉 try 块中的返回点。\n11.【推荐】防止 NPE，是程序员的基本修养，注意 NPE 产生的场景:\n正例: 使用 JDK8 的 Optional 类来防止 NPE 问题。\n12.【推荐】定义时区分 unchecked / checked 异常，避免直接抛出 new RuntimeException()， 更不允许抛出 Exception 或者 Throwable，应使用有业务含义的自定义异常。推荐业界已定 义过的自定义异常，如: DAOException / ServiceException 等。\n13.【参考】对于公司外的 http/api 开放接口必须使用 “错误码”; 而应用内部推荐异常抛出; 跨应用间 RPC 调用优先考虑使用 Result 方式，封装 isSuccess() 方法、“错误码”、“错误 简短信息”。\n(二) 日志规约\n【强制】应用中不可直接使用日志系统 (Log4j、Logback) 中的 API，而应依赖使用日志框架 SLF4J 中的 API，使用门面模式的日志框架，有利于维护和各个类的日志处理方式统一。\n【强制】应用中的扩展日志 (如打点、临时监控、访问日志等) 命名方式: appName_logType_logName.log。logType: 日志类型，如 stats/monitor/access 等; logName: 日志 描述。\n【强制】在日志输出时，字符串变量之间的拼接使用占位符的方式。\n正例: logger.debug(\u0026ldquo;Processing trade with id: {} and symbol: {}\u0026rdquo;, id, symbol);\n【强制】对于 trace/debug/info 级别的日志输出，必须进行日志级别的开关判断。 正例:\n// 如果判断为真，那么可以输出 trace 和 debug 级别的日志 if (logger.isDebugEnabled()) {\nlogger.debug(\u0026ldquo;Current ID is: {} and name is: {}\u0026rdquo;, id, getName());\n}\n【强制】异常信息应该包括两类信息: 案发现场信息和异常堆栈信息。如果不处理，那么通 过关键字 throws 往上抛出。 正例: logger.error(各类参数或者对象 toString() + \u0026ldquo;_\u0026rdquo; + e.getMessage(), e);\n三、单元测试\n【强制】好的单元测试必须遵守 AIR 原则。 A:Automatic(自动化)\nI:Independent(独立性)\nR:Repeatable(可重复)\n【强制】保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护，单元测试用例之 间决不能互相调用，也不能依赖执行的先后次序。\n【强制】对于单元测试，要保证测试粒度足够小，有助于精确定位问题。单测粒度至多是类 级别，一般是方法级别。\n【推荐】编写单元测试代码遵守 BCDE 原则，以保证被测试模块的交付质量。\nB:Border，边界值测试，包括循环边界、特殊取值、特殊时间点、数据顺序等。\nC:Correct，正确的输入，并得到预期的结果。\nD:Design，与设计文档相结合，来编写单元测试。\nE:Error，强制错误信息输入 (如: 非法数据、异常流程、业务允许外等)，并得到预期的结果。\n【推荐】对于数据库相关的查询，更新，删除等操作，不能假设数据库里的数据是存在的，或者直接操作数据库把数据插入进去，请使用程序插入或者导入数据的方式来准备数据。\n【推荐】和数据库相关的单元测试，可以设定自动回滚机制，不给数据库造成脏数据。或者对单元测试产生的数据有明确的前后缀标识。\n四、安全规约\n【强制】隶属于用户个人的页面或者功能必须进行权限控制校验。\n【强制】用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定，防止 SQL 注 入，禁止字符串拼接 SQL 访问数据库。\n【强制】用户请求传入的任何参数必须做有效性验证。\n【强制】表单、AJAX 提交必须执行 CSRF 安全验证。\n【强制】在使用平台资源，譬如短信、邮件、电话、下单、支付，必须实现正确的防重放的 机制，如数量限制、疲劳度控制、验证码校验，避免被滥刷而导致资损。\n【推荐】发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词 过滤等风控策略。\n五、MySQL 数据库\n(一) 建表规约\n【强制】表达是与否概念的字段，必须使用 is_xxx 命名，数据类型为 unsigned tinyint (1 表示是，0 表示否)\n【强制】表名、字段名必须使用小写字母或数字，禁止出现数字开头，禁止两个下划线中间 只出现数字。\n【强制】表名不使用复数名词。\n【强制】主键索引名为 pk_字段名; 唯一索引名为 uk_字段名; 普通索引名则为 idx_字段名。\n【强制】小数类型为 decimal，禁止使用 float 和 double。\n【强制】如果存储的字符串长度几乎相等，使用 char 定长字符串类型。\n【强制】varchar 是可变长字符串，不预先分配存储空间，长度不要超过 5000，如果存储长 度大于此值，定义字段类型为 text，独立出来一张表，用主键来对应，避免影响其它字段索 引效率。\n【强制】表必备三字段: id,create_time,update_time。\nid 为 bigint unsigned，create_time 和 update_time 为 datetime。\n【推荐】表的命名最好是遵循 “业务名称_表的作用”。 正例: alipay_task / force_project / trade_config\n【推荐】字段允许适当冗余，以提高查询性能，但必须考虑数据一致。冗余字段应遵循: 不是频繁修改的字段。\n不是 varchar 超长字段，更不能是 text 字段。\n不是唯一索引的字段。\n14.【推荐】单表行数超过 500 万行或者单表容量超过 2GB，才推荐进行分库分表。\n说明: 如果预计三年后的数据量根本达不到这个级别，请不要在创建表时就分库分表。\n【参考】合适的字符存储长度，不但节约数据库表空间、节约索引存储，更重要的是提升检 索速度。 (二) 索引规约\n【强制】业务上具有唯一特性的字段，即使是多个字段的组合，也必须建成唯一索引。\n【强制】超过三个表禁止 join。需要 join 的字段，数据类型必须绝对一致; 多表关联查询 时，保证被关联的字段需要有索引。\n【强制】在 varchar 字段上建立索引时，必须指定索引长度，没必要对全字段建立索引，根据实际文本区分度决定索引长度。\n说明: 索引的长度与区分度是一对矛盾体，一般对字符串类型数据，长度为 20 的索引，区分度会高达 90% 以上，可以使用 count(distinct left(列名, 索引长度))/count(*) 的区分度来确定。\n【强制】页面搜索严禁左模糊或者全模糊，如果需要请走搜索引擎来解决。 说明: 索引文件具有 B-Tree 的最左前缀匹配特性，如果左边的值未确定，那么无法使用此索引。\n【推荐】如果有 order by 的场景，请注意利用索引的有序性。order by 最后的字段是组合 索引的一部分，并且放在索引组合顺序的最后，避免出现 file_sort 的情况，影响查询性能。 正例: where a=? and b=? order by c; 索引: a_b_c\n【推荐】利用覆盖索引来进行查询操作，避免回表。 说明: 索引中字段已包含查询的所需结果，成为覆盖索引。\n正例: 能够建立索引的种类分为主键索引、唯一索引、普通索引三种，而覆盖索引只是一种查询的一种效 果，用 explain 的结果，extra 列会出现: using index。\n【推荐】利用延迟关联或者子查询优化超多分页场景。 说明: MySQL 并不是跳过 offset 行，而是取 offset+N 行，然后返回放弃前 offset 行，返回 N 行，那当 offset 特别大的时候，效率就非常的低下，要么控制返回的总页数，要么对超过特定阈值的页数进行 SQL 改写。\n正例: 先快速定位需要获取的 id 段，然后再关联:\nSELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20) b where a.id=b.id\n【推荐】建组合索引的时候，区分度最高的在最左边。 说明: 存在非等号和等号混合时，在建索引时，请把等号条件的列前置。如: where c\u0026gt;? and d=? 那么 即使 c 的区分度更高，也必须把 d 放在索引的最前列，即索引 idx_d_c。\n(三) SQL 语句\n【强制】不要使用 count(列名) 或 count(常量) 来替代 count(*)。 说明: count(*) 会统计值为 NULL 的行，而 count(列名) 不会统计此列为 NULL 值的行。\n【强制】使用 ISNULL() 来判断是否为 NULL 值。\n【强制】代码中写分页查询逻辑时，若 count 为 0 应直接返回，避免执行后面的分页语句。\n【强制】不得使用外键与级联，一切外键概念必须在应用层解决。\n说明: 以学生和成绩的关系为例，学生表中的 student_id 是主键，那么成绩表中的 student_id 则为外 键。如果更新学生表中的 student_id，同时触发成绩表中的 student_id 更新，即为级联更新。外键与级 联更新适用于单机低并发，不适合分布式、高并发集群; 级联更新是强阻塞，存在数据库更新风暴的风 险; 外键影响数据库的插入速度。\n【强制】数据订正 (特别是删除、修改记录操作) 时，要先 select，避免出现误删除，确认无误才能执行更新语句。\n【推荐】in 操作能避免则避免，若实在避免不了，需要仔细评估 in 后边的集合元素数量，控 制在 1000 个之内。\n(四) ORM 映射\n【强制】在表查询中，一律不要使用 * 作为查询的字段列表，需要哪些字段必须明确写明。 说明: 1) 增加查询分析器解析成本。2) 增减字段容易与 resultMap 配置不一致。3) 无用字段增加网络 消耗，尤其是 text 类型的字段。\n【强制】不要用 resultClass 当返回参数，即使所有类属性名与数据库字段一一对应，也需要 定义; 反过来，每一个表也必然有一个 POJO 类与之对应。\n说明: 配置映射关系，使字段与 DO 类解耦，方便维护。\n【强制】sql.xml 配置参数使用:#{}，#param# 不要使用 ${} 此种方式容易出现 SQL 注入。\n【强制】不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。\n【强制】更新数据表记录时，必须同时更新记录对应的 gmt_modified 字段值为当前时间。\n【推荐】不要写一个大而全的数据更新接口。传入为 POJO 类，不管是不是自己的目标更新 字段，都进行 update table set c1=value1,c2=value2,c3=value3; 这是不对的。执行 SQL 时，不要更新无改动的字段，一是易出错; 二是效率低; 三是增加 binlog 存储。\n【参考】@Transactional 事务不要滥用。事务会影响数据库的 QPS，另外使用事务的地方需要考虑各方面的回滚方案，包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。\n六、工程结构\n(一) 应用分层\n【参考】(分层异常处理规约) 在 DAO 层，产生的异常类型有很多，无法用细粒度的异常进 行 catch，使用 catch(Exception e) 方式，并 throw new DAOException(e)，不需要打印日志，因 为日志在 Manager/Service 层一定需要捕获并打印到日志文件中去，如果同台服务器再打日 志，浪费性能和存储。\n【参考】分层领域模型规约:\n• DO(Data Object): 此对象与数据库表结构一一对应，通过 DAO 层向上传输数据源对象。\n• DTO(Data Transfer Object): 数据传输对象，Service 或 Manager 向外传输的对象。\n• BO(Business Object): 业务对象，由 Service 层输出的封装业务逻辑的对象。\n• AO(Application Object): 应用对象，在 Web 层与 Service 层之间抽象的复用对象模型，极为贴 近展示层，复用度不高。\n• VO(View Object): 显示层对象，通常是 Web 向模板渲染引擎层传输的对象。\n• Query: 数据查询对象，各层接收上层的查询请求。注意超过 2 个参数的查询封装，禁止使用 Map 类来传输。\n(二) 二方库依赖\n【强制】定义 GAV 遵从以下规则: GroupID 格式: com.{公司 / BU}. 业务线 [. 子业务线]，最多 4 级。\nArtifactID 格式: 产品线名 - 模块名。正例: dubbo-client / fastjson-api / jstorm-tool\nVersion。\n【强制】二方库版本号命名方式: 主版本号. 次版本号. 修订号 说明: 注意起始版本号必须为: 1.0.0，而不是 0.0.1，正式版本号不允许覆盖升级。如当前版本: 1.3.3，那么下一个合理的版本号: 1.3.4 或 1.4.0 或 2.0.0\n【强制】二方库的新增或升级，保持除功能点之外的其它 jar 包仲裁结果不变。如果有改变，必须明确评估和验证。 说明: 在升级时，进行 dependency:resolve 前后信息比对，如果仲裁结果完全不一致，那么通过 dependency:tree 命令，找出差异点，进行 排除 jar 包。\n【强制】二方库里可以定义枚举类型，参数可以使用枚举类型，但是接口返回值不允许使用 枚举类型或者包含枚举类型的 POJO 对象。\n【强制】依赖于一个二方库群时，必须定义一个统一的版本变量，避免版本号不一致。\n【推荐】所有 pom 文件中的依赖声明放在 语句块中，所有版本仲裁放在 语句块中。 说明: 里只是声明版本，并不实现引入，因此子项目需要显式的声明依 赖，version 和 scope 都读取自父 pom。而 \u0026lt; dependencies \u0026gt; 所有声明在主 pom 的 \u0026lt; dependencies \u0026gt; 里 的依赖都会自动引入，并默认被所有的子项目继承。\n【推荐】二方库不要有配置项，最低限度不要再增加配置项。\n(三) 服务器\n【推荐】高并发服务器建议调小 TCP 协议的 time_wait 超时时间。\n【推荐】调大服务器所支持的最大文件句柄数 (FileDescriptor，简写为 fd)。\n【推荐】在线上生产环境，JVM 的 Xms 和 Xmx 设置一样大小的内存容量，避免在 GC 后调整 堆大小带来的压力。\n七、设计规约\n【强制】存储方案和底层数据结构的设计获得评审一致通过，并沉淀成为文档。\n【强制】在需求分析阶段，如果与系统交互的 User 超过一类并且相关的 UserCase 超过 5 个，使用用例图来表达更加清晰的结构化需求。\n【强制】如果某个业务对象的状态超过 3 个，使用状态图来表达并且明确状态变化的各个触 发条件。\n正例: 淘宝订单状态有已下单、待付款、已付款、待发货、已发货、已收货等。比如已下单与已收货这两 种状态之间是不可能有直接转换关系的。\n【强制】如果系统中某个功能的调用链路上的涉及对象超过 3 个，使用时序图来表达并且明确各调用环节的输入与输出。\n【强制】如果系统中模型类超过 5 个，并且存在复杂的依赖关系，使用类图来表达并且明确 类之间的关系。\n【强制】如果系统中超过 2 个对象之间存在协作关系，并且需要表示复杂的处理流程，使用活动图来表示。 说明: 活动图是流程图的扩展，增加了能够体现协作关系的对象泳道，支持表示并发等。\n【推荐】需求分析与系统设计在考虑主干功能的同时，需要充分评估异常流程与业务边界。\n【推荐】类在设计与实现时要符合单一原则。\n【推荐】谨慎使用继承的方式来进行扩展，优先使用聚合 / 组合的方式来实现。\n【推荐】系统设计时，根据依赖倒置原则，尽量依赖抽象类与接口，有利于扩展与维护。\n【推荐】系统设计时，注意对扩展开放，对修改闭合。\n【参考】设计的本质就是识别和表达系统难点，找到系统的变化点，并隔离变化点。\n","permalink":"https://leochu.work/blog/tech/engineering/%E9%98%BF%E9%87%8C%E7%BC%96%E7%A8%8B%E8%A7%84%E7%BA%A6/","summary":"\u003cp\u003e命名风格\u003c/p\u003e\n\u003col start=\"6\"\u003e\n\u003cli\u003e\n\u003cp\u003e【强制】抽象类命名使用 Abstract 或 Base 开头; 异常类命名使用 Exception 结尾; 测试类 命名以它要测试的类的名称开始，以 Test 结尾。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e【强制】POJO 类中布尔类型变量都不要加 is 前缀，否则部分框架解析会引起序列化错误。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e【强制】包名统一使用小写，点分隔符之间有且仅有一个自然语义的英语单词。包名统一使 用单数形式，但是类名如果有复数含义，类名可以使用复数形式。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e正例: 应用工具类包名为 com.alibaba.ai.util、类名为 MessageUtils\u003c/p\u003e\n\u003col start=\"13\"\u003e\n\u003cli\u003e【推荐】在常量与变量的命名时，表示类型的名词放在词尾，以提升辨识度。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e正例: startTime / workQueue / nameList / TERMINATED_THREAD_COUNT\u003c/p\u003e\n\u003cp\u003e17.【参考】枚举类名带上 Enum 后缀，枚举成员名称需要全大写，单词间用下划线隔开。\u003c/p\u003e\n\u003cp\u003eB) 领域模型命名规约\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e数据对象: xxxDO，xxx 即为数据表名。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e数据传输对象: xxxDTO，xxx 为业务领域相关的名称。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e展示对象: xxxVO，xxx 一般为网页名称。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003ePOJO 是 DO/DTO/BO/VO 的统称，禁止命名成 xxxPOJO。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e常量定义\u003c/p\u003e\n\u003col start=\"3\"\u003e\n\u003cli\u003e\n\u003cp\u003e【推荐】不要使用一个常量类维护所有常量，要按常量功能进行归类，分开维护。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e【推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eSPRING(1), SUMMER(2), AUTUMN(3), WINTER(4);\u003c/p\u003e\n\u003cp\u003e代码格式\u003c/p\u003e\n\u003col start=\"5\"\u003e\n\u003cli\u003e【强制】采用 4 个空格缩进，禁止使用 tab 字符。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eIDEA 设置 tab 为 4 个空格时，请勿勾选 Use tab character\u003c/p\u003e","title":"阿里编程规约"},{"content":"mysql/SQL server有，clickhouse也有，hive未验证。\nnull 值的比较这里另外说下 SQL 里 null 值的比较，任何与 null 值的比较结果，最后都会变成 null，以PostgreSQL为例，如下：select null != null;\nselect null = null; select null \u0026gt; 1; select null \u0026lt;\u0026gt; 1; 以上结果都是 null，而不是什么 true 或者 false。另外有些函数是不支持 null 值作为输入参数的，比如count()或者sum()等。\n在写 SQL 条件语句时经常用到 不等于 != 的筛选条件。此时要注意此条件会将字段为 Null 的数据也当做满足不等于的条件而将数据筛选掉。（也就是说会忽略过滤掉为 null 的数据，导致数据不准确）。\nSELECT * FROM A WHERE B1 != 1 不会查出null值\n修改：\nSELECT * FROM A WHERE B1 != 1 OR B1 is Null SELECT * FROM A WHERE IFNULL(B1,\u0026#39;\u0026#39;) != 1 为什么会这样呢？这还得从 mysql 的底层开始说起，因为 NULL 不是一个「值」，而是「没有值」。\n「没有值」不满足「值不等于1」这个条件，怎么解决可以使用 ifnull() 方法，将 null 转为空字符串，或者这个字段做出判断 加上OR a is null。当然使用 ifnull 相率会更好\nselect null = null返回也是null。null只能用is或is not\n","permalink":"https://leochu.work/blog/tech/database/sql%E4%B8%AD%E7%9A%84%E9%99%B7%E9%98%B1/","summary":"\u003cp\u003emysql/SQL server有，clickhouse也有，hive未验证。\u003c/p\u003e\n\u003cp\u003enull 值的比较这里另外说下 SQL 里 null 值的比较，任何与 null 值的比较结果，最后都会变成 null，以PostgreSQL为例，如下：select null != null;\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e以上结果都是 null，而不是什么 true 或者 false。另外有些函数是不支持 null 值作为输入参数的，比如count()或者sum()等。\u003c/p\u003e\n\u003cp\u003e在写 SQL 条件语句时经常用到 不等于 \u003ccode\u003e!=\u003c/code\u003e 的筛选条件。此时要注意此条件会将字段为 \u003ccode\u003eNull\u003c/code\u003e 的数据也当做满足不等于的条件而将数据筛选掉。（也就是说会忽略过滤掉为 null 的数据，导致数据不准确）。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSELECT\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eFROM\u003c/span\u003e A \u003cspan style=\"color:#66d9ef\"\u003eWHERE\u003c/span\u003e B1 \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e不会查出null值\u003c/p\u003e\n\u003cp\u003e修改：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSELECT\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eFROM\u003c/span\u003e A \u003cspan style=\"color:#66d9ef\"\u003eWHERE\u003c/span\u003e B1 \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eOR\u003c/span\u003e B1 \u003cspan style=\"color:#66d9ef\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eNull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSELECT\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eFROM\u003c/span\u003e A \u003cspan style=\"color:#66d9ef\"\u003eWHERE\u003c/span\u003e IFNULL(B1,\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e)  \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e为什么会这样呢？这还得\u003cstrong\u003e从 mysql 的底层开始说起，因为 NULL 不是一个「值」，而是「没有值」\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e　　\u003cstrong\u003e「没有值」不满足「值不等于1」这个条件，怎么解决可以使用 ifnull() 方法，将 null 转为空字符串，或者这个字段做出判断 加上OR a is null。当然使用 ifnull 相率会更好\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eselect null = null返回也是null。null只能用is或is not\u003c/p\u003e","title":"sql中!=的陷阱"},{"content":"根据日期查询数量\ndb.getCollection(\u0026#34;api_invocation_logs\u0026#34;).find({\u0026#34;createdDate\u0026#34;:{\u0026#34;$gte\u0026#34;:ISODate(\u0026#34;2024-01-09T00:00:00Z\u0026#34;),\u0026#34;$lt\u0026#34;:ISODate(\u0026#34;2024-01-10T00:00:00Z\u0026#34;)}}).count() 查看某个字段不为空的数据\ndb.getCollection(\u0026#39;waimao_company_info\u0026#39;).find({\u0026#39;numberOfEmployees\u0026#39;:{$ne:null}}) 查看某个表所有字段\nmr = db.runCommand({ \u0026#34;mapreduce\u0026#34;: \u0026#34;waimao_company_info\u0026#34;, \u0026#34;map\u0026#34;: function() { for (var key in this) { emit(key, null); } }, \u0026#34;reduce\u0026#34;: function(key, stuff) { return null; }, \u0026#34;out\u0026#34;: \u0026#34;waimao_company_info\u0026#34; + \u0026#34;_keys\u0026#34; }) db[mr.result].distinct(\u0026#34;_id\u0026#34;) 模糊查询\ndb.getCollection(\u0026#34;rocket\u0026#34;).find(\r{emails:/@/}\r) ","permalink":"https://leochu.work/blog/tech/database/mongodb%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/","summary":"\u003cp\u003e根据日期查询数量\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edb.getCollection(\u0026#34;api_invocation_logs\u0026#34;).find({\u0026#34;createdDate\u0026#34;:{\u0026#34;$gte\u0026#34;:ISODate(\u0026#34;2024-01-09T00:00:00Z\u0026#34;),\u0026#34;$lt\u0026#34;:ISODate(\u0026#34;2024-01-10T00:00:00Z\u0026#34;)}}).count()\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e查看某个字段不为空的数据\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edb.getCollection(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;waimao_company_info\u0026#39;\u003c/span\u003e).find(\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;numberOfEmployees\u0026#39;\u003c/span\u003e:\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{$\u003c/span\u003ene:\u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}}\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e查看某个表所有字段\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e db.runCommand(\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;mapreduce\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;waimao_company_info\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;map\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e() \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e (var \u003cspan style=\"color:#66d9ef\"\u003ekey\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ein\u003c/span\u003e this) \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            emit(\u003cspan style=\"color:#66d9ef\"\u003ekey\u003c/span\u003e, \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;reduce\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e(\u003cspan style=\"color:#66d9ef\"\u003ekey\u003c/span\u003e, stuff) \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;out\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;waimao_company_info\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;_keys\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edb[mr.\u003cspan style=\"color:#66d9ef\"\u003eresult\u003c/span\u003e].\u003cspan style=\"color:#66d9ef\"\u003edistinct\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;_id\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e模糊查询\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edb.getCollection(\u0026#34;rocket\u0026#34;).find(\r\n{emails:/@/}\r\n)\n\u003c/code\u003e\u003c/pre\u003e","title":"mongo操作"},{"content":"ES建表 PUT http://121.46.197.112:9200/cty_test Content-Type: application/json { \u0026#34;settings\u0026#34;: { \u0026#34;number_of_shards\u0026#34;: 5, \u0026#34;number_of_replicas\u0026#34;: 1, \u0026#34;analysis\u0026#34;: { \u0026#34;analyzer\u0026#34;: { \u0026#34;greek_lowercase_analyzer\u0026#34;: { \u0026#34;filter\u0026#34;: [ \u0026#34;lowercase\u0026#34; ], \u0026#34;type\u0026#34;: \u0026#34;custom\u0026#34;, \u0026#34;tokenizer\u0026#34;: \u0026#34;standard\u0026#34; } } } }, \u0026#34;mappings\u0026#34;: { \u0026#34;cty_test\u0026#34;: { \u0026#34;properties\u0026#34;: { \u0026#34;dupId\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;text\u0026#34;, \u0026#34;fields\u0026#34;: { \u0026#34;keyword\u0026#34;: { \u0026#34;ignore_above\u0026#34;: 256, \u0026#34;type\u0026#34;: \u0026#34;keyword\u0026#34; } } }, \u0026#34;id\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;text\u0026#34;, \u0026#34;fields\u0026#34;: { \u0026#34;keyword\u0026#34;: { \u0026#34;ignore_above\u0026#34;: 256, \u0026#34;type\u0026#34;: \u0026#34;keyword\u0026#34; } } }, \u0026#34;supplierId\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;long\u0026#34; }, \u0026#34;addTime\u0026#34;: { \u0026#34;format\u0026#34;: \u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;date\u0026#34; }, \u0026#34;scCountry\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;keyword\u0026#34; }, \u0026#34;shipName\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;text\u0026#34; }, \u0026#34;manifestQuanityDouble\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;float\u0026#34; }, \u0026#34;importerName\u0026#34;: { \u0026#34;analyzer\u0026#34;: \u0026#34;greek_lowercase_analyzer\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;text\u0026#34;, \u0026#34;fields\u0026#34;: { \u0026#34;keyword\u0026#34;: { \u0026#34;ignore_above\u0026#34;: 256, \u0026#34;type\u0026#34;: \u0026#34;keyword\u0026#34; } } }, \u0026#34;tradeType\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;integer\u0026#34; }, \u0026#34;arriveDate\u0026#34;: { \u0026#34;format\u0026#34;: \u0026#34;yyyy-MM-dd\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;date\u0026#34; }, \u0026#34;modeOfTransportation\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;keyword\u0026#34; }, \u0026#34;modeoftransPortation\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;text\u0026#34;, \u0026#34;fields\u0026#34;: { \u0026#34;keyword\u0026#34;: { \u0026#34;ignore_above\u0026#34;: 256, \u0026#34;type\u0026#34;: \u0026#34;keyword\u0026#34; } } }, \u0026#34;weight\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;double\u0026#34; } } } } } ES写入数据 POST http://121.46.197.112:9200/cty_test/cty_test Content-Type: application/json { \u0026#34;dupId\u0026#34;: \u0026#34;34324\u0026#34;, \u0026#34;id\u0026#34; : \u0026#34;545435\u0026#34;, \u0026#34;supplierId\u0026#34; : 43636, \u0026#34;addTime\u0026#34; : \u0026#34;2021-02-05 08:08:08\u0026#34;, \u0026#34;scCountry\u0026#34; : \u0026#34;rrr\u0026#34;, \u0026#34;shipName\u0026#34; : \u0026#34;uuu\u0026#34;, \u0026#34;manifestQuanityDouble\u0026#34; : 55.00, \u0026#34;importerName\u0026#34; : \u0026#34;ccd\u0026#34;, \u0026#34;tradeType\u0026#34; : 43, \u0026#34;arriveDate\u0026#34; : \u0026#34;2018-09-03\u0026#34;, \u0026#34;weight\u0026#34; : 33.0 } 查询某列不为空\nPOST looking_company_1/company/_search { \u0026#34;query\u0026#34;: { \u0026#34;bool\u0026#34;: { \u0026#34;must\u0026#34;: [ { \u0026#34;exists\u0026#34;: { \u0026#34;field\u0026#34;: \u0026#34;youtube\u0026#34; } } ], \u0026#34;should\u0026#34;: [ { \u0026#34;match_all\u0026#34;: {} } ] } }, \u0026#34;from\u0026#34;: 0, \u0026#34;size\u0026#34;: 10, \u0026#34;sort\u0026#34;: [], \u0026#34;aggs\u0026#34;: {} } 新建别名\nPOST http://elk-search.lcbint.cn:9200/_aliases\rContent-Type: application/json\r{\r\u0026#34;actions\u0026#34;: [\r{\r\u0026#34;add\u0026#34;: {\r\u0026#34;index\u0026#34;: \u0026#34;micro_indicator_event_mpviewscreen_pv\u0026#34;,\r\u0026#34;alias\u0026#34;: \u0026#34;micro_indicator_activity_overview_view\u0026#34; }\r},\r{\r\u0026#34;add\u0026#34;: {\r\u0026#34;index\u0026#34;: \u0026#34;micro_indicator_order_payment_nums\u0026#34;,\r\u0026#34;alias\u0026#34;: \u0026#34;micro_indicator_activity_overview_view\u0026#34; }\r},\r{\r\u0026#34;add\u0026#34;: {\r\u0026#34;index\u0026#34;: \u0026#34;micro_indicator_order_cancel_nums\u0026#34;,\r\u0026#34;alias\u0026#34;: \u0026#34;micro_indicator_activity_overview_view\u0026#34; }\r}\r]\r} 清空表\nPOST http://121.46.197.112:9200/looking_company_20220318_test/company/_delete_by_query Content-Type: application/json { \u0026#34;query\u0026#34;: { \u0026#34;match_all\u0026#34;: { } } } ","permalink":"https://leochu.work/blog/tech/database/elasticsearch%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/","summary":"\u003ch3 id=\"es建表\"\u003eES建表\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ePUT\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ehttp:\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e//121.46.197.112:9200/cty_test\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eContent-Type:\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eapplication/json\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;settings\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number_of_shards\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;number_of_replicas\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;analysis\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;analyzer\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;greek_lowercase_analyzer\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;filter\u0026#34;\u003c/span\u003e: [\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;lowercase\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;custom\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;tokenizer\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;standard\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;mappings\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;cty_test\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#f92672\"\u003e\u0026#34;properties\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;dupId\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fields\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ignore_above\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fields\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ignore_above\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;supplierId\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;long\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;addTime\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;format\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;yyyy-MM-dd HH:mm:ss\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;scCountry\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shipName\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;manifestQuanityDouble\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;float\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;importerName\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;analyzer\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;greek_lowercase_analyzer\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fields\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ignore_above\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;tradeType\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;integer\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;arriveDate\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;format\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;yyyy-MM-dd\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;date\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;modeOfTransportation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;modeoftransPortation\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;fields\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#f92672\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#f92672\"\u003e\u0026#34;ignore_above\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;keyword\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;weight\u0026#34;\u003c/span\u003e: {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;double\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"es写入数据\"\u003eES写入数据\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ePOST\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003ehttp:\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e//121.46.197.112:9200/cty_test/cty_test\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eContent-Type:\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003eapplication/json\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;dupId\u0026#34;\u003c/span\u003e: \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;34324\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;545435\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;supplierId\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#ae81ff\"\u003e43636\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;addTime\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2021-02-05 08:08:08\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;scCountry\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;rrr\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;shipName\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;uuu\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;manifestQuanityDouble\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#ae81ff\"\u003e55.00\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;importerName\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;ccd\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;tradeType\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#ae81ff\"\u003e43\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;arriveDate\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;2018-09-03\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003e\u0026#34;weight\u0026#34;\u003c/span\u003e : \u003cspan style=\"color:#ae81ff\"\u003e33.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e查询某列不为空\u003c/p\u003e","title":"es操作"},{"content":"在大数据平台运维中，会经常遇到集群资源争抢的问题。因为在公司内部，Hadoop Yarn 集群一般会被多个业务、多个用户同时使用，共享 Yarn 资源。此时，如果不对集群资源做规划和管理的话，那么就会出现 Yarn 的资源被某一个用户提交的 Application（App）占满，而其他用户只能等待；或者也可能会出现集群还有很多剩余资源，但 App 就是无法使用的情况。\n如何解决这个问题呢？此时就需要用到 Hadoop 中提供的资源调度器。\nYarn 多用户资源管理策略 Yarn 提供了可插拔的资源调度算法，用于解决 App 之间资源竞争的问题。在 Yarn 中有三种资源调度器可供选择，即 FIFO Scheduler、Capacity Scheduler、Fair Scheduler，目前使用比较多的是 Fair Scheduler 和 Capacity Scheduler。下面对这三种资源调度器分别进行介绍。\n1. FIFO Scheduler 在 Hadoop 1.x 系列版本中，默认使用的调度器是 FIFO，它采用队列方式将每个任务按照时间先后顺序进行服务。比如排在最前面的任务需要若干 Map Task 和 Reduce Task，当发现有空闲的服务器节点时就分配给这个任务，直到任务执行完毕。\n2. Capacity Scheduler 在 Hadoop 2.x/3.x 系列版本中，默认使用的调度器是 Capacity Scheduler（容量调度器），这是一种多用户、多队列的资源调度器。每个队列可以配置资源量，可限制每个用户、每个队列的并发运行作业量，也可限制每个作业使用的内存量；每个用户的作业有优先级，在单个队列中，作业按照先来先服务（实际上是先按照优先级，优先级相同的再按照作业提交时间）的原则进行调度。\n容量资源调度器，支持多队列，但默认情况下只有 root.default 这一个队列。\n当不同用户提交任务时，任务都会在这个队列里按照先进先出策略执行调度，很明显，单个队列会大大降低多用户的资源使用率。\n因此，要使用容量资源调度，一定要配置多个队列，每个队列可配置一定比率的资源量（CPU、内存）；同时为了防止同一个用户的任务独占队列的所有资源，调度器会对同一个用户提交的任务所占资源量进行限定。\n举个简单的例子，下图是容量调度器中配置好的一个队列树：\n上图通过队列树方式对 Yarn 集群资源做了一个划分，可以看到，在 root 队列下面定义了两个子队列 dev 和 test，分别占 30% 和 70% 的 Yarn 集群资源；而 dev 队列又被分成了 dev1 和 dev2 两个子队列，分别占用 dev 队列 30% 中的 40% 和 60% 的 Yarn 集群资源。\n容量调度除了可以配置队列及其容量外，还可以配置一个用户或任务可以分配的最大资源数量、同时可以配置运行应用的数量、队列的 ACL 认证等。\n如何让任务运行在指定的队列呢？ 有两种方式，一种是直接指定队列名，另一种是通过用户名、用户组和队列名进行对应。注意：对于容量调度器，我们的队列名必须是队列树中的最后一部分，如果使用队列树则不会被识别。例如，在上面配置中，可直接使用 dev1 和 dev2 作为队列名，但如果用 root.dev.dev1 或者 dev.dev2 则都是无效的。\n3. Fair Scheduler Fair Scheduler（公平调度器）支持多用户、多分组管理，每个分组可以配置资源量，也可限制每个用户和每个分组中并发运行的作业数量；每个用户的作业有优先级，优先级越高分配的资源就越多。公平调度器的主要目标是实现 Yarn 上运行的任务能公平的分配到资源。\nFair Scheduler 将整个 Yarn 的可用资源划分成多个队列资源池，每个队列中可以配置最小和最大的可用资源（内存和 CPU）、最大可同时运行 Application 数量、权重，以及可以提交和管理 Application 的用户等。\n资源池以及用户的对应关系如下图所示：\n在上图中，假设整个 Yarn 集群可用的 CPU 资源为 100vCPU，可用的内存资源为 100GB。现在为三个业务线各自划分一个队列，分别是 Queue1、Queue2 和 Queue3，每个队列可用的资源均为 20vCPU 和 20GB 内存，最后还规划了一个 default 队列，用于运行其他用户和业务提交的任务。可用资源为 40vCPU 和 40GB 内存，这样，四个队列将整个 Yarn 集群资源刚好分配完毕。\n在执行任务的时候，可以显性地指定任务运行的队列，但更多情况下不指定队列，而是通过用户名作为队列名称来提交任务，即用户 user1 提交的任务被分配到队列 Queue1 中，用户 user2 提交的任务被分配到资源池 Queue2 中。注意，这里的 user1 和 user2 是配置的固定用户，除了这些用户外，其他未指定的用户提交的任务将会被分配到 default 队列中。这里的用户名，就是提交 App 所使用的 Linux/Unix 的系统用户名。\n除了可以通过用户名作为队列名，在用户比较多的时候，还可以使用用户组，将同一类用户放到一个用户组下，然后将这个用户组配置到资源调度策略中。\n接下来，向你介绍 Fair Scheduler 调度的配置。\nFair Scheduler 调度的配置 要启用公平调度器，首先需要配置 yarn-site.xml 文件，添加如下设置：\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.resourcemanager.scheduler.class\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; 公平调度器的配置文件路径位于 HADOOP_CONF_DIR下 的 fair-scheduler.xml 文件中，这个路径可以通过配置 yarn-site.xml 文件，添加如下内容来实现：\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.fair.allocation.file\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;/etc/hadoop/conf/fair-scheduler.xml\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; 若没有这个配置文件，调度器会在用户提交第一个应用时为其自动创建一个队列，队列的名字就是用户名，所有的任务都会被分配到 default 队列中。\n接下来重点看看 fair-scheduler.xml 文件如何编写，此文件中定义队列的层次是通过嵌套元素实现的。所有的队列都是 root 队列的孩子，下面是一个定义好的公平调度策略：\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34;?\u0026gt; \u0026lt;allocations\u0026gt; \u0026lt;!-- users max running apps --\u0026gt; \u0026lt;userMaxAppsDefault\u0026gt;10\u0026lt;/userMaxAppsDefault\u0026gt; \u0026lt;queue name=\u0026#34;root\u0026#34;\u0026gt; \u0026lt;aclSubmitApps\u0026gt; \u0026lt;/aclSubmitApps\u0026gt; \u0026lt;aclAdministerApps\u0026gt; \u0026lt;/aclAdministerApps\u0026gt; \u0026lt;queue name=\u0026#34;default\u0026#34;\u0026gt; \u0026lt;minResources\u0026gt;12000mb,5vcores\u0026lt;/minResources\u0026gt; \u0026lt;maxResources\u0026gt;100000mb,50vcores\u0026lt;/maxResources\u0026gt; \u0026lt;maxRunningApps\u0026gt;22\u0026lt;/maxRunningApps\u0026gt; \u0026lt;schedulingMode\u0026gt;fair\u0026lt;/schedulingMode\u0026gt; \u0026lt;weight\u0026gt;1\u0026lt;/weight\u0026gt; \u0026lt;aclSubmitApps\u0026gt;*\u0026lt;/aclSubmitApps\u0026gt; \u0026lt;/queue\u0026gt; \u0026lt;queue name=\u0026#34;dev_group\u0026#34;\u0026gt; \u0026lt;minResources\u0026gt;115000mb,50vcores\u0026lt;/minResources\u0026gt; \u0026lt;maxResources\u0026gt;500000mb,150vcores\u0026lt;/maxResources\u0026gt; \u0026lt;maxRunningApps\u0026gt;181\u0026lt;/maxRunningApps\u0026gt; \u0026lt;schedulingMode\u0026gt;fair\u0026lt;/schedulingMode\u0026gt; \u0026lt;weight\u0026gt;5\u0026lt;/weight\u0026gt; \u0026lt;aclSubmitApps\u0026gt; dev_group\u0026lt;/aclSubmitApps\u0026gt; \u0026lt;aclAdministerApps\u0026gt;hadoop dev_group\u0026lt;/aclAdministerApps\u0026gt; \u0026lt;/queue\u0026gt; \u0026lt;queue name=\u0026#34;test_group\u0026#34;\u0026gt; \u0026lt;minResources\u0026gt;23000mb,10vcores\u0026lt;/minResources\u0026gt; \u0026lt;maxResources\u0026gt;300000mb,100vcores\u0026lt;/maxResources\u0026gt; \u0026lt;maxRunningApps\u0026gt;22\u0026lt;/maxRunningApps\u0026gt; \u0026lt;schedulingMode\u0026gt;fair\u0026lt;/schedulingMode\u0026gt; \u0026lt;weight\u0026gt;4\u0026lt;/weight\u0026gt; \u0026lt;aclSubmitApps\u0026gt; test_group\u0026lt;/aclSubmitApps\u0026gt; \u0026lt;aclAdministerApps\u0026gt;hadoop test_group\u0026lt;/aclAdministerApps\u0026gt; \u0026lt;/queue\u0026gt; \u0026lt;/queue\u0026gt; \u0026lt;queuePlacementPolicy\u0026gt; \u0026lt;rule name=\u0026#34;user\u0026#34; create=\u0026#34;false\u0026#34; /\u0026gt; \u0026lt;rule name=\u0026#34;primaryGroup\u0026#34; create=\u0026#34;false\u0026#34; /\u0026gt; \u0026lt;rule name=\u0026#34;secondaryGroupExistingQueue\u0026#34; create=\u0026#34;false\u0026#34; /\u0026gt; \u0026lt;rule name=\u0026#34;default\u0026#34; queue=\u0026#34;default\u0026#34; /\u0026gt; \u0026lt;/queuePlacementPolicy\u0026gt; \u0026lt;/allocations\u0026gt; 下面介绍这个配置中的几个配置项的含义：\n配置项 含义 userMaxAppsDefault 默认的用户最多可同时运行多少个应用程序 minResources 设置最少资源保证量，设置格式为“X mb, Y vcores”，当一个队列的最少资源保证量未满足时，它将优先于其他同级队列获得资源 maxResources 设置最多可以使用的资源量，fair scheduler 会保证每个队列使用的资源量不会超过该队列的最多可使用资源量 maxRunningApps 设置最多同时运行的应用程序数 schedulingMode 设置队列采用的调度模式，可以是 fifo、fair 或者 drf weight 设置队列的权重，权重越高，可获取的资源就越多 aclSubmitApps 表示可向队列中提交应用程序的用户和组列表，默认情况下为“*”，表示任何用户和组均可以向该队列提交应用程序 再来看一下队列执行规则列表（Queue Placement Policy），Fair 调度器采用了一套基于规则的配置来确定应用应该放到哪个队列中。在上面的例子中，我定义了一个规则列表，总共有四个规则，其中的每个规则会被逐个尝试，直到匹配成功。\n例如，第一个规则是 user，表示将提交任务的用户名作为队列名，然后将任务放到这个队列中执行；第二个规则 primaryGroup，表示将提交任务的用户所属的主组作为队列名；第三个规则 secondaryGroupExistingQueue 表示将提交任务的用户所属的附属组作为队列名；最后一个规则 default，表示当前面所有规则都不满足时，用户提交的任务会放到 default 队列中。\n除了上面的规则之外，还可以在 yarn-site.xml 文件添加如下配置：\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.fair.user-as-default-queue\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;true\u0026lt;/value\u0026gt; \u0026lt;description\u0026gt;default is True\u0026lt;/description\u0026gt; \u0026lt;/property\u0026gt; 此配置值默认为 true，表示当任务中未指定队列名时，将以用户名作为队列名，这个配置就实现了根据用户名自动分配队列；如果设置为 false，那么所有任务会被放入 default 队列，而不是放到基于用户名的队列中。\n另外，我们还可以在 yarn-site.xml 文件添加如下配置：\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.fair.allow-undeclared-pools\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;false\u0026lt;/value\u0026gt; \u0026lt;description\u0026gt;default is True\u0026lt;/description\u0026gt; \u0026lt;/property\u0026gt; 此配置表示是否允许创建未定义的队列，默认值为 true，表示 Yarn 将会自动创建任务中指定的未定义过的队列名。设置成 false 后，用户就无法创建队列了，该任务会被分配到 default 队列中。\n最后，再来说下资源抢占，当一个任务提交到一个繁忙集群中的空队列时，任务并不会马上执行，而是暂时阻塞，直到正在运行的任务释放系统资源，才开始执行。为了使提交的任务执行时间更具预测性（可以设置等待的超时时间），Fair 调度器支持抢占。\n抢占就是允许调度器杀掉占用超过其应占资源份额队列的 containers，这些 containers 资源释放后可，被分配到应该享有这些份额资源的队列中。需要注意，抢占会降低集群的执行效率，因为被终止的 containers 需要被重新执行。\n要启用抢占模式，可以在 yarn-site.xml 文件中添加如下配置：\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.fair.preemption\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;true\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; 可以设置此参数为 true 来启用抢占功能。此外，还需要在 fair-scheduler.xml 文件中添加一个参数用来控制抢占的过期时间，参数设置如下：\n\u0026lt;fairSharePreemptionTimeout\u0026gt;60\u0026lt;/fairSharePreemptionTimeout\u0026gt; 此参数用来设置某个队列的超时时间，如果队列在指定的时间内未获得最小的资源保障，调度器就会抢占 container。\n还可以在 fair-scheduler.xml 文件中添加全局配置参数，内容如下：\n\u0026lt;defaultFairSharePreemptionTimeout\u0026gt;60\u0026lt;/defaultFairSharePreemptionTimeout\u0026gt; 此参数用来配置所有队列的超时时间。\n这里需要注意，在 fair-scheduler.xml 配置中，添加了用户和用户组，这里的用户和用户组的对应关系，需要维护在 ResourceManager 上，ResourceManager 在分配资源池时候，是从 ResourceManager 所在的操作系统上读取用户和用户组的对应关系的，否则就会被分配到default 队列中。而客户端机器上的用户对应的用户组无关紧要。\n在 fair-scheduler.xml 第一次添加、配置完成后，需要重启 Yarn 集群才能生效，而后面再对 fair-scheduler.xml 进行修改用户或者调整资源池配额后，无须重启 yarn 集群，只需执行下面的命令刷新即可生效：\n[hadoop@yarnserver ~]$ yarn rmadmin -refreshQueues [hadoop@yarnserver ~]$ yarn rmadmin -refreshUserToGroupsMappings 动态更新只支持修改资源池配额，如果是新增或减少资源池，则还需要重启 Yarn 集群。\n容量调度与公平调度对比与选型 1. 相同\n容量调度和公平调度实现的功能基本一致，例如，它们都支持多用户、多队列，即都适用于多用户共享集群的应用环境。同时，单个队列均支持优先级和 FIFO 调度方式，还支持资源共享，即某个队列中的资源有剩余时，可共享给其他缺资源的队列。\n2. 不同\n核心调度策略不同 容量调度器的调度策略是，先选择资源利用率低的队列，然后在队列中同时考虑 FIFO 和内存因素；而公平调度器仅考虑公平，而公平是通过任务缺额体现的，调度器每次选择缺额最大的任务（队列的资源量，任务的优先级等仅用于计算任务缺额）。\n对特殊任务的处理不同 容量调度器调度任务时会考虑作业的内存限制，为了满足某些特殊任务的特殊内存需求，可能会为该任务分配多个 slot；而公平调度器对这种特殊的任务无能为力，只能杀掉这种任务。\n因此，具体选用哪种调度算法，可根据实际应用需求而定。一个基本的经验是，小型 Yarn 集群（100 个节点以内），可考虑使用公平调度器，而大型 Yarn 集群（超过 100 个节点）可采用容量调度器效果会更好。\n总结 本课时主要介绍了 Yarn 集群中常用的两个资源调度器：容量调度和公平调度。通过该课时的学习，我们了解到，在多个用户同时使用 Yarn 集群的时候，合理地设置调度器可以有效利用集群资源，并减少资源争抢，使集群资源利用率达到最大化。\n","permalink":"https://leochu.work/blog/tech/bigdata/yarn%E9%98%9F%E5%88%97%E8%AE%BE%E7%BD%AE/","summary":"\u003cp\u003e在大数据平台运维中，会经常遇到\u003cstrong\u003e集群资源争抢的问题\u003c/strong\u003e。因为在公司内部，Hadoop Yarn 集群一般会被多个业务、多个用户同时使用，共享 Yarn 资源。此时，如果不对集群资源做规划和管理的话，那么就会出现 Yarn 的资源被某一个用户提交的 Application（App）占满，而其他用户只能等待；或者也可能会出现集群还有很多剩余资源，但 App 就是无法使用的情况。\u003c/p\u003e\n\u003cp\u003e如何解决这个问题呢？此时就需要用到 Hadoop 中提供的\u003cstrong\u003e资源调度器\u003c/strong\u003e。\u003c/p\u003e\n\u003ch3 id=\"yarn-多用户资源管理策略\"\u003e\u003cstrong\u003eYarn 多用户资源管理策略\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eYarn 提供了可插拔的资源调度算法，用于解决 App 之间资源竞争的问题。在 Yarn 中有三种资源调度器可供选择，即 FIFO Scheduler、Capacity Scheduler、Fair Scheduler，目前使用比较多的是 Fair Scheduler 和 Capacity Scheduler。下面对这三种资源调度器分别进行介绍。\u003c/p\u003e\n\u003ch4 id=\"1-fifo-scheduler\"\u003e\u003cstrong\u003e1. FIFO Scheduler\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e在 Hadoop 1.x 系列版本中，默认使用的调度器是 FIFO，它采用队列方式将每个任务按照时间先后顺序进行服务。比如排在最前面的任务需要若干 Map Task 和 Reduce Task，当发现有空闲的服务器节点时就分配给这个任务，直到任务执行完毕。\u003c/p\u003e\n\u003ch4 id=\"2-capacity-scheduler\"\u003e\u003cstrong\u003e2. Capacity Scheduler\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e在 Hadoop 2.x/3.x 系列版本中，默认使用的调度器是 Capacity Scheduler（容量调度器），这是一种\u003cstrong\u003e多用户、多队列\u003c/strong\u003e的资源调度器。每个队列可以配置资源量，可限制每个用户、每个队列的并发运行作业量，也可限制每个作业使用的内存量；每个用户的作业有优先级，在单个队列中，作业按照先来先服务（实际上是先按照优先级，优先级相同的再按照作业提交时间）的原则进行调度。\u003c/p\u003e\n\u003cp\u003e容量资源调度器，支持多队列，\u003cstrong\u003e但默认情况下只有 root.default 这一个队列\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e当不同用户提交任务时，任务都会在这个队列里按照先进先出策略执行调度，很明显，单个队列会大大降低多用户的资源使用率。\u003c/p\u003e\n\u003cp\u003e因此，要使用容量资源调度，一定要配置多个队列，每个队列可配置一定比率的资源量（CPU、内存）；同时为了防止同一个用户的任务独占队列的所有资源，调度器会对同一个用户提交的任务所占资源量进行限定。\u003c/p\u003e\n\u003cp\u003e举个简单的例子，下图是容量调度器中配置好的一个队列树：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"队列树\" loading=\"lazy\" src=\"https://cdn.nlark.com/yuque/0/2022/png/21887514/1646927210814-732f7f8b-7cb1-4dc5-b649-f542a504f26b.png\"\u003e\u003c/p\u003e\n\u003cp\u003e上图通过队列树方式对 Yarn 集群资源做了一个划分，可以看到，在 root 队列下面定义了两个子队列 dev 和 test，分别占 30% 和 70% 的 Yarn 集群资源；而 dev 队列又被分成了 dev1 和 dev2 两个子队列，分别占用 dev 队列 30% 中的 40% 和 60% 的 Yarn 集群资源。\u003c/p\u003e","title":"yarn队列设置"},{"content":"/opt/cloudera/parcels/CDH/lib/sqoop/bin/sqoop import \\ --connect jdbc:[mysql://ip:port/T_Cloud_Promote](mysql://ipport) \\ --username rt_center \\ --password pwd\\ --query \u0026#34;select KeywordID,KeyType,Root,KeywordName,UID,Status,AddDate,UpdateDate,MigrateOID,PromotionStatus,WordType,IsEmphasis,KeywordLength,IsViolate,ViolateWord from T_Cloud_Promote.T_Cloud_User_KeywordLibrary where KeywordID \u0026lt; $[a+10000000] AND KeywordID \u0026gt;= ${a} AND AddDate != \u0026#39;0000-00-00 00:00:00\u0026#39; AND \\$CONDITIONS\u0026#34; \\ --fields-terminated-by \u0026#39;\\001\u0026#39; \\ --target-dir /tmp/t_cloud_order \\ --delete-target-dir \\ --hive-import \\ --hive-database T_Cloud_Promote \\ --hive-table ods_t_cloud_user_keywordlibraryb_tmp_01 \\ --null-string \u0026#39;\\\\N\u0026#39; \\ --null-non-string \u0026#39;\\\\N\u0026#39; \\ --hive-drop-import-delims \\ --num-mappers 5 \\ --split-by KeywordID \\ --hive-overwrite \u0026ndash;connect\nmysql连接\n\u0026ndash;username\n用户名\n\u0026ndash;password\n密码\n\u0026ndash;query\n条件查询语句\n\u0026ndash;fields-terminated-by\n分隔符\n\u0026ndash;target-dir\n临时存放位置\n\u0026ndash;delete-target-dir \\\n程序结束删除文件夹\n\u0026ndash;hive-import \\\n导入到hive\n\u0026ndash;hive-database T_Cloud_Promote \\\nhive的数据库\n\u0026ndash;hive-table\nhive表\n\u0026ndash;null-string \u0026lsquo;\\N\u0026rsquo; \\\n\u0026ndash;null-non-string \u0026lsquo;\\N\u0026rsquo; \\\n转换为hive空\n\u0026ndash;hive-drop-import-delims \\\n删除特殊分隔符如：\\n\\r \\0x01\n\u0026ndash;num-mappers 5 \\\n指定map的个数\n\u0026ndash;split-by KeywordID \\\n指定按哪个字段切分\n\u0026ndash;hive-overwrite\n覆盖hive表，若不写则追加\n","permalink":"https://leochu.work/blog/tech/bigdata/sqoop%E6%8F%90%E4%BA%A4%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90/","summary":"\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/opt/cloudera/parcels/CDH/lib/sqoop/bin/sqoop import \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--connect jdbc:\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003emysql://ip:port/T_Cloud_Promote\u003cspan style=\"color:#f92672\"\u003e](\u003c/span\u003emysql://ipport\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--username rt_center \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--password pwd\u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--query \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;select KeywordID,KeyType,Root,KeywordName,UID,Status,AddDate,UpdateDate,MigrateOID,PromotionStatus,WordType,IsEmphasis,KeywordLength,IsViolate,ViolateWord from T_Cloud_Promote.T_Cloud_User_KeywordLibrary where KeywordID \u0026lt; \u003c/span\u003e$\u003cspan style=\"color:#e6db74\"\u003e[a+10000000] AND KeywordID \u0026gt;= \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e${\u003c/span\u003ea\u003cspan style=\"color:#e6db74\"\u003e}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e AND AddDate != \u0026#39;0000-00-00 00:00:00\u0026#39; AND \\$CONDITIONS\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--fields-terminated-by \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\001\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--target-dir /tmp/t_cloud_order \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--delete-target-dir \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--hive-import \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--hive-database T_Cloud_Promote \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--hive-table ods_t_cloud_user_keywordlibraryb_tmp_01 \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--null-string \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\\\N\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--null-non-string \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\\\\N\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--hive-drop-import-delims \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--num-mappers \u003cspan style=\"color:#ae81ff\"\u003e5\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--split-by KeywordID \u003cspan style=\"color:#ae81ff\"\u003e\\ \u003c/span\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--hive-overwrite  \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;connect\u003cbr\u003e\nmysql连接\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;username\u003cbr\u003e\n用户名\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;password\u003cbr\u003e\n密码\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;query\u003cbr\u003e\n条件查询语句\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;fields-terminated-by\u003cbr\u003e\n分隔符\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;target-dir\u003cbr\u003e\n临时存放位置\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;delete-target-dir \\\u003cbr\u003e\n程序结束删除文件夹\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;hive-import \\\u003cbr\u003e\n导入到hive\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;hive-database T_Cloud_Promote \\\u003cbr\u003e\nhive的数据库\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;hive-table\u003cbr\u003e\nhive表\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u0026ndash;null-string \u0026lsquo;\\N\u0026rsquo; \\\u003cbr\u003e\n\u0026ndash;null-non-string \u0026lsquo;\\N\u0026rsquo; \\\u003cbr\u003e\n转换为hive空\u003c/p\u003e","title":"sqoop提交参数解析"},{"content":"spark-submit --master yarn --conf spark.default.parallelism=100 \\ --deploy-mode cluster --driver-memory 4G --executor-memory 4G \\ --num-executors 40 --executor-cores 3 \\ --conf spark.yarn.executor.memoryOverhead=5g \\ --class com.lz.hbase.CompanyInfo /tmp/test_langzi/original-spark_hbase01-1.0-SNAPSHOT.jar \\ --conf spark.dynamicAllocation.maxExecutors=40 ","permalink":"https://leochu.work/blog/tech/bigdata/spark%E6%8F%90%E4%BA%A4%E5%8F%82%E6%95%B0/","summary":"\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003espark-submit --master yarn --conf spark.default.parallelism\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--deploy-mode cluster --driver-memory 4G --executor-memory 4G \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--num-executors \u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e --executor-cores \u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--conf spark.yarn.executor.memoryOverhead\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e5g \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--class com.lz.hbase.CompanyInfo /tmp/test_langzi/original-spark_hbase01-1.0-SNAPSHOT.jar \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--conf spark.dynamicAllocation.maxExecutors\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e40\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"spark提交参数"},{"content":"文章目录 一、Spark Thrift Server 介绍 二、部署 Spark Thrift Server 三、Spark Thrift Server 的架构 四、Spark Thrift Server 如何执行 SQL 五、和 HiveServer2 的区别 Spark Thrift Server 的优点 Spark Thrift Server 的缺点 六、结论 一、Spark Thrift Server 介绍 Spark Thrift Server 是 Spark 社区基于 HiveServer2 实现的一个 Thrift 服务。旨在无缝兼容 HiveServer2。\n因为 Spark Thrift Server 的接口和协议都和 HiveServer2 完全一致，因此我们部署好 Spark Thrift Server 后，可以直接使用 hive 的 beeline 访问 Spark Thrift Server 执行相关语句。\nSpark Thrift Server 的目的也只是取代 HiveServer2，因此它依旧可以和 Hive Metastore 进行交互，获取到 hive 的元数据。\n二、部署 Spark Thrift Server 先将 hive-site.xml、hdfs-site.xml、core-site.xml 拷贝到 spark/conf 目录下。\n如果运行的 Hive MetaStore 版本不是 1.2，需要将 hive-site.xml 中的 hive.metastore.schema.verification 参数设置为 false。否则会因为版本不一致报错\n之后还需要拷贝相关 jar 包到 spark/jars 目录，否则会报 Could not load shims in class org.apache.hadoop.hive.schshim.FairSchedulerShim 错误。\ncp hive/lib/hive-shims* spark/jars\rcp hadoop/share/hadoop/yarn/hadoop-yarn-server-resourcemanager-2.7.4.jar spark/jars 之后启动 Thrift Server\n# ThriftServer的本质是将Server服务以spark job的形式提交到集群运行，所以需要指定队列\rsbin/start-thriftserver.sh --hiveconf spark.yarn.queue=root.bigdata.date 三、Spark Thrift Server 的架构 Spark Thrift Server 大量复用了 HiveServer2 的代码。\nHiveServer2 的架构主要是通过 ThriftCLIService 监听端口，然后获取请求后委托给 CLIService 处理。CLIService 又一层层的委托，最终交给 OperationManager 处理。OperationManager 会根据请求的类型创建一个 Operation 的具体实现处理。比如 Hive 中执行 sql 的 Operation 实现是 SQLOperation。\nSpark Thrift Server 做的事情就是实现自己的 CLIService——SparkSQLCLIService，接着也实现了 SparkSQLSessionManager 以及 SparkSQLOperationManager。另外还实现了一个处理 sql 的 Operation——SparkExecuteStatementOperation。这样，当 Spark Thrift Server 启动后，对于 sql 的执行就会最终交给 SparkExecuteStatementOperation 了。\nSpark Thrift Server 其实就重写了处理 sql 的逻辑，其他的请求处理就完全复用 HiveServer2 的代码了。比如建表、删除表、建 view 等操作，全部使用的是 Hive 的代码。\n四、Spark Thrift Server 如何执行 SQL Spark Thrift Server 的启动其实是通过 spark-submit 将 HiveThriftServer2 提交给集群执行的。因此执行 start-thriftserver.sh 时可以传入 spark-submit 的参数表示提交 HiveThriftServer2 时的参数。另外，因为 HiveThriftServer2 必须要在本地运行，所以提交时的 deployMode 必须是 client，如果设置成 cluster 会报错。HiveThriftServer2 运行起来后，就等于是一个 Driver 了，这个 Driver 会监听某个端口，等待请求。\n** 所以 HiveThriftServer2 程序运行起来后就等于是一个长期在集群上运行的 spark application。** 通过 yarn 或者 spark history server 页面我们都可以看到对应的任务。\n既然 HiveThriftServer2 就是 Driver，那么运行 SQL 就很简单了。Spark Thrift Server 收到请求后最终是交给 SparkExecuteStatementOperation 处理，SparkExecuteStatementOperation 拿到 SQLContext，然后调用 SQLContext.sql() 方法直接执行用户传过来的 sql 即可。后面的过程就和我们直接写了一个 Main 函数然后通过 spark-submit 提交到集群运行是一样的。\n五、和 HiveServer2 的区别 Hive on SparkSpark Thrift Server任务提交模式每个 session 都会创建一个 RemoteDriver，也就是对于一个 Application。之后将 sql 解析成执行的物理计划序列化后发到 RemoteDriver 执行本身的 Server 服务就是一个 Driver，直接接收 sql 执行。也就是所有的 session 都共享一个 Application性能性能一般如果存储格式是 orc 或者 parquet，性能会比 hive 高几倍，某些语句甚至会高几十倍。其他格式的话，性能相差不是很大，有时 hive 性能会更好并发如果任务执行不是异步的，就是在 thrift 的 worker 线程中执行，受 worker 线程数量的限制。异步的话则放到线程池执行，并发度受异步线程池大小限制。处理任务的模式和 Hive 一样。sql 兼容主要支持 ANSI SQL 2003，但并不完全遵守，只是大部分支持。并扩展了很多自己的语法Spark SQL 也有自己的实现标准，因此和 hive 不会完全兼容。具体哪些语句会不兼容需要测试才能知道HA可以通过 zk 实现 HA没有内置的 HA 实现，不过 spark 社区提了一个 issue 并带上了 patch，可以拿来用：https://issues.apache.org/jira/browse/SPARK-11100\rSpark Thrift Server 的优点 1、在大部分场景下，性能要比 Hive on spark 好，而且好很多\n2、SparkSQL 的社区活跃度也很高，基本每月都会发布一个版本，因此性能还会不断提高\nSpark Thrift Server 的缺点 1、因为 HiveThriftServer2 是以 Driver 的形式运行在集群的。因此它能使用的集群资源就和单个 Application 直接挂钩。如果 spark 集群没开启动态资源，那么 Spark Thrift Server 能得到的资源就始终都是固定的，这时候设置太大也不好，设置太小也不好。即使开启了动态资源，一般集群都会设置 maxExecutor，这时还是无法很好的利用集群的所有资源。如果将集群所有的资源都分配给了这个 Application，这样像 yarn、mesos 这些资源调度器就完全没有存在的意义了… 因此，单就这一点，Spark Thrift Server 就不是一个合格的企业级解决方案。\n2、从 https://issues.apache.org/jira/browse/SPARK-11100 官方的回答来看，spark 官方对于 Spark Thrift Server 这套解决方案也不是很满意。这也可以理解，毕竟 Spark Thrift Server 只是对 HiveServer2 进行的一些小改造。\n3、Spark Thrift Server 目前还是基于 Hive 的 1.2 版本做的改造，因此如果 MetaStore 的版本不是 1.2，那么也可能会有一些兼容性的潜在问题。\n六、结论 Spark Thrift Server 说白了就是小小的改动了下 HiveServer2，代码量也不多。虽然接口和 HiveServer2 完全一致，但是它以单个 Application 在集群运行的方式还是比较奇葩的。可能官方也是为了实现简单而没有再去做更多的优化。\n所以 Spark Thrift Server 最多搭建起来玩玩，或者自己内部做一些快速查询，并不适合真正放在企业级的使用上。\n","permalink":"https://leochu.work/blog/tech/bigdata/spark-thrift-server/","summary":"\u003ch4 id=\"文章目录\"\u003e文章目录\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#Spark_Thrift_Server_1\"\u003e一、Spark Thrift Server 介绍\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#Spark_Thrift_Server_9\"\u003e二、部署 Spark Thrift Server\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#Spark_Thrift_Server_29\"\u003e三、Spark Thrift Server 的架构\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#Spark_Thrift_ServerSQL_40\"\u003e四、Spark Thrift Server 如何执行 SQL\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#HiveServer2_48\"\u003e五、和 HiveServer2 的区别\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"#Spark_Thrift_Server_58\"\u003eSpark Thrift Server 的优点\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#Spark_Thrift_Server_64\"\u003eSpark Thrift Server 的缺点\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#_72\"\u003e六、结论\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"一spark-thrift-server-介绍\"\u003e一、Spark Thrift Server 介绍\u003c/h3\u003e\n\u003cp\u003eSpark Thrift Server 是 Spark 社区基于 HiveServer2 实现的一个 Thrift 服务。旨在无缝兼容 HiveServer2。\u003c/p\u003e\n\u003cp\u003e因为 Spark Thrift Server 的接口和协议都和 HiveServer2 完全一致，因此我们部署好 Spark Thrift Server 后，可以直接使用 hive 的 beeline 访问 Spark Thrift Server 执行相关语句。\u003c/p\u003e\n\u003cp\u003eSpark Thrift Server 的目的也只是取代 HiveServer2，因此它依旧可以和 Hive Metastore 进行交互，获取到 hive 的元数据。\u003c/p\u003e","title":"Spark Thrift Server"},{"content":"1.开启ranger-hive ranger-hdfs插件 2.修改hdfs配置 1.开启hdfs认证 hadoop.security.authorization = true\n","permalink":"https://leochu.work/blog/tech/bigdata/ranger%E9%85%8D%E7%BD%AE%E5%A4%A7%E6%95%B0%E6%8D%AE%E7%BB%84%E4%BB%B6/","summary":"\u003cp\u003e1.开启ranger-hive ranger-hdfs插件\n2.修改hdfs配置\n1.开启hdfs认证\nhadoop.security.authorization = true\u003c/p\u003e","title":"Ranger配置大数据组件"},{"content":"presto操作hive\npresto-cli --server 172.16.98.183:8050 --catalog hive ","permalink":"https://leochu.work/blog/tech/bigdata/presto%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/","summary":"\u003cp\u003epresto操作hive\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epresto-cli --server 172.16.98.183:8050 --catalog hive\n\u003c/code\u003e\u003c/pre\u003e","title":"presto"},{"content":"\n**1.**什么是 onedata\n面对爆炸式增长的数据，如何建设高效的数据模型和体系，对这些数据进行有序和有结构地分类组织和存储，避免重复建设和数据不一致性，保证数据的规范性，一直是大数据系统建设不断追求的方向。OneData 即是阿里巴巴内部进行数据整合及管理的方法体系和工具。阿里巴巴的大数据工程师在这一体系下，构建统一、规范、可共享的全域数据体系，避免数据的冗余和重复建设，规避数据烟囱和不一致性，充分发挥阿里巴巴在大数据海量、多样性方面的独特优势。借助这一统一化数据整合及管理的方法体系，我们构建了阿里巴巴的数据公共层，并可以帮助相似的大数据项目快速落地实现。\n**2.**指导思想\n阿里巴巴集团数据公共层设计理念遵循维度建模思想，可参考 StarSchema-The Complete Reference 和 The Data Warehouse Toolkit-The Definitive Guide to Dimensional Modeling。数据模型的维度设计主要以维度建模理论为基础，基于维度数据模型总线架构，构建一致性的维度和事实。其核心的实施指导方针如下：\n首先，要进行充分的业务调研和需求分析。\n其次，进行数据总体架构设计，主要是根据数据域对数据进行划分；按照维度建模理论，构建总线矩阵，抽象出业务过程和维度。\n再次，对报表需求进行抽象整理出相关指标体系，使用 One Data 工具完成指标规范定义和模型设计。\n最后，是代码研发和运维。其实施流程主要分为：数据调研、架构设计、规范定义和模型设计。\n**3.**业务调研\n业务调研：需要确认要规划进数仓的业务领域，以及各业务领域包含的功能模块，以阿里的业务为例，可规划如下矩阵\n需求调研：了解需求方关系哪些指标？需要哪些维度、度量？数据是否沉淀到汇总层等。\n可以想象一下，在没有考虑分析师、业务运营人员的数据需求的情况下，根据业务调研建设的数据仓库无疑等于闭门造车。了解了业务系统的业务后并不代表就可以进行实施了，此刻要做的就是收集数据使用者的需求，可以去找分析师、业务运营人员了解他们有什么数据诉求，此时更多的就是报表需求。\n需求调研的途径有两种:一是根据与分析师、业务运营人员的沟通(邮件、IM)获知需求；二是对报表系统中现有的报表进行研究分析。通过需求调研分析后，就清楚数据要做成什么样的。很多时候，都是由具体的数据需求驱动数据仓库团队去了解业务系统的业务数据，这两者并没有严格的先后顺序。\n举例：分析师需要了解大淘宝(淘宝、天猫、天猫国际)一级类目的成交金额。当获知这个需求后，我们要分析根据什么(维度)汇总，以及汇总什么(度量)，这里类目是维度，金额是度量；明细数据和汇总数据应该怎样设计?这是一个公用的报表吗?是需要沉淀到汇总表里面，还是在报表工具中进行汇总?\n**4.**架构设计\n4.1 数据域的划分\n数据域是指面向业务分析，将业务过程或者维度进行抽象的集合，一般数据域和应用系统(功能模块)有联系，可以考虑将同一个功能模块系统的业务过程划分到一个数据域。业务过程可以概括为一个个不可拆分的行为事件，如下单、支付、退款。为保障整个体系的生命力，数据域需要抽象提炼，并且长期维护和更新，但不轻易变动。在划分数据域时，既能涵盖当前所有的业务需求，又能在新业务进入时无影响地被包含进已有的数据域中或者扩展新的数据域。如表所示是功能模块/业务线的业务动作(部分示例)：\n根据业务过程进行归纳，可以抽象出如下数据域：\n4.2 构建总线矩阵\n在进行充分的业务调研和需求调研后，就要构建总线矩阵了，需要做两件事情：\n1.明确每个数据域下有哪些业务过程。\n2.业务过程与哪些维度相关，并通过总线矩阵定义每个数据域下的业务过程和维度。\n如下表是供应链管理业务过程示例：\n4.3 规范定义\n规范定义主要定义指标体系，包括原子指标、修饰词、时间周期和派生指标。\n4.4 模型设计\n模型设计主要包括维度及属性的规范定义，维表、明细事实表和汇总事实表的模型设计。\n4.5 架构总结\nOne Data 的实施过程是一个高度迭代和动态的过程，一般采用螺旋式实施方法。在总体架构设计完成之后，开始根据数据域进行迭代式模型设计和评审。在架构设计、规范定义和模型设计等模型实施过程中，都会引入评审机制，以确保模型实施过程的正确性。\n**5.**指标体系搭建\n5.1 指标体系核心结构\n具体模块说明：\n1.数据域：是指一个或多个业务过程或者维度的集合。\n2.原子指标：基于某一业务过程下的度量。例如：搜索+次数=访问次数 PV\n3.派生指标=时间修饰+其他修饰词+原子指标；属性是用来刻画某个实体对象维度的数据形态；事实叫做度量，如访问次数。\n4.修饰：指针对原子指标的业务场景限定抽象。例如：最近 N 天。\n5.2 指标体系中的数据规范\n5.3 指标体系的基本原则\n5.3.1 组成体系之间的关系\n1.原子指标、修饰类型及修饰词，直接归属在业务过程下，其中修饰词继承修饰类型的数据域。\n2.派生指标由原子指标、时间周期修饰词、若干其他修饰词组合得到。派生指标可以选择多个修饰词，修饰词之间的关系为‘或’或者‘且’的关系，具体由具体的派生指标语义决定。\n3.派生指标唯一归属一个原子指标，继承原子指标的数据域、与修饰词的数据域无关。一般而言：事务型指标和存量型指标只会唯一定位到一个业务过程，如果遇到同时有两个行为发生、需要多个修饰、生成一个派生指标的话，选择时间靠后的行为创建原子指标，另一个时间靠前的行为创建为修饰词。\n4.原子指标有确定的英文字段名、数据类型和算法说明；派生指标要继承原子指标的英文名、数据类型和算法要求。\n5.3.2 命名约定\n命名所用术语：尽量使用英文简写，其次是英文，当指标英文名太长时，可考虑用汉语拼音首字母命名。如中国质造，用 zgzc。在 OneData 工具中，维护了常用的名词术语，以用来进行命名。\n(1)业务过程：英文名——用英文的缩写或者英文或者中文拼音简写；中文名——具体的业务过程中文即可。\n(2)原子指标：英文名：动作+度量；中文名：动作+度量；原子指标必须挂靠在某个业务过程下。\n(3)修饰词：只有时间周期才会有英文名，且长度为 2 位，加上“_”为三位，例如_1d。其他修饰词无英文名。阿里常用的时间周期修饰词列表如下:\n(4)派生指标\n英文名：原子指标英文名+时间周期修饰词（=3 位，例如，_1d）+序号（=4 位，例如，_001）。\n中文名：时间周期修饰词+[其他修饰词]+原子指标。\n在 One Data 工具中，英文名与中文名都会由 One Data 工具自动生成。\n举例如下：\n注意：派生指标为了控制英文名称过长，在英文名的理解和规范上做了取舍，所有修饰词的含义都纳入了序号中。序号是根据原子指标+派生指标自增的。\n5.3.3 操作细则\n派生指标的分类：\n派生指标可以分为三类：事务型指标、存量型指标和复合型指标。按照其特性不同，有些必须新建原子指标，有些可以在其他类型原子指标基础上增加修饰词形成派生指标。\n事务型指标：是指对业务活动进行衡量的指标。例如，新发商品数，重发商品数，新增注册会员数，订单支付金额，这类指标 需维护原子指标及修饰词，在此基础上创建派生指标。\n存量型指标：是指对实体对象(如商品、会员)，某些状态的统计。例如，商品总数，注册会员总数，这类指标维护原子指标及修饰词，在此基础上创建派生指标，对应的时间周期一般为“历史截止到当前某个时间”。\n复合型指标：是在事务性指标和存量型指标基础上复合而成的，例如，浏览UV-下单买家数转化率，有些需要创建新原子指标，有些则可以在事务性或存量型原子指标基础上、增加修饰词得到派生指标。\n举个例子\n复合型指标的规则：\n1.比率型：需创建原子指标。例如，CTR，浏览 UV-下单买家数转化率，满意率等。\n举例：“最近 1 天店铺首页 CTR”，原子指标为“CTR”，时间周期为“最近 1天”，修饰类型为“页面类型”，修饰词为“店铺首页”。\n2.比例型：创建原子指标。例如，百分比、占比。举例:“最近 1 天无线支付金额占比”，原子指标为“支付金额占比”，修饰类型为“终端类型”，修饰词为“无线。\n3.变化量型：不创建原子指标，增加修饰词，在此基础上创建派生指标。举例：“最近 1 天订单支付金额上 1 天变化量”，原子指标为“订单支付金额”，时间周期为“最近 1 天”，修饰类型为“统计方法”，修饰词为“上 1 天变化量”。\n变化率型：创建原子指标。举例:“最近 7 天海外买家支付金额上 7 天变化率”，原子指标为”支付金额变化率”，修饰类型为“买家地域”，修饰词为“海外买家”。\n4.统计型（均值、分位数等）：不创建原子指标，增加修饰词，在此基础上创建派生指标；在修饰类型“统计方法”下增加修饰词：人均、日均、行业平均、商品平均、90 分位数、70 分位数等。举例：自然月日均 UV，原子指标为 UV，修饰词为“统计方法”，修饰词为“日均”。\n5.排名型：i.创建原子指标，一般为 top_xxx_xxx，有时会同时选择 rank 和top_xxx_xxx 组合使用。ii.创建派生指标时选择对应的修饰如下：统计方法（例如：降序，升序）；排名名次（例如：TOP10）；排名范围（例如：行业、省份、一级来源等）；根据什么排序(例如:搜索次数，浏览 PV)。示例如下：\n6.对象集合型：i. 创建原子指标，一般为 xxx 串； ii.创建派生指标时选择对应的修饰如下：统计方法（例如：降序，升序）；排名名次（例如：TOP10）；排名范围（例如：行业，区域)。示例如下：\n7.上下层级派生指标同时存在时：如最近 1 天定位次数，最近 1 天 PC 端定位次数，建议使用前者，把 PC 端作为维度属性存放在物理表中体现。\n8.父子关系原子指标存在时：当父子关系原子指标存在时，派生指标使用子原子指标创建派生指标。如 PV，IPV(商品详情页 PV)，当我们统计商品详情页 PV 时，优先选择子原子指标。\n5.4 维度属性的基本规则\n1.命名约定：维度和维度属性，尽量使用英文简写，其次是英文，当指标英文名太长时，可考虑用汉语拼音首字母命名。如中国质造，用 zgzc。在 OneData 工具中，维护了常用的名词术语，以用来进行命名。\n2.维度的归属：维度必须归属于某个数据域。\n3.在创建维度时，必须清楚维度的主键是什么：如会员维度的主键是会员 ID，商品维度的主键是商品 ID。从源系统引入的属性，如果没有重复，则尽量遵从源系统命名，如果有重复则与业务方沟通选择合理的命名以方便业务使用。\n4.维度属性：在维度下创建维度属性分为两类，一是直接从源系统引入，二是在源系统基础上、根据应用需求的数据挖掘出的属性特征作为维度的属性。\n5.关于行为维度和杂项维度：杂项维度一般指将事实表中的指示符、状态、分类、属性、标签等字段单独创建维表。比如交易订单、物流订单均可以称为杂项维度。行为维度是基于历史事实构建的维度，如会员最后一次登录时间，最后一次浏览的网页，这个可作为会员的维度属性，不建议单独创建维度。\n6.维度的拆解\na. 如果维度不由事实决定两者的关系，如商品属于某个类目，建议通过父子关系拆解成两个维度。\nb. 如果两个维度是由事实的发生才建立关系，则必须拆解成多个维度，如交易表中的商品和买家。\nc. 杂项维度需新建一个维度，行为维度建议放在主体对象维度中。\nd. 角色维度如会员在交易中即扮演买家的角色又扮演卖家的角色，这两种角色的会有较多不一样的维度属性，如果会员这个维度会在多个事实中存在买家和卖家的角色，则可创建买家和卖家维度，并作为会员维度的子维度。\n7.维度属性的层级关系：商品归属于类目，所以商品的父维度是类目，类目的子维度是商品。OneData 不支持商品维度中添加类目的 ID，但可通过 OneData 工具中父子层级关系的管理来解决。\n8.OneData 中为了操作的便利性，没有把维度层级的父子关系、维度角色的父子关系以及核心和自定义的父子关系进行区分,两者均可通过 OneData 的维度关系管理建立维度的父子关系。\na.维度层级关系:商品归属于类目，所以商品的父维度是类目，类目的子维度是商品。\nb.角色关系:如会员可作为卖家、也可作为买家，这两种角色就会有其自己相关的维度属性，我们建议创建买家和卖家维度并作为子维度挂在会员维度下核心和自定义。\n9.多值维度：如果是行为累计的串类维度，如车辆运行轨迹中的 GPS 点列表，需要对轨迹做详细分析时建议在用户到达杂项维度创建 GPS 轨迹列表维度属性。如果是由于维度在事实中扮演的多个角色造成的多值维度建议按照第三点中的角色维度处理。\n**6.**模型设计\n6.1 数据分层\n1.数仓为什么要分层？\n(1)用空间换时间，通过大量的预处理来提升应用系统的用户体验（效率），因此数据仓库会存在大量冗余的数据；不分层的话，如果源业务系统的业务规则发生变化将会影响整个数据清洗过程，工作量巨大。\n(2)通过数据分层管理可以简化数据清洗的过程，因为把原来一步的工作分到了多个步骤去完成，相当于把一个复杂的工作拆成了多个简单的工作，把一个大的黑盒变成了一个白盒，每一层的处理逻辑都相对简单和容易理解，这样我们比较容易保证每一个步骤的正确性，当数据发生错误的时候，往往我们只需要局部调整某个步骤即可。\n一个好的分层架构，有以下好处：\n(1)清晰数据结构：每一个数据分层都有对应的作用域，在使用数据的时候能更方便的定位和理解。\n(2)数据血缘追踪：提供给业务人员或下游系统的数据服务时都是目标数据，目标数据的数据来源一般都来自于多张表数据。若出现目标数据异常时，清晰的血缘关系可以快速定位问题所在。而且，血缘管理也是元数据管理重要的一部分。\n(3)减少重复开发：数据的逐层加工原则，下层包含了上层数据加工所需要的全量数据，这样的加工方式避免了每个数据开发人员都重新从源系统抽取数据进行加工。\n(4)数据关系条理化：源系统间存在复杂的数据关系，比如客户信息同时存在于核心系统、信贷系统、理财系统、资金系统，取数时该如何决策呢？数据仓库会对相同主题的数据进行统一建模，把复杂的数据关系梳理成条理清晰的数据模型，使用时就可避免上述问题了。\n(5)屏蔽原始数据的影响：数据的逐层加工原则，上层的数据都由下一层的数据加工获取，不允许跳级取数。而原始数据位于数仓的最底层，离应用层数据还有多层的数据加工，所以加工应用层数据的过程中就会把原始数据的变更消除掉，保持应用层的稳定性。\n业界对数仓分层的看法大同小异，大体上认为分为接入层、中间层和应用层三层，不过对中间层的理解有些差异。\n阿里巴巴的数据团队对模型层次做如下划分：\n特别说明：数据公共层（CDM，Common Dimenions Model）：存放明细事实数据、维表数据及公共指标汇总数据。其中，明细事实数据、维表数据一般根据ODS 层数据加工生成。公共指标汇总数据一般根据维表数据和明细事实数据加工生成。\nCDM 层又细分为维度层（DIM）、明细数据层（DWD）和汇总数据层（DWS），采用维度模型方法作为理论基础，可以定义维度模型主键与事实模型中外键关系，减少数据冗余，也提高明细数据表的易用性。在汇总数据层同样可以关联复用统计粒度中的维度，采取更多的宽表化手段构建公共指标数据层，提升公共指标的复用性，减少重复加工。\n6.1.1 接入层**(ods)**\n数据引入层（ODS，Operational Data Store，又称数据基础层）：将原始数据几乎无处理地存放在数据仓库系统中，结构上与源系统基本保持一致，是数据仓库的数据准备区。\n业务数据一般是采用 dataX 或者 sqoop 等以固定频率同步到数仓中构建 ODS层；如果是日志数据则通过 flume 或者 Kafka 等同步到数仓中。接入层一般不会对源数据做任何处理、清洗，便于之后回溯。\n6.1.2 通用维度层**(dim)**\n维度层（DIM，Dimension）：以维度作为建模驱动，基于每个维度的业务含义，通过添加维度属性、关联维度等定义计算逻辑，完成属性定义的过程并建立一致的数据分析维表。为了避免在维度模型中冗余关联维度的属性，基于雪花模型构建维度表。\n6.1.3 明细层**(dwd)**\n明细数据层（DWD，Data Warehouse Detail）：以业务过程作为建模驱动，基于每个具体的业务过程特点，构建最细粒度的明细事实表。可以结合企业的数据使用特点，将明细事实表的某些重要属性字段做适当冗余，也即宽表化处理。理论上明细层数据是对 ods 层数据进行清洗加工，提高 ods 层数据的可用性，对于 dwd 层数据是否同层引用的观点需要权衡：\n一般情况下 dwd 层不建议同层引用，这样做可以减少明细层任务之间的依赖，减少节点深度。但是在某些场景下，ods 层到 dwd 层数据加工逻辑复杂，计算开销大，这时可以权衡考虑适当复用 dwd 表来构建新的 dwd 表。\n6.1.4 汇总层**(dws)**\n汇总数据层（DWS，Data Warehouse Summary）：以分析的主题对象作为建模驱动，基于上层的应用和产品的指标需求，构建公共粒度的汇总指标表。以宽表化手段物理化模型，构建命名规范、口径一致的统计指标，为上层提供公共指标，建立汇总宽表、明细事实表。\n这一层依赖我们的指标体系，将 dwd 层的数据按照各个维度进行聚合计算。\n当我们有一些跨业务域的聚合统计需求时，放到这一层。\n6.1.5 应用层**(app)**\n数据应用层（ADS，Application Data Store）：存放数据产品个性化的统计指标数据，根据 CDM 层与 ODS 层加工生成。\n这一层主要针对汇总层，进行相关指标的组合，生成报表。在这里，主要是提供给数据产品和数据分析使用的数据，一般会存放在 ES、PostgreSql、Redis 等系统中供线上系统使用，也可能会存在 Hive 或者 Druid、Doris 中供数据分析和数据挖掘使用。比如我们经常说的报表数据，一般就放在这里。\n6.2 维度设计\n维度建模中，将度量称为事实，维度用于分析事实所需要的多样环境。维度的作用一般是查询、分类汇总以及排序。\n通过报表的约束条件，以及之前数据调研和业务方的沟通，我们可以获得维度。维度通过主键与事实表进行关联，维度表的主键分为代理键和自然键两种；\n代理键不具有业务含义，一般用于处理缓慢变化维度，自然键则具有业务含义。\n6.2.1 维度设计基本方法\n1.选择或者新建一个维度，通过之前总线矩阵的构建掌握了目前数仓架构中的维度。\n2.确定主维表。此处主维表一般是 ODS 表，直接与业务系统同步。\n3.确定相关维表。数仓是业务源系统的数据整合，不同业务系统或者同一业务系统中的表之间存在关联性。根据对业务的梳理，我们可以确认哪些表和主维表存在关联关系，并选择其中的某些表用于生成维度属性。\n4.确定维度属性。本步骤分为两阶段，第一阶段是从主维表中选择维度属性或生成新的维度属性；第二阶段是从相关维表中选择维度属性或生成新的维度属性。\n6.2.2 规范化和反规范化\n当具有多层次的维度属性，按照第三范式进行规范化后形成一系列维度表，而非单一维度表，这种建模称为雪花模式。\n将维度的属性层次合并到单个维度中的操作称为反规范化。\n6.2.3 一致性维度和交叉探查\n我们存在很多需求是对于不同数据域的业务过程或同一数据域的不同业务过程合并在一起观察。例如：对于日志数据域统计商品维度的近一天 PV 和 UV；\n对于交易数据域统计商品维度近一天的 GMV。\n这种将不同数据域的商品事实合并在一起进行数据探查，称为交叉探查。\n数仓能进行交叉探查的前提是，不同数据域要具有一致性维度。\n6.2.4 维度整合\n由于数仓的数据源来源于不同的应用系统，应用系统之间相互独立，因此对同一信息的描述、存储都可能具有差异。而这些具有差异的数据进入数仓后需要整合在一起：\n命名规范的统一。表名、字段名等统一。\n字段类型的统一。相同和相似字段的字段类型统一。\n公共代码以及代码值的统一。\n业务含义相同的表的统一。主要依据高内聚、低耦合的理念，将业务关系大，源系统影响差异小的表进行整合。表级别的整合主要有两种形式：\n垂直整合，即不同来源表包含相同的数据集，只是存储的信息不同，可以整合到同一个维度模型中。\n水平整合，即不同来源表包含不同的数据集，这些子集之间无交叉或存在部分交叉，如果有交叉则去重；如果无交叉，考虑不同子集的自然键是否冲突，不冲突则可以将各子集自然键作为整合后的自然键，或者将各自然键加工成一个超自然键\n6.3 事实表设计\n事实表中一条记录所表达的业务细节程度称为粒度。\n6.3.1 事实类型\n作为度量业务过程的事实，有可加性、半可加性和不可加性三种类型：\n可加性事实指可以按照与事实表关联的任意维度进行汇总。\n半可加事实只能按照特定维度汇总，不能对所有维度汇总。\n不可加性事实完全不具备可加性，比如比例事实。对于不可加性事实可考虑分解为可加的组件来实现聚合。\n6.3.2 事实表类型\n最常见的事实表有三种类型：事务事实表、周期快照事实表和累积快照事实表。\n事务事实表用来描述业务过程，表示对应时空上某点的度量事件，保存的是最原子的数据，也称为原子事实表。在实际使用中，一般作为明细层使用，例如下单明细、支付明细等。\n周期快照事实表的一行，以具有规律性的时间间隔记录事实。如每日库存快照表、每日用户余额快照表。\n累积快照事实表用来表述过程开始和结束之间的关键步骤事件，覆盖过程的整个生命周期，通常具有多个日期字段来记录关键时间点，当过程随着生命周期不断变化时，记录也会随着过程的变化而被修改。以事务事实表中提到的订单例子为例，可以做一个和订单相关的，涉及订单下单、推单、抢单、支付等各个环节的一张订单全生命周期快照表。\n此外，还有一种无事实的事实表，单纯只记录某一动作发生，其事件的量化是非数字的，比较典型的例子是访问点击日志。\n6.3.3 事实表设计原则\n尽可能包含所有与业务过程相关的事实。\n只选择与业务过程相关的事实。\n分解不可加性事实为可加的组件。\n在选择维度和事实之前必须先声明粒度。\n在同一个事实表中不能有多种不同粒度的事实。\n事实的单位要保持一致。\n对事实的 null 值要处理，建议用 0 填充。\n使用退化维度提高事实表的易用性。\n6.3.4 事实表设计方法\n主要步骤包括：选择业务过程及确认事实表类型；声明粒度；确定维度；确定事实；冗余维度。\n第一步:选择业务过程及确定事实表类型。\n在明确了业务需求以后，接下来需要进行详细的需求分析，对业务的整个生命周期进行分析，明确关键的业务步骤，从而选择与需求有关的业务过程。\n以淘宝的正向订单流转为例，如图所示。\n业务过程通常使用行为动词表示业务执行的活动。比如图中的淘宝订单流转的业务过程有四个:创建订单、买家付款、卖家发货、买家确认收货。在明确了流程所包含的业务过程后，需要根据具体的业务需求来选择与维度建模有关的业务过程。比如是选择买家付款这个业务过程，还是选择创建订单和买家付款这两个业务过程，具体根据业务情况来确定。\n在选择了业务过程以后，相应的事实表类型也随之确定了。比如选择买家付款这个业务过程，那么事实表应为只包含买家付款这一个业务过程的单事务事实表;如果选择的是所有四个业务过程，并且需要分析各个业务过程之间的时间间隔，那么所建立的事实表应为包含了所有四个业务过程的累积快照事实表。\n第二步:声明粒度。\n粒度的声明是事实表建模非常重要的一步,意味着精确定义事实表的每一行所表示的业务含义,粒度传递的是与事实表度量有关的细节层次。明确的粒度能确保对事实表中行的意思的理解不会产生混淆，保证所有的事实按照同样的细节层次记录。\n应该尽量选择最细级别的原子粒度，以确保事实表的应用具有最大的灵活性。同时对于订单过程而言粒度可以被定义为最细的订单级别。比如在淘宝订单中有父子订单的概念，即一个子订单对应一种商品，如果拍下了多种商品，则每种商品对应一个子订单;这些子订单一同结算的话，则会生成一个父订单。那么在这个例子中，事实表的粒度应该选择为子订单级别。\n第三步:确定维度。\n完成粒度声明以后，也就意味着确定了主键，对应的维度组合以及相关的维度字段就可以确定了，应该选择能够描述清楚业务过程所处的环境的维度信息。比如在淘宝订单付款事务事实表中，粒度为子订单，相关的维度有买家、卖家、商品、收货人信息、业务类型、订单时间等维度。\n第四步:确定事实。\n事实可以通过回答“过程的度量是什么”来确定。应该选择与业务过程有关的所有事实，且事实的粒度要与所声明的事实表的粒度一致。事实有可加性、半可加性、非可加性三种类型，需要将不可加性事实分解为可加的组件。\n比如在淘宝订单付款事务事实表中,同粒度的事实有子订单分摊的支付金额、邮费、优惠金额等。\n第五步:冗余维度。\n在传统的维度建模的星形模型中,对维度的处理是需要单独存放在专门的维表中的，通过事实表的外键获取维度。这样做的目的是为了减少事实表的维度冗余，从而减少存储消耗。而在大数据的事实表模型设计中,考虑更多的是提高下游用户的使用效率降低数据获取的复杂性减少关联的表数量。所以通常事实表中会冗\n余方便下游用户使用的常用维度，以实现对事实表的过滤查询、控制聚合层次、排序数据以及定义主从关系等操作。\n比如在淘宝订单付款事务事实表中,通常会冗余大量的常用维度字段，以及商品类目、卖家店铺等维度信息。\n","permalink":"https://leochu.work/blog/tech/bigdata/onedata%E6%95%B0%E6%8D%AE%E4%BB%93%E5%BA%93%E6%9E%B6%E6%9E%84/","summary":"\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E6%9E%B6%E6%9E%84.png\"\u003e\u003c/p\u003e\n\u003cp\u003e**1.**\u003cstrong\u003e什么是\u003c/strong\u003e \u003cstrong\u003eonedata\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e面对爆炸式增长的数据，如何建设高效的数据模型和体系，对这些数据进行有序和有结构地分类组织和存储，避免重复建设和数据不一致性，保证数据的规范性，一直是大数据系统建设不断追求的方向。OneData 即是阿里巴巴内部进行数据整合及管理的方法体系和工具。阿里巴巴的大数据工程师在这一体系下，构建统一、规范、可共享的全域数据体系，避免数据的冗余和重复建设，规避数据烟囱和不一致性，充分发挥阿里巴巴在大数据海量、多样性方面的独特优势。借助这一统一化数据整合及管理的方法体系，我们构建了阿里巴巴的数据公共层，并可以帮助相似的大数据项目快速落地实现。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E6%9E%B6%E6%9E%84.png\"\u003e\u003c/p\u003e\n\u003cp\u003e**2.**\u003cstrong\u003e指导思想\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e阿里巴巴集团数据公共层设计理念遵循维度建模思想，可参考 StarSchema-The Complete Reference 和 The Data Warehouse Toolkit-The Definitive Guide to Dimensional Modeling。数据模型的维度设计主要以维度建模理论为基础，基于维度数据模型总线架构，构建一致性的维度和事实。其核心的实施指导方针如下：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://s.w.org/images/core/emoji/17.0.2/svg/26ab.svg\"\u003e 首先，要进行充分的业务调研和需求分析。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://s.w.org/images/core/emoji/17.0.2/svg/26ab.svg\"\u003e 其次，进行数据总体架构设计，主要是根据数据域对数据进行划分；按照维度建模理论，构建总线矩阵，抽象出业务过程和维度。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://s.w.org/images/core/emoji/17.0.2/svg/26ab.svg\"\u003e 再次，对报表需求进行抽象整理出相关指标体系，使用 One Data 工具完成指标规范定义和模型设计。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://s.w.org/images/core/emoji/17.0.2/svg/26ab.svg\"\u003e 最后，是代码研发和运维。其实施流程主要分为：数据调研、架构设计、规范定义和模型设计。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E6%B5%81%E7%A8%8B.png\"\u003e\u003c/p\u003e\n\u003cp\u003e**3.**\u003cstrong\u003e业务调研\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e业务调研\u003c/strong\u003e：需要确认要规划进数仓的业务领域，以及各业务领域包含的功能模块，以阿里的业务为例，可规划如下矩阵\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E4%B8%9A%E5%8A%A1%E8%B0%83%E7%A0%94.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e需求调研\u003c/strong\u003e：了解需求方关系哪些指标？需要哪些维度、度量？数据是否沉淀到汇总层等。\u003c/p\u003e\n\u003cp\u003e可以想象一下，在没有考虑分析师、业务运营人员的数据需求的情况下，根据业务调研建设的数据仓库无疑等于闭门造车。了解了业务系统的业务后并不代表就可以进行实施了，此刻要做的就是收集数据使用者的需求，可以去找分析师、业务运营人员了解他们有什么数据诉求，此时更多的就是报表需求。\u003c/p\u003e\n\u003cp\u003e需求调研的途径有两种:一是根据与分析师、业务运营人员的沟通(邮件、IM)获知需求；二是对报表系统中现有的报表进行研究分析。通过需求调研分析后，就清楚数据要做成什么样的。很多时候，都是由具体的数据需求驱动数据仓库团队去了解业务系统的业务数据，这两者并没有严格的先后顺序。\u003c/p\u003e\n\u003cp\u003e举例：分析师需要了解大淘宝(淘宝、天猫、天猫国际)一级类目的成交金额。当获知这个需求后，我们要分析根据什么(维度)汇总，以及汇总什么(度量)，这里类目是维度，金额是度量；明细数据和汇总数据应该怎样设计?这是一个公用的报表吗?是需要沉淀到汇总表里面，还是在报表工具中进行汇总?\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E9%9C%80%E6%B1%82%E8%B0%83%E7%A0%94.png\"\u003e\u003c/p\u003e\n\u003cp\u003e**4.**\u003cstrong\u003e架构设计\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4.1 数据域的划分\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e数据域是指面向业务分析，将业务过程或者维度进行抽象的集合，一般数据域和应用系统(功能模块)有联系，可以考虑将同一个功能模块系统的业务过程划分到一个数据域。业务过程可以概括为一个个不可拆分的行为事件，如下单、支付、退款。为保障整个体系的生命力，数据域需要抽象提炼，并且长期维护和更新，但不轻易变动。在划分数据域时，既能涵盖当前所有的业务需求，又能在新业务进入时无影响地被包含进已有的数据域中或者扩展新的数据域。如表所示是功能模块/业务线的业务动作(部分示例)：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E4%B8%9A%E5%8A%A1%E5%8A%A8%E4%BD%9C.png\"\u003e\u003c/p\u003e\n\u003cp\u003e根据业务过程进行归纳，可以抽象出如下数据域：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E6%95%B0%E6%8D%AE%E5%9F%9F.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e4.2 构建总线矩阵\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e在进行充分的业务调研和需求调研后，就要构建总线矩阵了，需要做两件事情：\u003c/p\u003e\n\u003cp\u003e1.明确每个数据域下有哪些业务过程。\u003c/p\u003e\n\u003cp\u003e2.业务过程与哪些维度相关，并通过总线矩阵定义每个数据域下的业务过程和维度。\u003c/p\u003e\n\u003cp\u003e如下表是供应链管理业务过程示例：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E4%B8%9A%E5%8A%A1%E6%80%BB%E7%BA%BF%E7%9F%A9%E9%98%B5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e4.3 规范定义\u003c/p\u003e\n\u003cp\u003e规范定义主要定义指标体系，包括原子指标、修饰词、时间周期和派生指标。\u003c/p\u003e\n\u003cp\u003e4.4 模型设计\u003c/p\u003e\n\u003cp\u003e模型设计主要包括维度及属性的规范定义，维表、明细事实表和汇总事实表的模型设计。\u003c/p\u003e\n\u003cp\u003e4.5 架构总结\u003c/p\u003e\n\u003cp\u003eOne Data 的实施过程是一个高度迭代和动态的过程，一般采用螺旋式实施方法。在总体架构设计完成之后，开始根据数据域进行迭代式模型设计和评审。在架构设计、规范定义和模型设计等模型实施过程中，都会引入评审机制，以确保模型实施过程的正确性。\u003c/p\u003e\n\u003cp\u003e**5.**\u003cstrong\u003e指标体系搭建\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e5.1 指标体系核心结构\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/blog/resource/onedata_%E6%8C%87%E6%A0%87%E4%BD%93%E7%B3%BB%E6%A0%B8%E5%BF%83%E7%BB%93%E6%9E%84.png\"\u003e\u003c/p\u003e","title":"OneData数据仓库架构"},{"content":"zzz1\nuid is_delete 1 1 2 0 3 0 zzz2 uid is_delete 1 0 2 1 3 0 select * from zzz1 left join zzz2 on zzz1.uid = zzz2.uid where zzz1.is_delete = 0 and zzz2.is_delete = 0 结果: 3 0 3 0\nselect * from (select * from zzz1 where zzz1.is_delete = 0) z1 left join (select * from zzz2 where zzz2.is_delete = 0) z2 on z1.uid = z2.uid 结果: 2 0 NULL NULL 3 0 3 0\nselect * from (select * from zzz1 where zzz1.is_delete = 0) z1 left join (select * from zzz2 where zzz2.is_delete = 0) z2 on z1.uid = z2.uid where z2.uid is not null 结果: 3 0 3 0\nselect * from (select * from zzz1 where zzz1.is_delete = 0) z1 inner join (select * from zzz2 where zzz2.is_delete = 0) z2 on z1.uid = z2.uid 结果: 3 0 3 0\non的过滤条件只对右表有效\n","permalink":"https://leochu.work/blog/tech/bigdata/left-join%E8%B0%93%E8%AF%8D%E4%B8%8B%E6%8E%A8/","summary":"\u003cp\u003ezzz1\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth style=\"text-align: left\"\u003euid\u003c/th\u003e\n          \u003cth style=\"text-align: left\"\u003eis_delete\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n          \u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd style=\"text-align: left\"\u003e2\u003c/td\u003e\n          \u003ctd style=\"text-align: left\"\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd style=\"text-align: left\"\u003e3\u003c/td\u003e\n          \u003ctd style=\"text-align: left\"\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd style=\"text-align: left\"\u003ezzz2\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003euid\u003c/th\u003e\n          \u003cth style=\"text-align: left\"\u003eis_delete\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd style=\"text-align: left\"\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e2\u003c/td\u003e\n          \u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e3\u003c/td\u003e\n          \u003ctd style=\"text-align: left\"\u003e0\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efrom\u003c/span\u003e zzz1 \u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ejoin\u003c/span\u003e zzz2 \u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e zzz1.uid \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e zzz2.uid\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ewhere\u003c/span\u003e zzz1.is_delete \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eand\u003c/span\u003e zzz2.is_delete \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e结果:\n3       0       3       0\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efrom\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efrom\u003c/span\u003e zzz1 \u003cspan style=\"color:#66d9ef\"\u003ewhere\u003c/span\u003e zzz1.is_delete \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) z1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ejoin\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efrom\u003c/span\u003e zzz2 \u003cspan style=\"color:#66d9ef\"\u003ewhere\u003c/span\u003e zzz2.is_delete \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) z2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e z1.uid \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e z2.uid\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e结果:\n2       0       NULL    NULL\n3       0       3       0\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efrom\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efrom\u003c/span\u003e zzz1 \u003cspan style=\"color:#66d9ef\"\u003ewhere\u003c/span\u003e zzz1.is_delete \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) z1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eleft\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ejoin\u003c/span\u003e (\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efrom\u003c/span\u003e zzz2 \u003cspan style=\"color:#66d9ef\"\u003ewhere\u003c/span\u003e zzz2.is_delete \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e) z2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eon\u003c/span\u003e z1.uid \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e z2.uid \u003cspan style=\"color:#66d9ef\"\u003ewhere\u003c/span\u003e z2.uid \u003cspan style=\"color:#66d9ef\"\u003eis\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enot\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e结果:\n3       0       3       0\u003c/p\u003e","title":"left join 谓词下推"},{"content":"1. Kerberos是什么 Kerberos在古希腊神话中是指：一只有三个头的狗。这条狗守护在地狱之门外，防止活人闯入。\nKerberos是一个用于鉴定身份的协议，它采用对称密钥加密。\n在我们的CDH平台中，常用来作为一种安全验证，只有经过kerberos认证后的用户才可以访问大数据集群的服务。\n2.Kerberos中的一些概念 KDC(key distribution center): kerberos的认证中心，用来鉴别用户的身份的。想访问带kerberos的服务，得先过这个。\nPrincipal: kerberos中账户的概念，用户会以这个账户来被KDC认证。\n一个Principal由三个部分组成：primary, instance以及realm，其组成形式为primary/instance@realm。\rprimary : 可以是OS中的username，也可以是service name；\rinstance : 用于区分属于同一个user或者service的多个principals，该项为optional；\rrealm : 类似于DNS中的domain，定义了一组principals\r举个栗子：hive/hive@ZHENDAO.COM kafka/kafka@ZHENDAO.COM\rKeyTab: 以文件的形式呈现，存储了一个或多个Principal的长期的key，用途和密码类似，用于kerberos认证登录；\n其存在的意义在于让用户不需要明文的存储密码，和程序交互时不需要人为交互来输入密码。 TGT: 用户使用principal在KDC中进行认证后，KDC会返回给客户一个TGT，TGT会有过期时间。 Ticket：用户访问具体Server时，KDC会进行一次加密操作，返回给用户一张对应server的门票。\n3.Kerberos基本使用 kerberos管理员创建一个principal, 并生成对应的keytab文件 → 注册一个kerberos的账号，并配置了免密登录 拿着keytab去KDC中进行账户的认证 → kinit -kt /var/lib/hive/hive.keytab hive/hive@ZHENDAO.COM KDC返回一个这个principal对应的TGT文件 → 默认存放在linux的 /tmp 目录下 客户拿着TGT去过带有kerberos的服务端APP 4.Kerberos认证原理 客户拿着principal去KDC中进行认证的时候，kerberos会检查其库中是否有对应的principal，如果有的话，返回一个TGT。 客户拿着TGT向带有kerberos的服务端app发起请求时，请求会先发到KDC。 KDC会拿着客户的master key（客户密码的hash）和服务端的master key（比如说hive server的master key）进行加密操作，并返回给客户一个对应hive server的ticket。 客户拿着加密后的ticket,去请求hive服务的时候，hive会对ticket进行一个验证。验证通过即可访问hive 5.举个栗子：KAFKA认证过程 kafka producer拿着principal向KDC认证身份，通过则拿到KDC返回的TGT \u0026lt;这是第一次认证，就好比在游乐园的门口，做一次验票操作\u0026gt; producer拿着TGT向KDC请求kafka的服务，KDC验证TGT，看该账号能不能使用kafka，如果可以使用，那么进行加密，并返回一个ticket给producer \u0026lt;这是第二次认证，就好比你想玩游乐园中的具体项目，得看看你买的门票有没有这个资格玩VIP项目\u0026gt; producer拿着ticket和kafka server尝试建立连接，kafka server会使用server的master key对ticket进行一次解密操作，来验证producer的身份，通过则建立连接 \u0026lt;玩上具体的项目了\u0026gt; ","permalink":"https://leochu.work/blog/tech/bigdata/kerberos%E5%85%A5%E9%97%A8/","summary":"\u003ch2 id=\"1kerberos是什么\"\u003e1. Kerberos是什么\u003c/h2\u003e\n\u003cp\u003eKerberos在古希腊神话中是指：一只有三个头的狗。这条狗守护在地狱之门外，防止活人闯入。\u003c/p\u003e\n\u003cp\u003eKerberos是一个用于鉴定身份的协议，它采用对称密钥加密。\u003c/p\u003e\n\u003cp\u003e在我们的CDH平台中，常用来作为一种安全验证，只有经过kerberos认证后的用户才可以访问大数据集群的服务。\u003c/p\u003e\n\u003ch2 id=\"2kerberos中的一些概念\"\u003e2.Kerberos中的一些概念\u003c/h2\u003e\n\u003cp\u003eKDC(key distribution center): kerberos的认证中心，用来鉴别用户的身份的。想访问带kerberos的服务，得先过这个。\u003c/p\u003e\n\u003cp\u003ePrincipal: kerberos中账户的概念，用户会以这个账户来被KDC认证。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e一个Principal由三个部分组成：primary, instance以及realm，其组成形式为primary/instance@realm。\r\n\r\nprimary : 可以是OS中的username，也可以是service name；\r\ninstance : 用于区分属于同一个user或者service的多个principals，该项为optional；\r\nrealm : 类似于DNS中的domain，定义了一组principals\r\n\r\n举个栗子：hive/hive@ZHENDAO.COM    kafka/kafka@ZHENDAO.COM\r\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eKeyTab: 以文件的形式呈现，存储了一个或多个Principal的长期的key，用途和密码类似，用于kerberos认证登录；\u003c/p\u003e\n\u003cp\u003e其存在的意义在于让用户不需要明文的存储密码，和程序交互时不需要人为交互来输入密码。\n\u003cimg alt=\"Pasted image 20230327112709.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327112709.png\"\u003e\u003c/p\u003e\n\u003cp\u003eTGT: 用户使用principal在KDC中进行认证后，KDC会返回给客户一个TGT，TGT会有过期时间。\n\u003cimg alt=\"Pasted image 20230327112731.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230327112731.png\"\u003e\u003c/p\u003e\n\u003cp\u003eTicket：用户访问具体Server时，KDC会进行一次加密操作，返回给用户一张对应server的门票。\u003c/p\u003e\n\u003ch2 id=\"3kerberos基本使用\"\u003e3.Kerberos基本使用\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003ekerberos管理员创建一个principal, 并生成对应的keytab文件  → 注册一个kerberos的账号，并配置了免密登录\u003c/li\u003e\n\u003cli\u003e拿着keytab去KDC中进行账户的认证 → kinit  -kt  /var/lib/hive/hive.keytab  \u003ca href=\"mailto:hive/hive@ZHENDAO.COM\"\u003ehive/hive@ZHENDAO.COM\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eKDC返回一个这个principal对应的TGT文件 → 默认存放在linux的 /tmp 目录下\u003c/li\u003e\n\u003cli\u003e客户拿着TGT去过带有kerberos的服务端APP\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"4kerberos认证原理\"\u003e4.Kerberos认证原理\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e客户拿着principal去KDC中进行认证的时候，kerberos会检查其库中是否有对应的principal，如果有的话，返回一个TGT。\u003c/li\u003e\n\u003cli\u003e客户拿着TGT向带有kerberos的服务端app发起请求时，请求会先发到KDC。\u003c/li\u003e\n\u003cli\u003eKDC会拿着客户的master key（客户密码的hash）和服务端的master key（比如说hive server的master key）进行加密操作，并返回给客户一个对应hive  server的ticket。\u003c/li\u003e\n\u003cli\u003e客户拿着加密后的ticket,去请求hive服务的时候，hive会对ticket进行一个验证。验证通过即可访问hive\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"5举个栗子kafka认证过程\"\u003e5.举个栗子：KAFKA认证过程\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003ekafka producer拿着principal向KDC认证身份，通过则拿到KDC返回的TGT  \u0026lt;这是第一次认证，就好比在游乐园的门口，做一次验票操作\u0026gt;\u003c/li\u003e\n\u003cli\u003eproducer拿着TGT向KDC请求kafka的服务，KDC验证TGT，看该账号能不能使用kafka，如果可以使用，那么进行加密，并返回一个ticket给producer \u0026lt;这是第二次认证，就好比你想玩游乐园中的具体项目，得看看你买的门票有没有这个资格玩VIP项目\u0026gt;\u003c/li\u003e\n\u003cli\u003eproducer拿着ticket和kafka server尝试建立连接，kafka server会使用server的master key对ticket进行一次解密操作，来验证producer的身份，通过则建立连接 \u0026lt;玩上具体的项目了\u0026gt;\u003c/li\u003e\n\u003c/ol\u003e","title":"Kerberos入门"},{"content":"\n","permalink":"https://leochu.work/blog/tech/bigdata/kafka%E5%9C%A8zk%E4%B8%AD%E7%9A%84%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84/","summary":"\u003cp\u003e\u003cimg alt=\"kafkaInZK\" loading=\"lazy\" src=\"/blog/resource/kafkaInZK.png\"\u003e\u003c/p\u003e","title":"kafka在zk中的目录结构"},{"content":"1.Kafka中数据导入ClickHouse的标准流程 在ClickHouse中建立Kafka Engine 外表，作为Kafka数据源的一个接口 在ClickHouse中创建普通表(通常是MergeTree系列）存储Kafka中的数据 在ClickHouse中创建Materialized View, 监听Kafka中的数据，并将数据写入ClickHouse存储表中 上述三个步骤，就可以将Kafka中的数据导入到ClickHouse集群中。\n2. Kafka数据导入ClickHouse详细步骤 ClickHouse 提供了Kafka Engine 作为访问Kafka集群的一个接口（数据流）。有了这个接口后，导入数据就很方便了，具体步骤如下：\n步骤1：创建Kafka Engine CREATE TABLE source ( `ts` DateTime, `tag` String, `message` String ) ENGINE = Kafka() SETTINGS kafka_broker_list = \u0026#39;172.19.0.47:9092\u0026#39;, kafka_topic_list = \u0026#39;tag\u0026#39;, kafka_group_name = \u0026#39;clickhouse\u0026#39;, kafka_format = \u0026#39;JSONEachRow\u0026#39;, kafka_skip_broken_messages = 1, kafka_num_consumers = 2 必选参数：\nkafkabrokerlist: 这里填写Kafka服务的broker列表，用逗号分隔 kafkatopiclist: 这里填写Kafka topic,多个topic用逗号分隔 kafkagroupname：这里填写消费者group名称 kafkaformat__:Kafka数据格式, ClickHouse支持的Format, 详见这里 可选参数： kafkaskipbrokenmessages：填写大于等于0的整数，表示忽略解析异常的Kafka数据的条数。如果出现了N条异常后，后台线程结束，Materialized View会被重新安排后台线程去监听数据 kafkanumconsumers_: 单个Kafka Engine 的消费者数量，通过增加该参数，可以提高消费数据吞吐，但总数不应超过对应topic的partitions总数 kafkarowdelimiter: 消息分隔符 kafkaschema__:对于kafkaformat需要schema定义的时候，其schema由该参数确定 kafkamaxblocksize: 该参数控制Kafka数据写入目标表的Block大小，超过该数值后，就将数据刷盘。 步骤2：创建存储Kafka数据的目标表，该表就是最终存储Kafka数据 本文中，采用MergeTree来存储Kafka数据： CREATE TABLE target ( `ts` DateTime, `tag` String ) ENGINE = MergeTree() PARTITION BY toYYYYMM(ts) ORDER BY tag 步骤3：创建Metrialized View 抓取数据 本文中，采用如下语句创建MV:\nCREATE MATERIALIZED VIEW source_mv TO target AS SELECT ts, tag FROM source 完成上述三个步骤，我们就可以在表target中查询到来自Kafka的数据了。\n","permalink":"https://leochu.work/blog/tech/bigdata/kafka%E6%95%B0%E6%8D%AE%E5%AF%BC%E5%85%A5clickhouse/","summary":"\u003ch2 id=\"1kafka中数据导入clickhouse的标准流程\"\u003e1.Kafka中数据导入ClickHouse的标准流程\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e在ClickHouse中建立Kafka Engine 外表，作为Kafka数据源的一个接口\u003c/li\u003e\n\u003cli\u003e在ClickHouse中创建普通表(通常是MergeTree系列）存储Kafka中的数据\u003c/li\u003e\n\u003cli\u003e在ClickHouse中创建Materialized View, 监听Kafka中的数据，并将数据写入ClickHouse存储表中\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e上述三个步骤，就可以将Kafka中的数据导入到ClickHouse集群中。\u003c/p\u003e\n\u003ch2 id=\"2-kafka数据导入clickhouse详细步骤\"\u003e2. Kafka数据导入ClickHouse详细步骤\u003c/h2\u003e\n\u003cp\u003eClickHouse 提供了Kafka Engine 作为访问Kafka集群的一个接口（数据流）。有了这个接口后，导入数据就很方便了，具体步骤如下：\u003c/p\u003e\n\u003ch3 id=\"步骤1创建kafka-engine\"\u003e步骤1：创建Kafka Engine\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003esource\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003ets\u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003e DateTime, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003etag\u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003e String, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003emessage\u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003e String\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eENGINE \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Kafka()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSETTINGS kafka_broker_list \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;172.19.0.47:9092\u0026#39;\u003c/span\u003e, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         kafka_topic_list \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;tag\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         kafka_group_name \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;clickhouse\u0026#39;\u003c/span\u003e, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         kafka_format \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;JSONEachRow\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         kafka_skip_broken_messages \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e         kafka_num_consumers \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e必选参数：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ekafkabrokerlist: 这里填写Kafka服务的broker列表，用逗号分隔\u003c/li\u003e\n\u003cli\u003ekafkatopiclist: 这里填写Kafka topic,多个topic用逗号分隔\u003c/li\u003e\n\u003cli\u003ekafkagroupname：这里填写消费者group名称 \u003c/li\u003e\n\u003cli\u003ekafkaformat__:Kafka数据格式, ClickHouse支持的Format, 详见这里 可选参数： \u003c/li\u003e\n\u003cli\u003ekafkaskipbrokenmessages：填写大于等于0的整数，表示忽略解析异常的Kafka数据的条数。如果出现了N条异常后，后台线程结束，Materialized View会被重新安排后台线程去监听数据 \u003c/li\u003e\n\u003cli\u003ekafkanumconsumers_: 单个Kafka Engine 的消费者数量，通过增加该参数，可以提高消费数据吞吐，但总数不应超过对应topic的partitions总数\u003c/li\u003e\n\u003cli\u003ekafkarowdelimiter: 消息分隔符  \u003c/li\u003e\n\u003cli\u003ekafkaschema__:对于kafkaformat需要schema定义的时候，其schema由该参数确定\u003c/li\u003e\n\u003cli\u003ekafkamaxblocksize: 该参数控制Kafka数据写入目标表的Block大小，超过该数值后，就将数据刷盘。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"步骤2创建存储kafka数据的目标表该表就是最终存储kafka数据-本文中采用mergetree来存储kafka数据\"\u003e步骤2：创建存储Kafka数据的目标表，该表就是最终存储Kafka数据 本文中，采用MergeTree来存储Kafka数据：\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e target\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003ets\u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003e DateTime, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003etag\u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003e String\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eENGINE \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e MergeTree()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePARTITION \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e toYYYYMM(ts)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eORDER\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e tag\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"步骤3创建metrialized-view-抓取数据\"\u003e步骤3：创建Metrialized View 抓取数据\u003c/h3\u003e\n\u003cp\u003e本文中，采用如下语句创建MV:\u003c/p\u003e","title":"Kafka数据导入ClickHouse"},{"content":"控制器组件（Controller），是 Apache Kafka 的核心组件。它的主要作用是在 Apache ZooKeeper 的帮助下管理和协调整个 Kafka 集群。集群中任意一台 Broker 都能充当控制器的角色，但是，在运行过程中，只能有一个 Broker 成为控制器，行使其管理和协调的职责。换句话说，每个正常运转的 Kafka 集群，在任意时刻都有且只有一个控制器。官网上有个名为 activeController 的 JMX 指标，可以帮助我们实时监控控制器的存活状态。这个 JMX 指标非常关键，你在实际运维操作过程中，一定要实时查看这个指标的值。下面，我们就来详细说说控制器的原理和内部运行机制。\n在开始之前，我先简单介绍一下 Apache ZooKeeper 框架。要知道，控制器是重度依赖 ZooKeeper 的，因此，我们有必要花一些时间学习下 ZooKeeper 是做什么的。\nApache ZooKeeper 是一个提供高可靠性的分布式协调服务框架。它使用的数据模型类似于文件系统的树形结构，根目录也是以 “/” 开始。该结构上的每个节点被称为 znode，用来保存一些元数据协调信息。\n如果以 znode 持久性来划分，znode 可分为持久性 znode 和临时 znode。持久性 znode 不会因为 ZooKeeper 集群重启而消失，==而临时 znode 则与创建该 znode 的 ZooKeeper 会话绑定，一旦会话结束，该节点会被自动删除==。\n==ZooKeeper 赋予客户端监控 znode 变更的能力，即所谓的 Watch 通知功能。一旦 znode 节点被创建、删除，子节点数量发生变化，抑或是 znode 所存的数据本身变更，ZooKeeper 会通过节点变更监听器 (ChangeHandler) 的方式显式通知客户端。==\n依托于这些功能，ZooKeeper 常被用来实现集群成员管理、分布式锁、领导者选举等功能。==Kafka 控制器大量使用 Watch 功能实现对集群的协调管理==。我们一起来看一张图片，它展示的是 Kafka 在 ZooKeeper 中创建的 znode 分布。你不用了解每个 znode 的作用，但你可以大致体会下 Kafka 对 ZooKeeper 的依赖。\n掌握了 ZooKeeper 的这些基本知识，现在我们就可以开启对 Kafka 控制器的讨论了。\n控制器是如何被选出来的？ 你一定很想知道，控制器是如何被选出来的呢？我们刚刚在前面说过，每台 Broker 都能充当控制器，那么，当集群启动后，Kafka 怎么确认控制器位于哪台 Broker 呢？\n实际上，Broker 在启动时，会尝试去 ZooKeeper 中创建 /controller 节点。Kafka 当前选举控制器的规则是：第一个成功创建 /controller 节点的 Broker 会被指定为控制器。\n控制器是做什么的？ 我们经常说，控制器是起协调作用的组件，那么，这里的协调作用到底是指什么呢？我想了一下，控制器的职责大致可以分为 5 种，我们一起来看看。\n1. 主题管理（创建、删除、增加分区） 这里的主题管理，就是指控制器帮助我们完成对 Kafka 主题的创建、删除以及分区增加的操作。换句话说，当我们执行 kafka-topics 脚本时，大部分的后台工作都是控制器来完成的。关于 kafka-topics 脚本，我会在专栏后面的内容中，详细介绍它的使用方法。\n2. 分区重分配 分区重分配主要是指，kafka-reassign-partitions 脚本（关于这个脚本，后面我也会介绍）提供的对已有主题分区进行细粒度的分配功能。这部分功能也是控制器实现的。\n3.Preferred 领导者选举 Preferred 领导者选举主要是 Kafka 为了避免部分 Broker 负载过重而提供的一种换 Leader 的方案。在专栏后面说到工具的时候，我们再详谈 Preferred 领导者选举，这里你只需要了解这也是控制器的职责范围就可以了。\n4. 集群成员管理（新增 Broker、Broker 主动关闭、Broker 宕机） 这是控制器提供的第 4 类功能，包括自动检测新增 Broker、Broker 主动关闭及被动宕机。这种自动检测是依赖于前面提到的 Watch 功能和 ZooKeeper 临时节点组合实现的。\n比如，控制器组件会利用 Watch 机制检查 ZooKeeper 的 /brokers/ids 节点下的子节点数量变更。目前，当有新 Broker 启动后，它会在 /brokers 下创建专属的 znode 节点。一旦创建完毕，ZooKeeper 会通过 Watch 机制将消息通知推送给控制器，这样，控制器就能自动地感知到这个变化，进而开启后续的新增 Broker 作业。\n侦测 Broker 存活性则是依赖于刚刚提到的另一个机制：临时节点。每个 Broker 启动后，会在 /brokers/ids 下创建一个临时 znode。当 Broker 宕机或主动关闭后，该 Broker 与 ZooKeeper 的会话结束，这个 znode 会被自动删除。同理，ZooKeeper 的 Watch 机制将这一变更推送给控制器，这样控制器就能知道有 Broker 关闭或宕机了，从而进行 “善后”。\n5. 数据服务 控制器的最后一大类工作，就是向其他 Broker 提供数据服务。控制器上保存了最全的集群元数据信息，其他所有 Broker 会定期接收控制器发来的元数据更新请求，从而更新其内存中的缓存数据。\n控制器保存了什么数据？ 接下来，我们就详细看看，控制器中到底保存了哪些数据。我用一张图来说明一下。\n怎么样，图中展示的数据量是不是很多？几乎把我们能想到的所有 Kafka 集群的数据都囊括进来了。这里面比较重要的数据有：\n所有主题信息。包括具体的分区信息，比如领导者副本是谁，ISR 集合中有哪些副本等。 所有 Broker 信息。包括当前都有哪些运行中的 Broker，哪些正在关闭中的 Broker 等。 所有涉及运维任务的分区。包括当前正在进行 Preferred 领导者选举以及分区重分配的分区列表。 值得注意的是，==这些数据其实在 ZooKeeper 中也保存了一份==。每当控制器初始化时，它都会从 ZooKeeper 上读取对应的元数据并填充到自己的缓存中。有了这些数据，控制器就能对外提供数据服务了。这里的对外主要是指对其他 Broker 而言，控制器通过向这些 Broker 发送请求的方式将这些数据同步到其他 Broker 上。\n控制器故障转移（Failover） 我们在前面强调过，在 Kafka 集群运行过程中，只能有一台 Broker 充当控制器的角色，那么这就存在单点失效（Single Point of Failure）的风险，Kafka 是如何应对单点失效的呢？答案就是，为控制器提供故障转移功能，也就是说所谓的 Failover。\n故障转移指的是，当运行中的控制器突然宕机或意外终止时，Kafka 能够快速地感知到，并立即启用备用控制器来代替之前失败的控制器。这个过程就被称为 Failover，该过程是自动完成的，无需你手动干预。\n接下来，我们一起来看一张图，它简单地展示了控制器故障转移的过程。\n最开始时，Broker 0 是控制器。当 Broker 0 宕机后，ZooKeeper 通过 Watch 机制感知到并删除了 /controller 临时节点。之后，所有存活的 Broker 开始竞选新的控制器身份。Broker 3 最终赢得了选举，成功地在 ZooKeeper 上重建了 /controller 节点。之后，Broker 3 会从 ZooKeeper 中读取集群元数据信息，并初始化到自己的缓存中。至此，控制器的 Failover 完成，可以行使正常的工作职责了。\n控制器内部设计原理 在 Kafka 0.11 版本之前，控制器的设计是相当繁琐的，代码更是有些混乱，这就导致社区中很多控制器方面的 Bug 都无法修复。控制器是多线程的设计，会在内部创建很多个线程。比如，控制器需要为每个 Broker 都创建一个对应的 Socket 连接，然后再创建一个专属的线程，用于向这些 Broker 发送特定请求。如果集群中的 Broker 数量很多，那么控制器端需要创建的线程就会很多。另外，控制器连接 ZooKeeper 的会话，也会创建单独的线程来处理 Watch 机制的通知回调。除了以上这些线程，控制器还会为主题删除创建额外的 I/O 线程。\n比起多线程的设计，更糟糕的是，这些线程还会访问共享的控制器缓存数据。我们都知道，多线程访问共享可变数据是维持线程安全最大的难题。为了保护数据安全性，控制器不得不在代码中大量使用 ReentrantLock 同步机制，这就进一步拖慢了整个控制器的处理速度。\n鉴于这些原因，社区于 0.11 版本重构了控制器的底层设计，最大的改进就是，把多线程的方案改成了单线程加事件队列的方案。我直接使用社区的一张图来说明。\n从这张图中，我们可以看到，社区引入了一个事件处理线程，统一处理各种控制器事件，然后控制器将原来执行的操作全部建模成一个个独立的事件，发送到专属的事件队列中，供此线程消费。这就是所谓的单线程 + 队列的实现方式。\n值得注意的是，这里的单线程不代表之前提到的所有线程都被 “干掉” 了，控制器只是把缓存状态变更方面的工作委托给了这个线程而已。\n这个方案的最大好处在于，控制器缓存中保存的状态只被一个线程处理，因此不再需要重量级的线程同步机制来维护线程安全，Kafka 不用再担心多线程并发访问的问题，非常利于社区定位和诊断控制器的各种问题。事实上，自 0.11 版本重构控制器代码后，社区关于控制器方面的 Bug 明显少多了，这也说明了这种方案是有效的。\n针对控制器的第二个改进就是，将之前同步操作 ZooKeeper 全部改为异步操作。ZooKeeper 本身的 API 提供了同步写和异步写两种方式。之前控制器操作 ZooKeeper 使用的是同步的 API，性能很差，集中表现为，当有大量主题分区发生变更时，ZooKeeper 容易成为系统的瓶颈。新版本 Kafka 修改了这部分设计，完全摒弃了之前的同步 API 调用，转而采用异步 API 写入 ZooKeeper，性能有了很大的提升。根据社区的测试，改成异步之后，ZooKeeper 写入提升了 10 倍！\n除了以上这些，社区最近又发布了一个重大的改进！之前 Broker 对接收的所有请求都是一视同仁的，不会区别对待。这种设计对于控制器发送的请求非常不公平，因为这类请求应该有更高的优先级。\n举个简单的例子，假设我们删除了某个主题，那么控制器就会给该主题所有副本所在的 Broker 发送一个名为 StopReplica 的请求。如果此时 Broker 上存有大量积压的 Produce 请求，那么这个 StopReplica 请求只能排队等。如果这些 Produce 请求就是要向该主题发送消息的话，这就显得很讽刺了：主题都要被删除了，处理这些 Produce 请求还有意义吗？此时最合理的处理顺序应该是，赋予 StopReplica 请求更高的优先级，使它能够得到抢占式的处理。\n这在 2.2 版本之前是做不到的。不过自 2.2 开始，Kafka 正式支持这种不同优先级请求的处理。简单来说，==Kafka 将控制器发送的请求与普通数据类请求分开==，实现了控制器请求单独处理的逻辑。鉴于这个改进还是很新的功能，具体的效果我们就拭目以待吧。\n小结 好了，有关 Kafka 控制器的内容，我已经讲完了。最后，我再跟你分享一个小窍门。当你觉得控制器组件出现问题时，比如主题无法删除了，或者重分区 hang 住了，你不用重启 Kafka Broker 或控制器。有一个简单快速的方式是，去 ZooKeeper 中手动删除 /controller 节点。具体命令是: rmr /controller 这样做的好处是，既可以引发控制器的重选举，又可以避免重启 Broker 导致的消息处理中断。\n","permalink":"https://leochu.work/blog/tech/bigdata/kafka%E6%8E%A7%E5%88%B6%E5%99%A8%E7%BB%84%E4%BB%B6/","summary":"\u003cp\u003e\u003cstrong\u003e控制器组件（Controller），是 Apache Kafka 的核心组件。它的主要作用是在 Apache ZooKeeper 的帮助下管理和协调整个 Kafka 集群\u003c/strong\u003e。集群中任意一台 Broker 都能充当控制器的角色，但是，在运行过程中，只能有一个 Broker 成为控制器，行使其管理和协调的职责。换句话说，每个正常运转的 Kafka 集群，在任意时刻都有且只有一个控制器。官网上有个名为 activeController 的 JMX 指标，可以帮助我们实时监控控制器的存活状态。这个 JMX 指标非常关键，你在实际运维操作过程中，一定要实时查看这个指标的值。下面，我们就来详细说说控制器的原理和内部运行机制。\u003c/p\u003e\n\u003cp\u003e在开始之前，我先简单介绍一下 Apache ZooKeeper 框架。要知道，\u003cstrong\u003e控制器是重度依赖 ZooKeeper\u003c/strong\u003e 的，因此，我们有必要花一些时间学习下 ZooKeeper 是做什么的。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eApache ZooKeeper 是一个提供高可靠性的分布式协调服务框架\u003c/strong\u003e。它使用的数据模型类似于文件系统的树形结构，根目录也是以 “/” 开始。该结构上的每个节点被称为 znode，用来保存一些元数据协调信息。\u003c/p\u003e\n\u003cp\u003e如果以 znode 持久性来划分，\u003cstrong\u003eznode 可分为持久性 znode 和临时 znode\u003c/strong\u003e。持久性 znode 不会因为 ZooKeeper 集群重启而消失，==而临时 znode 则与创建该 znode 的 ZooKeeper 会话绑定，一旦会话结束，该节点会被自动删除==。\u003c/p\u003e\n\u003cp\u003e==ZooKeeper 赋予客户端监控 znode 变更的能力，即所谓的 Watch 通知功能。一旦 znode 节点被创建、删除，子节点数量发生变化，抑或是 znode 所存的数据本身变更，ZooKeeper 会通过节点变更监听器 (ChangeHandler) 的方式显式通知客户端。==\u003c/p\u003e\n\u003cp\u003e依托于这些功能，ZooKeeper 常被用来实现\u003cstrong\u003e集群成员管理、分布式锁、领导者选举\u003c/strong\u003e等功能。==Kafka 控制器大量使用 Watch 功能实现对集群的协调管理==。我们一起来看一张图片，它展示的是 Kafka 在 ZooKeeper 中创建的 znode 分布。你不用了解每个 znode 的作用，但你可以大致体会下 Kafka 对 ZooKeeper 的依赖。\u003c/p\u003e","title":"Kafka 中的控制器组件"},{"content":"命令行脚本概览 Kafka 默认提供了很多个命令行脚本，用于实现各种各样的功能和运维管理。今天我以 2.2 版本为例，详细地盘点下这些命令行工具。下图展示了 2.2 版本提供的所有命令行脚本。\n从图中我们可以知道，2.2 版本总共提供了 30 个 SHELL 脚本。图中的 windows 实际上是个子目录，里面保存了 Windows 平台下的 BAT 批处理文件。其他的. sh 文件则是 Linux 平台下的标准 SHELL 脚本。\n默认情况下，不加任何参数或携带 \u0026ndash;help 运行 SHELL 文件，会得到该脚本的使用方法说明。下面这张图片展示了 kafka-log-dirs 脚本的调用方法。\n有了这些基础的了解，我来逐一地说明这些脚本的用途，然后再给你详细地介绍一些常见的脚本。\n我们先来说说 connect-standalone 和 connect-distributed 两个脚本。这两个脚本是 Kafka Connect 组件的启动脚本。在专栏第 4 讲谈到 Kafka 生态时，我曾说过社区提供了 Kafka Connect 组件，用于实现 Kafka 与外部世界系统之间的数据传输。Kafka Connect 支持单节点的 Standalone 模式，也支持多节点的 Distributed 模式。这两个脚本分别是这两种模式下的启动脚本。鉴于 Kafka Connect 不在我们的讨论范围之内，我就不展开讲了。\n接下来是 kafka-acls 脚本。它是用于设置 Kafka 权限的，比如设置哪些用户可以访问 Kafka 的哪些主题之类的权限。在专栏后面，我会专门来讲 Kafka 安全设置的内容，到时候我们再细聊这个脚本。\n下面是 kafka-broker-api-versions 脚本。这个脚本的主要目的是验证不同 Kafka 版本之间服务器和客户端的适配性。我来举个例子，下面这两张图分别展示了 2.2 版本 Server 端与 2.2 版本 Client 端和 1.1.1 版本 Client 端的适配性。\n我截取了部分输出内容，现在我稍微解释一下这些输出的含义。我们以第一行为例： Produce(0): 0 to 7 [usable: 7]\n“Produce”表示 Produce 请求，生产者生产消息本质上就是向 Broker 端发送 Produce 请求。该请求是 Kafka 所有请求类型中的第一号请求，因此序号是 0。后面的 “0 to 7” 表示 Produce 请求在 Kafka 2.2 中总共有 8 个版本，序号分别是 0 到 7。“usable：7”表示当前连入这个 Broker 的客户端 API 能够使用的版本号是 7，即最新的版本。\n请注意这两张图中红线部分的差异。在第一张图中，我们使用 2.2 版本的脚本连接 2.2 版本的 Broker，usable 自然是 7，表示能使用最新版本。在第二张图中，我们使用 1.1 版本的脚本连接 2.2 版本的 Broker，usable 是 5，这表示 1.1 版本的客户端 API 只能发送版本号是 5 的 Produce 请求。\n如果你想了解你的客户端版本与服务器端版本的兼容性，那么最好使用这个命令来检验一下。值得注意的是，在 0.10.2.0 之前，Kafka 是单向兼容的，即高版本的 Broker 能够处理低版本 Client 发送的请求，反过来则不行。自 0.10.2.0 版本开始，Kafka 正式支持双向兼容，也就是说，低版本的 Broker 也能处理高版本 Client 的请求了。\n接下来是 kafka-configs 脚本。对于这个脚本，我想你应该已经很熟悉了，我们在讨论参数配置和动态 Broker 参数时都提到过它的用法，这里我就不再赘述了。\n下面的两个脚本是重量级的工具行脚本：kafka-console-consumer 和 kafka-console-producer。在某种程度上，说它们是最常用的脚本也不为过。这里我们暂时先跳过，后面我会重点介绍它们。\n关于 producer 和 consumer，成组出现的还有另外一组脚本：kafka-producer-perf-test 和 kafka-consumer-perf-test。它们分别是生产者和消费者的性能测试工具，非常实用，稍后我会重点介绍。\n接下来的 kafka-consumer-groups 命令，我在介绍重设消费者组位移时稍有涉及，后面我们来聊聊该脚本的其他用法。\nkafka-delegation-tokens 脚本可能不太为人所知，它是管理 Delegation Token 的。基于 Delegation Token 的认证是一种轻量级的认证机制，补充了现有的 SASL 认证机制。\nkafka-delete-records 脚本用于删除 Kafka 的分区消息。鉴于 Kafka 本身有自己的自动消息删除策略，这个脚本的实际出场率并不高。\nkafka-dump-log 脚本可谓是非常实用的脚本。它能查看 Kafka 消息文件的内容，包括消息的各种元数据信息，甚至是消息体本身。\nkafka-log-dirs 脚本是比较新的脚本，可以帮助查询各个 Broker 上的各个日志路径的磁盘占用情况。\nkafka-mirror-maker 脚本是帮助你实现 Kafka 集群间的消息同步的。在专栏后面，我会单独用一讲的内容来讨论它的用法。\nkafka-preferred-replica-election 脚本是执行 Preferred Leader 选举的。它可以为指定的主题执行 “换 Leader” 的操作。\nkafka-reassign-partitions 脚本用于执行分区副本迁移以及副本文件路径迁移。\nkafka-topics 脚本你应该很熟悉了，所有的主题管理操作，都是由该脚本来实现的。\nkafka-run-class 脚本则颇为神秘，你可以用这个脚本执行任何带 main 方法的 Kafka 类。在 Kafka 早期的发展阶段，很多工具类都没有自己专属的 SHELL 脚本，比如刚才提到的 kafka-dump-log，你只能通过运行 kafka-run-class kafka.tools.DumpLogSegments 的方式来间接实现。如果你用文本编辑器打开 kafka-dump-log.sh，你会发现它实际上调用的就是这条命令。后来社区逐渐为这些重要的工具类都添加了专属的命令行脚本，现在 kafka-run-class 脚本的出场率大大降低了。在实际工作中，你几乎遇不上要直接使用这个脚本的场景了。\n对于 kafka-server-start 和 kafka-server-stop 脚本，你应该不会感到陌生，它们是用于启动和停止 Kafka Broker 进程的。\nkafka-streams-application-reset 脚本用来给 Kafka Streams 应用程序重设位移，以便重新消费数据。如果你没有用到 Kafka Streams 组件，这个脚本对你来说是没有用的。\nkafka-verifiable-producer 和 kafka-verifiable-consumer 脚本是用来测试生产者和消费者功能的。它们是很 “古老” 的脚本了，你几乎用不到它们。另外，前面提到的 Console Producer 和 Console Consumer 完全可以替代它们。\n剩下的 zookeeper 开头的脚本是用来管理和运维 ZooKeeper 的，这里我就不做过多介绍了。\n最后说一下 trogdor 脚本。这是个很神秘的家伙，官网上也不曾出现它的名字。据社区内部资料显示，它是 Kafka 的测试框架，用于执行各种基准测试和负载测试。一般的 Kafka 用户应该用不到这个脚本。\n好了，Kafka 自带的所有脚本我全部梳理了一遍。虽然这些描述看起来有点流水账，但是，有了这些基础的认知，我们才能更好地利用这些脚本。下面我就来详细介绍一下重点的脚本操作。\n重点脚本操作 生产消息 生产消息使用 kafka-console-producer 脚本即可，一个典型的命令如下所示：\n$ bin/kafka-console-producer.sh --broker-list kafka-host:port --topic test-topic --request-required-acks -1 --producer-property compression.type=lz4 在这段命令中，我们指定生产者参数 acks 为 -1，同时启用了 LZ4 的压缩算法。这个脚本可以很方便地让我们使用控制台来向 Kafka 的指定主题发送消息。\n消费消息 下面再来说说数据消费。如果要快速地消费主题中的数据来验证消息是否存在，运行 kafka-console-consumer 脚本应该算是最便捷的方法了。常用的命令用法如下：\n$ bin/kafka-console-consumer.sh --bootstrap-server kafka-host:port --topic test-topic --group test-group --from-beginning --consumer-property enable.auto.commit=false 注意，在这段命令中，我们指定了 group 信息。如果没有指定的话，每次运行 Console Consumer，它都会自动生成一个新的消费者组来消费。久而久之，你会发现你的集群中有大量的以 console-consumer 开头的消费者组。通常情况下，你最好还是加上 group。\n另外，from-beginning 等同于将 Consumer 端参数 auto.offset.reset 设置成 earliest，表明我想从头开始消费主题。如果不指定的话，它会默认从最新位移读取消息。如果此时没有任何新消息，那么该命令的输出为空，你什么都看不到。\n最后，我在命令中禁掉了自动提交位移。通常情况下，让 Console Consumer 提交位移是没有意义的，毕竟我们只是用它做一些简单的测试。\n测试生产者性能 如果你想要对 Kafka 做一些简单的性能测试。那么不妨试试下面这一组工具。它们分别用于测试生产者和消费者的性能。\n我们先说测试生产者的脚本：kafka-producer-perf-test。它的参数有不少，但典型的命令调用方式是这样的。\n$ bin/kafka-producer-perf-test.sh --topic test-topic --num-records 10000000 --throughput -1 --record-size 1024 --producer-props bootstrap.servers=kafka-host:port acks=-1 linger.ms=2000 compression.type=lz 42175479 records sent, 435095.8 records/sec (424.90 MB/sec), 131.1 ms avg latency, 681.0 ms max latency.4190124 records sent, 838024.8 records/sec (818.38 MB/sec), 4.4 ms avg latency, 73.0 ms max latency.10000000 records sent, 737463.126844 records/sec (720.18 MB/sec), 31.81 ms avg latency, 681.00 ms max latency, 4 ms 50th, 126 ms 95th, 604 ms 99th, 672 ms 99.9th. 上述命令向指定主题发送了 1 千万条消息，每条消息大小是 1KB。该命令允许你在 producer-props 后面指定要设置的生产者参数，比如本例中的压缩算法、延时时间等。\n该命令的输出值得好好说一下。它会打印出测试生产者的吞吐量 (MB/s)、消息发送延时以及各种分位数下的延时。一般情况下，消息延时不是一个简单的数字，而是一组分布。或者说，我们应该关心延时的概率分布情况，仅仅知道一个平均值是没有意义的。这就是这里计算分位数的原因。通常我们关注到 99th 分位就可以了。比如在上面的输出中，99th 值是 604ms，这表明测试生产者生产的消息中，有 99% 消息的延时都在 604ms 以内。你完全可以把这个数据当作这个生产者对外承诺的 SLA。\n测试消费者性能 测试消费者也是类似的原理，只不过我们使用的是 kafka-consumer-perf-test 脚本，命令如下：\n$ bin/kafka-consumer-perf-test.sh --broker-list kafka-host:port --messages 10000000 --topic test-topic start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec, rebalance.time.ms, fetch.time.ms, fetch.MB.sec, fetch.nMsg.sec2019-06-26 15:24:18:138, 2019-06-26 15:24:23:805, 9765.6202, 1723.2434, 10000000, 1764602.0822, 16, 5651, 1728.1225, 1769598.3012 虽然输出格式有所差别，但该脚本也会打印出消费者的吞吐量数据。比如本例中的 1723MB/s。有点令人遗憾的是，它没有计算不同分位数下的分布情况。因此，在实际使用过程中，这个脚本的使用率要比生产者性能测试脚本的使用率低。\n查看主题消息总数 很多时候，我们都想查看某个主题当前的消息总数。令人惊讶的是，Kafka 自带的命令竟然没有提供这样的功能，我们只能 “绕道” 获取了。所谓的绕道，是指我们必须要调用一个未被记录在官网上的命令。命令如下：\n$ bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list kafka-host:port --time -2 --topic test-topic test-topic:0:0 test-topic:1:0 $ bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list kafka-host:port --time -1 --topic test-topic test-topic:0:5500000 test-topic:1:5500000 我们要使用 Kafka 提供的工具类 GetOffsetShell 来计算给定主题特定分区当前的最早位移和最新位移，将两者的差值累加起来，就能得到该主题当前总的消息数。对于本例来说，test-topic 总的消息数为 5500000 + 5500000，等于 1100 万条。\n查看消息文件数据 作为 Kafka 使用者，你是不是对 Kafka 底层文件里面保存的内容很感兴趣? 如果是的话，你可以使用 kafka-dump-log 脚本来查看具体的内容。\n$ bin/kafka-dump-log.sh --files ../data_dir/kafka_1/test-topic-1/00000000000000000000.log Dumping ../data_dir/kafka_1/test-topic-1/00000000000000000000.log Starting offset: 0 baseOffset: 0 lastOffset: 14 count: 15 baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: 0 isTransactional: false isControl: false position: 0 CreateTime: 1561597044933 size: 1237 magic: 2 compresscodec: LZ4 crc: 646766737 isvalid: true baseOffset: 15 lastOffset: 29 count: 15 baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: 0 isTransactional: false isControl: false position: 1237 CreateTime: 1561597044934 size: 1237 magic: 2 compresscodec: LZ4 crc: 3751986433 isvalid: true ...... 如果只是指定 \u0026ndash;files，那么该命令显示的是消息批次（RecordBatch）或消息集合（MessageSet）的元数据信息，比如创建时间、使用的压缩算法、CRC 校验值等。\n如果我们想深入看一下每条具体的消息，那么就需要显式指定 \u0026ndash;deep-iteration 参数，如下所示：\n$ bin/kafka-dump-log.sh --files ../data_dir/kafka_1/test-topic-1/00000000000000000000.log --deep-iteration Dumping ../data_dir/kafka_1/test-topic-1/00000000000000000000.log Starting offset: 0 baseOffset: 0 lastOffset: 14 count: 15 baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: 0 isTransactional: false isControl: false position: 0 CreateTime: 1561597044933 size: 1237 magic: 2 compresscodec: LZ4 crc: 646766737 isvalid: true | offset: 0 CreateTime: 1561597044911 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 1 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 2 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 3 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 4 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 5 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 6 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 7 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 8 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 9 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 10 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 11 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 12 CreateTime: 1561597044932 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 13 CreateTime: 1561597044933 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] | offset: 14 CreateTime: 1561597044933 keysize: -1 valuesize: 1024 sequence: -1 headerKeys: [] baseOffset: 15 lastOffset: 29 count: 15 baseSequence: -1 lastSequence: -1 producerId: -1 producerEpoch: -1 partitionLeaderEpoch: 0 isTransactional: false isControl: false position: 1237 CreateTime: 1561597044934 size: 1237 magic: 2 compresscodec: LZ4 crc: 3751986433 isvalid: true ...... 在上面的输出中，以竖线开头的就是消息批次下的消息信息。如果你还想看消息里面的实际数据，那么还需要指定 \u0026ndash;print-data-log 参数，如下所示：\n$ bin/kafka-dump-log.sh --files ../data_dir/kafka_1/test-topic-1/00000000000000000000.log --deep-iteration --print-data-log 查询消费者组位移 接下来，我们来看如何使用 kafka-consumer-groups 脚本查看消费者组位移。在上一讲讨论重设消费者组位移的时候，我们使用的也是这个命令。当时我们用的是 \u0026ndash;reset-offsets 参数，今天我们使用的是 \u0026ndash;describe 参数。假设我们要查询 Group ID 是 test-group 的消费者的位移，那么命令如图所示： 图中的 CURRENT-OFFSET 表示该消费者当前消费的最新位移，LOG-END-OFFSET 表示对应分区最新生产消息的位移，LAG 列是两者的差值。CONSUMER-ID 是 Kafka 消费者程序自动生成的一个 ID。截止到 2.2 版本，你都无法干预这个 ID 的生成过程。如果运行该命令时，这个消费者程序已经终止了，那么此列的值为空。\n小结 好了，我们小结一下。今天我们一起梳理了 Kafka 2.2 版本自带的所有脚本，我给出了常见的运维操作的工具行命令。希望这些命令对你操作和管理 Kafka 集群有所帮助。另外，我想强调的是，由于 Kafka 依然在不断演进，我们今天提到的命令的用法很可能会随着版本的变迁而发生变化。在具体使用这些命令时，你最好详细地阅读一下它们的 Usage 说明。\n","permalink":"https://leochu.work/blog/tech/bigdata/kafka%E5%B8%B8%E7%94%A8%E8%84%9A%E6%9C%AC%E6%B1%87%E6%80%BB/","summary":"\u003ch2 id=\"命令行脚本概览\"\u003e命令行脚本概览\u003c/h2\u003e\n\u003chr\u003e\n\u003cp\u003eKafka 默认提供了很多个命令行脚本，用于实现各种各样的功能和运维管理。今天我以 2.2 版本为例，详细地盘点下这些命令行工具。下图展示了 2.2 版本提供的所有命令行脚本。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230328115823.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230328115823.png\"\u003e\u003c/p\u003e\n\u003cp\u003e从图中我们可以知道，2.2 版本总共提供了 30 个 SHELL 脚本。图中的 windows 实际上是个子目录，里面保存了 Windows 平台下的 BAT 批处理文件。其他的. sh 文件则是 Linux 平台下的标准 SHELL 脚本。\u003c/p\u003e\n\u003cp\u003e默认情况下，不加任何参数或携带 \u0026ndash;help 运行 SHELL 文件，会得到该脚本的使用方法说明。下面这张图片展示了 kafka-log-dirs 脚本的调用方法。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230328115849.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230328115849.png\"\u003e\n有了这些基础的了解，我来逐一地说明这些脚本的用途，然后再给你详细地介绍一些常见的脚本。\u003c/p\u003e\n\u003cp\u003e我们先来说说 connect-standalone 和 connect-distributed 两个脚本。这两个脚本是 Kafka Connect 组件的启动脚本。在专栏第 4 讲谈到 Kafka 生态时，我曾说过社区提供了 Kafka Connect 组件，用于实现 Kafka 与外部世界系统之间的数据传输。Kafka Connect 支持单节点的 Standalone 模式，也支持多节点的 Distributed 模式。这两个脚本分别是这两种模式下的启动脚本。鉴于 Kafka Connect 不在我们的讨论范围之内，我就不展开讲了。\u003c/p\u003e\n\u003cp\u003e接下来是 kafka-acls 脚本。它是用于设置 Kafka 权限的，比如设置哪些用户可以访问 Kafka 的哪些主题之类的权限。在专栏后面，我会专门来讲 Kafka 安全设置的内容，到时候我们再细聊这个脚本。\u003c/p\u003e","title":"Kafka 常见的脚本汇总"},{"content":"Impala Daemon 命令行参数高级配置代码段（安全阀） -use_local_tz_for_unix_timestamp_conversions=true\r-convert_legacy_hive_parquet_utc_timestamps=true 在hive中，一个中文字符长度为1\n在impala中，一个中文字符长度为3\n","permalink":"https://leochu.work/blog/tech/bigdata/impala%E9%85%8D%E7%BD%AE%E8%B0%83%E4%BC%98/","summary":"\u003ch6 id=\"impala-daemon-命令行参数高级配置代码段安全阀\"\u003e\u003cstrong\u003eImpala Daemon 命令行参数高级配置代码段（安全阀）\u003c/strong\u003e\u003c/h6\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e-use_local_tz_for_unix_timestamp_conversions=true\r\n-convert_legacy_hive_parquet_utc_timestamps=true\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e在hive中，一个中文字符长度为1\u003c/p\u003e\n\u003cp\u003e在impala中，一个中文字符长度为3\u003c/p\u003e","title":"impala配置调优"},{"content":"1.创建临时函数 hive\u0026gt; add jar /home/hadoop/bigdata_udf.jar; hive\u0026gt; create temporary function isContains100 as \u0026#39;com.xx.hive.udf.hm2.IsContains100\u0026#39;; --验证 hive\u0026gt; select isContains100(t.col1) from t limit 10; hive\u0026gt; drop temporary function isContains100; 2.创建永久函数 hadoop fs -put /opt/bigdata_udf.jar /udf hive\u0026gt; create function default.url_decode as \u0026#39;com.xx.udf.DecodeURL\u0026#39; using jar \u0026#39;hdfs:///udf/bigdata_udf.jar\u0026#39;; --验证 hive\u0026gt; select default.url_decode(t.col1) from t limit 10; 注意:注册永久函数必须使用hdfs路径,不可使用本地路径 ","permalink":"https://leochu.work/blog/tech/bigdata/hive%E6%B3%A8%E5%86%8Cudf/","summary":"\u003ch4 id=\"1创建临时函数\"\u003e1.创建临时函数\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive\u0026gt; add jar /home/hadoop/bigdata_udf.jar;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive\u0026gt; create temporary \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e isContains100 as \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;com.xx.hive.udf.hm2.IsContains100\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--验证\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive\u0026gt; \u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e isContains100\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003et.col1\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e from t limit 10;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive\u0026gt; drop temporary \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e isContains100;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"2创建永久函数\"\u003e2.创建永久函数\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehadoop fs -put /opt/bigdata_udf.jar /udf\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive\u0026gt; create \u003cspan style=\"color:#66d9ef\"\u003efunction\u003c/span\u003e default.url_decode as \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;com.xx.udf.DecodeURL\u0026#39;\u003c/span\u003e using jar \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;hdfs:///udf/bigdata_udf.jar\u0026#39;\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e--验证\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehive\u0026gt; \u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e default.url_decode\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003et.col1\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e from t limit 10;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"注意注册永久函数必须使用hdfs路径不可使用本地路径\"\u003e注意:注册永久函数必须使用hdfs路径,不可使用本地路径\u003c/h4\u003e","title":"Hive注册udf"},{"content":"CREATE EXTERNAL TABLE `mongodb_dingtalk.hbasetohive_patent`( key String, abstracts String, address String) STORED BY \u0026#39;org.apache.hadoop.hive.hbase.HBaseStorageHandler\u0026#39; WITH SERDEPROPERTIES (\u0026#34;hbase.columns.mapping\u0026#34; = \u0026#34;:key,info:abstracts,info:address\u0026#34;) TBLPROPERTIES(\u0026#34;[hbase.table.name](http://hbase.table.name/)\u0026#34; = \u0026#34;dingtalk:patent\u0026#34;); 仅从HBase拉取数据使用，禁止利用此种方式往HBase写数据\n数据拉出时拉出hbase中数据对应当前版本的时间戳\ncreate external table ods.ods_zxk_hbase_wechat_public_account_mapping( id string comment \u0026#39;需要拆分的key\u0026#39;, ts timestamp comment \u0026#39;数据标识符，用来跟新数据做去重\u0026#39;) comment \u0026#39;高级搜索有无微信公众号\u0026#39; STORED BY \u0026#39;org.apache.hadoop.hive.hbase.HBaseStorageHandler\u0026#39; WITH SERDEPROPERTIES (\u0026#39;hbase.columns.mapping\u0026#39; = \u0026#39;:key,:timestamp\u0026#39;) TBLPROPERTIES(\u0026#39;[hbase.table.name](http://hbase.table.name/)\u0026#39; = \u0026#39;dingtalk:wechat_public_account\u0026#39;) ","permalink":"https://leochu.work/blog/tech/bigdata/hive%E6%98%A0%E5%B0%84hbase%E6%95%B0%E6%8D%AE%E6%BA%90/","summary":"\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eEXTERNAL\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003emongodb_dingtalk.hbasetohive_patent\u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003e(  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ekey\u003c/span\u003e String,  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eabstracts String,  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaddress String)  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSTORED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;org.apache.hadoop.hive.hbase.HBaseStorageHandler\u0026#39;\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eWITH\u003c/span\u003e SERDEPROPERTIES (\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;hbase.columns.mapping\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;:key,info:abstracts,info:address\u0026#34;\u003c/span\u003e)  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTBLPROPERTIES(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;[hbase.table.name](http://hbase.table.name/)\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;dingtalk:patent\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e仅从HBase拉取数据使用，禁止利用此种方式往HBase写数据\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e数据拉出时拉出hbase中数据对应当前版本的时间戳\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ecreate\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eexternal\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etable\u003c/span\u003e ods.ods_zxk_hbase_wechat_public_account_mapping(  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eid string \u003cspan style=\"color:#66d9ef\"\u003ecomment\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;需要拆分的key\u0026#39;\u003c/span\u003e,  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ets \u003cspan style=\"color:#66d9ef\"\u003etimestamp\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003ecomment\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;数据标识符，用来跟新数据做去重\u0026#39;\u003c/span\u003e)  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ecomment\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;高级搜索有无微信公众号\u0026#39;\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSTORED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;org.apache.hadoop.hive.hbase.HBaseStorageHandler\u0026#39;\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eWITH\u003c/span\u003e SERDEPROPERTIES (\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;hbase.columns.mapping\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;:key,:timestamp\u0026#39;\u003c/span\u003e)  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTBLPROPERTIES(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;[hbase.table.name](http://hbase.table.name/)\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;dingtalk:wechat_public_account\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Hive映射HBase数据源"},{"content":"一 hive导入es 1 创建hive-es映射表 CREATE EXTERNAL TABLE hive_es.re_run_test2( id STRING ,test STRING) STORED BY \u0026#39;org.elasticsearch.hadoop.hive.EsStorageHandler\u0026#39; TBLPROPERTIES(\u0026#39;es.resource\u0026#39; = \u0026#39;re_run_test2/test\u0026#39;, \u0026#39;es.nodes\u0026#39;=\u0026#39;172.16.98.113,172.16.98.149,172.16.98.150,172.16.98.151,172.16.98.152\u0026#39;, \u0026#39;es.port\u0026#39;=\u0026#39;9200\u0026#39;, \u0026#39;es.mapping.id\u0026#39; =\u0026#39;id\u0026#39;) 注： 1. es.resource对应es中的index/type 2. 1.es.mapping.names为hive和es字段名映射关系。\r2.如果hive表和es表字段名完全一致，可以省略此参数。\r3.hive中字段名不区分大小写，元数据寸的全是小写；es中字段大小写敏感，如果es中字段名出现大写，需认真填写。\r4.es中_id为自动生成，如若需要覆盖，需加参数\u0026#39;es.mapping.id\u0026#39;=\u0026#39;id\u0026#39; 2 先导入es映射表相关jar包 add jar /opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hive/auxlib/elasticsearch-hadoop-6.3.0.jar;\radd jar /data/jar/httpclient-4.5.5.jar;\radd jar /data/jar/org.apache.commons.httpclient.jar; 3 向映射表insert数据 二 es导入hive 1 建hive映射表 CREATE EXTERNAL TABLE hive_es.cty_test5( addTime string ) STORED BY \u0026#39;org.elasticsearch.hadoop.hive.EsStorageHandler\u0026#39; TBLPROPERTIES(\u0026#39;es.resource\u0026#39; = \u0026#39;cty_test/cty_test\u0026#39;, \u0026#39;es.nodes\u0026#39;=\u0026#39;172.16.98.113,172.16.98.149,172.16.98.150,172.16.98.151,172.16.98.152\u0026#39;, \u0026#39;es.port\u0026#39;=\u0026#39;9200\u0026#39;, \u0026#39;es.mapping.names\u0026#39;= \u0026#39;addTime:addTime\u0026#39;, \u0026#39;es.mapping.date.rich\u0026#39;=\u0026#39;false\u0026#39;, \u0026#39;es.index.auto.create\u0026#39;=\u0026#39;false\u0026#39;, ) 注意，hive表数据类型要和es一致，除了es的date要转成hive的string，同时要加参数\u0026rsquo;es.mapping.date.rich\u0026rsquo;=\u0026lsquo;false\u0026rsquo;,否则查询会报错. 2 通过映射表向其他表insert 参考： https://www.cnblogs.com/koushr/p/9505435.html\n","permalink":"https://leochu.work/blog/tech/bigdata/hive%E6%98%A0%E5%B0%84elasticsearch/","summary":"\u003ch3 id=\"一-hive导入es\"\u003e一 hive导入es\u003c/h3\u003e\n\u003ch4 id=\"1-创建hive-es映射表\"\u003e1 创建hive-es映射表\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eEXTERNAL\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e hive_es.re_run_test2(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eid STRING\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e,test STRING)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSTORED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;org.elasticsearch.hadoop.hive.EsStorageHandler\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTBLPROPERTIES(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.resource\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;re_run_test2/test\u0026#39;\u003c/span\u003e, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.nodes\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;172.16.98.113,172.16.98.149,172.16.98.150,172.16.98.151,172.16.98.152\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.port\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;9200\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.mapping.id\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;id\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"注\"\u003e注：\u003c/h3\u003e\n\u003ch3 id=\"1\"\u003e1.\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ees.resource对应es中的index/type\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2\"\u003e2.\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e1.es.mapping.names为hive和es字段名映射关系。\r\n2.如果hive表和es表字段名完全一致，可以省略此参数。\r\n3.hive中字段名不区分大小写，元数据寸的全是小写；es中字段大小写敏感，如果es中字段名出现大写，需认真填写。\r\n4.es中_id为自动生成，如若需要覆盖，需加参数\u0026#39;es.mapping.id\u0026#39;=\u0026#39;id\u0026#39;\n\u003c/code\u003e\u003c/pre\u003e\u003ch3\u003e\u003c/h3\u003e\n\u003ch4 id=\"2-先导入es映射表相关jar包\"\u003e2 先导入es映射表相关jar包\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eadd jar /opt/cloudera/parcels/CDH-6.3.2-1.cdh6.3.2.p0.1605554/lib/hive/auxlib/elasticsearch-hadoop-6.3.0.jar;\r\nadd jar /data/jar/httpclient-4.5.5.jar;\r\nadd jar /data/jar/org.apache.commons.httpclient.jar;\n\u003c/code\u003e\u003c/pre\u003e\u003ch4 id=\"3-向映射表insert数据\"\u003e3 向映射表insert数据\u003c/h4\u003e\n\u003ch3 id=\"二-es导入hive\"\u003e二 es导入hive\u003c/h3\u003e\n\u003ch4 id=\"1-建hive映射表\"\u003e1 建hive映射表\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eEXTERNAL\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e hive_es.cty_test5(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eaddTime string\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSTORED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;org.elasticsearch.hadoop.hive.EsStorageHandler\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTBLPROPERTIES(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.resource\u0026#39;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;cty_test/cty_test\u0026#39;\u003c/span\u003e, \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.nodes\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;172.16.98.113,172.16.98.149,172.16.98.150,172.16.98.151,172.16.98.152\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.port\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;9200\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.mapping.names\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;addTime:addTime\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.mapping.date.rich\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;false\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;es.index.auto.create\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;false\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch5 id=\"注意hive表数据类型要和es一致除了es的date要转成hive的string同时要加参数esmappingdaterichfalse否则查询会报错\"\u003e注意，hive表数据类型要和es一致，除了es的date要转成hive的string，同时要加参数\u0026rsquo;es.mapping.date.rich\u0026rsquo;=\u0026lsquo;false\u0026rsquo;,否则查询会报错.\u003c/h5\u003e\n\u003ch4 id=\"2-通过映射表向其他表insert\"\u003e2 通过映射表向其他表insert\u003c/h4\u003e\n\u003ch3 id=\"参考\"\u003e参考：\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"https://www.cnblogs.com/koushr/p/9505435.html\"\u003ehttps://www.cnblogs.com/koushr/p/9505435.html\u003c/a\u003e\u003c/p\u003e","title":"hive映射es"},{"content":"set spark.master=yarn-cluster; #设置spark提交模式 set hive.execution.engine=spark; #设置计算引擎 set spark.yarn.queue=queue_name; #设置作业提交队列 set spark.app.name=job_name; #设置作业名称 set spark.executor.instances=20; #设置执行器个数 set spark.executor.cores=4; #设置执行器计算核个数 set spark.executor.memory=8g; #设置执行器内存 set mapred.reduce.tasks=600; #设置任务并行度 set spark.yarn.executor.memoryOverhead=2048; #设置每个executor的jvm堆外内存 set spark.memory.fraction=0.8; #设置内存比例(spark2.0+) set spark.serializer=org.apache.serializer.KyroSerializer; #设置对象序列化方式 ","permalink":"https://leochu.work/blog/tech/bigdata/hive%E8%AE%BE%E7%BD%AEspark%E5%8F%82%E6%95%B0/","summary":"\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.master\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eyarn-cluster;          \u003cspan style=\"color:#75715e\"\u003e#设置spark提交模式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset hive.execution.engine\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003espark;         \u003cspan style=\"color:#75715e\"\u003e#设置计算引擎\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.yarn.queue\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003equeue_name;      \u003cspan style=\"color:#75715e\"\u003e#设置作业提交队列\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.app.name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ejob_name;         \u003cspan style=\"color:#75715e\"\u003e#设置作业名称\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.executor.instances\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e20;         \u003cspan style=\"color:#75715e\"\u003e#设置执行器个数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.executor.cores\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e4;            \u003cspan style=\"color:#75715e\"\u003e#设置执行器计算核个数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.executor.memory\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e8g;         \u003cspan style=\"color:#75715e\"\u003e#设置执行器内存\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset mapred.reduce.tasks\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e600;           \u003cspan style=\"color:#75715e\"\u003e#设置任务并行度\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.yarn.executor.memoryOverhead\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e2048;   \u003cspan style=\"color:#75715e\"\u003e#设置每个executor的jvm堆外内存\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.memory.fraction\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e0.8;          \u003cspan style=\"color:#75715e\"\u003e#设置内存比例(spark2.0+)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eset spark.serializer\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eorg.apache.serializer.KyroSerializer;  \u003cspan style=\"color:#75715e\"\u003e#设置对象序列化方式\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"hive设置spark参数"},{"content":"调优具体细节 Hive建表设计层面 Hive 的建表设计层面调优，主要讲的怎么样合理的组织数据，方便后续的高效计算。比如建表的类型，文件存储格式，是否压缩等等。\n利用分区表优化 关于 Hive 的表的类型有哪些？\r1、分区表 2、分桶表 分区表 是在某一个或者几个维度上对数据进行分类存储，一个分区对应一个目录。如果筛选条件里有分区字段，那么 Hive 只需要遍历对应分区目录下的文件即可，不需要遍历全局数据，使得处理的数据量大大减少，从而提高查询效率。\n也就是说：当一个 Hive 表的查询大多数情况下，会根据某一个字段进行筛选时，那么非常适合创建为分区表，该字段即为分区字段。\nselect1: select .... where country = \u0026#34;china\u0026#34;\rselect2: select .... where country = \u0026#34;china\u0026#34;\rselect3: select .... where country = \u0026#34;china\u0026#34;\rselect4: select .... where country = \u0026#34;china\u0026#34;\r..... 分门别类：这个city字段的每个值，就单独形成为一个分区。其实每个分区就对应带HDFS的一个目录\n在创建表时通过启用 partitioned by 实现，用来 partition 的维度并不是实际数据的某一列，具体分区的标志是由插入内容时给定的。当要查询某一分区的内容时可以采用 where 语句，形似 where tablename.partition_column = a 来实现。\n1、创建含分区的表：\nCREATE TABLE page_view(viewTime INT, userid BIGINT, page_url STRING, referrer_url STRING, ip STRING COMMENT \u0026#39;IP Address of the User\u0026#39;) PARTITIONED BY(date STRING, country STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0026#39;1\u0026#39; STORED AS TEXTFILE; 2、载入内容，并指定分区标志：\nload data local inpath \u0026#39;/home/bigdata/pv_2018-07-08_us.txt\u0026#39; into table page_view partition(date=\u0026#39;2018-07-08\u0026#39;, country=\u0026#39;US\u0026#39;); 3、查询指定标志的分区内容：\nSELECT page_views.* FROM page_views WHERE page_views.date \u0026gt;= \u0026#39;2008-03-01\u0026#39; AND page_views.date \u0026lt;= \u0026#39;2008-03-31\u0026#39; AND page_views.referrer_url like \u0026#39;%xyz.com\u0026#39;; 简单总结：\n1、当你意识到一个字段经常用来做where，建分区表，使用这个字段当做分区字段\r2、在查询的时候，使用分区字段来过滤，就可以避免全表扫描。只需要扫描这张表的一个分区的数据即可 利用分桶表优化 select a.*, b.* from a join b on a.id = b.id; 解决方案：\n1、两张表都得按照这个链接字段来进行分桶\r2、分桶数量要成倍数关系 跟分区的概念很相似，都是把数据分成多个不同的类别，区别就是规则不一样！\n1、分区：按照字段值来进行：一个分区，就只是包含这个这一个值的所有记录\r不是当前分区的数据一定不在当前分区\r当前分区也只会包含当前这个分区值的数据\r2、分桶：默认规则：Hash散列\r一个分桶中会有多个不同的值\r如果一个分桶中，包含了某个值，这个值的所有记录，必然都在这个分桶 Hive Bucket，分桶，是指将数据以指定列的值为 key 进行 hash，hash 到指定数目的桶中，这样做的目的和分区表类似，使得筛选时不用全局遍历所有的数据，只需要遍历所在桶就可以了。这样也可以支持高效采样。\n1、采样(采样多少百分比的数据，采样多少条的数据，采样多大量的数据)\r2、join(doris:OLAP引擎: colocation join: 两张表中的相同分桶编号的数据，在同一个节点) 如下例就是以 userid 这一列为 bucket 的依据，共设置 32 个 buckets\nCREATE TABLE page_view(viewTime INT, userid BIGINT, page_url STRING, referrer_url STRING, ip STRING COMMENT \u0026#39;IP Address of the User\u0026#39;) COMMENT \u0026#39;This is the page view table\u0026#39; PARTITIONED BY(dt STRING, country STRING) CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0026#39;1\u0026#39; COLLECTION ITEMS TERMINATED BY \u0026#39;2\u0026#39; MAP KEYS TERMINATED BY \u0026#39;3\u0026#39; STORED AS SEQUENCEFILE; 分桶的语法：\nCLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS CLUSTERED BY(userid) 表示按照 userid 来分桶\rSORTED BY(viewTime) 按照viewtime来进行桶内排序\rINTO 32 BUCKETS\t分成多少个桶 两个表以相同方式（相同字段）划分桶，两个表的桶个数是倍数关系\ncreate table order(cid int,price float) clustered by(cid) into 32 buckets; create table customer(id int,first string) clustered by(id) into 32 buckets; select price from order t join customer s on t.cid = s.id 通常情况下，Sampling 在全体数据上进行采样，这样效率自然就低，它要去访问所有数据。而如果一个表已经对某一列制作了 bucket，就可以采样所有桶中指定序号的某个桶，这就减少了访问量。\n如下例所示就是采样了 page_view 中 32 个桶中的第三个桶的全部数据：\nSELECT * FROM page_view TABLESAMPLE(BUCKET 3 OUT OF 32); 如下例所示就是采样了 page_view 中 32 个桶中的第三个桶的一半数据：\nSELECT * FROM page_view TABLESAMPLE(BUCKET 3 OUT OF 64); 详细说明：http://blog.csdn.net/zhongqi2513/article/details/74612701\n总结三种采样方式：\n分桶抽样： select * from student tablesample(bucket 3 out of 32); select * from student tablesample(bucket 1 out of 10 on rand()); 随机采样：rand() 函数 select * from student order by rand() limit 100; // 效率低 select * from student distribute by rand() sort by rand() limit 100; // 推荐使用这种 数据块抽样：tablesample()函数 select * from student tablesample(10 percent); # 百分比 select * from student tablesample(5 rows);\t# 行数 select * from student tablesample(5 M); # 大小 缺点：不随机。该方法实际上是按照文件中的顺序返回数据，对分区表，从头开始抽取，可能造成只有前面几个分区的数据。 优点：速度快。 select * from student where rand()\u0026lt;0.2 distribute by rand() sort by rand() limit 5; 创建一张表 即是分区表，也是分桶表！\nHive；数仓：select\rDoris： OLAP分析引擎\rOLAP分析引擎的SQL执行效率要比hive高的多，甚至亚秒级响应 OLAP：列存储 + 范围分区 + 排序 + 索引 + 压缩 + 预计算 + 数据倾斜\n选择合适的文件存储格式 简单的标准：\n1、原始数据，一般来说，都是普通文本格式\r2、计算的结果数据\r如果这种结果数据，通常还要作为另外一种计算的输入，那么可以让当前这个结果数据的格式易于被读取（序列化文件格式可用）\r如果这种结果数据，就是最终的结果了，那么看数据量的大小关系：\r如果比较大：可以考虑压缩\r如果不是特别大：直接普通文本格式，甚至存储在 RDBMS 3、计算引擎种应用程序在执行的时候：\r中间临时数据：执行引擎的特性：列式存储 + 序列化存储文件格式 业内三种常用的数据存储格式：\n水平行存储结构（horizontal row-store structure）\r垂直列存储结构（vertical column-store structure）\rPAX 混合存储结构（hybrid PAX store structure） Hive的默认存储格式：textfile\rstudent(id,name,sex,age,department,c1,c2,c3,c5,.....) 50个字段\rselect department, count(*) as total from student group by department; 在 HiveSQL 的 create table 语句中，可以使用 stored as ... 指定表的存储格式。Apache Hive 支持 Apache Hadoop 中使用的几种熟悉的文件格式，比如 TextFile、SequenceFile、RCFile、Avro、ORC、ParquetFile等。\n存储格式一般需要根据业务进行选择，在我们的实操中，绝大多数表都采用 TextFile 与 Parquet 两种存储格式之一。 TextFile 是最简单的存储格式，它是纯文本记录，也是 Hive 的默认格式。虽然它的磁盘开销比较大，查询效率也低，但它更多地是作为跳板来使用。RCFile、ORC、Parquet 等格式的表都不能由文件直接导入数据，必须由 TextFile 来做中转。 Parquet 和 ORC 都是 Apache 旗下的开源列式存储格式。列式存储比起传统的行式存储更适合批量 OLAP 查询，并且也支持更好的压缩和编码。\n创建表时，特别是宽表，尽量使用 ORC、ParquetFile 这些列式存储格式，因为列式存储的表，每一列的数据在物理上是存储在一起的，Hive 查询时会只遍历需要列数据，大大减少处理的数据量。\n第一种：TextFile\n1、存储方式：行存储。默认格式，如果建表时不指定默认为此格式。，\r2、每一行都是一条记录，每行都以换行符\u0026#34;\\n\u0026#34;结尾。数据不做压缩时，磁盘会开销比较大，数据解析开销也比较大。\r3、可结合Gzip、Bzip2等压缩方式一起使用（系统会自动检查，查询时会自动解压）,推荐选用可切分的压缩算法。 第二种：Sequence File\n1、一种Hadoop API提供的二进制文件，使用方便、可分割、个压缩的特点。\r2、支持三种压缩选择：NONE、RECORD、BLOCK。RECORD压缩率低，一般建议使用BLOCK压缩。 第三种：RC File\n1、存储方式：数据按行分块，每块按照列存储 。\rA、首先，将数据按行分块，保证同一个record在一个块上，避免读一个记录需要读取多个block。\rB、其次，块数据列式存储，有利于数据压缩和快速的列存取。\r2、相对来说，RCFile对于提升任务执行性能提升不大，但是能节省一些存储空间。可以使用升级版的ORC格式。 第四种：ORC File\n1、存储方式：数据按行分块，每块按照列存储\r2、Hive提供的新格式，属于RCFile的升级版，性能有大幅度提升，而且数据可以压缩存储，压缩快，快速列存取。\r3、ORC File会基于列创建索引，当查询的时候会很快。 第五种：Parquet File\n1、存储方式：列式存储。\r2、Parquet对于大型查询的类型是高效的。对于扫描特定表格中的特定列查询，Parquet特别有用。Parquet一般使用Snappy、Gzip压缩。默认Snappy。\r3、Parquet支持Impala查询引擎。\r4、表的文件存储格式尽量采用Parquet或ORC，不仅降低存储量，还优化了查询，压缩，表关联等性能。 选择合适的压缩格式 1、压缩算法什么时候选择用？\r任务：IO密集型 计算密集型\r2、各种不同压缩算法的使用场景\r压缩率：原来100G的数据，现在压缩完了20%\r压缩速率：压缩一定量的数据花了多长时间\r是否可拆分： Hive 语句最终是转化为 MapReduce 程序来执行的，而 MapReduce 的性能瓶颈在与 网络IO 和 磁盘IO，要解决性能瓶颈，最主要的是 减少数据量，对数据进行压缩是个好方式。压缩虽然是减少了数据量，但是压缩过程要消耗 CPU，但是在 Hadoop 中，往往性能瓶颈不在于 CPU，CPU 压力并不大，所以压缩充分利用了比较空闲的 CPU。\n常用压缩方法对比\n压缩格式 是否可拆分 是否自带 压缩率 速度 是否hadoop自带 gzip 否 是 很高 比较快 是 lzo 是 是 比较高 很快 否，要安装 snappy 否 是 比较高 很快 否，要安装 bzip2 是 否 最高 慢 是 如何选择压缩方式\n1、压缩比率\r2、压缩解压速度\r3、是否支持split 支持分割的文件可以并行的有多个 mapper 程序处理大数据文件，大多数文件不支持可分割是因为这些文件只能从头开始读。\n是否压缩\n1、计算密集型，不压缩，否则进一步增加了CPU的负担\r2、网络密集型，推荐压缩，减小网络数据传输 各个压缩方式所对应的Class类\n压缩格式 类 zlib org.apache.hadoop.io.compress.DefaultCodec gzip org.apache.hadoop.io.compress.GzipCodec bzip2 org.apache.hadoop.io.compress.Bzip2Codec lzo org.apache.hadoop.io.compress.lzo.LzoCodec lz4 org.apache.hadoop.io.compress.LzoCodec snappy org.apache.hadoop.io.compress.SnappyCodec 压缩使用：\nJob 输出文件按照 Block 以 Gzip 的方式进行压缩：\n## 默认值是false set mapreduce.output.fileoutputformat.compress=true; ## 默认值是Record set mapreduce.output.fileoutputformat.compress.type=BLOCK ## 默认值是org.apache.hadoop.io.compress.DefaultCodec set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec Map 输出结果也以 Gzip 进行压缩：\n## 启用map端输出压缩 set mapred.map.output.compress=true ## 默认值是org.apache.hadoop.io.compress.DefaultCodec set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.GzipCodec 对 Hive 输出结果和中间都进行压缩：\nset hive.exec.compress.output=true ## 默认值是false，不压缩 set hive.exec.compress.intermediate=true ## 默认值是false，为true时MR设置的压缩才启用 HQL语法和运行参数层面 HQL 语法和运行参数层面，主要跟大家讲讲如果写出高效的 HQL，以及如果利用一些控制参数来调优 HQL 的执行。这是 HQL 调优的一个大头。\n查看Hive执行计划 Hive 的 SQL 语句在执行之前需要将 SQL 语句转换成 MapReduce 任务，因此需要了解具体的转换过程，可以在 SQL 语句中输入如下命令查看具体的执行计划。\n## 查看执行计划，添加extended关键字可以查看更加详细的执行计划 explain [extended] query 例子：\nexplain select department, count(*) as total from student where age \u0026gt;= 18 group by department order by total desc limit 3; 关于 Hive 的执行计划中的 Operator 的概念：(逻辑执行计划：Operator Tree)\nselect ... from ... where ... group by ... having ... order by .... limit ....\rselect： FetchOperator\rfrom： TableScanOperator\rwhere+having： FilterOperator\r..... 列裁剪 列裁剪就是在查询时只读取需要的列，分区裁剪就是只读取需要的分区。当列很多或者数据量很大时，如果 select * 或者不指定分区，全列扫描和全表扫描效率都很低。\nHive 在读数据的时候，可以只读取查询中所需要用到的列，而忽略其他的列。这样做可以节省读取开销：中间表存储开销和数据整合开销。 set hive.optimize.cp = true; ## 列裁剪，取数只取查询中需要用到的列，默认是true set key; 查询key的value\rset key=value; 设置key的value 案例：\n一张表：id,name,sex,age,department\rSQL业务需求： select age,count(*) as total from student group by age; 谓词下推 将 SQL 语句中的 where 谓词逻辑都尽可能提前执行，减少下游处理的数据量。对应逻辑优化器是 PredicatePushDown。\nset hive.optimize.ppd=true; ## 默认是true 示例程序：\nselect a.*, b.* from a join b on a.id = b.id where b.age \u0026gt; 20; select a.*, c.* from a join (select * from b where age \u0026gt; 20) c on a.id = c.id; 分区裁剪 列裁剪就是在查询时只读取需要的列，分区裁剪就是只读取需要的分区。当列很多或者数据量很大时，如果 select * 或者不指定分区，全列扫描和全表扫描效率都很低。\n在查询的过程中只选择需要的分区，可以减少读入的分区数目，减少读入的数据量。\nHive 中与分区裁剪优化相关的则是：\nset hive.optimize.pruner=true; ## 默认是true 在 HiveQL 解析阶段对应的则是 ColumnPruner 逻辑优化器。\nselect * from student where department = \u0026#34;AAAA\u0026#34;; 合并小文件 大数据技术组件最怕的就是两个事儿：\r1、存储组件：海量小文件\rSQL需求；1000000M = 100000 * 10M 100000个小文件\r2、计算组件：数据倾斜\rSQL需求；1000000M = 100000 * 10M 100000个小文件 如果一个mapreduce job碰到一堆小文件作为输入，一个小文件启动一个Task\r在MR编程模型中，给我们提供了一个中叫做 CombineFileInputFormat的一个类：具备 把一个节点甚至一个机架上的多个小文件，划分到同一个 输入切片\rHive的默认INputFormat： TextInputFormat Map 输入合并\n在执行 MapReduce 程序的时候，一般情况是一个文件的一个数据分块需要一个 mapTask 来处理。但是如果数据源是大量的小文件，这样就会启动大量的 mapTask 任务，这样会浪费大量资源。可以将输入的小文件进行合并，从而减少 mapTask 任务数量。\n## Map端输入、合并文件之后按照block的大小分割（默认） set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; ## Map端输入，不合并 set hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat; Map/Reduce输出合并\n大量的小文件会给 HDFS 带来压力，影响处理效率。可以通过合并 Map 和 Reduce 的结果文件来消除影响。\n## 是否合并Map输出文件, 默认值为true set hive.merge.mapfiles=true; ## 是否合并Reduce端输出文件,默认值为false set hive.merge.mapredfiles=true; ## 合并文件的大小,默认值为256000000 = 256M set hive.merge.size.per.task=256000000; ## 每个Map 最大分割大小 set mapred.max.split.size=256000000; ## 一个节点上split的最少值 set mapred.min.split.size.per.node=1; // 服务器节点 # 如果有多个节点，都是只有一个小文件，这种情况当中，意味着压根没合并 # 如果这个节点有300个1M的文件。指定的输入切片大小是：256M， 44M的数据会传送给其他节点做合并 # 如果这个节点有500个1M的文件。指定的输入切片大小是：256M， 244M的数据会传送给其他节点做合并 ## 一个机架上split的最少值 set mapred.min.split.size.per.rack=1; // 服务器机架 hive.merge.size.per.task 和 mapred.min.split.size.per.node 联合起来：\n1、默认情况先把这个节点上的所有数据进行合并，如果合并的那个文件的大小超过了256M就开启另外一个文件继续合并\r2、如果当前这个节点上的数据不足256M，那么就都合并成一个逻辑切片。 现在有100个Task，总共有10000M的数据， 平均一下，每个Task执行100M的数据的计算。\n假设只启动10个Task，每个Task就要执行1000M的数据。 如果只有2个Task， 5000M\n合理设置MapTask并行度 1、当前mapper阶段的计算的数据来源： 原始数据， 上一个MR的输出结果\rminsize\rmaxsize\rblocksize 第一：MapReduce中的MapTask的并行度机制\n现在有100个节点\r1、每个任务512M的数据，但是只有 40 个节点运行了一个任务 = 40个MapTask\r256M 80个节点 80个MapTask\r2、每个任务512M的数据，1000 个MapTask\r1G 10/2 = 5个 Map数过大：当输入文件特别大，MapTask 特别多，每个计算节点分配执行的 MapTask 都很多，这时候可以考虑减少 MapTask 的数量。增大每个 MapTask 处理的数据量。而且 MapTask 过多，最终生成的结果文件数也太多。\n1、Map阶段输出文件太小，产生大量小文件\r2、初始化和创建Map的开销很大 Map数太小：当输入文件都很大，任务逻辑复杂，MapTask 执行非常慢的时候，可以考虑增加 MapTask 数，来使得每个 MapTask 处理的数据量减少，从而提高任务的执行效率。\n1、文件处理或查询并发度小，Job执行时间过长\r2、大量作业时，容易堵塞集群 在 MapReduce 的编程案例中，我们得知，一个MapReduce Job 的 MapTask 数量是由输入分片 InputSplit 决定的。而输入分片是由 FileInputFormat.getSplit() 决定的。一个输入分片对应一个 MapTask，而输入分片是由三个参数决定的：\n参数 默认值 意义 dfs.blocksize 128M HDFS默认数据块大小 mapreduce.input.fileinputformat.split.minsize 1 最小分片大小(MR) mapreduce.input.fileinputformat.split.maxsize 256M 最大分片大小(MR) 输入分片大小的计算是这么计算出来的：针对数据是原始数据的情况，最终调整的 splitsize 的大小最好是 blockSize 的整数倍\nlong splitSize = Math.max(minSize, Math.min(maxSize, blockSize)) 默认情况下，输入分片大小和 HDFS 集群默认数据块大小一致，也就是默认一个数据块，启用一个 MapTask 进行处理，这样做的好处是避免了服务器节点之间的数据传输，提高 job 处理效率\n两种经典的控制MapTask的个数方案：减少MapTask数 或者 增加MapTask数\n1、减少 MapTask 数是通过合并小文件来实现，这一点主要是针对数据源\r2、增加 MapTask 数可以通过控制上一个 job 的 reduceTask 个数\r重点注意：不推荐把这个值进行随意设置！ 推荐的方式：使用默认的切块大小即可。如果非要调整，最好是切块的N倍数 NodeManager 节点个数：N ===\u0026gt; Task = ( N * 0.95) * M\n第二：合理控制 MapTask 数量\n1、减少 MapTask 数可以通过合并小文件来实现 2、增加 MapTask 数可以通过控制上一个 ReduceTask 默认的 MapTask 个数\n计算方式\n输入文件总大小：total_size HDFS 设置的数据块大小：dfs_block_size default_mapper_num = total_size / dfs_block_size MapReduce 中提供了如下参数来控制 map 任务个数，从字面上看，貌似是可以直接设置 MapTask 个数的样子，但是很遗憾不行，这个参数设置只有在大于default_mapper_num 的时候，才会生效。\nset mapred.map.tasks=10;\t## 默认值是2 那如果我们需要减少 MapTask 数量，但是文件大小是固定的，那该怎么办呢?\n可以通过 mapred.min.split.size 设置每个任务处理的文件的大小，这个大小只有在大于 dfs_block_size 的时候才会生效\nsplit_size = max(mapred.min.split.size, dfs_block_size)\rsplit_num = total_size / split_size\rcompute_map_num = Math.min(split_num, Math.max(default_mapper_num, mapred.map.tasks)) 这样就可以减少 MapTask 数量了。\n总结一下控制 mapper 个数的方法：\n1、如果想增加 MapTask 个数，可以设置 mapred.map.tasks 为一个较大的值\r2、如果想减少 MapTask 个数，可以设置 maperd.min.split.size 为一个较大的值\r3、如果输入是大量小文件，想减少 mapper 个数，可以通过设置 hive.input.format 合并小文件 如果想要调整 mapper 个数，在调整之前，需要确定处理的文件大概大小以及文件的存在形式（是大量小文件，还是单个大文件），然后再设置合适的参数。不能盲目进行暴力设置，不然适得其反。\nMapTask 数量与输入文件的 split 数息息相关，在 Hadoop 源码 org.apache.hadoop.mapreduce.lib.input.FileInputFormat 类中可以看到 split 划分的具体逻辑。可以直接通过参数 mapred.map.tasks（默认值2）来设定 MapTask 数的期望值，但它不一定会生效。\n合理设置ReduceTask并行度 1、有一个推算机制\rhive.exec.reducers.bytes.per.reducer 每个reduceTask处理的最大数据大小\rhive.exec.reducers.max reduceTask的上限\r2、可以自己根据需求来进行常量设置\r如果设置了 mapreduce.job.reduces 为 n 在没有 ordre by 的场景中，就使用 n 个reduceTask 如果 ReduceTask 数量过多，一个 ReduceTask 会产生一个结果文件，这样就会生成很多小文件，那么如果这些结果文件会作为下一个 Job 的输入，则会出现小文件需要进行合并的问题，而且启动和初始化 ReduceTask 需要耗费资源。\n如果 ReduceTask 数量过少，这样一个 ReduceTask 就需要处理大量的数据，并且还有可能会出现数据倾斜的问题，使得整个查询耗时长。 默认情况下，Hive 分配的 reducer 个数由下列参数决定：\nHadoop MapReduce 程序中，ReducerTask 个数的设定极大影响执行效率，ReducerTask 数量与输出文件的数量相关。如果 ReducerTask 数太多，会产生大量小文件，对HDFS造成压力。如果 ReducerTask 数太少，每个ReducerTask 要处理很多数据，容易拖慢运行时间或者造成 OOM。这使得 Hive 怎样决定 ReducerTask 个数成为一个关键问题。遗憾的是 Hive 的估计机制很弱，不指定 ReducerTask 个数的情况下，Hive 会猜测确定一个ReducerTask 个数，基于以下两个设定：\n参数1：hive.exec.reducers.bytes.per.reducer\t(默认256M)\r参数2：hive.exec.reducers.max\t(默认为1009)\r参数3：mapreduce.job.reduces\t(默认值为-1，表示没有设置，那么就按照以上两个参数进行设置) ReduceTask 的计算公式为：\nN = Math.min(参数2，总输入数据大小 / 参数1) 可以通过改变上述两个参数的值来控制 ReduceTask 的数量。 也可以通过\nset mapred.map.tasks=10; set mapreduce.job.reduces=10; 通常情况下，有必要手动指定 ReduceTask 个数。考虑到 Mapper 阶段的输出数据量通常会比输入有大幅减少，因此即使不设定 ReduceTask 个数，重设 参数2 还是必要的。\n依据经验，可以将 参数2 设定为 M * （0.95 * N） (N为集群中 NodeManager 个数)。一般来说，NodeManage 和 DataNode 的个数是一样的。\nJoin优化 Join优化整体原则：\n1、优先过滤后再进行Join操作，最大限度的减少参与join的数据量（where能用就用）\r2、小表join大表，最好启动mapjoin，hive自动启用mapjoin, 小表不能超过25M，可以更改\r3、Join on的条件相同的话，最好放入同一个job，并且join表的排列顺序从小到大：select a.*, b.*, c.* from a join b on a.id = b.id join c on a.id = c.i\r4、如果多张表做join, 如果多个链接条件都相同，会转换成一个JOb 优先过滤数据\n尽量减少每个阶段的数据量，对于分区表能用上分区字段的尽量使用，同时只选择后面需要使用到的列，最大限度的减少参与 Join 的数据量。 小表 join 大表原则\n小表 join 大表的时应遵守小表 join 大表原则，原因是 join 操作的 reduce 阶段，位于 join 左边的表内容会被加载进内存，将条目少的表放在左边，可以有效减少发生内存溢出的几率。join 中执行顺序是从左到右生成 Job，应该保证连续查询中的表的大小从左到右是依次增加的。 使用相同的连接键\n在 hive 中，当对 3 个或更多张表进行 join 时，如果 on 条件使用相同字段，那么它们会合并为一个 MapReduce Job，利用这种特性，可以将相同的 join on 放入一个 job 来节省执行时间。 尽量原子操作\n尽量避免一个SQL包含复杂的逻辑，可以使用中间表来完成复杂的逻辑。 大表Join大表\n1、空key过滤：有时join超时是因为某些key对应的数据太多，而相同key对应的数据都会发送到相同的reducer上，从而导致内存不够。此时我们应该仔细分析这些异常的key，很多情况下，这些key对应的数据是异常数据，我们需要在SQL语句中进行过滤。\r2、空key转换：有时虽然某个key为空对应的数据很多，但是相应的数据不是异常数据，必须要包含在join的结果中，此时我们可以表a中key为空的字段赋一个随机的值，使得数据随机均匀地分不到不同的reducer上 启用 MapJoin 关于HIve调优中，能用就用的原则和方法：\n1、where 能用就用\r2、majoin\t资源允许的情况下\r3、局部聚合 combiner\t不改变业务结果的情况下 有两个优化：能用则用\r1、join场景：mapjoin\r2、聚合场景：combiner（在不影响业务结果的前提下） 这个优化措施，但凡能用就用！ 大表 join 小表 小表满足需求： 小表数据小于控制条件时 MapJoin 是将 join 双方比较小的表直接分发到各个 map 进程的内存中，在 map 进程中进行 join 操作，这样就不用进行 reduce 步骤，从而提高了速度。只有 join 操作才能启用 MapJoin。\n## 是否根据输入小表的大小，自动将reduce端的common join 转化为map join，将小表刷入内存中。 ## 对应逻辑优化器是MapJoinProcessor set hive.auto.convert.join = true; ## 刷入内存表的大小(字节) 25M = 2G set hive.mapjoin.smalltable.filesize = 25000000; ## hive会基于表的size自动的将普通join转换成mapjoin set hive.auto.convert.join.noconditionaltask=true; ## 多大的表可以自动触发放到内层LocalTask中，默认大小10M set hive.auto.convert.join.noconditionaltask.size=10000000; Hive 可以进行多表 Join。Join 操作尤其是 Join 大表的时候代价是非常大的。MapJoin 特别适合大小表 join的情况。在Hive join场景中，一般总有一张相对小的表和一张相对大的表，小表叫 build table，大表叫 probe table。Hive 在解析带 join 的 SQL 语句时，会默认将最后一个表作为 probe table，将前面的表作为 build table 并试图将它们读进内存。如果表顺序写反，probe table 在前面，引发 OOM 的风险就高了。在维度建模数据仓库中，事实表就是 probe table，维度表就是 build table。这种 Join 方式在 map 端直接完成 join 过程，消灭了 reduce，效率很高。而且 MapJoin 还支持非等值连接。\n当 Hive 执行 Join 时，需要选择哪个表被流式传输（stream），哪个表被缓存（cache）。Hive 将 JOIN 语句中的最后一个表用于流式传输，因此我们需要确保这个流表在两者之间是最大的。如果要在不同的 key 上 join 更多的表，那么对于每个 join 集，只需在 ON 条件右侧指定较大的表。\n也可以手动开启mapjoin：\n--SQL方式，在SQL语句中添加MapJoin标记（mapjoin hint） --将小表放到内存中，省去shffle操作 // 在没有开启mapjoin的情况下，执行的是reduceJoin SELECT /*+ MAPJOIN(smallTable) */ smallTable.key, bigTable.value FROM smallTable JOIN bigTable ON smallTable.key = bigTable.key; /*+mapjoin(a,b,c)*/ /*+mapjoin(a)*/ Sort-Merge-Bucket(SMB) Map Join\n它是另一种Hive Join优化技术，使用这个技术的前提是所有的表都必须是分桶表（bucket）和分桶排序的（sort）。分桶表的优化！\ndistribute by .... sort by .... 具体实现：\n1、针对参与join的这两张做相同的hash散列，每个桶里面的数据还要排序\r2、这两张表的分桶个数要成倍数。\r3、开启 SMB join 的开关！ 一些常见参数设置：\n## 当用户执行bucket map join的时候，发现不能执行时，禁止查询 set hive.enforce.sortmergebucketmapjoin=false; ## 如果join的表通过sort merge join的条件，join是否会自动转换为sort merge join set hive.auto.convert.sortmerge.join=true; ## 当两个分桶表 join 时，如果 join on的是分桶字段，小表的分桶数是大表的倍数时，可以启用 mapjoin 来提高效率。 # bucket map join优化，默认值是 false set hive.optimize.bucketmapjoin=false; ## bucket map join 优化，默认值是 false set hive.optimize.bucketmapjoin.sortedmerge=false; Join数据倾斜优化 在编写 Join 查询语句时，如果确定是由于 join 出现的数据倾斜，那么请做如下设置：\n# join的键对应的记录条数超过这个值则会进行分拆，值根据具体数据量设置 set hive.skewjoin.key=100000; # 如果是join过程出现倾斜应该设置为true set hive.optimize.skewjoin=false; 如果开启了，在 Join 过程中 Hive 会将计数超过阈值 hive.skewjoin.key（默认100000）的倾斜 key 对应的行临时写进文件中，然后再启动另一个 job 做 map join 生成结果。\n通过 hive.skewjoin.mapjoin.map.tasks 参数还可以控制第二个 job 的 mapper 数量，默认10000。\nset hive.skewjoin.mapjoin.map.tasks=10000; CBO优化 join的时候表的顺序的关系：前面的表都会被加载到内存中。后面的表进行磁盘扫描\nselect a.*, b.*, c.* from a join b on a.id = b.id join c on a.id = c.id; Hive 自 0.14.0 开始，加入了一项 \u0026ldquo;Cost based Optimizer\u0026rdquo; 来对 HQL 执行计划进行优化，这个功能通过 \u0026ldquo;hive.cbo.enable\u0026rdquo; 来开启。在 Hive 1.1.0 之后，这个 feature 是默认开启的，它可以 自动优化 HQL 中多个 Join 的顺序，并选择合适的 Join 算法。\nCBO，成本优化器，代价最小的执行计划就是最好的执行计划。传统的数据库，成本优化器做出最优化的执行计划是依据统计信息来计算的。Hive 的成本优化器也一样。\nHive 在提供最终执行前，优化每个查询的执行逻辑和物理执行计划。这些优化工作是交给底层来完成的。根据查询成本执行进一步的优化，从而产生潜在的不同决策：如何排序连接，执行哪种类型的连接，并行度等等。\n要使用基于成本的优化（也称为 CBO），请在查询开始设置以下参数：\nset hive.cbo.enable=true; set hive.compute.query.using.stats=true; set hive.stats.fetch.column.stats=true; set hive.stats.fetch.partition.stats=true; 怎样做笛卡尔积 需求：笛卡尔积操作，默认情况下是不允许使用的！底层使用一个ReduceTask来做。那么如果数据量很大呢？\n如果现在有这么一条SQL: select a.*, b.* from a, b; select a.*, b.* from a, b where a.id = b.id;\rselect a.*, b.* from a join b on a.id = b.id; 当 Hive 设定为严格模式（hive.mapred.mode=strict，nonstrict）时，不允许在 HQL 语句中出现笛卡尔积，这实际说明了 Hive 对笛卡尔积支持较弱。因为找不到 Join key，Hive 只能使用 1 个 reducer 来完成笛卡尔积。\n当然也可以使用 limit 的办法来减少某个表参与 join 的数据量，但对于需要笛卡尔积语义的需求来说，经常是一个大表和一个小表的 Join 操作，结果仍然很大（以至于无法用单机处理），这时 MapJoin 才是最好的解决办法。MapJoin，顾名思义，会在 Map 端完成 Join 操作。这需要将 Join 操作的一个或多个表完全读入内存。\nPS：MapJoin 在子查询中可能出现未知 BUG。在大表和小表做笛卡尔积时，规避笛卡尔积的方法是，给 Join 添加一个 Join key，原理很简单：将小表扩充一列 join key，并将小表的条目复制数倍，join key 各不相同；将大表扩充一列 join key 为随机数。\n精髓就在于复制几倍，最后就有几个 reduce 来做，而且大表的数据是前面小表扩张 key 值范围里面随机出来的，所以复制了几倍 n，就相当于这个随机范围就有多大 n，那么相应的，大表的数据就被随机的分为了 n 份。并且最后处理所用的 reduce 数量也是 n，而且也不会出现数据倾斜。\n两张表：\ra: a,b,c,d\t小表\rb: 1,2,3,4,5,6,7,8,9\t大表 Group By优化 默认情况下，Map 阶段同一个 Key 的数据会分发到一个 Reduce 上，当一个 Key 的数据过大时会产生 数据倾斜。进行group by操作时可以从以下两个方面进行优化：\n1. Map端部分聚合\n事实上并不是所有的聚合操作都需要在 Reduce 部分进行，很多聚合操作都可以先在 Map 端进行部分聚合，然后在 Reduce 端的得出最终结果。\n## 开启Map端聚合参数设置 set hive.map.aggr=true; # 设置map端预聚合的行数阈值，超过该值就会分拆job，默认值100000 set hive.groupby.mapaggr.checkinterval=100000 2. 有数据倾斜时进行负载均衡\n当 HQL 语句使用 group by 时数据出现倾斜时，如果该变量设置为 true，那么 Hive 会自动进行负载均衡。策略就是把 MapReduce 任务拆分成两个：第一个先做预汇总，第二个再做最终汇总。\n# 自动优化，有数据倾斜的时候进行负载均衡（默认是false） set hive.groupby.skewindata=false; 当选项设定为 true 时，生成的查询计划有两个 MapReduce 任务。\n1、在第一个 MapReduce 任务中，map 的输出结果会随机分布到 reduce 中，每个 reduce 做部分聚合操作，并输出结果，这样处理的结果是相同的`group by key`有可能分发到不同的 reduce 中，从而达到负载均衡的目的；\r2、第二个 MapReduce 任务再根据预处理的数据结果按照 group by key 分布到各个 reduce 中，最后完成最终的聚合操作。 Map 端部分聚合：并不是所有的聚合操作都需要在 Reduce 端完成，很多聚合操作都可以先在 Map 端进行部分聚合，最后在 Reduce 端得出最终结果，对应的优化器为 GroupByOptimizer。\n那么如何用 group by 方式同时统计多个列？\nselect t.a, sum(t.b), count(t.c), count(t.d) from some_table t group by t.a; 下面是解决方法：\nselect t.a, sum(t.b), count(t.c), count(t.d) from ( select a,b,null c,null d from some_table union all select a,0 b,c,null d from some_table group by a,c union all select a,0 b,null c,d from some_table group by a,d ) t; Order By优化 1、cluster by = distribute by + sort by\t可以使用多个reduceTask来运行\r2、order by 只能使用一个reduceTask来运行 order by只能是在一个 reduce 进程中进行，所以如果对一个大数据集进行order by，会导致一个 reduce 进程中处理的数据相当大，造成查询执行缓慢。\n1、在最终结果上进行order by，不要在中间的大数据集上进行排序。如果最终结果较少，可以在一个reduce上进行排序时，那么就在最后的结果集上进行order by。\r2、如果是取排序后的前N条数据，可以使用distribute by和sort by在各个reduce上进行排序后前N条，然后再对各个reduce的结果集合合并后在一个reduce中全局排序，再取前N条，因为参与全局排序的order by的数据量最多是reduce个数 * N，所以执行效率会有很大提升。 在Hive中，关于数据排序，提供了四种语法，一定要区分这四种排序的使用方式和适用场景。\n1、order by：全局排序，缺陷是只能使用一个reduce\r2、sort by：单机排序，单个reduce结果有序\r3、cluster by：对同一字段分桶并排序，不能和sort by连用\r4、distribute by + sort by：分桶，保证同一字段值只存在一个结果文件当中，结合sort by保证每个reduceTask结果有序 Hive HQL 中的 order by 与其他 SQL 方言中的功能一样，就是将结果按某字段全局排序，这会导致所有 map 端数据都进入一个 reducer 中，在数据量大时可能会长时间计算不完。\n如果使用 sort by，那么还是会视情况启动多个 reducer 进行排序，并且保证每个 reducer 内局部有序。为了控制map 端数据分配到 reducer 的 key，往往还要配合 distribute by 一同使用。如果不加 distribute by 的话，map 端数据就会随机分配到 reducer。\n提供一种方式实现全局排序：两种方式：\n1、建表导入数据准备\ncreate table if not exists student(id int, name string, sex string, age int, department string) row format delimited fields terminated by \u0026#34;,\u0026#34;; load data local inpath \u0026#34;/home/bigdata/students.txt\u0026#34; into table student; 2、第一种方式\n-- 直接使用order by来做。如果结果数据量很大，这个任务的执行效率会非常低 select id,name,age from student order by age desc limit 3; 3、第二种方式\n-- 使用distribute　by + sort by 多个reduceTask，每个reduceTask分别有序 set mapreduce.job.reduces=3; drop table student_orderby_result; -- 范围分桶 0 \u0026lt; 18 \u0026lt; 1 \u0026lt; 20 \u0026lt; 2 create table student_orderby_result as select * from student distribute by (case when age \u0026gt; 20 then 0 when age \u0026lt; 18 then 2 else 1 end) sort by (age desc); 关于分界值的确定，使用采样的方式，来估计数据分布规律。\nCount Distinct优化 当要统计某一列去重数时，如果数据量很大，count(distinct) 就会非常慢，原因与 group by 类似，count(distinct) 逻辑只会有很少的 reducer 来处理。这时可以用 group by 来改写：\n-- 先 group by 在 count select count(1) from ( select age from student where department \u0026gt;= \u0026#34;MA\u0026#34; group by age ) t; -- 直接使用 count(distinct xx) select count(distinct id) from student; 再来一个例子：\n优化前 ，一个普通的只使用一个reduceTask来进行count(distinct) 操作\n-- 优化前（只有一个reduce，先去重再count负担比较大）： select count(distinct id) from tablename; 优化后 ，但是这样写会启动两个MR job（单纯 distinct 只会启动一个），所以要确保数据量大到启动 job 的 overhead 远小于计算耗时，才考虑这种方法。当数据集很小或者 key 的倾斜比较明显时，group by 还可能会比 distinct 慢。\n-- 优化后（启动两个job，一个job负责子查询(可以有多个reduce)，另一个job负责count(1))： select count(1) from (select distinct id from tablename) tmp; select count(1) from (select id from tablename group by id) tmp; // 推荐使用这种 select t.a, count(t.b) , sum(t.c) from t group by t.a;\nselect t.a, count(distinct t.b， t.c) from t group by t.a;\n怎样写in/exists语句 在Hive的早期版本中，in/exists语法是不被支持的，但是从 hive-0.8x 以后就开始支持这个语法。但是不推荐使用这个语法。虽然经过测验，Hive-2.3.6 也支持 in/exists 操作，但还是推荐使用 Hive 的一个高效替代方案：left semi join\n比如说：\n-- in / exists 实现 select a.id, a.name from a where a.id in (select b.id from b); select a.id, a.name from a where exists (select id from b where a.id = b.id); 可以使用join来改写：\nselect a.id, a.name from a join b on a.id = b.id; 应该转换成：\n-- left semi join 实现 select a.id, a.name from a left semi join b on a.id = b.id; 使用 vectorization 技术 vectorization : 矢量计算的技术\n在计算类似 scan, filter, aggregation 的时候， vectorization 技术以设置批处理的增量大小为 1024 行单次来达到比单条记录单次获得更高的效率。\nset hive.vectorized.execution.enabled=true ;\rset hive.vectorized.execution.reduce.enabled=true; 多重模式 如果你碰到一堆SQL，并且这一堆SQL的模式还一样。都是从同一个表进行扫描，做不同的逻辑。 有可优化的地方：如果有n条SQL，每个SQL执行都会扫描一次这张表。 insert .... select id,name,sex, age from student where age \u0026gt; 17; insert .... select id,name,sex, age from student where age \u0026gt; 18; insert .... select id,name,sex, age from student where age \u0026gt; 19; -- 隐藏了一个问题：这种类型的SQL有多少个，那么最终。这张表就被全表扫描了多少次 insert int t_ptn partition(city=A). select id,name,sex, age from student where city= A; insert int t_ptn partition(city=B). select id,name,sex, age from student where city= B; insert int t_ptn partition(city=c). select id,name,sex, age from student where city= c; from student insert int t_ptn partition(city=A) select id,name,sex, age where city= A insert int t_ptn partition(city=B) select id,name,sex, age where city= B insert int t_ptn partition(city=C) select id,name,sex, age where city= C 如果一个 HQL 底层要执行 10 个 Job，那么能优化成 8 个一般来说，肯定能有所提高，多重插入就是一个非常实用的技能。一次读取，多次插入，有些场景是从一张表读取数据后，要多次利用，这时可以使用multi insert语法：\nfrom sale_detail insert overwrite table sale_detail_multi partition (sale_date=\u0026#39;2019\u0026#39;, region=\u0026#39;china\u0026#39; ) select shop_name, customer_id, total_price where ..... insert overwrite table sale_detail_multi partition (sale_date=\u0026#39;2020\u0026#39;, region=\u0026#39;china\u0026#39; ) select shop_name, customer_id, total_price where .....; 说明：multi insert语法有一些限制。\n1、一般情况下，单个SQL中最多可以写128路输出，超过128路，则报语法错误。\r2、在一个multi insert中：\r对于分区表，同一个目标分区不允许出现多次。\r对于未分区表，该表不能出现多次。\r3、对于同一张分区表的不同分区，不能同时有insert overwrite和insert into操作，否则报错返回。 Multi-Group by 是 Hive 的一个非常好的特性，它使得 Hive 中利用中间结果变得非常方便。例如：\nFROM (SELECT a.status, b.school, b.gender FROM status_updates a JOIN profiles b ON (a.userid = b.userid and a.ds=\u0026#39;2019-03-20\u0026#39; )) subq1 INSERT OVERWRITE TABLE gender_summary PARTITION(ds=\u0026#39;2019-03-20\u0026#39;) SELECT subq1.gender, COUNT(1) GROUP BY subq1.gender INSERT OVERWRITE TABLE school_summary PARTITION(ds=\u0026#39;2019-03-20\u0026#39;) SELECT subq1.school, COUNT(1) GROUP BY subq1.school; 上述查询语句使用了 Multi-Group by 特性连续 group by 了 2 次数据，使用不同的 Multi-Group by。这一特性可以减少一次 MapReduce 操作。\n启动中间结果压缩 map 输出压缩\nset mapreduce.map.output.compress=true; set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec; 中间数据压缩\n中间数据压缩就是对 hive 查询的多个 Job 之间的数据进行压缩。最好是选择一个节省CPU耗时的压缩方式。可以采用snappy压缩算法，该算法的压缩和解压效率都非常高。None Record Block\nset hive.exec.compress.intermediate=true; set hive.intermediate.compression.codec=org.apache.hadoop.io.compress.SnappyCodec; set hive.intermediate.compression.type=BLOCK; 结果数据压缩\n最终的结果数据（Reducer输出数据）也是可以进行压缩的，可以选择一个压缩效果比较好的，可以减少数据的大小和数据的磁盘读写时间； 注：常用的 gzip，snappy 压缩算法是不支持并行处理的，如果数据源是 gzip/snappy压缩文件大文件，这样只会有有个 mapper 来处理这个文件，会严重影响查询效率。 所以如果结果数据需要作为其他查询任务的数据源，可以选择支持 splitable 的LZO算法，这样既能对结果文件进行压缩，还可以并行的处理，这样就可以大大的提高 job 执行的速度了。\nset hive.exec.compress.output=true; set mapreduce.output.fileoutputformat.compress=true; set mapreduce.output.fileoutputformat.compress.codec=org.apache.hadoop.io.compress.GzipCodec; set mapreduce.output.fileoutputformat.compress.type=BLOCK; Hadoop集群支持的压缩算法：\norg.apache.hadoop.io.compress.DefaultCodec org.apache.hadoop.io.compress.GzipCodec org.apache.hadoop.io.compress.BZip2Codec org.apache.hadoop.io.compress.DeflateCodec org.apache.hadoop.io.compress.SnappyCodec org.apache.hadoop.io.compress.Lz4Codec com.hadoop.compression.lzo.LzoCodec com.hadoop.compression.lzo.LzopCodec Hive架构层面 启用本地抓取 Hive 的某些 SQL 语句需要转换成 MapReduce 的操作，某些 SQL 语句就不需要转换成 MapReduce 操作，但是同学们需要注意，理论上来说，所有的 SQL 语句都需要转换成 MapReduce 操作，只不过 Hive 在转换 SQL 语句的过程中会做部分优化，使某些简单的操作不再需要转换成 MapReduce，例如：\n1、只是 select * 的时候\r2、where 条件针对分区字段进行筛选过滤时\r3、带有 limit 分支语句时 Hive 从 HDFS 中读取数据，有两种方式：启用MapReduce读取（跑MR） 和 直接抓取（跑 FetchTask）。\n直接抓取数据比 MapReduce 方式读取数据要快的多，但是只有少数操作可以使用直接抓取方式。\n可以通过hive.fetch.task.conversion参数来配置在什么情况下采用直接抓取方式：\nminimal：只有 select * 、在分区字段上 where 过滤、有 limit 这三种场景下才启用直接抓取方式。\rmore：在 select、where 筛选、limit 时，都启用直接抓取方式。 查看 Hive 的抓取策略：\n## 查看 set hive.fetch.task.conversion; 设置Hive的抓取策略：\n## 默认more set hive.fetch.task.conversion=more; 请看 hive-default.xml 中关于这个参数的解释：\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;hive.fetch.task.conversion\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;more\u0026lt;/value\u0026gt; \u0026lt;description\u0026gt; Expects one of [none, minimal, more]. Some select queries can be converted to single FETCH task minimizing latency. Currently the query should be single sourced not having any subquery and should not have any aggregations or distincts (which incurs RS), lateral views and joins. 0. none : disable hive.fetch.task.conversion 1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only 2. more : SELECT, FILTER, LIMIT only (support TABLESAMPLE and virtual columns) \u0026lt;/description\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;hive.fetch.task.conversion.threshold\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;1073741824\u0026lt;/value\u0026gt; \u0026lt;description\u0026gt; Input threshold for applying hive.fetch.task.conversion. If target table is native, input length is calculated by summation of file lengths. If it\u0026#39;s not native, storage handler for the table can optionally implement org.apache.hadoop.hive.ql.metadata.InputEstimator interface. \u0026lt;/description\u0026gt; \u0026lt;/property\u0026gt; 本地执行优化 Hive 在集群上查询时，默认是在集群上多台机器上运行，需要多个机器进行协调运行，这种方式很好的解决了大数据量的查询问题。但是在 Hive 查询处理的数据量比较小的时候，其实没有必要启动分布式模式去执行，因为以分布式方式执行设计到跨网络传输、多节点协调等，并且消耗资源。对于小数据集，可以通过本地模式，在单台机器上处理所有任务，执行时间明显被缩短。\n启动本地模式涉及到三个参数：\n## 打开hive自动判断是否启动本地模式的开关 set hive.exec.mode.local.auto=true; ## map任务数最大值，不启用本地模式的task最大个数 set hive.exec.mode.local.auto.input.files.max=4; ## map输入文件最大大小，不启动本地模式的最大输入文件大小 set hive.exec.mode.local.auto.inputbytes.max=134217728; JVM重用 Hive 语句最终会转换为一系列的 MapReduce 任务，每一个MapReduce 任务是由一系列的 MapTask 和 ReduceTask 组成的，默认情况下，MapReduce 中一个 MapTask 或者 ReduceTask 就会启动一个 JVM 进程，一个 Task 执行完毕后，JVM 进程就会退出。这样如果任务花费时间很短，又要多次启动 JVM 的情况下，JVM 的启动时间会变成一个比较大的消耗，这时，可以通过重用 JVM 来解决。\nset mapred.job.reuse.jvm.num.tasks=5; JVM也是有缺点的，开启JVM重用会一直占用使用到的 task 的插槽，以便进行重用，直到任务完成后才会释放。如果某个不平衡的job中有几个 reduce task 执行的时间要比其他的 reduce task 消耗的时间要多得多的话，那么保留的插槽就会一直空闲却无法被其他的 job 使用，直到所有的 task 都结束了才会释放。\n根据经验，一般来说可以使用一个 cpu core 启动一个 JVM，假如服务器有 16 个 cpu core ，但是这个节点，可能会启动 32 个mapTask，完全可以考虑：启动一个JVM，执行两个Task\n并行执行 有的查询语句，Hive 会将其转化为一个或多个阶段，包括：MapReduce 阶段、抽样阶段、合并阶段、limit 阶段等。默认情况下，一次只执行一个阶段。但是，如果某些阶段不是互相依赖，是可以并行执行的。多阶段并行是比较耗系统资源的。\n一个 Hive SQL 语句可能会转为多个 MapReduce Job，每一个 job 就是一个 stage，这些 Job 顺序执行，这个在 cli 的运行日志中也可以看到。但是有时候这些任务之间并不是是相互依赖的，如果集群资源允许的话，可以让多个并不相互依赖 stage 并发执行，这样就节约了时间，提高了执行速度，但是如果集群资源匮乏时，启用并行化反倒是会导致各个 Job 相互抢占资源而导致整体执行性能的下降。启用并行化：\n## 可以开启并发执行。 set hive.exec.parallel=true; ##　同一个sql允许最大并行度，默认为8。 set hive.exec.parallel.thread.number=16; 执行模式： 走路\r1、并行\t我和你并排走\t分布式计算：本质就是大规模分阶段的并行计算引擎\r2、并发\t我走的时候你不走，你走的时候我不走\t3、串行\t我走完了，你再走 MR的方式：一个JVM 一个 Task\rSpark的方式：一个JVM中运行多个Task\r广播变量：\rMR：node1节点有20个Task，所以广播的数据存在20份\rSpark: node1节点有20个Task，但是只启动了2个JVM，广播的数据，只有2份 推测执行 假设一个Job有100个Task：其中95个任务的进度，要比另外5个任务的进度快一倍 在分布式集群环境下，因为程序Bug（包括Hadoop本身的bug），负载不均衡或者资源分布不均等原因，会造成同一个作业的多个任务之间运行速度不一致，有些任务的运行速度可能明显慢于其他任务（比如一个作业的某个任务进度只有50%，而其他所有任务已经运行完毕），则这些任务会拖慢作业的整体执行进度。为了避免这种情况发生，Hadoop采用了推测执行（Speculative Execution）机制，它根据一定的法则推测出“拖后腿”的任务，并为这样的任务启动一个备份任务，让该任务与原始任务同时处理同一份数据，并最终选用最先成功运行完成任务的计算结果作为最终结果。\n# 启动mapper阶段的推测执行机制 set mapreduce.map.speculative=true; # 启动reducer阶段的推测执行机制 set mapreduce.reduce.speculative=true; 建议：\n如果用户对于运行时的偏差非常敏感的话，那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间的MapTask或者ReduceTask的话，那么启动推测执行造成的浪费是非常巨大大。 设置开启推测执行参数：Hadoop 的 mapred-site.xml 文件中进行配置\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;mapreduce.map.speculative\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;true\u0026lt;/value\u0026gt; \u0026lt;description\u0026gt;If true, then multiple instances of some map tasks may be executed in parallel.\u0026lt;/description\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;mapreduce.reduce.speculative\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;true\u0026lt;/value\u0026gt; \u0026lt;description\u0026gt;If true, then multiple instances of some reduce tasks may be executed in parallel.\u0026lt;/description\u0026gt; \u0026lt;/property\u0026gt; Hive 本身也提供了配置项来控制 reduce-side 的推测执行：hive-site.xml\n\u0026lt;property\u0026gt; \u0026lt;name\u0026gt;hive.mapred.reduce.tasks.speculative.execution\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;true\u0026lt;/value\u0026gt; \u0026lt;description\u0026gt;Whether speculative execution for reducers should be turned on.\u0026lt;/description\u0026gt; \u0026lt;/property\u0026gt; 关于调优这些推测执行变量，还很难给一个具体的建议。如果用户对于运行时的偏差非常敏感的话，那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间的 mapTask 或者 reduceTask 的话，那么启动推测执行造成的浪费是非常巨大。\nHive严格模式 所谓严格模式，就是强制不允许用户执行有风险的 HiveQL 语句，一旦执行会直接失败。但是Hive中为了提高SQL语句的执行效率，可以设置严格模式，充分利用Hive的某些特点。\n## 设置Hive的严格模式 set hive.mapred.mode=strict; set hive.exec.dynamic.partition.mode=nostrict; 注意：当设置严格模式之后，会有如下限制：\n1、对于分区表，必须添加where对于分区字段的条件过滤 select * from student_ptn where age \u0026gt; 25 2、order by语句必须包含limit输出限制 select * from student order by age limit 100; 3、限制执行笛卡尔积的查询 select a.*, b.* from a, b; 4、在hive的动态分区模式下，如果为严格模式，则必须需要一个分区列式静态分区 假设现在动态分区插入的字段有两个： a\t1000个 b\t1000个 动态分区的个数： 1000 * 1000 = 1000000 ","permalink":"https://leochu.work/blog/tech/bigdata/hive%E8%B0%83%E4%BC%98%E5%A4%A7%E5%85%A8/","summary":"\u003ch1 id=\"调优具体细节\"\u003e调优具体细节\u003c/h1\u003e\n\u003ch2 id=\"hive建表设计层面\"\u003eHive建表设计层面\u003c/h2\u003e\n\u003cp\u003eHive 的建表设计层面调优，主要讲的怎么样合理的组织数据，方便后续的高效计算。比如建表的类型，文件存储格式，是否压缩等等。\u003c/p\u003e\n\u003ch3 id=\"利用分区表优化\"\u003e利用分区表优化\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e关于 Hive 的表的类型有哪些？\r\n1、分区表  \r\n2、分桶表\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e分区表 是在某一个或者几个维度上对数据进行分类存储，一个分区对应一个目录。如果筛选条件里有分区字段，那么 Hive 只需要遍历对应分区目录下的文件即可，不需要遍历全局数据，使得处理的数据量大大减少，从而提高查询效率。\u003c/p\u003e\n\u003cp\u003e也就是说：\u003cstrong\u003e当一个 Hive 表的查询大多数情况下，会根据某一个字段进行筛选时，那么非常适合创建为分区表，该字段即为分区字段。\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eselect1:  select ....  where country = \u0026#34;china\u0026#34;\r\nselect2:  select ....  where country = \u0026#34;china\u0026#34;\r\nselect3:  select ....  where country = \u0026#34;china\u0026#34;\r\nselect4:  select ....  where country = \u0026#34;china\u0026#34;\r\n.....\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e分门别类：这个city字段的每个值，就单独形成为一个分区。其实每个分区就对应带HDFS的一个目录\u003c/p\u003e\n\u003cp\u003e在创建表时通过启用 \u003ccode\u003epartitioned by\u003c/code\u003e 实现，用来 partition 的维度并不是实际数据的某一列，具体分区的标志是由插入内容时给定的。当要查询某一分区的内容时可以采用 where 语句，形似 \u003ccode\u003ewhere tablename.partition_column = a\u003c/code\u003e 来实现。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1、创建含分区的表：\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eCREATE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e page_view(viewTime INT, userid BIGINT,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                       page_url STRING, referrer_url STRING,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                       ip STRING \u003cspan style=\"color:#66d9ef\"\u003eCOMMENT\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;IP Address of the User\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ePARTITIONED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e(date STRING, country STRING)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eROW\u003c/span\u003e FORMAT DELIMITED FIELDS TERMINATED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;1\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eSTORED \u003cspan style=\"color:#66d9ef\"\u003eAS\u003c/span\u003e TEXTFILE;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e2、载入内容，并指定分区标志：\u003c/strong\u003e\u003c/p\u003e","title":"Hive调优大全"},{"content":"1.动态分区 SET hive.exec.dynamic.partition=true; SET hive.exec.dynamic.partition.mode=nonstrict; SET hive.exec.max.dynamic.partitions=100000; SET hive.exec.max.dynamic.partitions.pernode=100000; SET hive.exec.max.created.files=100000; 2.union all并发执行 --在使用union all的时候，系统资源足够的情况下，为了加快hive处理速度，可以设置如下参数实现并发执行 set mapred.job.priority=VERY_HIGH; set hive.exec.parallel=true; 3.设置map reduce个数 -- 设置map capacity set mapred.job.map.capacity=2000; set mapred.job.reduce.capacity=2000; -- 设置每个reduce的大小 set hive.exec.reducers.bytes.per.reducer=500000000; -- 直接设置个数 set mapred.reduce.tasks = 15; 4.文件合并 -- 设置文件合并 set abaci.is.dag.job=false; set hive.merge.mapredfiles=true; set mapred.combine.input.format.local.only=false; set hive.merge.smallfiles.avgsize=100000000; -- 在map only的情况下，如上的参数如果没有生效，可以设置如下 -- 在HQL的最外层增加distribute by rand() select * from XXX distribute by rand() 5.设置任务名称 -- 设置名称 set mapred.job.name=${my_job}; 6.设置引擎和指定队列 set hive.execution.engine=mr; set mapreduce.job.queuename=bigdata; ","permalink":"https://leochu.work/blog/tech/bigdata/hive%E5%B8%B8%E7%94%A8%E5%8F%82%E6%95%B0%E8%AF%AD%E5%8F%A5/","summary":"\u003ch4 id=\"1动态分区\"\u003e1.动态分区\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSET\u003c/span\u003e hive.\u003cspan style=\"color:#66d9ef\"\u003eexec\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edynamic\u003c/span\u003e.partition\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSET\u003c/span\u003e hive.\u003cspan style=\"color:#66d9ef\"\u003eexec\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edynamic\u003c/span\u003e.partition.\u003cspan style=\"color:#66d9ef\"\u003emode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003enonstrict;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSET\u003c/span\u003e hive.\u003cspan style=\"color:#66d9ef\"\u003eexec\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003emax\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edynamic\u003c/span\u003e.partitions\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e100000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSET\u003c/span\u003e hive.\u003cspan style=\"color:#66d9ef\"\u003eexec\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003emax\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003edynamic\u003c/span\u003e.partitions.pernode\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e100000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eSET\u003c/span\u003e hive.\u003cspan style=\"color:#66d9ef\"\u003eexec\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003emax\u003c/span\u003e.created.files\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e100000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"2union-all并发执行\"\u003e2.union all并发执行\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e--在使用union all的时候，系统资源足够的情况下，为了加快hive处理速度，可以设置如下参数实现并发执行\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e mapred.job.priority\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eVERY_HIGH;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e hive.\u003cspan style=\"color:#66d9ef\"\u003eexec\u003c/span\u003e.parallel\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"3设置map-reduce个数\"\u003e3.设置map reduce个数\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- 设置map capacity\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e mapred.job.\u003cspan style=\"color:#66d9ef\"\u003emap\u003c/span\u003e.capacity\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e mapred.job.reduce.capacity\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- 设置每个reduce的大小\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e hive.\u003cspan style=\"color:#66d9ef\"\u003eexec\u003c/span\u003e.reducers.bytes.per.reducer\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e500000000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- 直接设置个数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e mapred.reduce.tasks \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e15\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"4文件合并\"\u003e4.文件合并\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- 设置文件合并\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e abaci.\u003cspan style=\"color:#66d9ef\"\u003eis\u003c/span\u003e.dag.job\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e hive.merge.mapredfiles\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003etrue\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e mapred.combine.\u003cspan style=\"color:#66d9ef\"\u003einput\u003c/span\u003e.format.\u003cspan style=\"color:#66d9ef\"\u003elocal\u003c/span\u003e.\u003cspan style=\"color:#66d9ef\"\u003eonly\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e hive.merge.smallfiles.avgsize\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e100000000\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- 在map only的情况下，如上的参数如果没有生效，可以设置如下\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- 在HQL的最外层增加distribute by rand()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eselect\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efrom\u003c/span\u003e XXX distribute \u003cspan style=\"color:#66d9ef\"\u003eby\u003c/span\u003e rand()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"5设置任务名称\"\u003e5.设置任务名称\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e-- 设置名称\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e mapred.job.name\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e${\u003c/span\u003emy_job\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e}\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"6设置引擎和指定队列\"\u003e6.设置引擎和指定队列\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e hive.execution.engine\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003emr;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e mapreduce.job.queuename\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003ebigdata;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Hive常用参数语句"},{"content":"1.MongoDB拉出到hive # 导入mongodb的包到hadoop add jar /var/lib/hadoop-hdfs/bin/hive_mongoDB/mongo-hadoop-core-2.0.2.jar; add jar /var/lib/hadoop-hdfs/bin/hive_mongoDB/mongo-hadoop-hive-2.0.2.jar; add jar /var/lib/hadoop-hdfs/bin/hive_mongoDB/mongo-java-driver-3.12.8.jar; set mongo.input.split.create_input_splits=false; DROP TABLE IF EXISTS ods.ods_ex_trade_mdb_wmb_rocket_waimao_company_info_mapping; drop table ods.ods_zxk_annualBusiness_mapping; create external table ods.ods_zxk_annualBusiness_mapping( `_id` string, companyName string, Tel string )STORED BY \u0026#39;com.mongodb.hadoop.hive.MongoStorageHandler\u0026#39; WITH SERDEPROPERTIES(\u0026#39;mongo.columns.mapping\u0026#39;=\u0026#39;{\u0026#34;_id\u0026#34;:\u0026#34;_id\u0026#34;,\u0026#34;companyName\u0026#34;:\u0026#34;companyName\u0026#34;,\u0026#34;Tel\u0026#34;:\u0026#34;Tel\u0026#34;}\u0026#39;) TBLPROPERTIES(\u0026#39;mongo.uri\u0026#39;=\u0026#39;mongodb://username:password@172.16.98.159:21000/annualReport.annualBusiness?authSource=admin\u0026#39;); ","permalink":"https://leochu.work/blog/tech/bigdata/hive-mongo%E5%AF%BC%E5%85%A5%E5%AF%BC%E5%87%BA/","summary":"\u003ch4 id=\"1mongodb拉出到hive\"\u003e1.MongoDB拉出到hive\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-sql\" data-lang=\"sql\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e#\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e导入\u003c/span\u003emongodb的包到hadoop\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eadd\u003c/span\u003e jar \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003evar\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003elib\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ehadoop\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ebin\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ehive_mongoDB\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emongo\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ehadoop\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ecore\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e.\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e.\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e.jar;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eadd\u003c/span\u003e jar \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003evar\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003elib\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ehadoop\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ebin\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ehive_mongoDB\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emongo\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ehadoop\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ehive\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e.\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e.\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e.jar;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eadd\u003c/span\u003e jar \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003evar\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003elib\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ehadoop\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ebin\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003ehive_mongoDB\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003emongo\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003ejava\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003edriver\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e3\u003c/span\u003e.\u003cspan style=\"color:#ae81ff\"\u003e12\u003c/span\u003e.\u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e.jar;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eset\u003c/span\u003e mongo.\u003cspan style=\"color:#66d9ef\"\u003einput\u003c/span\u003e.split.create_input_splits\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003efalse\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eDROP\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eTABLE\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eIF\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eEXISTS\u003c/span\u003e ods.ods_ex_trade_mdb_wmb_rocket_waimao_company_info_mapping;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edrop\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etable\u003c/span\u003e ods.ods_zxk_annualBusiness_mapping;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ecreate\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eexternal\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003etable\u003c/span\u003e ods.ods_zxk_annualBusiness_mapping(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003e_id\u003cspan style=\"color:#f92672\"\u003e`\u003c/span\u003e string,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecompanyName string,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTel string\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)STORED \u003cspan style=\"color:#66d9ef\"\u003eBY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;com.mongodb.hadoop.hive.MongoStorageHandler\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eWITH\u003c/span\u003e SERDEPROPERTIES(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;mongo.columns.mapping\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;{\u0026#34;_id\u0026#34;:\u0026#34;_id\u0026#34;,\u0026#34;companyName\u0026#34;:\u0026#34;companyName\u0026#34;,\u0026#34;Tel\u0026#34;:\u0026#34;Tel\u0026#34;}\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eTBLPROPERTIES(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;mongo.uri\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;mongodb://username:password@172.16.98.159:21000/annualReport.annualBusiness?authSource=admin\u0026#39;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"Hive-mongo导入导出"},{"content":"Hive 字段中文乱码，如执行 show create table xxx 时，表级别注释、字段级别注释发现有乱码现象（都是？？？？）， 一般都是由 hive 元数据库的配置不当造成的。 此时可按如下步骤进行配置调整：\n登录 hive 的元数据库 mysql 中：\n1、设置 hive 元数据库字符集\nshow create database hive;\n查看为 utf8，需变更为 latin1\n_alter database hive character set latin1; 2、更改如下表字段为字符集编码为 utf8\n①修改表字段注解和表注解\nalter table COLUMNS_V2 modify column COMMENT varchar(256) character set utf8\nalter table TABLE_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8\n② 修改分区字段注解：\nalter table PARTITION_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8 ;\nalter table PARTITION_KEYS modify column PKEY_COMMENT varchar(4000) character set utf8;\n③修改索引注解：\nalter table INDEX_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;\nhive连接元数据库的jdbc url 加上useUnicode=true\u0026amp;characterEncoding=UTF-8 jdbc:mysql://m1.node.dev/hive_dev?useUnicode=true\u0026amp;characterEncoding=UTF-8 ","permalink":"https://leochu.work/blog/tech/bigdata/hive%E5%AD%97%E6%AE%B5%E4%B8%AD%E6%96%87%E6%B3%A8%E9%87%8A%E4%B9%B1%E7%A0%81%E4%BF%AE%E5%A4%8D/","summary":"\u003cp\u003eHive 字段中文乱码，如执行 show create table xxx 时，表级别注释、字段级别注释发现有乱码现象（都是？？？？）， 一般都是由 hive 元数据库的配置不当造成的。 \u003c/p\u003e\n\u003cp\u003e此时可按如下步骤进行配置调整：\u003c/p\u003e\n\u003cp\u003e登录 hive 的元数据库 mysql 中：\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e1、设置 hive 元数据库字符集\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eshow create database hive;\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e查看为 utf8，需变更为 latin1\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e_alter database hive character set latin1; \u003c/p\u003e\n\u003cp\u003e\u003cem\u003e2、更改如下表字段为字符集编码为 utf8\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e①修改表字段注解和表注解\u003cbr\u003e\nalter table COLUMNS_V2 modify column COMMENT varchar(256) character set utf8\u003cbr\u003e\nalter table TABLE_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8\u003cbr\u003e\n② 修改分区字段注解：\u003cbr\u003e\nalter table PARTITION_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8 ;\u003cbr\u003e\nalter table PARTITION_KEYS modify column PKEY_COMMENT varchar(4000) character set utf8;\u003cbr\u003e\n③修改索引注解：\u003cbr\u003e\nalter table INDEX_PARAMS modify column PARAM_VALUE varchar(4000) character set utf8;\u003c/p\u003e","title":"Hive 字段中文注释乱码"},{"content":" 在数仓中，建议大家除了接口表（从其他数据库导入或者是最后要导出到其他数据库的表），其余表的存储格式与压缩格式保持一致。\n在数仓中，建议大家除了接口表（从其他数据库导入或者是最后要导出到其他数据库的表），其余表的存储格式与压缩格式保持一致。\n我们先来说一下目前 Hive 表主流的存储格式与压缩方式\n从 Hive 官网得知，Apache Hive 支持 Apache Hadoop 中使用的几种熟悉的文件格式，如 TextFile（文本格式），RCFile（行列式文件），SequenceFile（二进制序列化文件），AVRO，ORC（优化的行列式文件）和Parquet 格式，而这其中我们目前使用最多的是TextFile，SequenceFile，ORC和Parquet。\n下面来详细了解下这 2 种行列式存储。\n1、ORC 1.1 ORC 的存储结构 我们先从官网上拿到 ORC 的存储模型图\n看起来略微有点复杂，那我们稍微简化一下，我画了一个简单的图来说明一下\n但是由于索引的高成本，在**「目前的 Hive3.X 中，已经废除了索引」**，当然也早就引入了列式存储。\n列式存储的存储方式，是按照一列一列存储的，如上图中的右图，这样的话如果查询一个字段的数据，就等于是索引查询，效率高。但是如果需要查全表，它因为需要分别取所有的列最后汇总，反而更占用资源。于是 ORC 行列式存储出现了。\n在需要全表扫描时，可以按照行组读取 如果需要取列数据，在行组的基础上，读取指定的列，而不需要所有行组内所有行的数据和一行内所有字段的数据。 了解了 ORC 存储的基本逻辑后，我们再来看看它的存储模型图。\n同时我也把详细的文字也附在下面，大家可以对照着看看：\n条带 (stripe)：ORC 文件存储数据的地方，每个 stripe 一般为 HDFS 的块大小。（包含以下 3 部分） index data:保存了所在条带的一些统计信息,以及数据在 stripe中的位置索引信息。\rrows data:数据存储的地方,由多个行组构成，每10000行构成一个行组，数据以流( stream)的形式进行存储。\rstripe footer:保存数据所在的文件目录 文件脚注 (file footer)：包含了文件中 sipe 的列表, 每个 stripe 的行数, 以及每个列的数据类型。它还包含每个列的最小值、最大值、行计数、求和等聚合信息。 postscript：含有压缩参数和压缩大小相关的信息 所以其实发现，ORC 提供了 3 级索引，文件级、条带级、行组级，所以在查询的时候，利用这些索引可以规避大部分不满足查询条件的文件和数据块。\n但注意，ORC 中所有数据的描述信息都是和存储的数据放在一起的，并没有借助外部的数据库。\n「特别注意：ORC 格式的表还支持事务 ACID，但是支持事务的表必须为分桶表，所以适用于更新大批量的数据，不建议用事务频繁的更新小批量的数据」\n#开启并发支持,支持插入、删除和更新的事务\rset hive. support concurrency=truei\rset hive. enforce bucketing=truei\rset hive.exec,dynamicpartition.mode-nonstrict\r#设置事务所管理类型为 org. apache.hive.q1. lockage. DbTxnManager\r#原有的org. apache. hadoop.hive.q1.1 eckmar. DummyTxnManager不支持事务\rset hive. txn. manager=org. apache. hadoop. hive. q1. lockmgr DbTxnManageri\r#开启在相同的一个 meatore实例运行初始化和清理的线程\rset hive. compactor initiator on=true:\r#设置每个 metastore实例运行的线程数 hadoop\rset hive. compactor. worker threads=l\r#(2)创建表\rcreate table student_txn\r(id int,\rname string\r)\rclustered by (id) into 2 buckets\rstored as orc\rTBLPROPERTIES(\u0026#39;transactional\u0026#39;=\u0026#39;true‘);\r#(3)插入数据\r#插入id为1001,名字为student 1001\rinsert into table student_txn values(\u0026#39;1001\u0026#39;,\u0026#39;student 1001\u0026#39;);\r#(4)更新数据\rupdate student_txn set name= \u0026#39;student 1zh\u0026#39; where id=\u0026#39;1001\u0026#39;;\r# (5)查看表的数据,最终会发现id为1001被改为 sutdent_1zh 1.2 关于 ORC 的 Hive 配置 表配置属性（建表时配置，例如tblproperties ('orc.compress'='snappy');）\norc.compress: 表示 ORC 文件的压缩类型， 「可选的类型有 NONE、ZLB 和 SNAPPY，默认值是 ZLIB（Snappy 不支持切片）」—这个配置是最关键的。 orc. compress.Slze: 表示压缩块 (chunk) 的大小, 默认值是 262144(256KB)。 orc. stripe.size: 写 stripe, 可以使用的内存缓冲池大小, 默认值是 67108864(64MB） orc. row. index. stride: 行组级别索引的数据量大小, 默认是 10000, 必须要设置成大于等于 10000 的数 orc. create index: 是否创建行组级别索引, 默认是 true orc. bloom filter. columns: 需要创建布隆过滤的组。 orc. bloom filter fpp: 使用布隆过滤器的假正 (False Positive) 概率, 默认值是 0. 扩展: 在 Hive 中使用 bloom 过滤器, 可以用较少的文件空间快速判定数据是否存表中, 但是也存在将不属于这个表的数据判定为属于这个这表的情况, 这个称之为假正概率, 开发者可以调整该概率, 但概率越低, 布隆过滤器所需要\n2、Parquet 上面说过 ORC 后，我们对行列式存储也有了基本的了解，而 Parquet 是另一种高性能的行列式存储结构。\n2.1 Parquet 的存储结构 既然 ORC 都那么高效了，那为什么还要再来一个 Parquet，那是因为**「Parquet 是为了使 Hadoop 生态系统中的任何项目都可以使用压缩的，高效的列式数据表示形式」**\n❝\nParquet 是语言无关的，而且不与任何一种数据处理框架绑定在一起，适配多种语言和组件，能够与 Parquet 配合的组件有：\n查询引擎: Hive, Impala, Pig, Presto, Drill, Tajo, HAWQ, IBM Big SQL\n计算框架: MapReduce, Spark, Cascading, Crunch, Scalding, Kite\n数据模型: Avro, Thrift, Protocol Buffers, POJOs❞\n再来看看 Parquet 的存储结构吧，先看官网给的\n嗯，略微有点头大，我画个简易版\nParquet 文件是以二进制方式存储的，所以不可以直接读取，和 ORC 一样，文件的元数据和数据一起存储，所以 Parquet 格式文件是自解析的。\n行组 (Row Group)：每一个行组包含一定的行数，在一个 HDFS 文件中至少存储一个行组，类似于 orc 的 stripe 的概念。 列块 (Column Chunk)：在一个行组中每一列保存在一个列块中，行组中的所有列连续的存储在这个行组文件中。一个列块中的值都是相同类型的，不同的列块可能使用不同的算法进行压缩。 页 (Page)：每一个列块划分为多个页，一个页是最小的编码的单位，在同一个列块的不同页可能使用不同的编码方式。 2.2Parquet 的表配置属性 parquet. block size: 默认值为 134217728byte, 即 128MB, 表示 Row Group 在内存中的块大小。该值设置得大, 可以提升 Parquet 文件的读取效率, 但是相应在写的时候需要耗费更多的内存 parquet. page:size: 默认值为 1048576byt, 即 1MB, 表示每个页 (page) 的大小。这个特指压缩后的页大小, 在读取时会先将页的数据进行解压。页是 Parquet 操作数据的最小单位, 每次读取时必须读完一整页的数据才能访问数据。这个值如果设置得过小, 会导致压缩时出现性能问题 parquet. compression: 默认值为 UNCOMPRESSED，表示页的压缩方式。 「可以使用的压缩方式有 UNCOMPRESSED、 SNAPPY、GZP 和 LZO」。 Parquet enable. dictionary: 默认为 tue, 表示是否启用字典编码。 parquet. dictionary page.size: 默认值为 1048576byte, 即 1MB。在使用字典编码时, 会在 Parquet 的每行每列中创建一个字典页。使用字典编码, 如果存储的数据页中重复的数据较多, 能够起到一个很好的压缩效果, 也能减少每个页在内存的占用。 3、ORC 和 Parquet 对比 同时，从《Hive 性能调优实战》作者的案例中，2 张分别采用 ORC 和 Parquet 存储格式的表，导入同样的数据，进行 sql 查询，「发现使用 ORC 读取的行远小于 Parquet」，所以使用 ORC 作为存储，可以借助元数据过滤掉更多不需要的数据，查询时需要的集群资源比 Parquet 更少。（查看更详细的性能分析，请移步 https://blog.csdn.net/yu616568/article/details/51188479）\n「所以 ORC 在存储方面看起来还是更胜一筹」\n格式可分割平均压缩速度文本文件压缩效率Hadoop 压缩编解码器纯 Java 实现原生备注gzip否快高org.apache.hadoop.io.compress.GzipCodec是是lzo是（取决于所使用的库）非常快中等com.hadoop.compression.lzo.LzoCodec是是需要在每个节点上安装 LZObzip2是慢非常高org.apache.hadoop.io.compress.Bzip2Codec是是为可分割版本使用纯 Javazlib否慢中等org.apache.hadoop.io.compress.DefaultCodec是是Hadoop 的默认压缩编解码器Snappy否非常快低org.apache.hadoop.io.compress.SnappyCodec否是Snappy 有纯 Java 的移植版，但是在 Spark/Hadoop 中不能用\r根据 ORC 和 parquet 的要求，一般就有了\n1、ORC 格式存储，Snappy 压缩 create table stu_orc(id int,name string)\rstored as orc tblproperties (\u0026#39;orc.compress\u0026#39;=\u0026#39;snappy\u0026#39;); 2、Parquet 格式存储，Lzo 压缩 create table stu_par(id int,name string)\rstored as parquet tblproperties (\u0026#39;parquet.compression\u0026#39;=\u0026#39;lzo\u0026#39;); 3、Parquet 格式存储，Snappy 压缩 create table stu_par(id int,name string)\rstored as parquet tblproperties (\u0026#39;parquet.compression\u0026#39;=\u0026#39;snappy\u0026#39;); 因为 Hive 的 SQL 会转化为 MR 任务，如果该文件是用 ORC 存储，Snappy 压缩的，因为 Snappy 不支持文件分割操作，所以压缩文件**「只会被一个任务所读取」，如果该压缩文件很大，那么处理该文件的 Map 需要花费的时间会远多于读取普通文件的 Map 时间，这就是常说的「Map 读取文件的数据倾斜」**。\n那么为了避免这种情况的发生，就需要在数据压缩的时候采用 bzip2 和 Zip 等支持文件分割的压缩算法。但恰恰 ORC 不支持刚说到的这些压缩方式，所以这也就成为了大家在可能遇到大文件的情况下不选择 ORC 的原因，避免数据倾斜。\n在 Hve on Spark 的方式中，也是一样的，Spark 作为分布式架构，通常会尝试从多个不同机器上一起读入数据。要实现这种情况，每个工作节点都必须能够找到一条新记录的开端，也就需要该文件可以进行分割，但是有些不可以分割的压缩格式的文件，必须要单个节点来读入所有数据，这就很容易产生性能瓶颈。(下一篇文章详细写 Spark 读取文件的源码分析)\n「所以在实际生产中，使用 Parquet 存储，lzo 压缩的方式更为常见，这种情况下可以避免由于读取不可分割大文件引发的数据倾斜。但是，如果数据量并不大（预测不会有超大文件，若干 G 以上）的情况下，使用 ORC 存储，snappy 压缩的效率还是非常高。」\nDOIT.EDU 资料来自网络收集整理，如有侵权请告知\n","permalink":"https://leochu.work/blog/tech/bigdata/hive%E5%BB%BA%E8%A1%A8%E6%A0%BC%E5%BC%8F%E9%80%89%E5%9E%8B-orc-parquet-lzo-snappy/","summary":"\u003cblockquote\u003e\n\u003cp\u003e在数仓中，建议大家除了接口表（从其他数据库导入或者是最后要导出到其他数据库的表），其余表的存储格式与压缩格式保持一致。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e在数仓中，建议大家除了接口表（从其他数据库导入或者是最后要导出到其他数据库的表），其余表的存储格式与压缩格式保持一致。\u003c/p\u003e\n\u003cp\u003e我们先来说一下目前 Hive 表主流的存储格式与压缩方式\u003c/p\u003e\n\u003cp\u003e从 Hive 官网得知，Apache Hive 支持 Apache Hadoop 中使用的几种熟悉的文件格式，如 \u003ccode\u003eTextFile（文本格式）\u003c/code\u003e，\u003ccode\u003eRCFile（行列式文件）\u003c/code\u003e，\u003ccode\u003eSequenceFile（二进制序列化文件）\u003c/code\u003e，\u003ccode\u003eAVRO\u003c/code\u003e，\u003ccode\u003eORC（优化的行列式文件）\u003c/code\u003e和\u003ccode\u003eParquet\u003c/code\u003e 格式，而这其中我们目前使用最多的是\u003ccode\u003eTextFile\u003c/code\u003e，\u003ccode\u003eSequenceFile\u003c/code\u003e，\u003ccode\u003eORC\u003c/code\u003e和\u003ccode\u003eParquet\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e下面来详细了解下这 2 种行列式存储。\u003c/p\u003e\n\u003ch2 id=\"1orc\"\u003e1、ORC\u003c/h2\u003e\n\u003ch3 id=\"11-orc-的存储结构\"\u003e1.1 ORC 的存储结构\u003c/h3\u003e\n\u003cp\u003e我们先从官网上拿到 ORC 的存储模型图\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"http://www.doitedu.cn/wp-content/uploads/2021/12/a.png\"\u003e\u003c/p\u003e\n\u003cp\u003e看起来略微有点复杂，那我们稍微简化一下，我画了一个简单的图来说明一下\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"http://www.doitedu.cn/wp-content/uploads/2021/12/b-1024x456.png\"\u003e\u003c/p\u003e\n\u003cp\u003e但是由于索引的高成本，在**「目前的 Hive3.X 中，已经废除了索引」**，当然也早就引入了列式存储。\u003c/p\u003e\n\u003cp\u003e列式存储的存储方式，是按照一列一列存储的，如上图中的右图，这样的话如果查询一个字段的数据，就等于是索引查询，效率高。但是如果需要查全表，它因为需要分别取所有的列最后汇总，反而更占用资源。于是 ORC 行列式存储出现了。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e在需要全表扫描时，可以按照行组读取\u003c/li\u003e\n\u003cli\u003e如果需要取列数据，在行组的基础上，读取指定的列，而不需要所有行组内所有行的数据和一行内所有字段的数据。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e了解了 ORC 存储的基本逻辑后，我们再来看看它的存储模型图。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"http://www.doitedu.cn/wp-content/uploads/2021/12/c.png\"\u003e\u003c/p\u003e\n\u003cp\u003e同时我也把详细的文字也附在下面，大家可以对照着看看：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e条带 (stripe)：ORC 文件存储数据的地方，每个 stripe 一般为 HDFS 的块大小。（包含以下 3 部分）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eindex data:保存了所在条带的一些统计信息,以及数据在 stripe中的位置索引信息。\r\nrows data:数据存储的地方,由多个行组构成，每10000行构成一个行组，数据以流( stream)的形式进行存储。\r\nstripe footer:保存数据所在的文件目录\n\u003c/code\u003e\u003c/pre\u003e\u003cul\u003e\n\u003cli\u003e文件脚注 (file footer)：包含了文件中 sipe 的列表, 每个 stripe 的行数, 以及每个列的数据类型。它还包含每个列的最小值、最大值、行计数、求和等聚合信息。\u003c/li\u003e\n\u003cli\u003epostscript：含有压缩参数和压缩大小相关的信息\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e所以其实发现，ORC 提供了 3 级索引，文件级、条带级、行组级，所以在查询的时候，利用这些索引可以规避大部分不满足查询条件的文件和数据块。\u003c/p\u003e","title":"Hive 数仓建表该选用 ORC 还是 Parquet，压缩选 LZO 还是 Snappy"},{"content":" 端口 用途 9000 fs.defaultFS，如：hdfs://172.25.40.171:9000 9001 dfs.namenode.rpc-address，DataNode会连接这个端口 50070 dfs.namenode.http-address 50470 dfs.namenode.https-address 50100 dfs.namenode.backup.address 50105 dfs.namenode.backup.http-address 50090 dfs.namenode.secondary.http-address，如：172.25.39.166:50090 50091 dfs.namenode.secondary.https-address，如：172.25.39.166:50091 50020 dfs.datanode.ipc.address 50075 dfs.datanode.http.address 50475 dfs.datanode.https.address 50010 dfs.datanode.address，DataNode的数据传输端口 8480 dfs.journalnode.rpc-address 8481 dfs.journalnode.https-address 8032 yarn.resourcemanager.address 8088 yarn.resourcemanager.webapp.address，YARN的http端口 8090 yarn.resourcemanager.webapp.https.address 8030 yarn.resourcemanager.scheduler.address 8031 yarn.resourcemanager.resource-tracker.address 8033 yarn.resourcemanager.admin.address 8042 yarn.nodemanager.webapp.address 8040 yarn.nodemanager.localizer.address 8188 yarn.timeline-service.webapp.address 10020 mapreduce.jobhistory.address 19888 mapreduce.jobhistory.webapp.address 2888 ZooKeeper，如果是Leader，用来监听Follower的连接 3888 ZooKeeper，用于Leader选举 2181 ZooKeeper，用来监听客户端的连接 60010 hbase.master.info.port，HMaster的http端口 60000 hbase.master.port，HMaster的RPC端口 60030 hbase.regionserver.info.port，HRegionServer的http端口 60020 hbase.regionserver.port，HRegionServer的RPC端口 8080 hbase.rest.port，HBase REST server的端口 10000 hive.server2.thrift.port 9083 hive.metastore.uris ","permalink":"https://leochu.work/blog/tech/bigdata/hadoop%E9%BB%98%E8%AE%A4%E7%AB%AF%E5%8F%A3/","summary":"\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e端口\u003c/th\u003e\n          \u003cth\u003e用途\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e9000\u003c/td\u003e\n          \u003ctd\u003efs.defaultFS，如：hdfs://172.25.40.171:9000\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e9001\u003c/td\u003e\n          \u003ctd\u003edfs.namenode.rpc-address，DataNode会连接这个端口\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50070\u003c/td\u003e\n          \u003ctd\u003edfs.namenode.http-address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50470\u003c/td\u003e\n          \u003ctd\u003edfs.namenode.https-address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50100\u003c/td\u003e\n          \u003ctd\u003edfs.namenode.backup.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50105\u003c/td\u003e\n          \u003ctd\u003edfs.namenode.backup.http-address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50090\u003c/td\u003e\n          \u003ctd\u003edfs.namenode.secondary.http-address，如：172.25.39.166:50090\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50091\u003c/td\u003e\n          \u003ctd\u003edfs.namenode.secondary.https-address，如：172.25.39.166:50091\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50020\u003c/td\u003e\n          \u003ctd\u003edfs.datanode.ipc.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50075\u003c/td\u003e\n          \u003ctd\u003edfs.datanode.http.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50475\u003c/td\u003e\n          \u003ctd\u003edfs.datanode.https.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e50010\u003c/td\u003e\n          \u003ctd\u003edfs.datanode.address，DataNode的数据传输端口\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8480\u003c/td\u003e\n          \u003ctd\u003edfs.journalnode.rpc-address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8481\u003c/td\u003e\n          \u003ctd\u003edfs.journalnode.https-address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8032\u003c/td\u003e\n          \u003ctd\u003eyarn.resourcemanager.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8088\u003c/td\u003e\n          \u003ctd\u003eyarn.resourcemanager.webapp.address，YARN的http端口\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8090\u003c/td\u003e\n          \u003ctd\u003eyarn.resourcemanager.webapp.https.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8030\u003c/td\u003e\n          \u003ctd\u003eyarn.resourcemanager.scheduler.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8031\u003c/td\u003e\n          \u003ctd\u003eyarn.resourcemanager.resource-tracker.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8033\u003c/td\u003e\n          \u003ctd\u003eyarn.resourcemanager.admin.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8042\u003c/td\u003e\n          \u003ctd\u003eyarn.nodemanager.webapp.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8040\u003c/td\u003e\n          \u003ctd\u003eyarn.nodemanager.localizer.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8188\u003c/td\u003e\n          \u003ctd\u003eyarn.timeline-service.webapp.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e10020\u003c/td\u003e\n          \u003ctd\u003emapreduce.jobhistory.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e19888\u003c/td\u003e\n          \u003ctd\u003emapreduce.jobhistory.webapp.address\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e2888\u003c/td\u003e\n          \u003ctd\u003eZooKeeper，如果是Leader，用来监听Follower的连接\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e3888\u003c/td\u003e\n          \u003ctd\u003eZooKeeper，用于Leader选举\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e2181\u003c/td\u003e\n          \u003ctd\u003eZooKeeper，用来监听客户端的连接\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e60010\u003c/td\u003e\n          \u003ctd\u003ehbase.master.info.port，HMaster的http端口\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e60000\u003c/td\u003e\n          \u003ctd\u003ehbase.master.port，HMaster的RPC端口\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e60030\u003c/td\u003e\n          \u003ctd\u003ehbase.regionserver.info.port，HRegionServer的http端口\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e60020\u003c/td\u003e\n          \u003ctd\u003ehbase.regionserver.port，HRegionServer的RPC端口\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8080\u003c/td\u003e\n          \u003ctd\u003ehbase.rest.port，HBase REST server的端口\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e10000\u003c/td\u003e\n          \u003ctd\u003ehive.server2.thrift.port\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e9083\u003c/td\u003e\n          \u003ctd\u003ehive.metastore.uris\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e","title":"hadoop默认端口"},{"content":"conf：\n# ex_trade.conf:外贸数据自kafka接入hdfs # 配置负责人：褚天宇 # 配置时间： 2022-01-17 # 配置Agent ex_trade_agent各个组件的名称 ex_trade_agent.sources = r1 ex_trade_agent.sinks = k1 ex_trade_agent.channels = c1 # 配置Agent ex_trade_agent的source r1的属性 # 设置kafka源 ex_trade_agent.sources.r1.type = org.apache.flume.source.kafka.KafkaSource # 设置kafka消费者组id （不能改动！改动会丢失已记录的offset） ex_trade_agent.sources.r1.kafka.consumer.group.id = ex_trade_flume ex_trade_agent.sources.r1.kafka.bootstrap.servers = 192.168.102.2:9092,192.168.102.3:9092,192.168.102.7:9092 ex_trade_agent.sources.r1.kafka.topics = ex_trade # 一批写入 channel 的最大消息数 ex_trade_agent.sources.r1.batchSize = 2000 # 一个批次写入 channel 之前的最大等待时间（毫秒）。达到等待时间或者数量达到 batchSize 都会触发写操作 ex_trade_agent.sources.r1.batchDurationMillis = 2000 # 设置kafka-offset策略为从头消费 ex_trade_agent.sources.r1.kafka.consumer.auto.offset.reset = earliest # 配置Agent ex_trade_agent的sink k1的属性 # 配置hdfs目标 ex_trade_agent.sinks.k1.type = hdfs # 配置hdfs写入目录，每天一个文件夹，为之后hive表按日分区做准备 ex_trade_agent.sinks.k1.hdfs.path = /flume/ex_trade/date=%Y%m%d/ # 配置文件前缀 ex_trade_agent.sinks.k1.hdfs.filePrefix = ex_trade_ # 配置数据类型为原样输出 ex_trade_agent.sinks.k1.hdfs.fileType = DataStream # 当前文件写入达到该值时间后触发滚动创建新文件，单位：秒，设置为1天防止产生小文件 ex_trade_agent.sinks.k1.hdfs.rollInterval = 86400 # 当前文件写入达到该大小后触发滚动创建新文件，单位：字节，设置为128M ex_trade_agent.sinks.k1.hdfs.rollSize = 134217700 # 向 HDFS 写入内容时每次批量操作的 Event 数量 ex_trade_agent.sinks.k1.hdfs.batchSize = 2000 # 不根据 Event 数量来分割文件 ex_trade_agent.sinks.k1.hdfs.rollCount = 0 # 文件写入格式设置为 Text，否则 Impala或 Apache Hive 无法读取这些文件。 ex_trade_agent.sinks.k1.hdfs.writeFormat = Text # 配置Agent ex_trade_agent的channel c1的属性，用来缓冲数据 ex_trade_agent.channels.c1.type = file # 配置通道的检查点目录 ex_trade_agent.channels.c1.checkpointDir = /data/deploy/flume/checkpoint # 配置通道的数据目录 ex_trade_agent.channels.c1.dataDirs = /data/deploy/flume/data # 把source和sink绑定到channel上 ex_trade_agent.sources.r1.channels = c1 ex_trade_agent.sinks.k1.channel = c1 启动脚本：\n#!/bin/bash #ex_trade-hdfs34560.sh 34560为agent监控web端口号，下次再启动别的agent使用端口号为34561，依次累加 #负责人：褚天宇 #note：外贸数据接入hdfs的flume脚本 #参数：可选start|stop|restart function start(){ echo \u0026#34;正在启动flume进程：ex_trade_agent......\u0026#34; num=`ps -ef|grep java|grep ex_trade_agent|wc -l` #判断是否有flume进程运行，如果无则运行执行nohup命令 if [ \u0026#34;$num\u0026#34; = \u0026#34;0\u0026#34; ] ;then cd /data/deploy/flume/log/ex_trade_agent/ nohup flume-ng agent --conf /data/deploy/flume/conf/ --conf-file /data/deploy/flume/conf/ex_trade.conf --name ex_trade_agent -Dflume.root.logger=INFO,console -Dflume.monitoring.type=http -Dflume.monitoring.port=34560 \u0026amp; echo \u0026#34;启动ex_trade_agent成功！\u0026#34; echo \u0026#34;日志路径: /data/deploy/flume/log/ex_trade_agent/nohup.out\u0026#34; else echo \u0026#34;ex_trade_agent进程已经存在,启动失败,请检查......\u0026#34; exit 0 fi } function stop(){ echo \u0026#34;正在停止flume进程：ex_trade_agent......\u0026#34; num=`ps -ef|grep java|grep ex_trade_agent|wc -l` if [ \u0026#34;$num\u0026#34; != \u0026#34;0\u0026#34; ];then ps -ef|grep java|grep ex_trade_agent|awk \u0026#39;{print $2;}\u0026#39;|xargs kill echo \u0026#34;ex_trade_agent进程已经关闭......\u0026#34; else echo \u0026#34;ex_trade_agent进程未启动，无须停止......\u0026#34; fi } function restart(){ stop num=`ps -ef|grep java|grep ex_trade_agent|wc -l` #stop完成之后，查找flume的进程数，判断进程数是否为0，如果不为0，则休眠5秒，再次查看，直到进程数为0 while [ \u0026#34;$num\u0026#34; != \u0026#34;0\u0026#34; ];do sleep 5 num=`ps -ef|grep java|grep ex_trade_agent|wc -l` done echo \u0026#34;ex_trade_agent进程已经关闭，正在重新启动......\u0026#34; start echo \u0026#34;启动ex_trade_agent成功!\u0026#34; } #case 命令获取输入的参数，如果参数为start,执行start函数，如果参数为stop执行stop函数，如果参数为restart，执行restart函数 case \u0026#34;$1\u0026#34; in \u0026#34;start\u0026#34;) start ;; \u0026#34;stop\u0026#34;) stop ;; \u0026#34;restart\u0026#34;) restart ;; *) ;; esac ","permalink":"https://leochu.work/blog/tech/bigdata/flume%E7%A4%BA%E4%BE%8B/","summary":"\u003cp\u003econf：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ex_trade.conf:外贸数据自kafka接入hdfs\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置负责人：褚天宇\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置时间： 2022-01-17\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置Agent ex_trade_agent各个组件的名称\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e r1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e k1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echannels \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e c1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置Agent ex_trade_agent的source r1的属性\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 设置kafka源\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003er1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etype \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e org\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eapache\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eflume\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esource\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ekafka\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eKafkaSource\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 设置kafka消费者组id （不能改动！改动会丢失已记录的offset）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003er1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ekafka\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003econsumer\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003egroup\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eid \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e ex_trade_flume\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003er1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ekafka\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebootstrap\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eservers \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e192.168.102.2\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9092\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.3\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9092\u003c/span\u003e,\u003cspan style=\"color:#ae81ff\"\u003e192.168.102.7\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e9092\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003er1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ekafka\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etopics \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e ex_trade\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 一批写入 channel 的最大消息数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003er1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebatchSize \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 一个批次写入 channel 之前的最大等待时间（毫秒）。达到等待时间或者数量达到 batchSize 都会触发写操作\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003er1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebatchDurationMillis \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 设置kafka-offset策略为从头消费\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003er1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ekafka\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003econsumer\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eauto\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eoffset\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ereset \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e earliest\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置Agent ex_trade_agent的sink k1的属性\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置hdfs目标\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etype \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e hdfs\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置hdfs写入目录，每天一个文件夹，为之后hive表按日分区做准备\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epath \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003eflume\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003eex_trade\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003edate\u003cspan style=\"color:#f92672\"\u003e=%\u003c/span\u003eY\u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003em\u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003ed\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置文件前缀\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efilePrefix \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e ex_trade_\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置数据类型为原样输出\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efileType \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e DataStream\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 当前文件写入达到该值时间后触发滚动创建新文件，单位：秒，设置为1天防止产生小文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erollInterval \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e86400\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 当前文件写入达到该大小后触发滚动创建新文件，单位：字节，设置为128M\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erollSize \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e134217700\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 向 HDFS 写入内容时每次批量操作的 Event 数量\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ebatchSize \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e2000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 不根据 Event 数量来分割文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erollCount \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 文件写入格式设置为 Text，否则 Impala或 Apache Hive 无法读取这些文件。\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehdfs\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ewriteFormat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Text\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置Agent ex_trade_agent的channel c1的属性，用来缓冲数据\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echannels\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ec1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etype \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e file\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置通道的检查点目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echannels\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ec1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echeckpointDir \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003edata\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003edeploy\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003eflume\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003echeckpoint\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 配置通道的数据目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echannels\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ec1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003edataDirs \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003edata\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003edeploy\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003eflume\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003edata\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 把source和sink绑定到channel上\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esources\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003er1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echannels \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e c1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eex_trade_agent\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esinks\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ek1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003echannel \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e c1\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e启动脚本：\u003c/p\u003e","title":"flume示例"},{"content":"Flume NG支持运行时动态修改配置的配置模块 细说一下PollingPropertiesFileConfigurationProvider提供的运行时动态修改配置并生效的能力。\n要实现动态修改配置文件并生效，主要有两个待实现的功能\n观察配置文件是否修改\n如果修改，将修改的内容通知给观察者\n对于第一点，监控配置文件是否修改，Flume NG定义了一个FileWatcherRunnable对象来监控配置文件，启动了一个单独的线程采用定时轮询的方式来监控，轮询频率是30毫秒一次，比较file.lastModified属性与lastChange时间戳，当file.lastModified \u0026gt; lastChange时表示文件被修改\npublic class FileWatcherRunnable implements Runnable { private final File file; private final CounterGroup counterGroup; private long lastChange; public FileWatcherRunnable(File file, CounterGroup counterGroup) { super(); this.file = file; this.counterGroup = counterGroup; this.lastChange = 0L; } @Override public void run() { LOGGER.debug(\u0026#34;Checking file:{} for changes\u0026#34;, file); counterGroup.incrementAndGet(\u0026#34;file.checks\u0026#34;); long lastModified = file.lastModified(); if (lastModified \u0026gt; lastChange) { LOGGER.info(\u0026#34;Reloading configuration file:{}\u0026#34;, file); counterGroup.incrementAndGet(\u0026#34;file.loads\u0026#34;); lastChange = lastModified; try { eventBus.post(getConfiguration()); } catch (Exception e) { LOGGER.error(\u0026#34;Failed to load configuration data. Exception follows.\u0026#34;, e); } catch (NoClassDefFoundError e) { LOGGER.error(\u0026#34;Failed to start agent because dependencies were not \u0026#34; + \u0026#34;found in classpath. Error follows.\u0026#34;, e); } catch (Throwable t) { // caught because the caller does not handle or log Throwables LOGGER.error(\u0026#34;Unhandled error\u0026#34;, t); } } } } // PollingPropertiesFileConfigurationProvider.start()启动一个单独的线程来监控properties配置文件 public void start() { LOGGER.info(\u0026#34;Configuration provider starting\u0026#34;); Preconditions.checkState(file != null, \u0026#34;The parameter file must not be null\u0026#34;); executorService = Executors.newSingleThreadScheduledExecutor( new ThreadFactoryBuilder().setNameFormat(\u0026#34;conf-file-poller-%d\u0026#34;) .build()); FileWatcherRunnable fileWatcherRunnable = new FileWatcherRunnable(file, counterGroup); executorService.scheduleWithFixedDelay(fileWatcherRunnable, 0, interval, TimeUnit.SECONDS); lifecycleState = LifecycleState.START; LOGGER.debug(\u0026#34;Configuration provider started\u0026#34;); } 对于第二点，利用Guava EventBus提供的发布订阅模式机制，将配置修改封装成事件传递给Application，来重新加载配置\n// FileWatcherRunnable.run方法 发布配置修改的事件 eventBus.post(getConfiguration()); // Application.main方法来注册事件订阅 Application application; if(reload) { EventBus eventBus = new EventBus(agentName + \u0026#34;-event-bus\u0026#34;); PollingPropertiesFileConfigurationProvider configurationProvider = new PollingPropertiesFileConfigurationProvider(agentName, configurationFile, eventBus, 30); components.add(configurationProvider); application = new Application(components); eventBus.register(application); } // Application类采用@Subscribe标注来定义订阅方法，即配置修改后会执行handleConfigurationEvent方法，这个方法是线程安全的 @Subscribe public synchronized void handleConfigurationEvent(MaterializedConfiguration conf) { stopAllComponents(); startAllComponents(conf); } ","permalink":"https://leochu.work/blog/tech/bigdata/flume%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E9%85%8D%E7%BD%AE%E5%8E%9F%E7%90%86/","summary":"\u003ch2 id=\"flume-ng支持运行时动态修改配置的配置模块\"\u003eFlume NG支持运行时动态修改配置的配置模块\u003c/h2\u003e\n\u003cp\u003e细说一下PollingPropertiesFileConfigurationProvider提供的运行时动态修改配置并生效的能力。\u003c/p\u003e\n\u003cp\u003e要实现动态修改配置文件并生效，主要有两个待实现的功能\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e观察配置文件是否修改\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e如果修改，将修改的内容通知给观察者\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e对于第一点，监控配置文件是否修改，Flume NG定义了一个FileWatcherRunnable对象来监控配置文件，启动了一个单独的线程采用定时轮询的方式来监控，轮询频率是30毫秒一次，比较file.lastModified属性与lastChange时间戳，当file.lastModified \u0026gt; lastChange时表示文件被修改\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-java\" data-lang=\"java\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eclass\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFileWatcherRunnable\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eimplements\u003c/span\u003e Runnable {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efinal\u003c/span\u003e File file;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efinal\u003c/span\u003e CounterGroup counterGroup;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003eprivate\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003elong\u003c/span\u003e lastChange;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eFileWatcherRunnable\u003c/span\u003e(File file, CounterGroup counterGroup) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003esuper\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003efile\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e file;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ecounterGroup\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e counterGroup;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003ethis\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003elastChange\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e 0L;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#a6e22e\"\u003e@Override\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erun\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      LOGGER.\u003cspan style=\"color:#a6e22e\"\u003edebug\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Checking file:{} for changes\u0026#34;\u003c/span\u003e, file);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      counterGroup.\u003cspan style=\"color:#a6e22e\"\u003eincrementAndGet\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;file.checks\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003elong\u003c/span\u003e lastModified \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e file.\u003cspan style=\"color:#a6e22e\"\u003elastModified\u003c/span\u003e();\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      \u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e (lastModified \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e lastChange) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        LOGGER.\u003cspan style=\"color:#a6e22e\"\u003einfo\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Reloading configuration file:{}\u0026#34;\u003c/span\u003e, file);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        counterGroup.\u003cspan style=\"color:#a6e22e\"\u003eincrementAndGet\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;file.loads\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        lastChange \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e lastModified;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003etry\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          eventBus.\u003cspan style=\"color:#a6e22e\"\u003epost\u003c/span\u003e(getConfiguration());\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003ecatch\u003c/span\u003e (Exception e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          LOGGER.\u003cspan style=\"color:#a6e22e\"\u003eerror\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Failed to load configuration data. Exception follows.\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003ecatch\u003c/span\u003e (NoClassDefFoundError e) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          LOGGER.\u003cspan style=\"color:#a6e22e\"\u003eerror\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Failed to start agent because dependencies were not \u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e              \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;found in classpath. Error follows.\u0026#34;\u003c/span\u003e, e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        } \u003cspan style=\"color:#66d9ef\"\u003ecatch\u003c/span\u003e (Throwable t) {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          \u003cspan style=\"color:#75715e\"\u003e// caught because the caller does not handle or log Throwables\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e          LOGGER.\u003cspan style=\"color:#a6e22e\"\u003eerror\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Unhandled error\u0026#34;\u003c/span\u003e, t);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// PollingPropertiesFileConfigurationProvider.start()启动一个单独的线程来监控properties配置文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#66d9ef\"\u003epublic\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003evoid\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estart\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    LOGGER.\u003cspan style=\"color:#a6e22e\"\u003einfo\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Configuration provider starting\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    Preconditions.\u003cspan style=\"color:#a6e22e\"\u003echeckState\u003c/span\u003e(file \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;The parameter file must not be null\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    executorService \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e Executors.\u003cspan style=\"color:#a6e22e\"\u003enewSingleThreadScheduledExecutor\u003c/span\u003e(\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e ThreadFactoryBuilder().\u003cspan style=\"color:#a6e22e\"\u003esetNameFormat\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;conf-file-poller-%d\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                .\u003cspan style=\"color:#a6e22e\"\u003ebuild\u003c/span\u003e());\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    FileWatcherRunnable fileWatcherRunnable \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#66d9ef\"\u003enew\u003c/span\u003e FileWatcherRunnable(file, counterGroup);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    executorService.\u003cspan style=\"color:#a6e22e\"\u003escheduleWithFixedDelay\u003c/span\u003e(fileWatcherRunnable, 0, interval,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        TimeUnit.\u003cspan style=\"color:#a6e22e\"\u003eSECONDS\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    lifecycleState \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e LifecycleState.\u003cspan style=\"color:#a6e22e\"\u003eSTART\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    LOGGER.\u003cspan style=\"color:#a6e22e\"\u003edebug\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Configuration provider started\u0026#34;\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e           }\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e对于第二点，利用Guava EventBus提供的发布订阅模式机制，将配置修改封装成事件传递给Application，来重新加载配置\u003c/p\u003e","title":"flume动态加载配置原理"},{"content":"1.YARN参数调优 检查项 当前值 修改值 JobHistory Server 的 Java 堆栈大小 1GB 2GB NodeManager 的 Java 堆栈大小 1GB 2GB ResourceManager 的 Java 堆栈大小 1GB 2GB 容器内存 yarn.nodemanager.resource.memory-mb 24GB 32GB 最小容器内存 yarn.scheduler.minimum-allocation-mb 10GB 8GB 最大容器内存 yarn.scheduler.maximum-allocation-mb 40GB 56GB Map 任务内存 mapreduce.map.memory.mb 0M 12GB Reduce 任务内存 mapreduce.reduce.memory.mb 0M 24GB Application Master容器内存 yarn.app.mapreduce.am.resource.mb 24GB 32GB Map 任务 Java 选项库 mapreduce.map.java.opts -Djava.net.preferIPv4Stack=true -Dmapreduce.map.java.opts=-Xmx2048m Reduce 任务 Java 选项库 mapreduce.reduce.java.opts -Djava.net.preferIPv4Stack=true -Dmapreduce.reduce.java.opts=-Xmx2048m yarn.resourcemanager.scheduler.class org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler yarn.scheduler.capacity.root.queues:\n当前值：\n\u0026lt;configuration\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.queues\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;default\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;100\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;100\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/configuration\u0026gt; 修改值：\n\u0026lt;configuration\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.maximum-applications\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;10000\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.maximum-am-resource-percent\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;0.1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.queues\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;default,bigdata,analysis,prd\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;30\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;30\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;20\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;20\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.user-limit-factor\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;3\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.user-limit-factor\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;3\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.user-limit-factor\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;3\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.user-limit-factor\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;3\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.maximum-capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;70\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.maximum-capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;90\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.maximum-capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;60\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.maximum-capacity\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;90\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.state\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;RUNNING\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.state\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;RUNNING\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.state\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;RUNNING\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.state\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;RUNNING\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.acl_submit_applications\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.acl_submit_applications\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.acl_submit_applications\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.acl_submit_applications\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.acl_administer_queue\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.acl_administer_queue\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.acl_administer_queue\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.acl_administer_queue\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.acl_application_max_priority\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.acl_application_max_priority\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.acl_application_max_priority\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.acl_application_max_priority\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;*\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.maximum-application-lifetime\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.maximum-application-lifetime\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.maximum-application-lifetime\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.maximum-application-lifetime\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.default.default-application-lifetime\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.bigdata.default-application-lifetime\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.analysis.default-application-lifetime\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.root.prd.default-application-lifetime\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.node-locality-delay\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;40\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.rack-locality-additional-delay\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;-1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.queue-mappings\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.queue-mappings-override.enable\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;false\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;yarn.scheduler.capacity.per-node-heartbeat.maximum-offswitch-assignments\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;1\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;/configuration\u0026gt; 修改完队列名称要记得刷新队列，执行此命令：yarn rmadmin -refreshQueues\n2.spark参数调优 检查项 当前值 修改值 Java Heap Size of History Server in Bytes history_server_max_heapsize 512MB 2GB 3.kudu参数调优 检查项 当前值 修改值 备注 gflagfile 的 Master 高级配置代码段（安全阀） 无 -max_num_columns=10000 -unlock_unsafe_flags=true 调整Kudu单表最大300列限制 Kudu Tablet Server Hard Memory Limit memory_limit_hard_bytes 4GB 32G Kudu Tablet Server Block Cache Capacity block_cache_capacity_mb 512MB 4G 4.impala参数调优 检查项 当前值 修改值 备注 Catalog Server 的 Java 堆栈大小（字节） 50M 12GB Impala Daemon 内存限制 mem_limit 256M 80GB Impala Daemon 查询选项高级配置代码段（安全阀） default_query_options 无 PARQUET_FALLBACK_SCHEMA_RESOLUTION=name 防止查询错列报错 Impala Daemon 命令行参数高级配置代码段（安全阀） 无 -use_local_tz_for_unix_timestamp_conversions=true -convert_legacy_hive_parquet_utc_timestamps=true 启用本地时区 并且兼容hive parquet 5.HDFS参数调优 检查项 当前值 修改值 备注 hdfs-site.xml 的 HDFS 服务高级配置代码段（安全阀） 无 dfs.client.datanode-restart.timeout 30 防止flink提交任务失败，出现以下错误：java.lang.NumberFormatException: For input string: \u0026ldquo;30s\u0026rdquo; ","permalink":"https://leochu.work/blog/tech/bigdata/cdh%E7%BB%84%E4%BB%B6%E5%8F%82%E6%95%B0%E8%B0%83%E4%BC%98/","summary":"\u003ch3 id=\"1yarn参数调优\"\u003e1.YARN参数调优\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003cstrong\u003e检查项\u003c/strong\u003e\u003c/th\u003e\n          \u003cth\u003e\u003cstrong\u003e当前值\u003c/strong\u003e\u003c/th\u003e\n          \u003cth\u003e\u003cstrong\u003e修改值\u003c/strong\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eJobHistory Server 的 Java 堆栈大小\u003c/td\u003e\n          \u003ctd\u003e1GB\u003c/td\u003e\n          \u003ctd\u003e2GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eNodeManager 的 Java 堆栈大小\u003c/td\u003e\n          \u003ctd\u003e1GB\u003c/td\u003e\n          \u003ctd\u003e2GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eResourceManager 的 Java 堆栈大小\u003c/td\u003e\n          \u003ctd\u003e1GB\u003c/td\u003e\n          \u003ctd\u003e2GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e容器内存  yarn.nodemanager.resource.memory-mb\u003c/td\u003e\n          \u003ctd\u003e24GB\u003c/td\u003e\n          \u003ctd\u003e32GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e最小容器内存   yarn.scheduler.minimum-allocation-mb\u003c/td\u003e\n          \u003ctd\u003e10GB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e最大容器内存  yarn.scheduler.maximum-allocation-mb\u003c/td\u003e\n          \u003ctd\u003e40GB\u003c/td\u003e\n          \u003ctd\u003e56GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMap 任务内存   mapreduce.map.memory.mb\u003c/td\u003e\n          \u003ctd\u003e0M\u003c/td\u003e\n          \u003ctd\u003e12GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eReduce 任务内存   mapreduce.reduce.memory.mb\u003c/td\u003e\n          \u003ctd\u003e0M\u003c/td\u003e\n          \u003ctd\u003e24GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eApplication Master容器内存  yarn.app.mapreduce.am.resource.mb\u003c/td\u003e\n          \u003ctd\u003e24GB\u003c/td\u003e\n          \u003ctd\u003e32GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMap 任务 Java 选项库  mapreduce.map.java.opts\u003c/td\u003e\n          \u003ctd\u003e-Djava.net.preferIPv4Stack=true\u003c/td\u003e\n          \u003ctd\u003e-Dmapreduce.map.java.opts=-Xmx2048m\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eReduce 任务 Java 选项库  mapreduce.reduce.java.opts\u003c/td\u003e\n          \u003ctd\u003e-Djava.net.preferIPv4Stack=true\u003c/td\u003e\n          \u003ctd\u003e-Dmapreduce.reduce.java.opts=-Xmx2048m\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eyarn.resourcemanager.scheduler.class\u003c/td\u003e\n          \u003ctd\u003eorg.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler\u003c/td\u003e\n          \u003ctd\u003eorg.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eyarn.scheduler.capacity.root.queues:\u003c/p\u003e\n\u003cp\u003e当前值：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;configuration\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;property\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;name\u0026gt;\u003c/span\u003eyarn.scheduler.capacity.root.queues\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/name\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;value\u0026gt;\u003c/span\u003edefault\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/value\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/property\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;property\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;name\u0026gt;\u003c/span\u003eyarn.scheduler.capacity.root.capacity\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/name\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;value\u0026gt;\u003c/span\u003e100\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/value\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/property\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;property\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;name\u0026gt;\u003c/span\u003eyarn.scheduler.capacity.root.default.capacity\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/name\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026lt;value\u0026gt;\u003c/span\u003e100\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/value\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026lt;/property\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e\u0026lt;/configuration\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e修改值：\u003c/p\u003e","title":"CDH组件参数调优"},{"content":"1. 服务器配置 主节点： hostname: m1,m2,m3\nvcore：48\n内存：128G\nSSD：1T（不算系统盘）\n工作节点： hostname: n1,n2,n3,n4\nvcore : 48\n内存：256G\nSSD:1T（不算系统盘）\n2. 节点职责描述 m1: 控制核心；cdh核心，hadoop主节点\nm2: 网关入口；主节点高可用，一些组件的web ui，用户入口 ，（前期做计算任务的driver端，后期优化driver打散到各节点）\nm3: 后台服务；组件元数据库，任务的history服务 ，（后期做元数据HA）\nn1~n4: 算存一体；提供存储，计算等服务\n3.角色分配策略 一、hdfs NameNode一般在主节点上，初始化安装的时候没有高可用，所以有SecondaryNameNode的作为一个备份,NameNode它会将它拆分后进行分布式存储，其中的数据是分散在各个DataNode节点，且默认都会有3个副本，防止其中一台机器宕机使得数据缺失。balancer一般与namenode搭建在一起。\n二、hive hive metastore server与hiveServer2一般搭载一起上，但也可以分开，因为hive服务需要启动hiveServer2，访问sparksql需要启动metastore而hive gateway,事实上并不是真正的角色，也没有状态，但它们充当了告诉客户端配置应该放置在哪里。 添加Hive服务时，默认情况下会创建Hive网关。\n三、cloudera manager server 这个可以根据实际搭建，这个相当于是集群的监听器，在网页上出现的的图表也就是这个监听器类似的，这个可以搭建在主节点上，但若是主节点上分配的角色过多会影响其服务器的性能。\n四、spark 这个角色可以分配这任意的机器上，按实际情况调整。spark-gateway全部部署在各个机器上，这个对于个人理解来说相当于spark、spark2机器之间的通信功能。\n五、yarn jobhistory与resourcemanager进行通信，所以部署上一般在同一台机器上放在主节点上，而nodemanager分配在各个节点上\n六、zookeeper 这个若是机器足够一般是奇数的，所以部署在m节点上比较合适。奇数台、高可用、与管理角色共置\n七、hue 会对外提供一个web ui，以便于数据分析和数据开发做即席查询。这个服务随意部署，根据自己的机器部署情况来看。\n4.角色划分详情表 控制核心 网关+入口 元数据+历史服务 存算一体 m1 m2 m3 n1 n2 n3 n4 cloudera management Alert Publisher ✅ Event Server ✅ Host Monitor ✅ Service Monitor ✅ hdfs NameNode ✅ ✅ JournalNode ✅ ✅ ✅ Failover Controller ✅ ✅ HttpFs ✅ DataNode ✅ ✅ ✅ ✅ yarn ResourceManager ✅ ✅ NodeManager ✅ ✅ ✅ ✅ JobHistory Server ✅ hive Hive MetaStore Server ✅ HiveServer2 ✅ HiveGateway ✅ ✅ ✅ ✅ ✅ ✅ ✅ spark Spark History Server ✅ Spark Gateway ✅ ✅ ✅ ✅ ✅ ✅ ✅ impala Impala StateStore ✅ Impala catalog Server ✅ Impala Daemon ✅ ✅ ✅ ✅ zookeeper zk-node ✅ ✅ ✅ hue Hue Server ✅ Hue Load Balancer ✅ 5. 未来升级项 work节点扩展硬盘，只需将新盘挂载到新目录/hadoop/data2或/hadoop/data3，更新hdfs配置就能完成存储扩展（支持热加入）\n新增work节点，配置完全复制目前的work节点角色即可\n后期大数据任务多时，将m2的driver职责下沉到其他节点\n后期元数据库，元数据服务可做高可用\n","permalink":"https://leochu.work/blog/tech/bigdata/cdh%E8%A7%92%E8%89%B2%E5%88%92%E5%88%86/","summary":"\u003ch3 id=\"1-服务器配置\"\u003e\u003cstrong\u003e1. 服务器配置\u003c/strong\u003e\u003c/h3\u003e\n\u003ch4 id=\"主节点\"\u003e\u003cstrong\u003e主节点：\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003ehostname: m1,m2,m3\u003c/p\u003e\n\u003cp\u003evcore：48\u003c/p\u003e\n\u003cp\u003e内存：128G\u003c/p\u003e\n\u003cp\u003eSSD：1T（不算系统盘）\u003c/p\u003e\n\u003ch4 id=\"工作节点\"\u003e\u003cstrong\u003e工作节点：\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003ehostname: n1,n2,n3,n4\u003c/p\u003e\n\u003cp\u003evcore : 48\u003c/p\u003e\n\u003cp\u003e内存：256G\u003c/p\u003e\n\u003cp\u003eSSD:1T（不算系统盘）\u003c/p\u003e\n\u003ch3 id=\"2-节点职责描述\"\u003e\u003cstrong\u003e2. 节点职责描述\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003em1: 控制核心；cdh核心，hadoop主节点\u003c/p\u003e\n\u003cp\u003em2: 网关入口；主节点高可用，一些组件的web ui，用户入口 ，（前期做计算任务的driver端，后期优化driver打散到各节点）\u003c/p\u003e\n\u003cp\u003em3: 后台服务；组件元数据库，任务的history服务 ，（后期做元数据HA）\u003c/p\u003e\n\u003cp\u003en1~n4: 算存一体；提供存储，计算等服务\u003c/p\u003e\n\u003ch3 id=\"3角色分配策略\"\u003e\u003cstrong\u003e3.角色分配策略\u003c/strong\u003e\u003c/h3\u003e\n\u003ch4 id=\"一hdfs\"\u003e\u003cstrong\u003e一、hdfs\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003eNameNode一般在主节点上，初始化安装的时候没有高可用，所以有SecondaryNameNode的作为一个备份,NameNode它会将它拆分后进行分布式存储，其中的数据是分散在各个DataNode节点，且默认都会有3个副本，防止其中一台机器宕机使得数据缺失。balancer一般与namenode搭建在一起。\u003c/p\u003e\n\u003ch4 id=\"二hive\"\u003e\u003cstrong\u003e二、hive\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003ehive metastore server与hiveServer2一般搭载一起上，但也可以分开，因为hive服务需要启动hiveServer2，访问sparksql需要启动metastore而hive gateway,事实上并不是真正的角色，也没有状态，但它们充当了告诉客户端配置应该放置在哪里。 添加Hive服务时，默认情况下会创建Hive网关。\u003c/p\u003e\n\u003ch4 id=\"三cloudera-manager-server\"\u003e\u003cstrong\u003e三、cloudera manager server\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e这个可以根据实际搭建，这个相当于是集群的监听器，在网页上出现的的图表也就是这个监听器类似的，这个可以搭建在主节点上，但若是主节点上分配的角色过多会影响其服务器的性能。\u003c/p\u003e\n\u003ch4 id=\"四spark\"\u003e\u003cstrong\u003e四、spark\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e这个角色可以分配这任意的机器上，按实际情况调整。spark-gateway全部部署在各个机器上，这个对于个人理解来说相当于spark、spark2机器之间的通信功能。\u003c/p\u003e\n\u003ch4 id=\"五yarn\"\u003e\u003cstrong\u003e五、yarn\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003ejobhistory与resourcemanager进行通信，所以部署上一般在同一台机器上放在主节点上，而nodemanager分配在各个节点上\u003c/p\u003e\n\u003ch4 id=\"六zookeeper\"\u003e\u003cstrong\u003e六、zookeeper\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e这个若是机器足够一般是奇数的，所以部署在m节点上比较合适。\u003cstrong\u003e奇数台、高可用、与管理角色共置\u003c/strong\u003e\u003c/p\u003e\n\u003ch4 id=\"七hue\"\u003e\u003cstrong\u003e七、hue\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e会对外提供一个web ui，以便于数据分析和数据开发做即席查询。这个服务随意部署，根据自己的机器部署情况来看。\u003c/p\u003e\n\u003ch3 id=\"4角色划分详情表\"\u003e\u003cstrong\u003e4.角色划分详情表\u003c/strong\u003e\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e控制核心\u003c/td\u003e\n          \u003ctd\u003e网关+入口\u003c/td\u003e\n          \u003ctd\u003e元数据+历史服务\u003c/td\u003e\n          \u003ctd\u003e存算一体\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003em1\u003c/td\u003e\n          \u003ctd\u003em2\u003c/td\u003e\n          \u003ctd\u003em3\u003c/td\u003e\n          \u003ctd\u003en1\u003c/td\u003e\n          \u003ctd\u003en2\u003c/td\u003e\n          \u003ctd\u003en3\u003c/td\u003e\n          \u003ctd\u003en4\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ecloudera management\u003c/td\u003e\n          \u003ctd\u003eAlert Publisher\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eEvent Server\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHost Monitor\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eService Monitor\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ehdfs\u003c/td\u003e\n          \u003ctd\u003eNameNode\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eJournalNode\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eFailover Controller\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHttpFs\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eDataNode\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eyarn\u003c/td\u003e\n          \u003ctd\u003eResourceManager\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eNodeManager\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eJobHistory Server\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ehive\u003c/td\u003e\n          \u003ctd\u003eHive MetaStore Server\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHiveServer2\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHiveGateway\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003espark\u003c/td\u003e\n          \u003ctd\u003eSpark History Server\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSpark Gateway\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eimpala\u003c/td\u003e\n          \u003ctd\u003eImpala StateStore\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eImpala catalog Server\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eImpala Daemon\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ezookeeper\u003c/td\u003e\n          \u003ctd\u003ezk-node\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ehue\u003c/td\u003e\n          \u003ctd\u003eHue Server\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHue Load Balancer\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"5-未来升级项\"\u003e5. 未来升级项\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003ework节点扩展硬盘，只需将新盘挂载到新目录/hadoop/data2或/hadoop/data3，更新hdfs配置就能完成存储扩展（支持热加入）\u003c/p\u003e","title":"CDH角色划分"},{"content":"beeline -n chutianyu -p chutianyu \u0026ndash;showHeader=false \u0026ndash;outputformat=csv2 -e \u0026ldquo;select * from smp.india_imp_json_test2mongo ;\u0026rdquo; \u0026gt; india_imp_test1.json\n","permalink":"https://leochu.work/blog/tech/bigdata/beeline%E5%AF%BC%E5%87%BA%E6%95%B0%E6%8D%AE/","summary":"\u003cp\u003ebeeline  -n  chutianyu -p chutianyu \u0026ndash;showHeader=false  \u0026ndash;outputformat=csv2  -e  \u0026ldquo;select * from smp.india_imp_json_test2mongo ;\u0026rdquo; \u0026gt;  india_imp_test1.json\u003c/p\u003e","title":"beeline导出数据"},{"content":"为什么要重设消费者组位移？ 我们知道，Kafka 和传统的消息引擎在设计上是有很大区别的，其中一个比较显著的区别就是，Kafka 的消费者读取消息是可以重演的（replayable）。\n像 RabbitMQ 或 ActiveMQ 这样的传统消息中间件，它们处理和响应消息的方式是破坏性的（destructive），即一旦消息被成功处理，就会被从 Broker 上删除。\n反观 Kafka，由于它是基于日志结构（log-based）的消息引擎，消费者在消费消息时，仅仅是从磁盘文件上读取数据而已，是只读的操作，因此消费者不会删除消息数据。同时，由于位移数据是由消费者控制的，因此它能够很容易地修改位移的值，实现重复消费历史数据的功能。\n对了，之前有很多同学在专栏的留言区提问：在实际使用场景中，我该如何确定是使用传统的消息中间件，还是使用 Kafka 呢？我在这里统一回答一下。如果在你的场景中，消息处理逻辑非常复杂，处理代价很高，同时你又不关心消息之间的顺序，那么传统的消息中间件是比较合适的；反之，如果你的场景需要较高的吞吐量，但每条消息的处理时间很短，同时你又很在意消息的顺序，此时，Kafka 就是你的首选。\n重设位移策略 不论是哪种设置方式，重设位移大致可以从两个维度来进行。\n位移维度。这是指根据位移值来重设。也就是说，直接把消费者的位移值重设成我们给定的位移值。 时间维度。我们可以给定一个时间，让消费者把位移调整成大于该时间的最小位移；也可以给出一段时间间隔，比如 30 分钟前，然后让消费者直接将位移调回 30 分钟之前的位移值。 下面的这张表格罗列了 7 种重设策略。接下来，我来详细解释下这些策略。\nEarliest 策略表示将位移调整到主题当前最早位移处。这个最早位移不一定就是 0，因为在生产环境中，很久远的消息会被 Kafka 自动删除，所以当前最早位移很可能是一个大于 0 的值。如果你想要重新消费主题的所有消息，那么可以使用 Earliest 策略。\nLatest 策略表示把位移重设成最新末端位移。如果你总共向某个主题发送了 15 条消息，那么最新末端位移就是 15。如果你想跳过所有历史消息，打算从最新的消息处开始消费的话，可以使用 Latest 策略。\nCurrent 策略表示将位移调整成消费者当前提交的最新位移。有时候你可能会碰到这样的场景：你修改了消费者程序代码，并重启了消费者，结果发现代码有问题，你需要回滚之前的代码变更，同时也要把位移重设到消费者重启时的位置，那么，Current 策略就可以帮你实现这个功能。\n表中第 4 行的 Specified-Offset 策略则是比较通用的策略，表示消费者把位移值调整到你指定的位移处。这个策略的典型使用场景是，消费者程序在处理某条错误消息时，你可以手动地 “跳过” 此消息的处理。在实际使用过程中，可能会出现 corrupted 消息无法被消费的情形，此时消费者程序会抛出异常，无法继续工作。一旦碰到这个问题，你就可以尝试使用 Specified-Offset 策略来规避。\n如果说 Specified-Offset 策略要求你指定位移的绝对数值的话，那么 Shift-By-N 策略指定的就是位移的相对数值，即你给出要跳过的一段消息的距离即可。这里的 “跳” 是双向的，你既可以向前“跳”，也可以向后“跳”。比如，你想把位移重设成当前位移的前 100 条位移处，此时你需要指定 N 为 -100。\n刚刚讲到的这几种策略都是位移维度的，下面我们来聊聊从时间维度重设位移的 DateTime 和 Duration 策略。\nDateTime 允许你指定一个时间，然后将位移重置到该时间之后的最早位移处。常见的使用场景是，你想重新消费昨天的数据，那么你可以使用该策略重设位移到昨天 0 点。\nDuration 策略则是指给定相对的时间间隔，然后将位移调整到距离当前给定时间间隔的位移处，具体格式是 PnDTnHnMnS。如果你熟悉 Java 8 引入的 Duration 类的话，你应该不会对这个格式感到陌生。它就是一个符合 ISO-8601 规范的 Duration 格式，以字母 P 开头，后面由 4 部分组成，即 D、H、M 和 S，分别表示天、小时、分钟和秒。举个例子，如果你想将位移调回到 15 分钟前，那么你就可以指定 PT0H15M0S。\n我会在后面分别给出这 7 种重设策略的实现方式。不过在此之前，我先来说一下重设位移的方法。目前，重设消费者组位移的方式有两种。\n通过消费者 API 来实现。\n通过 kafka-consumer-groups 命令行脚本来实现。\n消费者 API 方式设置 首先，我们来看看如何通过 API 的方式来重设位移。我主要以 Java API 为例进行演示。如果你使用的是其他语言，方法应该是类似的，不过你要参考具体的 API 文档。\n通过 Java API 的方式来重设位移，你需要调用 KafkaConsumer 的 seek 方法，或者是它的变种方法 seekToBeginning 和 seekToEnd。我们来看下它们的方法签名。\nvoid seek(TopicPartition partition, long offset); void seek(TopicPartition partition, OffsetAndMetadata offsetAndMetadata); void seekToBeginning(Collection\u0026lt;TopicPartition\u0026gt; partitions); void seekToEnd(Collection\u0026lt;TopicPartition\u0026gt; partitions); 根据方法的定义，我们可以知道，每次调用 seek 方法只能重设一个分区的位移。OffsetAndMetadata 类是一个封装了 Long 型的位移和自定义元数据的复合类，只是一般情况下，自定义元数据为空，因此你基本上可以认为这个类表征的主要是消息的位移值。seek 的变种方法 seekToBeginning 和 seekToEnd 则拥有一次重设多个分区的能力。我们在调用它们时，可以一次性传入多个主题分区。\n好了，有了这些方法，我们就可以逐一地实现上面提到的 7 种策略了。我们先来看 Earliest 策略的实现方式，代码如下：\nProperties consumerProperties = new Properties(); consumerProperties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, groupID); consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, \u0026#34;earliest\u0026#34;); consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName()); consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, brokerList); String topic = \u0026#34;test\u0026#34;; // 要重设位移的Kafka主题 try (final KafkaConsumer\u0026lt;String, String\u0026gt; consumer = new KafkaConsumer\u0026lt;\u0026gt;(consumerProperties)) { consumer.subscribe(Collections.singleton(topic)); consumer.poll(0); consumer.seekToBeginning( consumer.partitionsFor(topic).stream().map(partitionInfo -\u0026gt; new TopicPartition(topic, partitionInfo.partition())) .collect(Collectors.toList())); } 这段代码中有几个比较关键的部分，你需要注意一下。\n你要创建的消费者程序，要禁止自动提交位移。 组 ID 要设置成你要重设的消费者组的组 ID。 调用 seekToBeginning 方法时，需要一次性构造主题的所有分区对象。 最重要的是，一定要调用带长整型的 poll 方法，而不要调用consumer.poll(Duration.ofSecond(0))。 虽然社区已经不推荐使用 poll(long) 了，但短期内应该不会移除它，所以你可以放心使用。另外，为了避免重复，在后面的实例中，我只给出最关键的代码。\nLatest 策略和 Earliest 是类似的，我们只需要使用 seekToEnd 方法即可，如下面的代码所示：\nconsumer.seekToEnd( consumer.partitionsFor(topic).stream().map(partitionInfo -\u0026gt; new TopicPartition(topic, partitionInfo.partition())) .collect(Collectors.toList())); 实现 Current 策略的方法很简单，我们需要借助 KafkaConsumer 的 committed 方法来获取当前提交的最新位移，代码如下：\nconsumer.partitionsFor(topic).stream().map(info -\u0026gt; new TopicPartition(topic, info.partition())) .forEach(tp -\u0026gt; { long committedOffset = consumer.committed(tp).offset(); consumer.seek(tp, committedOffset); }); 这段代码首先调用 partitionsFor 方法获取给定主题的所有分区，然后依次获取对应分区上的已提交位移，最后通过 seek 方法重设位移到已提交位移处。\n如果要实现 Specified-Offset 策略，直接调用 seek 方法即可，如下所示：\nlong targetOffset = 1234L; for (PartitionInfo info : consumer.partitionsFor(topic)) { TopicPartition tp = new TopicPartition(topic, info.partition()); consumer.seek(tp, targetOffset); } 这次我没有使用 Java 8 Streams 的写法，如果你不熟悉 Lambda 表达式以及 Java 8 的 Streams，这种写法可能更加符合你的习惯。\n接下来我们来实现 Shift-By-N 策略，主体代码逻辑如下：\nfor (PartitionInfo info : consumer.partitionsFor(topic)) { TopicPartition tp = new TopicPartition(topic, info.partition()); // 假设向前跳123条消息 long targetOffset = consumer.committed(tp).offset() + 123L; consumer.seek(tp, targetOffset); } 如果要实现 DateTime 策略，我们需要借助另一个方法：KafkaConsumer. offsetsForTimes 方法。假设我们要重设位移到 2019 年 6 月 20 日晚上 8 点，那么具体代码如下：\nlong ts = LocalDateTime.of( 2019, 6, 20, 20, 0).toInstant(ZoneOffset.ofHours(8)).toEpochMilli(); Map\u0026lt;TopicPartition, Long\u0026gt; timeToSearch = consumer.partitionsFor(topic).stream().map(info -\u0026gt; new TopicPartition(topic, info.partition())) .collect(Collectors.toMap(Function.identity(), tp -\u0026gt; ts)); for (Map.Entry\u0026lt;TopicPartition, OffsetAndTimestamp\u0026gt; entry : consumer.offsetsForTimes(timeToSearch).entrySet()) { consumer.seek(entry.getKey(), entry.getValue().offset()); } 这段代码构造了 LocalDateTime 实例，然后利用它去查找对应的位移值，最后调用 seek，实现了重设位移。\n最后，我来给出实现 Duration 策略的代码。假设我们要将位移调回 30 分钟前，那么代码如下：\nMap\u0026lt;TopicPartition, Long\u0026gt; timeToSearch = consumer.partitionsFor(topic).stream() .map(info -\u0026gt; new TopicPartition(topic, info.partition())) .collect(Collectors.toMap(Function.identity(), tp -\u0026gt; System.currentTimeMillis() - 30 * 1000 * 60)); for (Map.Entry\u0026lt;TopicPartition, OffsetAndTimestamp\u0026gt; entry : consumer.offsetsForTimes(timeToSearch).entrySet()) { consumer.seek(entry.getKey(), entry.getValue().offset()); } 总之，使用 Java API 的方式来实现重设策略的主要入口方法，就是 seek 方法。\n命令行方式设置 位移重设还有另一个重要的途径：通过 kafka-consumer-groups 脚本。需要注意的是，这个功能是在 Kafka 0.11 版本中新引入的。这就是说，如果你使用的 Kafka 是 0.11 版本之前的，那么你只能使用 API 的方式来重设位移。\n比起 API 的方式，用命令行重设位移要简单得多。针对我们刚刚讲过的 7 种策略，有 7 个对应的参数。下面我来一一给出实例。\nEarliest 策略直接指定 \u0026ndash;to-earliest。\nbin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --all-topics --to-earliest –execute Latest 策略直接指定 \u0026ndash;to-latest。\nbin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --all-topics --to-latest --execute Current 策略直接指定 \u0026ndash;to-current。\nbin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --all-topics --to-current --execute Specified-Offset 策略直接指定 \u0026ndash;to-offset。\nbin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --all-topics --to-offset \u0026lt;offset\u0026gt; --execute Shift-By-N 策略直接指定 \u0026ndash;shift-by N。\nbin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --shift-by \u0026lt;offset_N\u0026gt; --execute DateTime 策略直接指定 \u0026ndash;to-datetime。\nbin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --to-datetime 2019-06-20T20:00:00.000 --execute 最后是实现 Duration 策略，我们直接指定 \u0026ndash;by-duration。\nbin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port --group test-group --reset-offsets --by-duration PT0H30M0S --execute 小结 至此，重设消费者组位移的 2 种方式我都讲完了。我们来小结一下。今天，我们主要讨论了在 Kafka 中，为什么要重设位移以及如何重设消费者组位移。重设位移主要是为了实现消息的重演。目前 Kafka 支持 7 种重设策略和 2 种重设方法。在实际使用过程中，我推荐你使用第 2 种方法，即用命令行的方式来重设位移。毕竟，执行命令要比写程序容易得多。但是需要注意的是，0.11 及 0.11 版本之后的 Kafka 才提供了用命令行调整位移的方法。如果你使用的是之前的版本，那么就只能依靠 API 的方式了。\n","permalink":"https://leochu.work/blog/tech/bigdata/%E5%A6%82%E4%BD%95%E9%87%8D%E8%AE%BE%E6%B6%88%E8%B4%B9%E8%80%85%E7%BB%84%E4%BD%8D%E7%A7%BB/","summary":"\u003ch2 id=\"为什么要重设消费者组位移\"\u003e为什么要重设消费者组位移？\u003c/h2\u003e\n\u003chr\u003e\n\u003cp\u003e我们知道，Kafka 和传统的消息引擎在设计上是有很大区别的，其中一个比较显著的区别就是，Kafka 的消费者读取消息是可以重演的（replayable）。\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e像 RabbitMQ 或 ActiveMQ 这样的传统消息中间件，它们处理和响应消息的方式是破坏性的（destructive），即一旦消息被成功处理，就会被从 Broker 上删除。\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e反观 Kafka，由于它是基于日志结构（log-based）的消息引擎，消费者在消费消息时，仅仅是从磁盘文件上读取数据而已，是只读的操作，因此消费者不会删除消息数据。同时，由于位移数据是由消费者控制的，因此它能够很容易地修改位移的值，实现重复消费历史数据的功能。\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e对了，之前有很多同学在专栏的留言区提问：\u003cem\u003e在实际使用场景中，我该如何确定是使用传统的消息中间件，还是使用 Kafka 呢？我在这里统一回答一下。如果在你的场景中，消息处理逻辑非常复杂，处理代价很高，同时你又不关心消息之间的顺序，那么传统的消息中间件是比较合适的；反之，如果你的场景需要较高的吞吐量，但每条消息的处理时间很短，同时你又很在意消息的顺序，此时，Kafka 就是你的首选。\u003c/em\u003e\u003c/p\u003e\n\u003ch2 id=\"重设位移策略\"\u003e重设位移策略\u003c/h2\u003e\n\u003chr\u003e\n\u003cp\u003e不论是哪种设置方式，重设位移大致可以从两个维度来进行。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e位移维度。这是指根据位移值来重设。也就是说，直接把消费者的位移值重设成我们给定的位移值。\u003c/li\u003e\n\u003cli\u003e时间维度。我们可以给定一个时间，让消费者把位移调整成大于该时间的最小位移；也可以给出一段时间间隔，比如 30 分钟前，然后让消费者直接将位移调回 30 分钟之前的位移值。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e下面的这张表格罗列了 7 种重设策略。接下来，我来详细解释下这些策略。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230328140507.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230328140507.png\"\u003e\nEarliest 策略表示将位移调整到主题当前最早位移处。这个最早位移不一定就是 0，因为在生产环境中，很久远的消息会被 Kafka 自动删除，所以当前最早位移很可能是一个大于 0 的值。\u003cstrong\u003e如果你想要重新消费主题的所有消息，那么可以使用 Earliest 策略\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003eLatest 策略表示把位移重设成最新末端位移。如果你总共向某个主题发送了 15 条消息，那么最新末端位移就是 15。\u003cstrong\u003e如果你想跳过所有历史消息，打算从最新的消息处开始消费的话，可以使用 Latest 策略\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eCurrent 策略表示将位移调整成消费者当前提交的最新位移\u003c/em\u003e。有时候你可能会碰到这样的场景：你修改了消费者程序代码，并重启了消费者，结果发现代码有问题，你需要回滚之前的代码变更，同时也要把位移重设到消费者重启时的位置，那么，Current 策略就可以帮你实现这个功能。\u003c/p\u003e\n\u003cp\u003e表中第 4 行的 Specified-Offset 策略则是比较通用的策略，表示消费者把位移值调整到你指定的位移处。\u003cstrong\u003e这个策略的典型使用场景是，消费者程序在处理某条错误消息时，你可以手动地 “跳过” 此消息的处理\u003c/strong\u003e。在实际使用过程中，可能会出现 corrupted 消息无法被消费的情形，此时消费者程序会抛出异常，无法继续工作。一旦碰到这个问题，你就可以尝试使用 Specified-Offset 策略来规避。\u003c/p\u003e\n\u003cp\u003e如果说 Specified-Offset 策略要求你指定位移的\u003cstrong\u003e绝对数值\u003c/strong\u003e的话，那么 Shift-By-N 策略指定的就是位移的\u003cstrong\u003e相对数值\u003c/strong\u003e，即你给出要跳过的一段消息的距离即可。这里的 “跳” 是双向的，你既可以向前“跳”，也可以向后“跳”。比如，你想把位移重设成当前位移的前 100 条位移处，此时你需要指定 N 为 -100。\u003c/p\u003e","title":"如何重设消费者组位移"},{"content":"调优目标 在做调优之前，我们必须明确优化 Kafka 的目标是什么。通常来说，调优是为了满足系统常见的非功能性需求。在众多的非功能性需求中，性能绝对是我们最关心的那一个。不同的系统对性能有不同的诉求，比如对于数据库用户而言，性能意味着请求的响应时间，用户总是希望查询或更新请求能够被更快地处理完并返回。\n对 Kafka 而言，性能一般是指吞吐量和延时。\n吞吐量，也就是 TPS，是指 Broker 端进程或 Client 端应用程序每秒能处理的字节数或消息数，这个值自然是越大越好。\n延时和我们刚才说的响应时间类似，它表示从 Producer 端发送消息到 Broker 端持久化完成之间的时间间隔。这个指标也可以代表端到端的延时（End-to-End，E2E），也就是从 Producer 发送消息到 Consumer 成功消费该消息的总时长。和 TPS 相反，我们通常希望延时越短越好。\n总之，高吞吐量、低延时是我们调优 Kafka 集群的主要目标，一会儿我们会详细讨论如何达成这些目标。在此之前，我想先谈一谈优化漏斗的问题。\n优化漏斗 优化漏斗是一个调优过程中的分层漏斗，我们可以在每一层上执行相应的优化调整。总体来说，层级越靠上，其调优的效果越明显，整体优化效果是自上而下衰减的，如下图所示：\n第 1 层：应用程序层。它是指优化 Kafka 客户端应用程序代码。比如，使用合理的数据结构、缓存计算开销大的运算结果，抑或是复用构造成本高的对象实例等。这一层的优化效果最为明显，通常也是比较简单的。\n第 2 层：框架层。它指的是合理设置 Kafka 集群的各种参数。毕竟，直接修改 Kafka 源码进行调优并不容易，但根据实际场景恰当地配置关键参数的值，还是很容易实现的。\n第 3 层：JVM 层。Kafka Broker 进程是普通的 JVM 进程，各种对 JVM 的优化在这里也是适用的。优化这一层的效果虽然比不上前两层，但有时也能带来巨大的改善效果。\n第 4 层：操作系统层。对操作系统层的优化很重要，但效果往往不如想象得那么好。与应用程序层的优化效果相比，它是有很大差距的。\n基础性调优 接下来，我就来分别介绍一下优化漏斗的 4 个分层的调优。\n操作系统调优 我先来说说操作系统层的调优。在操作系统层面，你最好在挂载（Mount）文件系统时禁掉 atime 更新。atime 的全称是 access time，记录的是文件最后被访问的时间。记录 atime 需要操作系统访问 inode 资源，而禁掉 atime 可以避免 inode 访问时间的写入操作，减少文件系统的写操作数。你可以执行 mount -o noatime 命令进行设置。\n至于文件系统，我建议你至少选择 ext4 或 XFS。尤其是 XFS 文件系统，它具有高性能、高伸缩性等特点，特别适用于生产服务器。值得一提的是，在去年 10 月份的 Kafka 旧金山峰会上，有人分享了 ZFS 搭配 Kafka 的案例，我们在专栏第 8 讲提到过与之相关的数据报告。该报告宣称 ZFS 多级缓存的机制能够帮助 Kafka 改善 I/O 性能，据说取得了不错的效果。如果你的环境中安装了 ZFS 文件系统，你可以尝试将 Kafka 搭建在 ZFS 文件系统上。\n另外就是 swap 空间的设置。我个人建议将 swappiness 设置成一个很小的值，比如 1～10 之间，以防止 Linux 的 OOM Killer 开启随意杀掉进程。你可以执行 sudo sysctl vm.swappiness=N 来临时设置该值，如果要永久生效，可以修改 /etc/sysctl.conf 文件，增加 vm.swappiness=N，然后重启机器即可。\n操作系统层面还有两个参数也很重要，它们分别是 ulimit -n 和 vm.max_map_count。前者如果设置得太小，你会碰到 Too Many File Open 这类的错误，而后者的值如果太小，在一个主题数超多的 Broker 机器上，你会碰到 OutOfMemoryError：Map failed 的严重错误，因此，我建议在生产环境中适当调大此值，比如将其设置为 655360。具体设置方法是修改 /etc/sysctl.conf 文件，增加 vm.max_map_count=655360，保存之后，执行 sysctl -p 命令使它生效。\n最后，不得不提的就是操作系统页缓存大小了，这对 Kafka 而言至关重要。在某种程度上，我们可以这样说：给 Kafka 预留的页缓存越大越好，最小值至少要容纳一个日志段的大小，也就是 Broker 端参数 log.segment.bytes 的值。该参数的默认值是 1GB。预留出一个日志段大小，至少能保证 Kafka 可以将整个日志段全部放入页缓存，这样，消费者程序在消费时能直接命中页缓存，从而避免昂贵的物理磁盘 I/O 操作。\nJVM 层调优 说完了操作系统层面的调优，我们来讨论下 JVM 层的调优，其实，JVM 层的调优，我们还是要重点关注堆设置以及 GC 方面的性能。\n设置堆大小。 如何为 Broker 设置堆大小，这是很多人都感到困惑的问题。我来给出一个朴素的答案：将你的 JVM 堆大小设置成 6～8GB。\n在很多公司的实际环境中，这个大小已经被证明是非常合适的，你可以安心使用。如果你想精确调整的话，我建议你可以查看 GC log，特别是关注 Full GC 之后堆上存活对象的总大小，然后把堆大小设置为该值的 1.5～2 倍。如果你发现 Full GC 没有被执行过，手动运行 jmap -histo:live \u0026lt;pid\u0026gt; 就能人为触发 Full GC。\n2.GC 收集器的选择。\n我强烈建议你使用 G1 收集器，主要原因是方便省事，至少比 CMS 收集器的优化难度小得多。另外，你一定要尽力避免 Full GC 的出现。其实，不论使用哪种收集器，都要竭力避免 Full GC。在 G1 中，Full GC 是单线程运行的，它真的非常慢。如果你的 Kafka 环境中经常出现 Full GC，你可以配置 JVM 参数 -XX:+PrintAdaptiveSizePolicy，来探查一下到底是谁导致的 Full GC。\n使用 G1 还很容易碰到的一个问题，就是大对象（Large Object），反映在 GC 上的错误，就是 “too many humongous allocations”。所谓的大对象，一般是指至少占用半个区域（Region）大小的对象。举个例子，如果你的区域尺寸是 2MB，那么超过 1MB 大小的对象就被视为是大对象。要解决这个问题，除了增加堆大小之外，你还可以适当地增加区域大小，设置方法是增加 JVM 启动参数 -XX:+G1HeapRegionSize=N。默认情况下，如果一个对象超过了 N/2，就会被视为大对象，从而直接被分配在大对象区。如果你的 Kafka 环境中的消息体都特别大，就很容易出现这种大对象分配的问题。\nBroker 端调优 我们继续沿着漏斗往上走，来看看 Broker 端的调优。\nBroker 端调优很重要的一个方面，就是合理地设置 Broker 端参数值，以匹配你的生产环境。不过，后面我们在讨论具体的调优目标时再详细说这部分内容。这里我想先讨论另一个优化手段，即尽力保持客户端版本和 Broker 端版本一致。不要小看版本间的不一致问题，它会令 Kafka 丧失很多性能收益，比如 Zero Copy。下面我用一张图来说明一下。\n图中蓝色的 Producer、Consumer 和 Broker 的版本是相同的，它们之间的通信可以享受 Zero Copy 的快速通道；相反，一个低版本的 Consumer 程序想要与 Producer、Broker 交互的话，就只能依靠 JVM 堆中转一下，丢掉了快捷通道，就只能走慢速通道了。因此，在优化 Broker 这一层时，你只要保持服务器端和客户端版本的一致，就能获得很多性能收益了。\n应用层调优 现在，我们终于来到了漏斗的最顶层。其实，这一层的优化方法各异，毕竟每个应用程序都是不一样的。不过，有一些公共的法则依然是值得我们遵守的。\n不要频繁地创建 Producer 和 Consumer 对象实例。构造这些对象的开销很大，尽量复用它们。\n用完及时关闭。这些对象底层会创建很多物理资源，如 Socket 连接、ByteBuffer 缓冲区等。不及时关闭的话，势必造成资源泄露。\n合理利用多线程来改善性能。Kafka 的 Java Producer 是线程安全的，你可以放心地在多个线程中共享同一个实例；而 Java Consumer 虽不是线程安全的，但我们在专栏第 20 讲讨论过多线程的方案，你可以回去复习一下。\n性能指标调优 接下来，我会给出调优各个目标的参数配置以及具体的配置原因，希望它们能够帮助你更有针对性地调整你的 Kafka 集群。\n调优吞吐量 首先是调优吞吐量。很多人对吞吐量和延时之间的关系似乎有些误解。比如有这样一种提法还挺流行的：假设 Kafka 每发送一条消息需要花费 2ms，那么延时就是 2ms。显然，吞吐量就应该是 500 条 / 秒，因为 1 秒可以发送 1 / 0.002 = 500 条消息。因此，吞吐量和延时的关系可以用公式来表示：TPS = 1000 / Latency(ms)。但实际上，吞吐量和延时的关系远不是这么简单。\n我们以 Kafka Producer 为例。假设它以 2ms 的延时来发送消息，如果每次只是发送一条消息，那么 TPS 自然就是 500 条 / 秒。但如果 Producer 不是每次发送一条消息，而是在发送前等待一段时间，然后统一发送一批消息，比如 Producer 每次发送前先等待 8ms，8ms 之后，Producer 共缓存了 1000 条消息，此时总延时就累加到 10ms（即 2ms + 8ms）了，而 TPS 等于 1000 / 0.01 = 100,000 条 / 秒。由此可见，虽然延时增加了 4 倍，但 TPS 却增加了将近 200 倍。这其实也是批次化（batching）或微批次化（micro-batching）目前会很流行的原因。\n在实际环境中，用户似乎总是愿意用较小的延时增加的代价，去换取 TPS 的显著提升。毕竟，从 2ms 到 10ms 的延时增加通常是可以忍受的。事实上，Kafka Producer 就是采取了这样的设计思想。\n当然，你可能会问：发送一条消息需要 2ms，那么等待 8ms 就能累积 1000 条消息吗？答案是可以的！Producer 累积消息时，一般仅仅是将消息发送到内存中的缓冲区，而发送消息却需要涉及网络 I/O 传输。内存操作和 I/O 操作的时间量级是不同的，前者通常是几百纳秒级别，而后者则是从毫秒到秒级别不等，因此，Producer 等待 8ms 积攒出的消息数，可能远远多于同等时间内 Producer 能够发送的消息数。\n好了，说了这么多，我们该怎么调优 TPS 呢？我来跟你分享一个参数列表。\n我稍微解释一下表格中的内容。\nBroker 端参数 num.replica.fetchers 表示的是 Follower 副本用多少个线程来拉取消息，默认使用 1 个线程。如果你的 Broker 端 CPU 资源很充足，不妨适当调大该参数值，加快 Follower 副本的同步速度。因为在实际生产环境中，配置了 acks=all 的 Producer 程序吞吐量被拖累的首要因素，就是副本同步性能。增加这个值后，你通常可以看到 Producer 端程序的吞吐量增加。\n另外需要注意的，就是避免经常性的 Full GC。目前不论是 CMS 收集器还是 G1 收集器，其 Full GC 采用的是 Stop The World 的单线程收集策略，非常慢，因此一定要避免。\n在 Producer 端，如果要改善吞吐量，通常的标配是增加消息批次的大小以及批次缓存时间，即 batch.size 和 linger.ms。目前它们的默认值都偏小，特别是默认的 16KB 的消息批次大小一般都不适用于生产环境。假设你的消息体大小是 1KB，默认一个消息批次也就大约 16 条消息，显然太小了。我们还是希望 Producer 能一次性发送更多的消息。\n除了这两个，你最好把压缩算法也配置上，以减少网络 I/O 传输量，从而间接提升吞吐量。当前，和 Kafka 适配最好的两个压缩算法是 LZ4 和 zstd，不妨一试。\n同时，由于我们的优化目标是吞吐量，最好不要设置 acks=all 以及开启重试。前者引入的副本同步时间通常都是吞吐量的瓶颈，而后者在执行过程中也会拉低 Producer 应用的吞吐量。\n最后，如果你在多个线程中共享一个 Producer 实例，就可能会碰到缓冲区不够用的情形。倘若频繁地遭遇 TimeoutException：Failed to allocate memory within the configured max blocking time 这样的异常，那么你就必须显式地增加 buffer.memory 参数值，确保缓冲区总是有空间可以申请的。\n说完了 Producer 端，我们来说说 Consumer 端。Consumer 端提升吞吐量的手段是有限的，你可以利用多线程方案增加整体吞吐量，也可以增加 fetch.min.bytes 参数值。默认是 1 字节，表示只要 Kafka Broker 端积攒了 1 字节的数据，就可以返回给 Consumer 端，这实在是太小了。我们还是让 Broker 端一次性多返回点数据吧。\n调优延时 讲完了调优吞吐量，我们来说说如何优化延时，下面是调优延时的参数列表。\n在 Broker 端，我们依然要增加 num.replica.fetchers 值以加快 Follower 副本的拉取速度，减少整个消息处理的延时。\n在 Producer 端，我们希望消息尽快地被发送出去，因此不要有过多停留，所以必须设置 linger.ms=0，同时不要启用压缩。因为压缩操作本身要消耗 CPU 时间，会增加消息发送的延时。另外，最好不要设置 acks=all。我们刚刚在前面说过，Follower 副本同步往往是降低 Producer 端吞吐量和增加延时的首要原因。\n在 Consumer 端，我们保持 fetch.min.bytes=1 即可，也就是说，只要 Broker 端有能返回的数据，立即令其返回给 Consumer，缩短 Consumer 消费延时。\n小结 好了，我们来小结一下。今天，我跟你分享了 Kafka 调优方面的内容。我们先从调优目标开始说起，然后我给出了调优层次漏斗，接着我分享了一些基础性调优，包括操作系统层调优、JVM 层调优以及应用程序调优等。最后，针对 Kafka 关心的两个性能指标吞吐量和延时，我分别从 Broker、Producer 和 Consumer 三个维度给出了一些参数值设置的最佳实践。\n最后，我来分享一个性能调优的真实小案例。\n曾经，我碰到过一个线上环境的问题：该集群上 Consumer 程序一直表现良好，但是某一天，它的性能突然下降，表现为吞吐量显著降低。我在查看磁盘读 I/O 使用率时，发现其明显上升，但之前该 Consumer Lag 很低，消息读取应该都能直接命中页缓存。此时磁盘读突然飙升，我就怀疑有其他程序写入了页缓存。后来经过排查，我发现果然有一个测试 Console Consumer 程序启动，“污染” 了部分页缓存，导致主业务 Consumer 读取消息不得不走物理磁盘，因此吞吐量下降。找到了真实原因，解决起来就简单多了。\n其实，我给出这个案例的真实目的是想说，对于性能调优，我们最好按照今天给出的步骤一步一步地窄化和定位问题。一旦定位了原因，后面的优化就水到渠成了。\n","permalink":"https://leochu.work/blog/tech/bigdata/kafka%E8%B0%83%E4%BC%98%E6%8C%87%E5%8D%97/","summary":"\u003ch2 id=\"调优目标\"\u003e调优目标\u003c/h2\u003e\n\u003chr\u003e\n\u003cp\u003e在做调优之前，我们必须明确优化 Kafka 的目标是什么。通常来说，调优是为了满足系统常见的非功能性需求。在众多的非功能性需求中，性能绝对是我们最关心的那一个。不同的系统对性能有不同的诉求，比如对于数据库用户而言，性能意味着请求的响应时间，用户总是希望查询或更新请求能够被更快地处理完并返回。\u003c/p\u003e\n\u003cp\u003e对 Kafka 而言，性能一般是指吞吐量和延时。\u003c/p\u003e\n\u003cp\u003e吞吐量，也就是 TPS，是指 Broker 端进程或 Client 端应用程序每秒能处理的字节数或消息数，这个值自然是越大越好。\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e延时和我们刚才说的响应时间类似，它表示从 Producer 端发送消息到 Broker 端持久化完成之间的时间间隔。这个指标也可以代表端到端的延时（End-to-End，E2E），也就是从 Producer 发送消息到 Consumer 成功消费该消息的总时长\u003c/em\u003e。和 TPS 相反，我们通常希望延时越短越好。\u003c/p\u003e\n\u003cp\u003e总之，高吞吐量、低延时是我们调优 Kafka 集群的主要目标，一会儿我们会详细讨论如何达成这些目标。在此之前，我想先谈一谈优化漏斗的问题。\u003c/p\u003e\n\u003ch2 id=\"优化漏斗\"\u003e优化漏斗\u003c/h2\u003e\n\u003chr\u003e\n\u003cp\u003e优化漏斗是一个调优过程中的分层漏斗，我们可以在每一层上执行相应的优化调整。总体来说，层级越靠上，其调优的效果越明显，整体优化效果是自上而下衰减的，如下图所示：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20230328142440.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020230328142440.png\"\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e第 1 层：应用程序层。它是指优化 Kafka 客户端应用程序代码。比如，使用合理的数据结构、缓存计算开销大的运算结果，抑或是复用构造成本高的对象实例等。这一层的优化效果最为明显，通常也是比较简单的。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e第 2 层：框架层。它指的是合理设置 Kafka 集群的各种参数。毕竟，直接修改 Kafka 源码进行调优并不容易，但根据实际场景恰当地配置关键参数的值，还是很容易实现的。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e第 3 层：JVM 层。Kafka Broker 进程是普通的 JVM 进程，各种对 JVM 的优化在这里也是适用的。优化这一层的效果虽然比不上前两层，但有时也能带来巨大的改善效果。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e第 4 层：操作系统层。对操作系统层的优化很重要，但效果往往不如想象得那么好。与应用程序层的优化效果相比，它是有很大差距的。\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"基础性调优\"\u003e基础性调优\u003c/h2\u003e\n\u003chr\u003e\n\u003cp\u003e接下来，我就来分别介绍一下优化漏斗的 4 个分层的调优。\u003c/p\u003e\n\u003ch2 id=\"操作系统调优\"\u003e操作系统调优\u003c/h2\u003e\n\u003cp\u003e我先来说说操作系统层的调优。在操作系统层面，你最好在挂载（Mount）文件系统时禁掉 atime 更新。atime 的全称是 access time，记录的是文件最后被访问的时间。记录 atime 需要操作系统访问 inode 资源，而禁掉 atime 可以避免 inode 访问时间的写入操作，减少文件系统的写操作数。你可以执行 \u003ccode\u003emount -o noatime\u003c/code\u003e 命令进行设置。\u003c/p\u003e","title":"如何调优 Kafka"},{"content":"问题描述 原因排查 HDP 的 hive 使用的版本较高为3.1.0 ，默认建表都是使用 ACID 的事务表。而 HDP的spark版本较低为2.3 目前还不支持 hive 的 ACID 功能，因此无法读取 ACID 表的数据，准确来说是内表的数据。\n官方资料 spark issues 地址： SPARK-15348 Hive ACID\n解决方案 修改hive以下配置，重启hive\nhive.strict.managed.tables =false\nhive.create.as.insert.only =false\nmetastore.create.as.acid =false\n其他资料 Hive 中支持的表类型和 ACID 特性 表类型 ACID 文件格式 插入 更新 / 删除 托管表：CRUD 事务 是 ORC 是 是 托管表：仅插入式事务 是 任意格式 是 没有 托管表：临时 没有 任意格式 是 没有 外部表 没有 任意格式 是 没有 查看表的属性，指令：desc formatted tb_name\n非 ACID 表：\n| Table Type: | MANAGED_TABLE | NULL | | Table Parameters: | NULL | NULL | | | COLUMN_STATS_ACCURATE | {\u0026quot;BASIC_STATS\u0026quot;:\u0026quot;true\u0026quot;} | | | numFiles | 36554 | | | numPartitions | 4582 | | | numRows | 656353471 | | | rawDataSize | 3687190343914 | | | totalSize | 191558565757 | | | transient_lastDdlTime | 1626229162 |\nACID 表：\n| Table Type: | MANAGED_TABLE | NULL | | Table Parameters: | NULL | NULL | | | bucketing_version | 2 | | | comment | 交易日志表 | | | numFiles | 2131 | | | numPartitions | 2131 | | | numRows | 1303371497 | | | rawDataSize | 0 | | | totalSize | 669842162666 | | | transactional | true | | | transactional_properties | insert_only | | | transient_lastDdlTime | 1613705157 |\n其中：\ntransactional=true，标识为事务表 transactional_properties=insert_only 事务表的适用场景 对于数仓中的行级数据更新删除需求比较频繁的，可以考虑使用事务表。\n但平常的 hive 表并不建议使用事务表。因为事务表的限制很多，加上由于 hive 表的特性，也很难满足高并发的场景。\n另外，如果事务表太多，并且存在大量的更新操作，metastore 后台启动的合并线程会定期的提交 MapReduce Job，也会一定程度上增重集群的负担。\n结论： 除非有非常迫切的行级更新需求，又只能用 hive 表来做，才需要去考虑事务表。\n注意事项\n不支持 BEGIN、COMMIT、ROLLBACK 等语句，所有的语句都是自动提交 仅支持 ORC 格式 设计原理与实现 文件管理格式 HDFS 本身是不支持直接修改文件的, 也不能保证有人追加内容时的读一致性。\n因此，为了支持 ACID 的特性，Hive 只能使用其他数据仓库常用的方法，也就是增量的形式记录更新和删除（也称做读时更新）。\n存储在事务表中的数据会被分成两种类型的文件：\nbase 文件，用来存放平常的数据\ndelta 文件，用来存储新增、更新、删除的数据。每一个事务处理数据的结果都会单独新建一个 delta 文件夹用来存储数据。\n在有用户要读取这个表的数据时，就会将 base 文件和 delta 文件都读取到内存，然后进行合并（就是判断哪些记录有被修改，哪些记录被删除等）。\nACID 表的文件结构：${Hive_path}/db/table_name/base_{id}/file。\nFound 2 items | | -rw-rw\u0026mdash;-+ 3 hdfs hadoop 1091048351 2021-07-13 15:00 /warehouse/tablespace/managed/hive/db_name.db/tb_name/date=2021-06-30/base_0002252/000000_0 | -rw-rw\u0026mdash;-+ 3 hdfs hadoop 1091048351 2021-07-13 15:00 /warehouse/tablespace/managed/hive/db_name.db/tb_name/date=2021-06-30/delta_0000023_0000023_0000/000000_0\n其中0000023标识事务 ID，0000 表示序号，从上面的表中我们可以看到有一个事务的数据还未合并成 base。\n非 ACID 表，通过别的表格导入数据，文件目录为：\n+\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-+ | DFS Output | +\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-+ | 2.6 M 7.9 M /warehouse/tablespace/managed/hive/db_name.db/tb_name/day=2021-07-18/-ext-10000/000000_0 | +\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-+\n普通内部表文件目录：\n+\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-+ | DFS Output | +\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-+ | 7.4 M 22.1 M /warehouse/tablespace/managed/hive/db_name.db/tb_name/day=2021-07-18/000000_0 | +\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-+\n","permalink":"https://leochu.work/blog/tech/bigdata/%E5%8F%96%E6%B6%88hdp-hive%E9%BB%98%E8%AE%A4%E5%BC%80%E5%90%AFacid%E9%85%8D%E7%BD%AE/","summary":"\u003ch3 id=\"问题描述\"\u003e问题描述\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20231220150234.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020231220150234.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"原因排查\"\u003e原因排查\u003c/h3\u003e\n\u003cp\u003eHDP 的 hive 使用的版本较高为3.1.0  ，默认建表都是使用 ACID 的事务表。而 HDP的spark版本较低为2.3 目前还不支持 hive 的 ACID 功能，因此无法读取 ACID 表的数据，准确来说是内表的数据。\u003c/p\u003e\n\u003ch3 id=\"官方资料\"\u003e官方资料\u003c/h3\u003e\n\u003cp\u003espark issues 地址： \u003ca href=\"https://issues.apache.org/jira/browse/SPARK-15348\"\u003eSPARK-15348 Hive ACID\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"解决方案\"\u003e解决方案\u003c/h3\u003e\n\u003cp\u003e修改hive以下配置，重启hive\u003c/p\u003e\n\u003cp\u003ehive.strict.managed.tables =false\u003cbr\u003e\nhive.create.as.insert.only =false\u003cbr\u003e\nmetastore.create.as.acid =false\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20231220150309.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020231220150309.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20231220150319.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020231220150319.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Pasted image 20231220150325.png\" loading=\"lazy\" src=\"/blog/resource/Pasted%20image%2020231220150325.png\"\u003e\u003c/p\u003e\n\u003ch3 id=\"其他资料\"\u003e其他资料\u003c/h3\u003e\n\u003ch4 id=\"hive-中支持的表类型和-acid-特性\"\u003eHive 中支持的表类型和 ACID 特性\u003c/h4\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e表类型\u003c/td\u003e\n          \u003ctd\u003eACID\u003c/td\u003e\n          \u003ctd\u003e文件格式\u003c/td\u003e\n          \u003ctd\u003e插入\u003c/td\u003e\n          \u003ctd\u003e更新 / 删除\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e托管表：CRUD 事务\u003c/td\u003e\n          \u003ctd\u003e是\u003c/td\u003e\n          \u003ctd\u003eORC\u003c/td\u003e\n          \u003ctd\u003e是\u003c/td\u003e\n          \u003ctd\u003e是\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e托管表：仅插入式事务\u003c/td\u003e\n          \u003ctd\u003e是\u003c/td\u003e\n          \u003ctd\u003e任意格式\u003c/td\u003e\n          \u003ctd\u003e是\u003c/td\u003e\n          \u003ctd\u003e没有\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e托管表：临时\u003c/td\u003e\n          \u003ctd\u003e没有\u003c/td\u003e\n          \u003ctd\u003e任意格式\u003c/td\u003e\n          \u003ctd\u003e是\u003c/td\u003e\n          \u003ctd\u003e没有\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e外部表\u003c/td\u003e\n          \u003ctd\u003e没有\u003c/td\u003e\n          \u003ctd\u003e任意格式\u003c/td\u003e\n          \u003ctd\u003e是\u003c/td\u003e\n          \u003ctd\u003e没有\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e查看表的属性，指令：\u003ccode\u003edesc formatted tb_name\u003c/code\u003e\u003c/p\u003e","title":"取消HDP hive默认开启ACID配置"},{"content":"Linux（vi/vim） 一般模式 语法 功能描述 yy 复制光标当前一行 y数字y 复制一段（从第几行到第几行） p 箭头移动到目的行粘贴 u 撤销上一步 dd 删除光标当前行 d数字d 删除光标（含）后多少行 x 删除一个字母，相当于del X 删除一个字母，相当于Backspace yw 复制一个词 dw 删除一个词 shift+^ 移动到行头 shift+$ 移动到行尾 1+shift+g 移动到页头，数字 shift+g 移动到页尾 数字N+shift+g 移动到目标行 编辑模式 按键 功能 i 当前光标前 a 当前光标后 o 当前光标行的下一行 I 光标所在行最前 A 光标所在行最后 O 当前光标行的上一行 指令模式 命令 功能 :w 保存 :q 退出 :! 强制执行 /要查找的词 n 查找下一个，N 往上查找 ? 要查找的词 n是查找上一个，shift+n是往下查找 :set nu 显示行号 :set nonu 关闭行号 压缩和解压 gzip/gunzip 压缩 （1）只能压缩文件不能压缩目录\n（2）不保留原来的文件\ngzip压缩：gzip hello.txt\ngunzip解压缩文件：gunzip hello.txt.gz\nzip/unzip 压缩 可以压缩目录且保留源文件\nzip压缩（压缩 1.txt 和2.txt，压缩后的名称为mypackage.zip）：zip hello.zip hello.txt world.txt\nunzip解压：unzip hello.zip\nunzip解压到指定目录：unzip hello.zip -d /opt\ntar 打包 tar压缩多个文件：tar -zcvf hello.txt world.txt\ntar压缩目录：tar -zcvf hello.tar.gz opt/\ntar解压到当前目录：tar -zxvf hello.tar.gz\ntar解压到指定目录：tar -zxvf hello.tar.gz -C /opt\nRPM RPM查询命令：rpm -qa |grep firefox\nRPM卸载命令：\nrpm -e xxxxxx\nrpm -e \u0026ndash;nodeps xxxxxx（不检查依赖）\nRPM安装命令：\nrpm -ivh xxxxxx.rpm\nrpm -ivh \u0026ndash;nodeps fxxxxxx.rpm（\u0026ndash;nodeps，不检测依赖进度）\n选项 功能 -i -i=install，安装 -v -v=verbose，显示详细信息 -h -h=hash，进度条 \u0026ndash;nodeps \u0026ndash;nodeps，不检测依赖进度 Shell 输入/输出重定向 命令 功能说明 command \u0026gt; file 将输出重定向到 file command \u0026lt; file 将输入重定向到 file command \u0026raquo; file 将输出以追加的方式重定向到 file n \u0026gt; file 将文件描述符为 n 的文件重定向到 file n \u0026raquo; file 将文件描述符为 n 的文件以追加的方式重定向到 file n \u0026gt;\u0026amp; m 将输出文件 m 和 n 合并 n \u0026lt;\u0026amp; m 将输入文件 m 和 n 合并 \u0026laquo; tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入 脚本编辑 快捷方式 功能说明 shift 参数左移 $@ 所有的参数 $# 参数的个数 Hadoop 启动类命令 功能说明 命令脚本 启动hdfs集群 sbin/start-dfs.sh 启动yarn sbin/start-yarn.sh hadoop fs/hdfs dfs 命令 功能说明 命令 创建目录 hdfs dfs -mkdir -p /data/flink 显示目录 hdfs dfs -ls / 从HDFS拷贝到本地 hdfs dfs -copyToLocal /data/data.txt ./ 文件上传到集群(从本地) hhdfs dfs -copyFromLocal data.txt / 文件下载 hdfs dfs -get /data/flink 删除集群的文件 hdfs dfs -rm /data/flink 删除文件夹 hdfs dfs -rm -r -skipTrash /data 从本地剪切粘贴到HDFS hdfs dfs -moveFromLocal data.txt /data/ 追加一个文件到已经存在的文件末尾hdfs dfs -appendToFile data1.txt /data/data.txt 显示文件内容 hdfs dfs -cat data.txt 修改文件所属权限 hdfs dfs -chmod 777 xxx.sh 修改文件所属用户组 hdfs dfs -chown root:root data.txt 从HDFS的一个路径拷贝到HDFS的另一个路径 hdfs dfs -cp data.txt /data1.txt 在HDFS目录中移动文件 hdfs dfs -mv data.txt /opt/ 合并下载多个文件 hdfs dfs -getmerge /data/* ./data_merge.txt hadoop fs -put 等同于copyFromLocal 显示一个文件的末尾 hdfs dfs -tail data.txt 删除文件或文件夹 hdfs dfs -rm /data/data.txt 删除空目录 hdfs dfs -rmdir /data 统计文件夹的大小信息 hdfs dfs -s -h /data 统计文件夹下的文件大小信息 hdfs dfs -h /data 设置HDFS中文件的副本数量 hdfs dfs -setrep 3 /data/data.txt yarn命令 功能说明 命令 查看正在运行的yarn任务列表 yarn application -list appID kill掉指定id的yarn任务 yarn application -kill appID 查看任务日志信息 yarn logs -applicationId appID Zookeeper 启动命令 功能说明 命令脚本 启动zookeeper服务 zkServer.sh start 查看zookeeper状态 zkServer.sh status 停止zookeeper服务 zkServer.sh stop 启动zookeeper客户端 zkCli.sh -server 127.0.0.1:2181 退出zookeeper客户端 quit 基本操作 功能说明 命令脚本 当前znode中所包含的内容 ls / 创建普通节点(前面是节点的路径，后面是值） create /bigdata/flink \u0026ldquo;flink\u0026rdquo; 获取节点的值 get /bigdata 修改节点的值 set /bigdata/flink \u0026ldquo;flinksql\u0026rdquo; 删除节点 delete /bigdata/flink 递归删除节点 rmr /bigdata 四字母命令 命令 功能说明 例子 conf zk服务配置的详细信息 echo conf | nc 127.0.0.1 2181 stat 客户端与zk连接的简要信息 参考上面 srvr zk服务的详细信息 参考上面 cons 客户端与zk连接的详细信息 参考上面 mntr zk服务目前的性能状况 参考上面 crst 重置当前的所有连接、会话 参考上面 dump 列出未经处理的会话和连接信息 参考上面 envi 列出zk的版本信息、主机名称、Java版本、服务器名称等等 参考上面 ruok 测试服务器是否正在运行，如果在运行返回imok，否则返回空 参考上面 srst 重置Zookeeper的所有统计信息 参考上面 wchs 列出watch的总数，连接数 参考上面 wchp 列出所有watch的路径及sessionID 参考上面 mntr 列出集群的关键性能数据，包括zk的版本、node数量、临时节点数等等 参考上面 Kafka 注: 这里机器我只写一个。命令你们也可使用 ./bin/xx.sh (如：./bin/kafka-topics.sh)\n查看当前服务器中的所有topic kafka-topics --zookeeper xxxxxx:2181 --list --exclude-internal 说明： exclude-internal：排除kafka内部topic 比如： --exclude-internal --topic \u0026#34;test_.*\u0026#34; 创建topic kafka-topics --zookeeper 192.168.102.2:2181,192.168.102.3:2181,192.168.102.7:2181 --create --replication-factor 2 --partitions 1 --topic ex_trade_company 说明： --topic 定义topic名 --replication-factor 定义副本数 --partitions 定义分区数 删除topic 注意: 需要server.properties中设置delete.topic.enable=true否则只是标记删除\nkafka-topics --zookeeper xxxxxx:2181 --delete --topic topic_name 生产者 kafka-console-producer --broker-list xxxxxx:9092 --topic topic_name 可加：--property parse.key=true（有key消息） 消费者 kafka-console-consumer --bootstrap-server xxxxxx:9092 --topic topic_name 注：可选 --from-beginning：会把主题中以往所有的数据都读取出来 --whitelist \u0026#39;.*\u0026#39; ：消费所有的topic --property print.key=true：显示key进行消费 --partition 0：指定分区消费 --offset：指定起始偏移量消费 查看某个Topic的详情 kafka-topics --zookeeper 192.168.102.2:2181,192.168.102.3:2181,192.168.102.7:2181 --describe --topic ex_trade 修改分区数 kafka-topics --zookeeper xxxxxx:2181 --alter --topic topic_name --partitions 6 查看某个消费者组信息 kafka-consumer-groups --bootstrap-server xxxxxx:9092 --describe --group group_name 删除消费者组 kafka-consumer-groups --bootstrap-server xxxxxx:9092 ---delete --group group_name 重置offset kafka-consumer-groups --bootstrap-server xxxxxx:9092 --group group_name --reset-offsets --all-topics --to-latest --execute leader重新选举 指定Topic指定分区用重新PREFERRED：优先副本策略 进行Leader重选举\nkafka-leader-election --bootstrap-server xxxxxx:9092 --topic topic_name --election-type PREFERRED --partition 0 所有Topic所有分区用重新PREFERRED：优先副本策略 进行Leader重选举\nkafka-leader-election --bootstrap-server xxxxxx:9092 --election-type preferred --all-topic-partitions 查询kafka版本信息 kafka-configs --bootstrap-server xxxxxx:9092 --describe --version 增删改配置 功能说明 参数 选择类型 \u0026ndash;entity-type (topics/clients/users/brokers/broker- loggers) 类型名称 \u0026ndash;entity-name 删除配置 \u0026ndash;delete-config k1=v1,k2=v2 添加/修改配置 \u0026ndash;add-config k1,k2 topic添加/修改动态配置\nkafka-configs --bootstrap-server xxxxxx:9092 --alter --entity-type topics --entity-name topic_name --add-config file.delete.delay.ms=222222,retention.ms=999999 topic删除动态配置\nkafka-configs --bootstrap-server xxxxxx:9092 --alter --entity-type topics --entity-name topic_name --delete-config file.delete.delay.ms,retention.ms 持续批量拉取消息 单次最大消费10条消息(不加参数意为持续消费)\nkafka-verifiable-consumer --bootstrap-server xxxxxx:9092 --group group_name --topic topic_name --max-messages 10 删除指定分区的消息 删除指定topic的某个分区的消息删除至offset为1024\njson文件offset-json-file.json\n{\r\u0026#34;partitions\u0026#34;: [\r{\r\u0026#34;topic\u0026#34;: \u0026#34;topic_name\u0026#34;,\r\u0026#34;partition\u0026#34;: 0,\r\u0026#34;offset\u0026#34;: 1024\r}\r],\r\u0026#34;version\u0026#34;: 1\r} kafka-delete-records --bootstrap-server xxxxxx:9092 --offset-json-file offset-json-file.json 查看Broker磁盘信息 查询指定topic磁盘信息\nkafka-log-dirs --bootstrap-server xxxxxx:9090 --describe --topic-list topic1,topic2 查询指定Broker磁盘信息\nkafka-log-dirs --bootstrap-server xxxxxx:9090 --describe --topic-list topic1 --broker-list 0 Hive 启动类 功能说明 命令 启动hiveserver2服务 bin/hiveserver2 启动beeline bin/beeline 连接hiveserver2 beeline\u0026gt; !connect jdbc:hive2://hadoop102:10000 metastroe服务 bin/hive \u0026ndash;service metastore hive 启动元数据服务（metastore和hiveserver2）和优雅关闭脚本\n启动： hive.sh start\r关闭： hive.sh stop\r重启： hive.sh restart\r状态： hive.sh status 脚本如下\n#!/bin/bash\rHIVE_LOG_DIR=$HIVE_HOME/logs\rmkdir -p $HIVE_LOG_DIR\r#检查进程是否运行正常，参数1为进程名，参数2为进程端口\rfunction check_process()\r{\rpid=$(ps -ef 2\u0026gt;/dev/null | grep -v grep | grep -i $1 | awk \u0026#39;{print $2}\u0026#39;)\rppid=$(netstat -nltp 2\u0026gt;/dev/null | grep $2 | awk \u0026#39;{print $7}\u0026#39; | cut -d \u0026#39;/\u0026#39; -f 1)\recho $pid\r\u0026#34;$pid\u0026#34; =~ \u0026#34;$ppid\u0026#34; \u0026amp;\u0026amp; [ \u0026#34;$ppid\u0026#34; ] \u0026amp;\u0026amp; return 0 || return 1\r}\rfunction hive_start()\r{\rmetapid=$(check_process HiveMetastore 9083)\rcmd=\u0026#34;nohup hive --service metastore \u0026gt;$HIVE_LOG_DIR/metastore.log 2\u0026gt;\u0026amp;1 \u0026amp;\u0026#34;\rcmd=$cmd\u0026#34; sleep4; hdfs dfsadmin -safemode wait \u0026gt;/dev/null 2\u0026gt;\u0026amp;1\u0026#34;\r[ -z \u0026#34;$metapid\u0026#34; ] \u0026amp;\u0026amp; eval $cmd || echo \u0026#34;Metastroe服务已启动\u0026#34;\rserver2pid=$(check_process HiveServer2 10000)\rcmd=\u0026#34;nohup hive --service hiveserver2 \u0026gt;$HIVE_LOG_DIR/hiveServer2.log 2\u0026gt;\u0026amp;1 \u0026amp;\u0026#34;\r[ -z \u0026#34;$server2pid\u0026#34; ] \u0026amp;\u0026amp; eval $cmd || echo \u0026#34;HiveServer2服务已启动\u0026#34;\r}\rfunction hive_stop()\r{\rmetapid=$(check_process HiveMetastore 9083)\r[ \u0026#34;$metapid\u0026#34; ] \u0026amp;\u0026amp; kill $metapid || echo \u0026#34;Metastore服务未启动\u0026#34;\rserver2pid=$(check_process HiveServer2 10000)\r[ \u0026#34;$server2pid\u0026#34; ] \u0026amp;\u0026amp; kill $server2pid || echo \u0026#34;HiveServer2服务未启动\u0026#34;\r}\rcase $1 in\r\u0026#34;start\u0026#34;)\rhive_start\r;;\r\u0026#34;stop\u0026#34;)\rhive_stop\r;;\r\u0026#34;restart\u0026#34;)\rhive_stop\rsleep 2\rhive_start\r;;\r\u0026#34;status\u0026#34;)\rcheck_process HiveMetastore 9083 \u0026gt;/dev/null \u0026amp;\u0026amp; echo \u0026#34;Metastore服务运行正常\u0026#34; || echo \u0026#34;Metastore服务运行异常\u0026#34;\rcheck_process HiveServer2 10000 \u0026gt;/dev/null \u0026amp;\u0026amp; echo \u0026#34;HiveServer2服务运行正常\u0026#34; || echo \u0026#34;HiveServer2服务运行异常\u0026#34;\r;;\r*)\recho Invalid Args!\recho \u0026#39;Usage: \u0026#39;$(basename $0)\u0026#39; start|stop|restart|status\u0026#39;\r;;\resac 常用交互命令 功能说明 命令 不进入hive的交互窗口执行sql bin/hive -e \u0026ldquo;sql语句\u0026rdquo; 执行脚本中sql语句 bin/hive -f hive.sql 退出hive窗口 exit 或 quit 命令窗口中查看hdfs文件系统 dfs -ls / 命令窗口中查看hdfs文件系统 ! ls /data/h SQL类(特殊的) 说明 语句 查看hive中的所有数据库 show databases 用default数据库 use default 查询表结构 desc table_name 查看数据库 show databases 重命名表名 alter table table1 rename to table2 修改表中字段 alter table table_name change name user_name String 修改字段类型 alter table table_name change salary salary Double 创建外部表 create external table \u0026hellip;. 查询外部表信息 desc formatted outsidetable 创建视图 create view view_name as select * from table_name \u0026hellip;.. 添加数据 load data local inpath \u0026lsquo;xxx\u0026rsquo; overwrite into table table_name partition(day=\u0026lsquo;2021-12-01\u0026rsquo;) 内置函数 （1） NVL\n给值为NULL的数据赋值，它的格式是NVL( value，default_value)。它的功能是如果value为NULL，则NVL函数返回default_value的值，否则返回value的值，如果两个参数都为NULL ，则返回NULL\nselect nvl(column, 0) from xxx； （2）行转列\n函数 描述 CONCAT(string A/col, string B/col…) 返回输入字符串连接后的结果，支持任意个输入字符串 CONCAT_WS(separator, str1, str2,\u0026hellip;) 第一个参数参数间的分隔符，如果分隔符是 NULL，返回值也将为 NULL。这个函数会跳过分隔符参数后的任何 NULL 和空字符串。分隔符将被加到被连接的字符串之间。 COLLECT_SET(col) 将某字段的值进行去重汇总，产生array类型字段 COLLECT_LIST(col) 函数只接受基本数据类型，它的主要作用是将某字段的值进行不去重汇总，产生array类型字段。 （3）列转行(一列转多行)\nSplit(str, separator)： 将字符串按照后面的分隔符切割，转换成字符array。\nEXPLODE(col)： 将hive一列中复杂的array或者map结构拆分成多行。\nLATERAL VIEW\n用法：\rLATERAL VIEW udtf(expression) tableAlias AS columnAlias 解释：lateral view用于和split, explode等UDTF一起使用，它能够将一行数据拆成多行数据，在此基础上可以对拆分后的数据进行聚合。\nlateral view首先为原始表的每行调用UDTF，UDTF会把一行拆分成一或者多行，lateral view再把结果组合，产生一个支持别名表的虚拟表。\n准备数据源测试\nmovie category 《功勋》 记录,剧情 《战狼2》 战争,动作,灾难 SQL\nSELECT movie,category_name FROM movie_info lateral VIEW\rexplode(split(category,\u0026#34;,\u0026#34;)) movie_info_tmp AS category_name ; 测试结果\n《功勋》 记录\r《功勋》 剧情\r《战狼2》 战争\r《战狼2》 动作\r《战狼2》 灾难 窗口函数 （1）OVER()\n定分析函数工作的数据窗口大小，这个数据窗口大小可能会随着行的变而变化。\n（2）CURRENT ROW（当前行）\nn PRECEDING：往前n行数据\rn FOLLOWING：往后n行数据 （3）UNBOUNDED（无边界）\nUNBOUNDED PRECEDING 前无边界，表示从前面的起点\rUNBOUNDED FOLLOWING后无边界，表示到后面的终点 SQL案例：由起点到当前行的聚合\nselect sum(money) over(partition by user_id order by pay_time rows between UNBOUNDED PRECEDING and current row) from or_order; SQL案例：当前行和前面一行做聚合\nselect sum(money) over(partition by user_id order by pay_time rows between 1 PRECEDING and current row) from or_order; SQL案例：当前行和前面一行和后一行做聚合\nselect sum(money) over(partition by user_id order by pay_time rows between 1 PRECEDING AND 1 FOLLOWING )\rfrom or_order; SQL案例：当前行及后面所有行\nselect sum(money) over(partition by user_id order by pay_time rows between current row and UNBOUNDED FOLLOWING )\rfrom or_order; （4）LAG(col,n,default_val)\n往前第n行数据，没有的话default_val\n（5）LEAD(col,n, default_val)\n往后第n行数据，没有的话default_val\nSQL案例：查询用户购买明细以及上次的购买时间和下次购买时间\nselect user_id,,pay_time,money,\rlag(pay_time,1,\u0026#39;1970-01-01\u0026#39;) over(PARTITION by name order by pay_time) prev_time,\rlead(pay_time,1,\u0026#39;1970-01-01\u0026#39;) over(PARTITION by name order by pay_time) next_time\rfrom or_order; （6）FIRST_VALUE(col,true/false)\n当前窗口下的第一个值，第二个参数为true，跳过空值。\n（7）LAST_VALUE (col,true/false)\n当前窗口下的最后一个值，第二个参数为true，跳过空值。\nSQL案例：查询用户每个月第一次的购买时间 和 每个月的最后一次购买时间\nselect\rFIRST_VALUE(pay_time) over(\rpartition by user_id,month(pay_time) order by pay_time rows between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING\r) first_time,\rLAST_VALUE(pay_time) over(partition by user_id,month(pay_time) order by pay_time rows between UNBOUNDED PRECEDING and UNBOUNDED FOLLOWING\r) last_time\rfrom or_order; （8）NTILE(n)\n把有序窗口的行分发到指定数据的组中，各个组有编号，编号从1开始，对于每一行，NTILE返回此行所属的组的编号。（用于将分组数据按照顺序切分成n片，返回当前切片值）\nSQL案例：查询前25%时间的订单信息\nselect * from (\rselect User_id,pay_time,money,\rntile(4) over(order by pay_time) sorted\rfrom or_order\r) t\rwhere sorted = 1; 4个By （1）Order By\n全局排序，只有一个Reducer。\n（2）Sort By\n分区内有序。\n（3）Distrbute By\n类似MR中Partition，进行分区，结合sort by使用。\n（4） Cluster By\n当Distribute by和Sorts by字段相同时，可以使用Cluster by方式。Cluster by除了具有Distribute by的功能外还兼具Sort by的功能。但是排序只能是升序排序，不能指定排序规则为ASC或者DESC。\n在生产环境中Order By用的比较少，容易导致OOM。\n在生产环境中Sort By+ Distrbute By用的多。\n排序函数 （1）RANK()\n排序相同时会重复，总数不会变\n1\r1\r3\r3\r5 （2）DENSE_RANK()\n排序相同时会重复，总数会减少\n1\r1\r2\r2\r3 （3）ROW_NUMBER()\n会根据顺序计算\n1\r2\r3\r4\r5 日期函数 datediff：返回结束日期减去开始日期的天数\ndatediff(string enddate, string startdate) select datediff(\u0026#39;2021-11-20\u0026#39;,\u0026#39;2021-11-22\u0026#39;) date_add：返回开始日期startdate增加days天后的日期\ndate_add(string startdate, int days) select date_add(\u0026#39;2021-11-20\u0026#39;,3) date_sub：返回开始日期startdate减少days天后的日期\ndate_sub (string startdate, int days) select date_sub(\u0026#39;2021-11-22\u0026#39;,3) Impala(时间处理，可类比到hive可用) 1. 获取当前时间 timestamp 样式2021-12-10 04:36:48.147046000\nbigint 样式 1639110956\n函数/方法 输出类型 说明/样式 current_timestamp() timestamp 所在时区的当前时间 now() timestamp 所在时区的当前时间 unix_timestamp() bigint 所在时区的当前时间戳 utc_timestamp timestamp UTC时区的当前时间 timeofday() string Fri Dec 10 12:39:46 2021 CST 2. 获取时间指定单位函数 序号 函数/方法 输出类型 说明/样式 1 year(timestamp/date) int 获取年 yyyy 2 quarter(timestamp/date) int 获取季节（1,2,3,4） 3 month(timestamp/date) int 获取月 4 monthname(timestamp/date) string 获取月份名称 December 5 week(timestamp/date) int 获取周（1-53） 6 weekofyear(timestamp/date) int 获取周（1-53） 7 dayofweek(timestamp/date) int 获取天（本周第多少天,周日算第一天） 8 dayname(timestamp/date) string 获取天（星期几）Friday 9 next_day(timestamp/date, 10 day(string)) timestamp/date 11 day(timestamp/date) int 获取天（本月第多少天） 12 dayofmonth(timestamp/date) int 获取天（本月第多少天） 13 last_day(timestamp/date) timestamp/date 获取天（本月的最后一天日期） 14 dayofyear(timestamp/date) int 获取天（本年第多少天） 15 hour(timestamp/date) int 获取小时 16 minute(timestamp date) int 获取分钟 17 second(timestamp date) int 获取秒 18 millisecond(timestamp date) int 获取毫秒 19 extract (YEAR FROM timestamp) bigint 获取参数指定的时间单位 YEAR MONTH DAY HOUR MINUTE SECOND 20 date_part(\u0026lsquo;year\u0026rsquo;,timestamp) bigint 获取参数指定的时间单位 YEAR MONTH DAY HOUR MINUTE SECOND 21 trunc(timestamp/date,unit) timestamp/date 获取截断为指定单位的时间 unit 截取说明 SYYYY，YYYY，YEAR，SYEAR，YYY，YY，Y 年 Q 季节 MONTH，MON，MM，RM 月 WW 最近的日期是与一年中的第一天相同的日期 W 最近的日期是与该月的第一天相同的星期几 DDD，DD，J 天 DAY，DY，D 星期几（星期一）的开始 HH，HH12，HH24 小时 MI 分钟 3. 时间比较函数 序号 函数/方法 输出类型 说明/样式 1 datediff(timestamp enddate,startdate) int 返回endDate比startDate多多少天 2 int_months_between(timestamp t1,t2) int 返回两个日期相差的整数月份个数 3 months_between(timestamp t1,t2) double 返回浮点数的月数相差的数 4 date_cmp(DATE date1, DATE date2) int 比较是否相等，返回-1,0,1,null四种数值 5 timestamp_cmp(timestamp t1，timestamp t2) int 比较是否相等，返回-1,0,1,null四种数值 4. 时间格式转换函数 序号 函数/方法 输出类型 说明/样式 1 to_date(timestamp date) string 返回时间戳对应的date 2 to_timestamp(bigint unixtime) timestamp 返回整数对应的timestamp值 3 to_timestamp(string date，string pattern) timestamp 返回字符串对应的timestamp值 4 to_utc_timestamp(timestamp t，string timezone) timestamp 指定时区的时间戳转化为UTC时区的时间戳 5 from_timestamp(timestamp t，string pattern) string 把timestamp按照pattern进行格式化 6 from_timestamp(string date，string pattern) string 把date按照pattern进行格式化 7 from_unixtime(bigint unixtime) string 把时间戳秒数转化为本地地区中的字符串 8 from_unixtime(bigint unixtime，string pattern） string 时间戳转化为本地时区字符串，pattern格式 9 from_utc_timestamp（timestamp t，string timezone） timestamp UTC时区指定时间戳转化为指定时区时间戳 10 unix_timestamp(string datetime) bigint 把string类型的date或日期转化成时间戳Unix 11 unix_timestamp(timestamp datetime) bigint 把string类型的timestamp转化成时间戳Unix 12 unix_timestamp(string datetime，string pattern) bigint 日期按pattern转化成时间戳Unix 5. 时间计算函数 序号 函数/方法 输出类型 说明/样式 1 years_add(timestamp/date date, int/bigint years) timestamp/date 增加指定年数 2 years_sub(timestamp/date date, int/bigint years) timestamp/date 减少指定年数 3 months_add(timestamp/date date, int/bigint months) timestamp/date 增加指定月数 4 months_sub(timestamp/date date, int/bigint months) timestamp/date 减少指定月数 5 add_months(timestamp/date date, int/bigint months) timestamp/date 增加指定月数 6 weeks_add(timestamp/date date, int/bigint weeks) timestamp/date 增加指定周数 7 weeks_sub(timestamp/date date, int/bigint weeks) timestamp/date 减少指定周数 8 days_add(timestamp/date startdate, int/bigint days) timestamp/date 增加指定天数 9 days_sub(timestamp/date startdate, int/bigint days) timestamp/date 减少指定天数 10 date_add(timestamp/date startdate, int/bigint days) timestamp/date 增加指定天数 11 date_sub(timestamp/date startdate, int/bigint days) timestamp/date 减少指定天数 12 adddate(timestamp/date startdate, int/int days) timestamp/date 增加指定天数 13 subdate(timestamp/date startdate，bigint/int days) timestamp/date 减少指定天数 14 hours_add(timestamp date, int/bigint hours) timestamp 增加指定小时 15 hours_sub(timestamp date, int/bigint hours) timestamp 减少指定小时 16 minutes_add(timestamp date, int/bigint minutes) timestamp 增加指定分钟 17 minutes_sub(timestamp date, int/bigint minutes) timestamp 减少指定分钟 18 seconds_add(timestamp date, int/bigint seconds) timestamp 增加指定秒数 19 seconds_sub(timestamp date, int/bigint seconds) timestamp 减少指定秒数 20 milliseconds_add(timestamp t, int/bigint s） timestamp 增加指定毫秒数 21 milliseconds_sub(timestamp t, int/bigint s） timestamp 减少指定毫秒数 22 microseconds_add(timestamp t, int/bigint s) timestamp 增加指定微秒数 23 microseconds_sub(timestamp t, int/bigint s) timestamp 减少指定微秒数 24 nanoseconds_add(timestamp t, int/bigint s） timestamp 增加指定纳秒数 25 nanoseconds_sub(timestamp t, int/bigint s） timestamp 减少指定纳秒数 26 date_add(timestamp/date startdate, interval_expression) timestamp/date 使用参数计算日期增量值（增加） 27 date_sub(timestamp/date startdate, interval_expression) timestamp/date 使用参数计算日期增量值（减少） Redis 启动类 key 命令 功能说明 keys * 查看当前库的所有键 exists 判断某个键是否存在 type 查看键的类型 del 删除某个键 expire 为键值设置过期时间，单位秒 ttl 查看还有多久过期,-1表示永不过期,-2表示已过期 dbsize 查看当前数据库中key的数量 flushdb 清空当前库 Flushall 通杀全部库 String 命令 功能说明 get 查询对应键值 set 添加键值对 append 将给定的追加到原值的末尾 strlen 获取值的长度 setnx 只有在key 不存在时设置key的值 incr 将key中存储的数字值增1只能对数字值操作，如果为空，新增值为1 decr 将key中存储的数字值减1只能对数字之操作，如果为空,新增值为-1 incrby /decrby 步长 将key中存储的数字值增减，自定义步长 mset 同时设置一个或多个key-value对 mget 同时获取一个或多个value msetnx 同时设置一个或多个key-value对，当且仅当所有给定的key都不存在 getrange \u0026lt;起始位置\u0026gt; \u0026lt;结束位置\u0026gt; 获得值的范围,类似java中的substring setrange \u0026lt;起始位置\u0026gt; 用覆盖所存储的字符串值，从\u0026lt;起始位置\u0026gt;开始 setex \u0026lt;过期时间\u0026gt; 设置键值的同时，设置过去时间，单位秒 getset 以新换旧,设置了新值的同时获取旧值 List 命令 功能说明 lpush/rpush 从左边/右边插入一个或多个值。 lpop/rpop 从左边/右边吐出一个值。值在键在，值光键亡。 rpoplpush 从列表右边吐出一个值，插到列表左边 lrange 按照索引下标获得元素(从左到右) lindex 按照索引下标获得元素(从左到右) llen 获得列表长度 linsert before 在的后面插入 插入值 lrem 从左边删除n个value(从左到右) Set 命令 功能说明 sadd \u0026hellip;. 将一个或多个 member 元素加入到集合 key 当中，已经存在于集合的 member 元素将被忽略。 smembers 取出该集合的所有值。 sismember 判断集合是否为含有该值，有返回1，没有返回0 scard 返回该集合的元素个数。 srem \u0026hellip;. 删除集合中的某个元素。 spop 随机从该集合中吐出一个值。 srandmember 随机从该集合中取出n个值。不会从集合中删除 sinter 返回两个集合的交集元素。 sunion 返回两个集合的并集元素。 sdiff 返回两个集合的差集元素。 Hash 命令 功能说明 hset 给集合中的 键赋值 hget 从集合 取出 value hmset \u0026hellip; 批量设置hash的值 hexists key 查看哈希表 key 中，给定域 field 是否存在。 hkeys 列出该hash集合的所有field hvals 列出该hash集合的所有value hincrby 为哈希表 key 中的域 field 的值加上增量 increment hsetnx 将哈希表 key 中的域 field 的值设置为 value ，当且仅当域 field 不存在 zset(Sorted set) 命令 功能说明 zadd \u0026hellip; 将一个或多个 member 元素及其 score 值加入到有序集 key 当中 zrange [WITHSCORES] 返回有序集 key 中，下标在 之间的元素带WITHSCORES，可以让分数一起和值返回到结果集。 zrangebyscore key min max [withscores] [limit offset count] 返回有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。 zrevrangebyscore key max min [withscores] [limit offset count] 同上，改为从大到小排列。 zincrby 为元素的score加上增量 zrem 删除该集合下，指定值的元素 zcount 统计该集合，分数区间内的元素个数 zrank 返回该值在集合中的排名，从0开始。 Flink 启动 ./start-cluster.sh run ./bin/flink run [OPTIONS]\r./bin/flink run -m yarn-cluster -c com.wang.flink.WordCount /opt/app/WordCount.jar OPTIONS 功能说明 -d detached 是否使用分离模式 -m jobmanager 指定提交的jobmanager -yat –yarnapplicationType 设置yarn应用的类型 -yD 使用给定属性的值 -yd –yarndetached 使用yarn分离模式 -yh –yarnhelp yarn session的帮助 -yid –yarnapplicationId 挂到正在运行的yarnsession上 -yj –yarnjar Flink jar文件的路径 -yjm –yarnjobManagerMemory jobmanager的内存(单位M) -ynl –yarnnodeLabel 指定 YARN 应用程序 YARN 节点标签 -ynm –yarnname 自定义yarn应用名称 -yq –yarnquery 显示yarn的可用资源 -yqu –yarnqueue 指定yarn队列 -ys –yarnslots 指定每个taskmanager的slots数 -yt yarnship 在指定目录中传输文件 -ytm –yarntaskManagerMemory 每个taskmanager的内存 -yz –yarnzookeeperNamespace 用来创建ha的zk子路径的命名空间 -z –zookeeperNamespace 用来创建ha的zk子路径的命名空间 -p 并行度 -yn 需要分配的YARN容器个数(=任务管理器的数量) info ./bin/flink info [OPTIONS] OPTIONS 功能说明 -c 程序进入点，主类 -p 并行度 list ./bin/flink list [OPTIONS] OPTIONS 功能说明 -a –all 显示所有应用和对应的job id -r –running 显示正在运行的应用和job id -s –scheduled 显示调度的应用和job id -m –jobmanager 指定连接的jobmanager -yid –yarnapplicationId 挂到指定的yarn id对应的yarn session上 -z –zookeeperNamespace 用来创建ha的zk子路径的命名空间 stop ./bin/flink stop [OPTIONS] \u0026lt;Job ID\u0026gt; OPTIONS 功能说明 -d 在采取保存点和停止管道之前，发送MAX_WATERMARK -p savepointPath 保存点的路径 \u0026lsquo;xxxxx\u0026rsquo; -m –jobmanager 指定连接的jobmanager -yid –yarnapplicationId 挂到指定的yarn id对应的yarn session上 -z –zookeeperNamespace 用来创建ha的zk子路径的命名空间 cancel(弱化) ./bin/flink cancel [OPTIONS] \u0026lt;Job ID\u0026gt; OPTIONS 功能说明 -s 使用 \u0026ldquo;stop \u0026ldquo;代替 -D 允许指定多个通用配置选项 -m 要连接的JobManager的地址 -yid –yarnapplicationId 挂到指定的yarn id对应的yarn session上 -z –zookeeperNamespace 用来创建ha的zk子路径的命名空间 savepoint ./bin/flink savepoint [OPTIONS] \u0026lt;Job ID\u0026gt; OPTIONS 功能说明 -d 要处理的保存点的路径 -j Flink程序的JAR文件 -m 要连接的JobManager的地址 -yid –yarnapplicationId 挂到指定的yarn id对应的yarn session上 -z –zookeeperNamespace 用来创建ha的zk子路径的命名空间 HDFS 文件级操作 查看文件行数\nhdfs dfs -cat /path/to/file/* | wc -l\n查看文件后n行\nhdfs dfs -cat /path/to/file | tail -5\n查看未关闭文件\nhdfs fsck /path/ -openforwrite\n修复未关闭文件\nhdfs debug recoverLease -path /path/to/file -retries 3\nKUDU 修复磁盘\nkudu fs check [-fs_wal_dir=\u0026lt;dir\u0026gt;] [-fs_data_dirs=] -repair\n建表\nCREATE TABLE dwd.example_kudu ( id STRING, dt STRING, PRIMARY KEY (id) ) PARTITION BY HASH (id) PARTITIONS 3 STORED AS KUDU TBLPROPERTIES (\u0026#39;kudu.master_addresses\u0026#39; = \u0026#39;master:7051\u0026#39;); Linux 快捷命令 ping 端口连通性\ntelnet ip port\n查看端口占用\nnetstat -tunlp | grep 34560\n计算行数\nwc -l\nHive set 参数 -- 查看某参数 set hive.execution.engine; -- 修复分区 msck repair table tableName; -- 动态分区 set hive.exec.dynamic.partition=true; set hive.exec.dynamic.partition.mode=nonstrict; -- 执行引擎 set hive.execution.engine=spark; -- Spark 资源 set spark.executor.memory=8G; set spark.yarn.queue=root.users.hdfs; -- MR 队列 set mapreduce.job.queuename=prd; Java JAR 启动 nohup java -Xms256m -Xmx512m -jar xxx.jar \u0026gt; xxx.log 2\u0026gt;\u0026amp;1 \u0026amp; ","permalink":"https://leochu.work/blog/tech/bigdata/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/","summary":"\u003ch2 id=\"linuxvivim\"\u003eLinux（vi/vim）\u003c/h2\u003e\n\u003ch3 id=\"一般模式\"\u003e一般模式\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e语法\u003c/th\u003e\n          \u003cth\u003e功能描述\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eyy\u003c/td\u003e\n          \u003ctd\u003e复制光标当前一行\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ey数字y\u003c/td\u003e\n          \u003ctd\u003e复制一段（从第几行到第几行）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ep\u003c/td\u003e\n          \u003ctd\u003e箭头移动到目的行粘贴\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eu\u003c/td\u003e\n          \u003ctd\u003e撤销上一步\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003edd\u003c/td\u003e\n          \u003ctd\u003e删除光标当前行\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ed数字d\u003c/td\u003e\n          \u003ctd\u003e删除光标（含）后多少行\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ex\u003c/td\u003e\n          \u003ctd\u003e删除一个字母，相当于del\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eX\u003c/td\u003e\n          \u003ctd\u003e删除一个字母，相当于Backspace\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eyw\u003c/td\u003e\n          \u003ctd\u003e复制一个词\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003edw\u003c/td\u003e\n          \u003ctd\u003e删除一个词\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eshift+^\u003c/td\u003e\n          \u003ctd\u003e移动到行头\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eshift+$\u003c/td\u003e\n          \u003ctd\u003e移动到行尾\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1+shift+g\u003c/td\u003e\n          \u003ctd\u003e移动到页头，数字\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eshift+g\u003c/td\u003e\n          \u003ctd\u003e移动到页尾\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e数字N+shift+g\u003c/td\u003e\n          \u003ctd\u003e移动到目标行\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"编辑模式\"\u003e编辑模式\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e按键\u003c/th\u003e\n          \u003cth\u003e功能\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ei\u003c/td\u003e\n          \u003ctd\u003e当前光标前\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ea\u003c/td\u003e\n          \u003ctd\u003e当前光标后\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eo\u003c/td\u003e\n          \u003ctd\u003e当前光标行的下一行\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eI\u003c/td\u003e\n          \u003ctd\u003e光标所在行最前\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eA\u003c/td\u003e\n          \u003ctd\u003e光标所在行最后\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eO\u003c/td\u003e\n          \u003ctd\u003e当前光标行的上一行\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"指令模式\"\u003e指令模式\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e命令\u003c/th\u003e\n          \u003cth\u003e功能\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e:w\u003c/td\u003e\n          \u003ctd\u003e保存\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e:q\u003c/td\u003e\n          \u003ctd\u003e退出\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e:!\u003c/td\u003e\n          \u003ctd\u003e强制执行\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e/要查找的词\u003c/td\u003e\n          \u003ctd\u003en 查找下一个，N 往上查找\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e? 要查找的词\u003c/td\u003e\n          \u003ctd\u003en是查找上一个，shift+n是往下查找\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e:set nu\u003c/td\u003e\n          \u003ctd\u003e显示行号\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e:set nonu\u003c/td\u003e\n          \u003ctd\u003e关闭行号\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"压缩和解压\"\u003e压缩和解压\u003c/h3\u003e\n\u003ch4 id=\"gzipgunzip-压缩\"\u003egzip/gunzip 压缩\u003c/h4\u003e\n\u003cp\u003e（1）只能压缩文件不能压缩目录\u003c/p\u003e","title":"大数据常用命令"}]