├── .gitignore ├── README.md ├── deploy-to-gh.sh ├── deploy.sh ├── dev.sh ├── docs ├── .vuepress │ ├── components │ │ ├── Valine.vue │ │ └── showtime.vue │ ├── config.js │ ├── dist │ │ └── assets │ │ │ ├── css │ │ │ ├── 1.styles.3c3b5234.css │ │ │ ├── 2.styles.03e870dd.css │ │ │ └── styles.49589207.css │ │ │ ├── img │ │ │ └── search.83621669.svg │ │ │ └── js │ │ │ ├── 1.3c3b5234.js │ │ │ ├── 10.a05ce450.js │ │ │ ├── 11.9d6ca9a1.js │ │ │ ├── 12.6fbc6a1d.js │ │ │ ├── 13.1cfaa934.js │ │ │ ├── 14.890efe50.js │ │ │ ├── 2.03e870dd.js │ │ │ ├── 3.7acafdd6.js │ │ │ ├── 4.8667836b.js │ │ │ ├── 5.15ad4af7.js │ │ │ ├── 6.2e4520b1.js │ │ │ ├── 7.c96373d9.js │ │ │ ├── 8.7ab82f6f.js │ │ │ ├── 9.121948bf.js │ │ │ └── app.49589207.js │ ├── override.styl │ ├── public │ │ ├── flash.png │ │ └── images │ │ │ ├── grid_017277d.png │ │ │ ├── laptop_fd4699c.png │ │ │ ├── mac_3336890.png │ │ │ ├── machine_b35833f.png │ │ │ └── phone_66669bd.png │ └── styles │ │ ├── data.css │ │ └── index.styl ├── It-chat │ └── case.md ├── README.md ├── about │ ├── README.md │ └── bigdata.md ├── artical │ └── artical.md ├── distribute │ └── distribute.md ├── liuyan │ └── README.md ├── ml │ ├── README.md │ ├── ml-guid.md │ ├── ml-term.md │ ├── study-road.md │ └── study-website.md ├── ziyuan01 │ ├── BigDataIs.md │ ├── Hive.md │ ├── MR.md │ ├── README.md │ ├── distribute.md │ └── jianjie.md ├── ziyuan02 │ ├── MRyuanli.md │ ├── NameNodeDetail.md │ ├── README.md │ ├── kafka-01.md │ ├── secondarySort.md │ └── skewindata.md └── ziyuan03 │ └── README.md ├── package-lock.json ├── package.json ├── push.sh └── testfine.py /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist/* 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![自学大数据](https://img.shields.io/badge/%E8%87%AA%E5%AD%A6-%E5%A4%A7%E6%95%B0%E6%8D%AE-brightgreen.svg) 2 | ![自学机器学习](https://img.shields.io/badge/%E8%87%AA%E5%AD%A6-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-brightgreen.svg) 3 | ![大数据进击之路](https://img.shields.io/badge/%E8%87%AA%E5%AD%A6-%E5%A4%A7%E6%95%B0%E6%8D%AE%E8%BF%9B%E5%87%BB%E4%B9%8B%E8%B7%AF-blue.svg) 4 | 5 | # 大数据学习资源整合 6 | 7 | 大数据与机器学习笔记,持续更新中。 8 | 9 | # 文章分类 10 | - 大数据技术周报 11 | - [大数据技术周报,每周更新](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU0OTgxNjMyNA==&action=getalbum&album_id=2052897255342309378&scene=173&from_msgid=2247484324&from_itemidx=1&count=3&nolastread=1#wechat_redirect) 12 | 13 | - 机器学习 14 | - [从机器学习谈起](https://github.com/aikuyun/bigdata-doc/blob/master/docs/ml/ml-guid.md) 15 | - [机器学习术语](https://github.com/aikuyun/bigdata-doc/blob/master/docs/ml/ml-term.md) 16 | - [机器学习路线](https://github.com/aikuyun/bigdata-doc/blob/master/docs/ml/study-road.md) 17 | - [推荐两个网站,认清自己的阶段](https://github.com/aikuyun/bigdata-doc/blob/master/docs/ml/study-website.md) 18 | 19 | - 分布式基础 20 | 21 | - [分布式基础](https://github.com/aikuyun/ziyuan/blob/master/docs/distribute/distribute.md) 22 | 23 | - 大数据生态 24 | - [HDFS](https://github.com/aikuyun/ziyuan/tree/master/docs/ziyuan01#hdfs) 25 | 26 | - [MapReduce](https://github.com/aikuyun/ziyuan/tree/master/docs/ziyuan01#mapreduce) 27 | 28 | - [Hive](https://github.com/aikuyun/ziyuan/blob/master/docs/ziyuan01/Hive.md) 29 | 30 | - 深挖底层 31 | - [Hadoop HA 机制](https://github.com/aikuyun/ziyuan/tree/master/docs/ziyuan02#hadoop-ha-%E6%9C%BA%E5%88%B6) 32 | 33 | - [MR原理和运行过程](https://github.com/aikuyun/ziyuan/blob/master/docs/ziyuan02/MRyuanli.md) 34 | 35 | - [NameNode内部解析](https://github.com/aikuyun/ziyuan/blob/master/docs/ziyuan02/MRyuanli.md) 36 | 37 | - [二次排序](https://github.com/aikuyun/ziyuan/blob/master/docs/ziyuan02/secondarySort.md) 38 | 39 | - [kafka](https://github.com/aikuyun/ziyuan/blob/master/docs/ziyuan02/kafka-01.md) 40 | 41 | - 解决方案 42 | - [很多大厂解决方案](https://github.com/aikuyun/ziyuan/blob/master/docs/It-chat/case.md) 43 | - [日均万亿条数据如何处理?爱奇艺实时计算平台这样做](https://mp.weixin.qq.com/s/DKP08aUSNMOySNcs_y6ODA) 44 | - [揭秘微信「看一看」 是如何为你推荐的](https://mp.weixin.qq.com/s/Regv8UUc5PH9HcnUq_zq3A) 45 | 46 | - 技术文章整理 47 | 48 | - [技术文章整理](https://github.com/aikuyun/ziyuan/blob/master/docs/artical/artical.md) 49 | 50 | - Spark 51 | - [Spark 调优](https://mp.weixin.qq.com/s/iNovecaYkKrytNgQMvIMZw) 52 | - [Spark shuffle 寻址流程](https://mp.weixin.qq.com/s/0eQPmVnXCbEr1ziPAW569A) 53 | - [Spark shuffle 调优](https://mp.weixin.qq.com/s/keJnU0trtTW9W-zBWPKD5A) 54 | - [Spark 数据本地化级别](https://mp.weixin.qq.com/s/kF4zjiambBohSJG9gZW8_g) 55 | - [Spark 的核心 RDD 以及 Stage 划分细节,运行模式总结](https://mp.weixin.qq.com/s/aPwsPTkFakBwv3MIioaOOg) 56 | 57 | - kafka 58 | - [kafka + sparkstreaming](https://mp.weixin.qq.com/s/wKjSalxFdVkRXGPnNVg_2g) 59 | - [kafka 数据丢失与重复消费](https://mp.weixin.qq.com/s/ROoVOVgNW8jzdCZeAwLTDQ) 60 | 61 | - HBase 62 | - [HBase 架构](https://mp.weixin.qq.com/s/j2Kbi003Etzw_15KwV0TyQ) 63 | - [HBase 架构补充](https://mp.weixin.qq.com/s/7yRequ0pqGN_00zi704wwA) 64 | 65 | - Hadoop 66 | - [Hadoop HA 原理分析](https://mp.weixin.qq.com/s/BmVvoi8k0mU9pmGQCl2Sug) 67 | - [Hadoop系列之 1.0 和 2.0 架构](https://mp.weixin.qq.com/s/B_wOtK1gSVlmB4cF5hZG2A) 68 | - [Hadoop系列之 Hive](https://mp.weixin.qq.com/s/fWKX6NR908fLbVUMFwpj8A) 69 | - [Hadoop系列之 Mapreduce](https://mp.weixin.qq.com/s/JDDTTy6QfZtwz547M88GMQ) 70 | - [Hadoop系列之 HDFS](https://mp.weixin.qq.com/s/Dcsat0-iRB_xYRBoMfhoXg) 71 | 72 | - Flink 73 | - [Flink社区电子书](https://mp.weixin.qq.com/s?__biz=MzIwMjA2MTk4Ng==&mid=2247485438&idx=1&sn=2bb7f82402dc4607f94cdb78e48cd48b&chksm=96e52633a192af25a5c6b2371dfed395aa46168639c01bb49dbc36381f2b3dd889bfe9256d6a&xtrack=1&scene=0&subscene=91&sessionid=1555230598&clicktime=1555230760&ascene=7&devicetype=android-27&version=27000334&nettype=cmnet&abtest_cookie=BAABAAoACwASABMABQAjlx4AVpkeAMeZHgDRmR4A3JkeAAAA&lang=zh_CN&pass_ticket=cRjrq%2F8EqXfIhZvDoJO4rqTvtx1hEu4fyHiignznzsezMHPtQ83VFn8G02ozwToC&wx_header=1) 74 | - [Flink 里程碑版本即将发布,快点入手](https://mp.weixin.qq.com/s/OmTmPHaP0vSPT128eAf2Ig) 75 | - [重磅福利!《Apache Flink 十大技术难点实战》发布,帮你从容应对生产环境中的技术难题](https://mp.weixin.qq.com/s/U3c4oXFLPuc4XiUNUY55gg) 76 | - [2020 年 Flink 学习资料整合,建议收藏](https://mp.weixin.qq.com/s/wuKBvNbkO-pTWZEMSvGLNg) 77 | 78 | 79 | 80 | - 数据仓库 81 | - [离线数仓与实时数仓(一)](https://mp.weixin.qq.com/s/dpwQ4sx-IWL66m03lPa6rg) 82 | - [58全站用户行为数据仓库建设及实践](https://mp.weixin.qq.com/s/MnfdsLHGjK9okv020cS_Kg) 83 | - [干货 | 携程机票数据仓库建设之路](https://mp.weixin.qq.com/s/oPQFDl-A-6BnPXhNdwnePA) 84 | - [干货 | 携程Hadoop跨机房架构实践](https://mp.weixin.qq.com/s/S5SXNabYqwyUMl1ReLayKw) 85 | 86 | - Hive 基础 87 | - [Hive 数据压缩格式总结](https://mp.weixin.qq.com/s/T6Y4vMYghb_asWdtsZnjpA) 88 | - [CombineFileInputFormat 文件分片总结](https://mp.weixin.qq.com/s/DZ-CfrVrr7i0iA2GRdBN1g) 89 | - [Hive SQL 窗口函数](https://mp.weixin.qq.com/s/qhP2tOS5plxaczPN1JkWJw) 90 | - [Hive SQL 分析函数](https://mp.weixin.qq.com/s/6nNr97z-Rj5Alofl8wCwhw) 91 | 92 | - 底层基础 93 | - [深入理解 MySQL 索引底层原理](https://mp.weixin.qq.com/s/J7eQcwBgQEGJk4bGIa9wDA) 94 | - [缓存击穿、缓存失效及热点key的解决方案](https://mp.weixin.qq.com/s/TqqTDy2YizLMwE0tyHxKVA) 95 | 96 | ## 欢迎关注原创公众号 97 | 98 | 公众号:大数据学习指南 专注大数据数据技术 99 | 100 | ![扫我](https://cdn.nlark.com/yuque/0/2021/png/199648/1631944506464-83677e15-283f-43de-b106-5ff823300c85.png?x-oss-process=image%2Fresize%2Cw_900%2Climit_0) 101 | 102 | 其他平台,会不定时同步更新。 103 | 104 | - [语雀](https://www.yuque.com/cuteximi/base) 105 | - [知乎](https://zhuanlan.zhihu.com/bigdata1995) 106 | - [头条号](https://www.toutiao.com/c/user/70068423102/#mid=1579500719412238) 107 | -------------------------------------------------------------------------------- /deploy-to-gh.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 确保脚本抛出遇到的错误 4 | set -e 5 | 6 | # 生成静态文件 7 | npm run docs:build 8 | 9 | # 进入生成的文件夹 10 | cd docs/.vuepress/dist 11 | 12 | # 如果是发布到自定义域名 13 | # echo 'www.example.com' > CNAME 14 | 15 | git init 16 | git add -A 17 | git commit -m 'deploy' 18 | 19 | # 如果发布到 https://.github.io 20 | # git push -f git@github.com:/.github.io.git master 21 | 22 | # 如果发布到 https://.github.io/ 23 | 24 | git push -f git@github.com:aikuyun/ziyuan.git master:gh-pages 25 | 26 | cd - 27 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 确保脚本抛出遇到的错误 4 | set -e 5 | 6 | # 生成静态文件 7 | npm run docs:build 8 | 9 | # 进入生成的文件夹 10 | cd docs/.vuepress/ 11 | # 把该文件夹复制出来, 12 | cp -R dist/ ../../dist 13 | -------------------------------------------------------------------------------- /dev.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 确保脚本抛出遇到的错误 4 | set -e 5 | 6 | # 本地渲染 7 | npm run docs:dev 8 | 9 | # dev 10 | -------------------------------------------------------------------------------- /docs/.vuepress/components/Valine.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 29 | -------------------------------------------------------------------------------- /docs/.vuepress/components/showtime.vue: -------------------------------------------------------------------------------- 1 | 273 | 274 | 279 | 280 | 284 | 285 | 288 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | const sidebar = { 2 | '/It-chat/':[{ 3 | title:'案例', 4 | children:[ 5 | 'case' 6 | ] 7 | } 8 | ], 9 | '/artical/':[{ 10 | title:'文章', 11 | children:[ 12 | 'artical' 13 | ] 14 | }], 15 | '/distribute/': [{ 16 | title: '分布式', 17 | children: [ 18 | 'distribute', 19 | ] 20 | }], 21 | '/ml/': [{ 22 | title: '机器学习基础', 23 | children: [ 24 | 'ml-guid', 25 | 'ml-term', 26 | 'study-website', 27 | 'study-road' 28 | ] 29 | }], 30 | '/ziyuan01/': [{ 31 | title: '大数据简介', 32 | children: [ 33 | 'jianjie', 34 | 'BigDataIs' 35 | ] 36 | }, { 37 | title: '大数据生态', 38 | children: [ 39 | '', 40 | 'MR', 41 | 'Hive' 42 | ] 43 | }, { 44 | title: '分布式', 45 | children: [ 46 | 'distribute', 47 | ] 48 | }, ], 49 | 50 | '/ziyuan02/': [{ 51 | title: '深挖底层', 52 | children: [ 53 | '', /* /foo/ */ 54 | 'MRyuanli', 55 | 'NameNodeDetail' 56 | ] 57 | }, { 58 | title: '优化', 59 | children: [ 60 | 'secondarySort', 61 | 'skewindata' 62 | ] 63 | },{ 64 | title: 'kafka', 65 | children: [ 66 | 'kafka-01', 67 | ] 68 | }], 69 | '/ziyuan03/': [{ 70 | title: 'Mac', 71 | children: [ 72 | '', 73 | ] 74 | }] 75 | } 76 | 77 | var nav = [{ 78 | text: '大数据', 79 | link: '/ziyuan01/' 80 | }, { 81 | text: '👨‍💻‍机器学习', 82 | link: '/ml/' 83 | }, { 84 | text: '深挖底层', 85 | link: '/ziyuan02/' 86 | }, { 87 | text: '技术文章整合', 88 | link: '/artical/artical.html' 89 | },{ 90 | text: '优秀案例', 91 | link: '/It-chat/case.html' 92 | } 93 | , 94 | { 95 | text: '工具', 96 | link: '/liuyan/' 97 | }, 98 | { 99 | text: ' Mac 软件', 100 | link: '/ziyuan03/' 101 | }, { 102 | text: '主站', 103 | link: 'http://cuteximi.com' 104 | }]; 105 | 106 | module.exports = { 107 | title: '大数据进击之路🔥', 108 | description: '沉淀两年,砥砺前行', 109 | head: [ 110 | ['link', { 111 | rel: 'icon', 112 | href: '/flash.png' 113 | }] 114 | ], 115 | base: '/', 116 | markdown: { 117 | lineNumbers: true, 118 | anchor: { 119 | permalink: true, 120 | permalinkBefore: true, 121 | permalinkSymbol: '#' 122 | }, 123 | }, 124 | 125 | //evergreen: true, 126 | 127 | themeConfig: { 128 | nav, 129 | sidebar, 130 | }, 131 | 132 | //sidebar: 'auto', 133 | 134 | displayAllHeaders: true, // 显示所有页面的链接,默认值 false 135 | search: true, //开启显示搜索框,开箱即用的搜索功能。 136 | searchMaxSuggestions: 10, //搜索结果的数量 137 | lastUpdated: '上次更新', 138 | 139 | repo: 'https://github.com/aikuyun/ziyuan', 140 | // 自定义仓库链接文字。默认从 `themeConfig.repo` 中自动推断为 141 | // "GitHub"/"GitLab"/"Bitbucket" 其中之一,或是 "Source"。 142 | repoLabel: '查看源码', 143 | 144 | // 以下为可选的编辑链接选项 145 | 146 | // 假如你的文档仓库和项目本身不在一个仓库: 147 | //docsRepo: 'vuejs/vuepress', 148 | // 假如文档不是放在仓库的根目录下: 149 | docsDir: 'docs', 150 | // 假如文档放在一个特定的分支下: 151 | docsBranch: 'master', 152 | // 默认是 false, 设置为 true 来启用 153 | editLinks: true, 154 | // 默认为 "Edit this page" 155 | editLinkText: '帮助改善此页面!', 156 | 157 | plugins: ['@vuepress/back-to-top'] 158 | } 159 | -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/css/1.styles.3c3b5234.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aikuyun/bigdata-doc/5763d360077d840a3dd9a5e333e85cd264d4dd4b/docs/.vuepress/dist/assets/css/1.styles.3c3b5234.css -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/css/2.styles.03e870dd.css: -------------------------------------------------------------------------------- 1 | .badge[data-v-099ab69c]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff;margin-right:5px;background-color:#42b983}.badge.middle[data-v-099ab69c]{vertical-align:middle}.badge.top[data-v-099ab69c]{vertical-align:top}.badge.green[data-v-099ab69c],.badge.tip[data-v-099ab69c]{background-color:#42b983}.badge.error[data-v-099ab69c]{background-color:#da5961}.badge.warn[data-v-099ab69c],.badge.warning[data-v-099ab69c],.badge.yellow[data-v-099ab69c]{background-color:#e7c000} -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/css/styles.49589207.css: -------------------------------------------------------------------------------- 1 | .home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto}.home .hero{text-align:center}.home .hero img{max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#3eaf7c;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #389d70}.home .hero .action-button:hover{background-color:#4abf8a}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.sidebar-button{display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#3eaf7c}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:1.5rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#3eaf7c}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title{display:block}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:.45rem 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#3eaf7c}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #3eaf7c;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper .dropdown-title .arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #ccc;border-bottom:0}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid #ddd;border-bottom-color:#ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#3eaf7c}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #46bd87}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem;position:relative}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:.9rem;position:absolute;right:1.5rem;top:.7rem;display:flex}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}.navbar .links .nav-links{flex:1}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .links{padding-left:1.5rem}}.page-edit,.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit,.page-nav{padding:2rem}}@media (max-width:419px){.page-edit,.page-nav{padding:1.5rem}}.page{padding-bottom:2rem}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#aaa}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#3eaf7c}a.sidebar-link.active{font-weight:600;color:#3eaf7c;border-left-color:#3eaf7c}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}.sidebar-group:not(.first){margin-top:1em}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading{cursor:auto;color:inherit}.sidebar-heading{color:#999;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:0 1.5rem;margin-top:0;margin-bottom:.5rem}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading:.open .arrow{top:-.18em}.sidebar-group-items{transition:height .1s ease-out;overflow:hidden}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem 0}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-links{padding:1.5rem 0}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-links{padding:1rem 0}}.sw-update-popup{position:fixed;right:1em;bottom:1em;padding:1em;border:1px solid #3eaf7c;border-radius:3px;background:#fff;box-shadow:0 4px 16px rgba(0,0,0,.5);text-align:center}.sw-update-popup button{margin-top:.5em;padding:.25em 2em}.sw-update-popup-enter-active,.sw-update-popup-leave-active{transition:opacity .3s,transform .3s}.sw-update-popup-enter,.sw-update-popup-leave-to{opacity:0;transform:translateY(50%) scale(.5)}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;tab-size:4;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}#nprogress{pointer-events:none}#nprogress .bar{background:#3eaf7c;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #3eaf7c,0 0 5px #3eaf7c;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#3eaf7c;border-left-color:#3eaf7c;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.content pre,.content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}.content pre[class*=language-] code,.content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:" ";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number,div[class*=language-].line-numbers-mode .line-numbers-wrapper br{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:"js"}div[class~=language-ts]:before{content:"ts"}div[class~=language-html]:before{content:"html"}div[class~=language-md]:before{content:"md"}div[class~=language-vue]:before{content:"vue"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"stylus"}div[class~=language-go]:before{content:"go"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-sh]:before{content:"sh"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-py]:before{content:"py"}div[class~=language-javascript]:before{content:"js"}div[class~=language-typescript]:before{content:"ts"}div[class~=language-markup]:before{content:"html"}div[class~=language-markdown]:before{content:"md"}div[class~=language-json]:before{content:"json"}div[class~=language-ruby]:before{content:"rb"}div[class~=language-python]:before{content:"py"}div[class~=language-bash]:before{content:"sh"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.content:not(.custom){padding:2rem}}@media (max-width:419px){.content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:15px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.content:not(.custom)>:first-child{margin-top:3.6rem}.content:not(.custom) a:hover{text-decoration:underline}.content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.content:not(.custom) img{max-width:100%}.content.custom{padding:0;margin:0}.content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#3eaf7c}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1.2rem;color:#999;border-left:.25rem solid #dfe2e5;margin-left:0;padding-left:1rem}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.content:not(.custom)>h1,.content:not(.custom)>h2,.content:not(.custom)>h3,.content:not(.custom)>h4,.content:not(.custom)>h5,.content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.content:not(.custom)>h1:first-child,.content:not(.custom)>h2:first-child,.content:not(.custom)>h3:first-child,.content:not(.custom)>h4:first-child,.content:not(.custom)>h5:first-child,.content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.content:not(.custom)>h1:first-child+.custom-block,.content:not(.custom)>h1:first-child+p,.content:not(.custom)>h1:first-child+pre,.content:not(.custom)>h2:first-child+.custom-block,.content:not(.custom)>h2:first-child+p,.content:not(.custom)>h2:first-child+pre,.content:not(.custom)>h3:first-child+.custom-block,.content:not(.custom)>h3:first-child+p,.content:not(.custom)>h3:first-child+pre,.content:not(.custom)>h4:first-child+.custom-block,.content:not(.custom)>h4:first-child+p,.content:not(.custom)>h4:first-child+pre,.content:not(.custom)>h5:first-child+.custom-block,.content:not(.custom)>h5:first-child+p,.content:not(.custom)>h5:first-child+pre,.content:not(.custom)>h6:first-child+.custom-block,.content:not(.custom)>h6:first-child+p,.content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.custom-layout{padding-top:3.6rem}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-navbar .custom-layout{padding-top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.icon.outbound{color:#aaa;display:inline-block} -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/img/search.83621669.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/1.3c3b5234.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[1],{145:function(n,w,o){}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/10.a05ce450.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[10],{148:function(t,e,r){"use strict";r.r(e);var a=r(0),s=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("[文章] "),r("a",{attrs:{href:"https://flaviocopes.com/chrome-devtools-tips/#drag-and-drop-in-the-elements-panel",target:"_blank",rel:"noopener noreferrer"}},[t._v("Chrome DevTools 的一些使用技巧"),r("OutboundLink")],1)]),t._v(" "),t._m(2),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/shobrook/rebound",target:"_blank",rel:"noopener noreferrer"}},[t._v("rebound"),r("OutboundLink")],1)]),t._v(" "),t._m(3),t._v(" "),r("p",[t._v("有人终于把这个工具写出来了,一旦 Python 或 JS 脚本报错,就到 Stack Overflow 取回报错信息的解释。")]),t._v(" "),t._m(4),t._v(" "),r("p",[r("a",{attrs:{href:"https://mustard-ui.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mustard UI"),r("OutboundLink")],1)]),t._v(" "),t._m(5),t._v(" "),r("p",[t._v("一个简洁、好看的 CSS 框架,压缩后只有5.28KB。")]),t._v(" "),t._m(6),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/rsuite/rsuite",target:"_blank",rel:"noopener noreferrer"}},[t._v("RSUITE"),r("OutboundLink")],1)]),t._v(" "),t._m(7),t._v(" "),r("p",[t._v("一个国产的 React 组件库。")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),r("p",[r("a",{attrs:{href:"https://desktop.github.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://desktop.github.com/"),r("OutboundLink")],1)])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"工具篇"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#工具篇","aria-hidden":"true"}},[this._v("#")]),this._v(" 工具篇")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"chrome-devtools-的一些使用技巧"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#chrome-devtools-的一些使用技巧","aria-hidden":"true"}},[this._v("#")]),this._v(" Chrome DevTools 的一些使用技巧")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"rebound"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#rebound","aria-hidden":"true"}},[this._v("#")]),this._v(" rebound")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://www.ruanyifeng.com/blogimg/asset/2018/bg2018042812.jpg",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"mustard-ui"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#mustard-ui","aria-hidden":"true"}},[this._v("#")]),this._v(" Mustard UI")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://www.ruanyifeng.com/blogimg/asset/2018/bg2018042811.png",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"react-组件库"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#react-组件库","aria-hidden":"true"}},[this._v("#")]),this._v(" React 组件库")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://www.ruanyifeng.com/blogimg/asset/2018/bg2018042814.png",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("RSUITE(React Suite)是一套用于企业系统产品的 React 组件库。由 HYPERS 前端团队和 UX 团队共同构建,主要服务于公司的大数据产品")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"github-桌面"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#github-桌面","aria-hidden":"true"}},[this._v("#")]),this._v(" GItHub 桌面")])}],!1,null,null,null);s.options.__file="Tools.md";e.default=s.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/11.9d6ca9a1.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[11],{147:function(t,e,r){"use strict";r.r(e);var a=r(0),i=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/jwasham/coding-interview-university/blob/master/translations/README-cn.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("Google 面试自学手册"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("一份爱好者整理的 Google 面试准备指南。")]),t._v(" "),t._m(2),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/guoxiaoxing/android-interview-guide",target:"_blank",rel:"noopener noreferrer"}},[t._v("Andrid 工程师面试指南"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("给Android开发工程师的一份面试指南,包含面试题集与简历模板。")]),t._v(" "),t._m(3),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/CyC2018/CS-Notes",target:"_blank",rel:"noopener noreferrer"}},[t._v("CS-Notes"),r("OutboundLink")],1)])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"面试篇"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#面试篇","aria-hidden":"true"}},[this._v("#")]),this._v(" 面试篇")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"电子书-google-面试自学手册"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#电子书-google-面试自学手册","aria-hidden":"true"}},[this._v("#")]),this._v(" [电子书] Google 面试自学手册")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"github-android开发工程师面试指南"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#github-android开发工程师面试指南","aria-hidden":"true"}},[this._v("#")]),this._v(" [github] Android开发工程师面试指南")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"代码库-面试中需要的基础知识整理收集"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#代码库-面试中需要的基础知识整理收集","aria-hidden":"true"}},[this._v("#")]),this._v(" [代码库] 面试中需要的基础知识整理收集")])}],!1,null,null,null);i.options.__file="Interview.md";e.default=i.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/12.6fbc6a1d.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[12],{156:function(a,t,r){"use strict";r.r(t);var e=r(0),s=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var a=this,t=a.$createElement,r=a._self._c||t;return r("div",{staticClass:"content"},[r("h1",{attrs:{id:"介绍"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#介绍","aria-hidden":"true"}},[a._v("#")]),a._v(" 介绍")]),a._v(" "),r("p",[a._v("本网站主要目的是做一个资源的整合,为大家提供一个优秀网站的导航和学习资源的分享。大部分学习资源均为开源且免费的资源,不存在收费课程的搬运。")]),a._v(" "),r("h2",{attrs:{id:"如何使用?"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#如何使用?","aria-hidden":"true"}},[a._v("#")]),a._v(" 如何使用?")]),a._v(" "),r("h3",{attrs:{id:"搜索关键词"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#搜索关键词","aria-hidden":"true"}},[a._v("#")]),a._v(" 搜索关键词")]),a._v(" "),r("p",[a._v("本站点支持本地搜索,试试搜一下你需要的资源「 关键词 」,或许就在其中哦!")]),a._v(" "),r("h3",{attrs:{id:"意外惊喜"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#意外惊喜","aria-hidden":"true"}},[a._v("#")]),a._v(" 意外惊喜")]),a._v(" "),r("p",[a._v("你可以本着涉猎的心理查看本站点的资源,或许有你意想不到的资源在等着你。")]),a._v(" "),r("h3",{attrs:{id:"分类清晰"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#分类清晰","aria-hidden":"true"}},[a._v("#")]),a._v(" 分类清晰")]),a._v(" "),r("p",[a._v("大多数优秀的开源资源都具备良好的入门指导,本站尽量声明哪些是入门资源,进阶资源等等。")]),a._v(" "),r("h2",{attrs:{id:"学习的目标"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#学习的目标","aria-hidden":"true"}},[a._v("#")]),a._v(" 学习的目标")]),a._v(" "),r("p",[a._v("优秀的课程来自优秀的工程师,大多都是行业的佼佼者,不乏开源社区的活跃着。站在巨人的肩膀上学习,是现在学习的先天优势,接下就就是制定有计划的学习路径,\n努力学习,自我驱动,异或带新学习。")]),a._v(" "),r("h3",{attrs:{id:"系统化学习"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#系统化学习","aria-hidden":"true"}},[a._v("#")]),a._v(" 系统化学习")]),a._v(" "),r("p",[a._v("本站收集的资源大部分均为系统化的学习资源,需要耐着性子一步一步,踏踏实实的学习。不可东一棒子,西一榔头。")]),a._v(" "),r("h3",{attrs:{id:"提升自己"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#提升自己","aria-hidden":"true"}},[a._v("#")]),a._v(" 提升自己")])])}],!1,null,null,null);s.options.__file="README.md";t.default=s.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/13.1cfaa934.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[13],{153:function(t,s,e){"use strict";e.r(s);var a=e(0),i=Object(a.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[e("h1",{attrs:{id:"代码库板块说明"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#代码库板块说明","aria-hidden":"true"}},[t._v("#")]),t._v(" 代码库板块说明")]),t._v(" "),e("p",[t._v("不看 star, 看质量,持续推荐优质的代码仓库。")]),t._v(" "),e("p",[t._v("同时,本站点欢迎大家一起来贡献。如果你有什么好的资源,欢迎提交。")]),t._v(" "),e("div",{staticClass:"tip custom-block"},[e("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),e("p",[t._v("Github地址在网站右上角。")])])])}],!1,null,null,null);i.options.__file="README.md";s.default=i.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/14.890efe50.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[14],{150:function(t,e,r){"use strict";r.r(e);var s=r(0),a=Object(s.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/OpenGenus/cosmos",target:"_blank",rel:"noopener noreferrer"}},[t._v("cosmos"),r("OutboundLink")],1)]),t._v(" "),t._m(2),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/AllThingsSmitty/css-protips/tree/master/translations/zh-CN",target:"_blank",rel:"noopener noreferrer"}},[t._v("css-protips"),r("OutboundLink")],1)]),t._v(" "),t._m(3),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/davideuler/architecture.of.internet-product",target:"_blank",rel:"noopener noreferrer"}},[t._v("技术架构"),r("OutboundLink")],1)])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"优秀的代码仓库"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#优秀的代码仓库","aria-hidden":"true"}},[this._v("#")]),this._v(" 优秀的代码仓库")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"_1-收集算法实现的代码库"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1-收集算法实现的代码库","aria-hidden":"true"}},[this._v("#")]),this._v(" 1.收集算法实现的代码库")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"_2-收集-css-使用技巧"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2-收集-css-使用技巧","aria-hidden":"true"}},[this._v("#")]),this._v(" 2.收集 css 使用技巧")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"_3-国内各大互联网的技术架构"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_3-国内各大互联网的技术架构","aria-hidden":"true"}},[this._v("#")]),this._v(" 3.国内各大互联网的技术架构")])}],!1,null,null,null);a.options.__file="daimaku.md";e.default=a.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/2.03e870dd.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[2],{143:function(t,e,n){},144:function(t,e,n){"use strict";var a=n(143);n.n(a).a},152:function(t,e,n){"use strict";n.r(e);var a={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,e){var n=e.props,a=e.slots;return t("span",{class:["badge",n.type,n.vertical]},n.text||a().default)}},i=(n(144),n(0)),o=Object(i.a)(a,void 0,void 0,!1,null,"099ab69c",null);o.options.__file="Badge.vue";e.default=o.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/3.7acafdd6.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[3],{146:function(t,n,e){"use strict";e.r(n);var s=e(0),i=Object(s.a)({},function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"content"})},[],!1,null,null,null);i.options.__file="README.md";n.default=i.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/4.8667836b.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[4],{157:function(t,e,r){"use strict";r.r(e);var a=r(0),n=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("[文章] "),r("a",{attrs:{href:"http://jamie-wong.com/post/color/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Color: From Hexcodes to Eyeballs"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("读懂这篇文章需要一点物理学知识。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"科普"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#科普","aria-hidden":"true"}},[this._v("#")]),this._v(" 科普")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"人眼如何感受到色彩"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#人眼如何感受到色彩","aria-hidden":"true"}},[this._v("#")]),this._v(" 人眼如何感受到色彩")])}],!1,null,null,null);n.options.__file="Polular-Science.md";e.default=n.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/5.15ad4af7.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{155:function(a,t,r){"use strict";r.r(t);var e=r(0),s=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var a=this,t=a.$createElement,r=a._self._c||t;return r("div",{staticClass:"content"},[r("h1",{attrs:{id:"介绍"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#介绍","aria-hidden":"true"}},[a._v("#")]),a._v(" 介绍")]),a._v(" "),r("p",[a._v("本网站主要目的是做一个资源的整合,为大家提供一个优秀网站的导航和学习资源的分享。大部分学习资源均为开源且免费的资源,不存在收费课程的搬运。")]),a._v(" "),r("h2",{attrs:{id:"如何使用?"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#如何使用?","aria-hidden":"true"}},[a._v("#")]),a._v(" 如何使用?")]),a._v(" "),r("h3",{attrs:{id:"搜索关键词"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#搜索关键词","aria-hidden":"true"}},[a._v("#")]),a._v(" 搜索关键词")]),a._v(" "),r("p",[a._v("本站点支持本地搜索,试试搜一下你需要的资源「 关键词 」,或许就在其中哦!")]),a._v(" "),r("h3",{attrs:{id:"意外惊喜"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#意外惊喜","aria-hidden":"true"}},[a._v("#")]),a._v(" 意外惊喜")]),a._v(" "),r("p",[a._v("你可以本着涉猎的心理查看本站点的资源,或许有你意想不到的资源在等着你。")]),a._v(" "),r("h3",{attrs:{id:"分类清晰"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#分类清晰","aria-hidden":"true"}},[a._v("#")]),a._v(" 分类清晰")]),a._v(" "),r("p",[a._v("大多数优秀的开源资源都具备良好的入门指导,本站尽量声明哪些是入门资源,进阶资源等等。")]),a._v(" "),r("h2",{attrs:{id:"学习的目标"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#学习的目标","aria-hidden":"true"}},[a._v("#")]),a._v(" 学习的目标")]),a._v(" "),r("p",[a._v("优秀的课程来自优秀的工程师,大多都是行业的佼佼者,不乏开源社区的活跃着。站在巨人的肩膀上学习,是现在学习的先天优势,接下就就是制定有计划的学习路径,\n努力学习,自我驱动,异或带新学习。")]),a._v(" "),r("h3",{attrs:{id:"系统化学习"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#系统化学习","aria-hidden":"true"}},[a._v("#")]),a._v(" 系统化学习")]),a._v(" "),r("p",[a._v("本站收集的资源大部分均为系统化的学习资源,需要耐着性子一步一步,踏踏实实的学习。不可东一棒子,西一榔头。")]),a._v(" "),r("h3",{attrs:{id:"提升自己"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#提升自己","aria-hidden":"true"}},[a._v("#")]),a._v(" 提升自己")])])}],!1,null,null,null);s.options.__file="README.md";t.default=s.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/6.2e4520b1.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[6],{154:function(t,e,r){"use strict";r.r(e);var a=r(0),n=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("[免费电子书] "),r("a",{attrs:{href:"http://www.mlyearning.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("《Machine Learning Yearning》"),r("OutboundLink")],1),t._v(",by 吴恩达")]),t._v(" "),t._m(2),t._v(" "),r("div",{staticClass:"tip custom-block"},[r("p",{staticClass:"custom-block-title"},[t._v("TIP")]),t._v(" "),r("p",[t._v("吴恩达(Andrew Ng)是斯坦福大学的教授,人工智能领域的权威,曾经担任过百度的首席科学家。\n他的新书《Machine Learning Yearning》现在可以"),r("a",{attrs:{href:"http://www.mlyearning.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("免费订阅"),r("OutboundLink")],1),t._v("。今后几个月里面,他每完成一个部分,你就会得到邮件通知,可以立即读到。根据说明,这本书大概100页左右,每章的长度很短,非常容易阅读。内容主要关于如何实现你自己的机器学习项目,重点不是算法,而是如何运用算法到真实项目。")])]),t._v(" "),t._m(3),t._v(" "),r("p",[t._v("[免费视频教程] "),r("a",{attrs:{href:"https://data.berkeley.edu/education/data-8x",target:"_blank",rel:"noopener noreferrer"}},[t._v("《Foundations of Data Science》"),r("OutboundLink")],1),t._v(",by 加州大学伯克利分校")]),t._v(" "),r("p",[t._v("加州大学伯克利分校的视频课程"),r("a",{attrs:{href:"https://data.berkeley.edu/education/data-8x",target:"_blank",rel:"noopener noreferrer"}},[t._v("《Foundations of Data Science》"),r("OutboundLink")],1),t._v("(数据科学基础),现在上网了。报名学习是免费的,如果需要证书才收费。")]),t._v(" "),r("p",[t._v("课程分成三个部分,每个部分需要5个星期学习,都由加大的老师亲自教授。整个课程针对初学者,不需要任何统计学或编程的基础。")]),t._v(" "),r("ul",[r("li",[t._v("第一部分:"),r("a",{attrs:{href:"https://www.edx.org/course/foundations-data-science-computational-uc-berkeleyx-data8-1x",target:"_blank",rel:"noopener noreferrer"}},[t._v("Python 数据处理"),r("OutboundLink")],1)]),t._v(" "),r("li",[t._v("第二部分:"),r("a",{attrs:{href:"https://www.edx.org/course/foundations-data-science-inferential-uc-berkeleyx-data8-2x",target:"_blank",rel:"noopener noreferrer"}},[t._v("抽样推断"),r("OutboundLink")],1)]),t._v(" "),r("li",[t._v("第三部分:"),r("a",{attrs:{href:"https://www.edx.org/course/foundations-data-science-prediction-uc-berkeleyx-data8-3x",target:"_blank",rel:"noopener noreferrer"}},[t._v("预测与机器学习"),r("OutboundLink")],1)])]),t._v(" "),t._m(4),t._v(" "),r("p",[t._v("[文章] "),r("a",{attrs:{href:"https://github.com/openblockchains/programming-blockchains-step-by-step",target:"_blank",rel:"noopener noreferrer"}},[t._v("Programming Blockchains Step-by-Step"),r("OutboundLink")],1),t._v(", by Gerald Bauer")]),t._v(" "),r("p",[t._v("介绍如何使用 Ruby 语言从零开始写一个区块链实现,代码非常好懂,并有各种基础概念的解释。")]),t._v(" "),t._m(5),t._v(" "),r("p",[t._v("[文章] "),r("a",{attrs:{href:"http://www.catonmat.net/blog/low-level-bit-hacks-you-absolutely-must-know/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Low Level Bit Hacks You Absolutely Must Know"),r("OutboundLink")],1),t._v(", by Peter Krumins")]),t._v(" "),r("p",[t._v("位运算(bit operation)的用途,有很多例子。")]),t._v(" "),t._m(6),t._v(" "),r("p",[t._v("[文章] "),r("a",{attrs:{href:"https://github.com/reactjs/react-basic",target:"_blank",rel:"noopener noreferrer"}},[t._v("React - Basic Theoretical Concepts"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("React 官方关于 React 原始设计思想的解释。")]),t._v(" "),r("p",[t._v("6、[图片] "),r("a",{attrs:{href:"https://github.com/wojtekmaj/react-lifecycle-methods-diagram",target:"_blank",rel:"noopener noreferrer"}},[t._v("React v16.3 生命周期的示意图"),r("OutboundLink")],1)]),t._v(" "),t._m(7),t._v(" "),r("p",[t._v("1、[电子书] "),r("a",{attrs:{href:"https://stevedonovan.github.io/rust-gentle-intro/readme.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("A Gentle Introduction To Rust"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("Rust 语言入门教程")]),t._v(" "),r("p",[t._v("2、[电子书] "),r("a",{attrs:{href:"https://mrpandey.github.io/d3graphTheory/index.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("D3 Graph Theory"),r("OutboundLink")],1)]),t._v(" "),t._m(8),t._v(" "),r("p",[t._v("图理论(graph theory)是重要的数学分支,在数据处理领域有着重要应用。这个教程采用可视化库 D3,把图理论变成了可视化互动教程。")]),t._v(" "),r("p",[t._v("3、[文章] "),r("a",{attrs:{href:"https://www.listendata.com/2018/03/regression-analysis.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("数据回归的15种类型"),r("OutboundLink")],1)]),t._v(" "),t._m(9),t._v(" "),r("p",[t._v("回归(regression)是数据处理的常用技术,用来找出数据的模式。本文介绍数据回归的15种拟合。")]),t._v(" "),r("p",[t._v("4、[视频课程] "),r("a",{attrs:{href:"https://education.github.community/t/20-of-the-most-popular-courses-on-github/27832",target:"_blank",rel:"noopener noreferrer"}},[t._v("GitHub 最受欢迎的20个课程仓库"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("很多开放课程的仓库放在 GitHub 上面,GItHub 官方列出了最受欢迎的20个仓库。")]),t._v(" "),r("p",[t._v("5、[文章] "),r("a",{attrs:{href:"https://betanalpha.github.io/assets/case_studies/probability_theory.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("Probability Theory (For Scientists and Engineers)"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("一个概率论的概览性介绍,每个章节后面有一个 R 语言的小例子。")]),t._v(" "),r("p",[t._v("6、[电子书] "),r("a",{attrs:{href:"http://d3indepth.com/",target:"_blank",rel:"noopener noreferrer"}},[t._v("D3 In Depth"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("可视化引擎 D3 的教程。")]),t._v(" "),r("p",[t._v("7、[文章] "),r("a",{attrs:{href:"http://blog.pragmaticengineer.com/distributed-architecture-concepts-i-have-learned-while-building-payments-systems/",target:"_blank",rel:"noopener noreferrer"}},[t._v("分布式系统的几个基本概念"),r("OutboundLink")],1)]),t._v(" "),r("p",[t._v("Uber 架构师分享在搭建分布式支付系统过程中,遇到的最重要的几个概念:SLA、scaling、Consistency、Durability、Idempotency等。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"机器学习"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#机器学习","aria-hidden":"true"}},[this._v("#")]),this._v(" 机器学习")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"吴恩达的免费电子书"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#吴恩达的免费电子书","aria-hidden":"true"}},[this._v("#")]),this._v(" 吴恩达的免费电子书")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://www.ruanyifeng.com/blogimg/asset/2018/bg2018042302.png",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"数据科学基础"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#数据科学基础","aria-hidden":"true"}},[this._v("#")]),this._v(" 数据科学基础")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"使用-ruby-语言从零开始写一个区块链实"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#使用-ruby-语言从零开始写一个区块链实","aria-hidden":"true"}},[this._v("#")]),this._v(" 使用 Ruby 语言从零开始写一个区块链实")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"位运算的用途"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#位运算的用途","aria-hidden":"true"}},[this._v("#")]),this._v(" 位运算的用途")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"react-原始设计官方解释"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#react-原始设计官方解释","aria-hidden":"true"}},[this._v("#")]),this._v(" React 原始设计官方解释")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://www.ruanyifeng.com/blogimg/asset/2018/bg2018042304.png",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://www.ruanyifeng.com/blogimg/asset/2018/bg2018042809.jpg",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"http://www.ruanyifeng.com/blogimg/asset/2018/bg2018042810.jpg",alt:""}})])}],!1,null,null,null);n.options.__file="ziyuan01.md";e.default=n.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/7.c96373d9.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[7],{158:function(a,t,r){"use strict";r.r(t);var e=r(0),s=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var a=this,t=a.$createElement,r=a._self._c||t;return r("div",{staticClass:"content"},[r("h1",{attrs:{id:"介绍"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#介绍","aria-hidden":"true"}},[a._v("#")]),a._v(" 介绍")]),a._v(" "),r("p",[a._v("本网站主要目的是做一个资源的整合,为大家提供一个优秀网站的导航和学习资源的分享。大部分学习资源均为开源且免费的资源,不存在收费课程的搬运。")]),a._v(" "),r("h2",{attrs:{id:"如何使用?"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#如何使用?","aria-hidden":"true"}},[a._v("#")]),a._v(" 如何使用?")]),a._v(" "),r("h3",{attrs:{id:"搜索关键词"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#搜索关键词","aria-hidden":"true"}},[a._v("#")]),a._v(" 搜索关键词")]),a._v(" "),r("p",[a._v("本站点支持本地搜索,试试搜一下你需要的资源「 关键词 」,或许就在其中哦!")]),a._v(" "),r("h3",{attrs:{id:"意外惊喜"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#意外惊喜","aria-hidden":"true"}},[a._v("#")]),a._v(" 意外惊喜")]),a._v(" "),r("p",[a._v("你可以本着涉猎的心理查看本站点的资源,或许有你意想不到的资源在等着你。")]),a._v(" "),r("h3",{attrs:{id:"分类清晰"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#分类清晰","aria-hidden":"true"}},[a._v("#")]),a._v(" 分类清晰")]),a._v(" "),r("p",[a._v("大多数优秀的开源资源都具备良好的入门指导,本站尽量声明哪些是入门资源,进阶资源等等。")]),a._v(" "),r("h2",{attrs:{id:"学习的目标"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#学习的目标","aria-hidden":"true"}},[a._v("#")]),a._v(" 学习的目标")]),a._v(" "),r("p",[a._v("优秀的课程来自优秀的工程师,大多都是行业的佼佼者,不乏开源社区的活跃着。站在巨人的肩膀上学习,是现在学习的先天优势,接下就就是制定有计划的学习路径,\n努力学习,自我驱动,异或带新学习。")]),a._v(" "),r("h3",{attrs:{id:"系统化学习"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#系统化学习","aria-hidden":"true"}},[a._v("#")]),a._v(" 系统化学习")]),a._v(" "),r("p",[a._v("本站收集的资源大部分均为系统化的学习资源,需要耐着性子一步一步,踏踏实实的学习。不可东一棒子,西一榔头。")]),a._v(" "),r("h3",{attrs:{id:"提升自己"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#提升自己","aria-hidden":"true"}},[a._v("#")]),a._v(" 提升自己")])])}],!1,null,null,null);s.options.__file="README.md";t.default=s.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/8.7ab82f6f.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[8],{151:function(t,e,s){"use strict";s.r(e);var a=s(0),i=Object(a.a)({},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"content"},[this._m(0),this._v(" "),this._m(1),this._v(" "),e("p",[e("a",{attrs:{href:"https://github.com/luhuisicnu/The-Flask-Mega-Tutorial-zh",target:"_blank",rel:"noopener noreferrer"}},[e("OutboundLink")],1)]),this._v(" "),e("p",[this._v("Flask 是 Python 的 Web 开发框架。这个教程教你如何用 Python 和 Flask 来创建 Web 应用。")])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"自学编程"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#自学编程","aria-hidden":"true"}},[this._v("#")]),this._v(" 自学编程")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"flask-中文教程"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#flask-中文教程","aria-hidden":"true"}},[this._v("#")]),this._v(" Flask 中文教程")])}],!1,null,null,null);i.options.__file="coding.md";e.default=i.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/dist/assets/js/9.121948bf.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[9],{149:function(a,t,r){"use strict";r.r(t);var e=r(0),s=Object(e.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var a=this,t=a.$createElement,r=a._self._c||t;return r("div",{staticClass:"content"},[r("h1",{attrs:{id:"介绍"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#介绍","aria-hidden":"true"}},[a._v("#")]),a._v(" 介绍")]),a._v(" "),r("p",[a._v("本网站主要目的是做一个资源的整合,为大家提供一个优秀网站的导航和学习资源的分享。大部分学习资源均为开源且免费的资源,不存在收费课程的搬运。")]),a._v(" "),r("h2",{attrs:{id:"如何使用?"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#如何使用?","aria-hidden":"true"}},[a._v("#")]),a._v(" 如何使用?")]),a._v(" "),r("h3",{attrs:{id:"搜索关键词"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#搜索关键词","aria-hidden":"true"}},[a._v("#")]),a._v(" 搜索关键词")]),a._v(" "),r("p",[a._v("本站点支持本地搜索,试试搜一下你需要的资源「 关键词 」,或许就在其中哦!")]),a._v(" "),r("h3",{attrs:{id:"意外惊喜"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#意外惊喜","aria-hidden":"true"}},[a._v("#")]),a._v(" 意外惊喜")]),a._v(" "),r("p",[a._v("你可以本着涉猎的心理查看本站点的资源,或许有你意想不到的资源在等着你。")]),a._v(" "),r("h3",{attrs:{id:"分类清晰"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#分类清晰","aria-hidden":"true"}},[a._v("#")]),a._v(" 分类清晰")]),a._v(" "),r("p",[a._v("大多数优秀的开源资源都具备良好的入门指导,本站尽量声明哪些是入门资源,进阶资源等等。")]),a._v(" "),r("h2",{attrs:{id:"学习的目标"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#学习的目标","aria-hidden":"true"}},[a._v("#")]),a._v(" 学习的目标")]),a._v(" "),r("p",[a._v("优秀的课程来自优秀的工程师,大多都是行业的佼佼者,不乏开源社区的活跃着。站在巨人的肩膀上学习,是现在学习的先天优势,接下就就是制定有计划的学习路径,\n努力学习,自我驱动,异或带新学习。")]),a._v(" "),r("h3",{attrs:{id:"系统化学习"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#系统化学习","aria-hidden":"true"}},[a._v("#")]),a._v(" 系统化学习")]),a._v(" "),r("p",[a._v("本站收集的资源大部分均为系统化的学习资源,需要耐着性子一步一步,踏踏实实的学习。不可东一棒子,西一榔头。")]),a._v(" "),r("h3",{attrs:{id:"提升自己"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#提升自己","aria-hidden":"true"}},[a._v("#")]),a._v(" 提升自己")])])}],!1,null,null,null);s.options.__file="README.md";t.default=s.exports}}]); -------------------------------------------------------------------------------- /docs/.vuepress/override.styl: -------------------------------------------------------------------------------- 1 | /* .vuepress/override.styl */ 2 | -------------------------------------------------------------------------------- /docs/.vuepress/public/flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aikuyun/bigdata-doc/5763d360077d840a3dd9a5e333e85cd264d4dd4b/docs/.vuepress/public/flash.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/grid_017277d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aikuyun/bigdata-doc/5763d360077d840a3dd9a5e333e85cd264d4dd4b/docs/.vuepress/public/images/grid_017277d.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/laptop_fd4699c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aikuyun/bigdata-doc/5763d360077d840a3dd9a5e333e85cd264d4dd4b/docs/.vuepress/public/images/laptop_fd4699c.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/mac_3336890.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aikuyun/bigdata-doc/5763d360077d840a3dd9a5e333e85cd264d4dd4b/docs/.vuepress/public/images/mac_3336890.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/machine_b35833f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aikuyun/bigdata-doc/5763d360077d840a3dd9a5e333e85cd264d4dd4b/docs/.vuepress/public/images/machine_b35833f.png -------------------------------------------------------------------------------- /docs/.vuepress/public/images/phone_66669bd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aikuyun/bigdata-doc/5763d360077d840a3dd9a5e333e85cd264d4dd4b/docs/.vuepress/public/images/phone_66669bd.png -------------------------------------------------------------------------------- /docs/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | .content { 2 | font-size 30px 3 | } 4 | -------------------------------------------------------------------------------- /docs/It-chat/case.md: -------------------------------------------------------------------------------- 1 | # 行业案例 2 | 3 | ## OLAP 4 | 5 | 1. Apache Kylin在美团数十亿数据OLAP场景下的实践 [查看文章](https://mp.weixin.qq.com/s/rUSlZ_Pp4OPECiYQzbfb2A) 6 | 7 | 2. 小米大数据:借助Apache Kylin打造高效、易用的一站式OLAP解决方案 [查看文章](https://mp.weixin.qq.com/s/JPK_wHHaI6KPbw3hP34H5g) 8 | 9 | 3. 美团点评基于 Flink 的实时数仓建设实践 [查看文章](https://tech.meituan.com/meishi_data_flink.html) 10 | 11 | 4. 快手万亿级实时 OLAP 平台的建设与实践 [查看文章](https://mp.weixin.qq.com/s/bKDtv892f4TJVV-JjW0vfQ) 12 | 13 | 5. 58 HBase 平台实践和应用-OLAP篇, Kylin 是一个底层使用 HBase 作为存储引擎和查询引擎的的多维分析平台,并对外提供标准SQL查询功能。在超大规模数据集上,Kylin 还能达到亚秒级的查询响应。[查看文章](https://mp.weixin.qq.com/s/g4KYKvb48ZosIvPfHPgVIw) 14 | 15 | ## 消息队列 16 | 17 | 0. Kafka文件存储机制那些事 [查看文章](https://tech.meituan.com/kafka_fs_design_theory.html) 18 | 19 | ## Java 20 | 21 | 0. 不得不说的Java “锁” 事 [查看文章](https://tech.meituan.com/Java_Lock.html) 22 | 23 | ## 搜索 24 | 25 | 0. 京东到家订单中心 Elasticsearch 演进历程 [查看文章](https://mp.weixin.qq.com/s/TrCJJtvhjB2m29fOOa3Rzg) 26 | 27 | ## HBase 28 | 29 | 0. HBase实战 | 58HBase平台实践和应用-平台建设篇 [查看文章](https://mp.weixin.qq.com/s/x4Hx0402HEEtNchYEzlwww) 30 | ## 评价交流 31 | 32 | > 欢迎留下的你的想法~ 33 | 34 | 35 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /flash.png 4 | actionText: 开始学习 5 | actionLink: /ziyuan01/ 6 | features: 7 | - title: 集成 8 | details: 优质学习资源 9 | - title: 持续 10 | details: 分享有价值的开源资料 11 | - title: 高效 12 | details: 有目的的学习,高效升级打怪 13 | footer: Copyright © 2016 - 2019 cuteximi.com 14 | --- 15 | -------------------------------------------------------------------------------- /docs/about/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: custom-page-class 3 | layout: showtime 4 | --- 5 | 6 |
7 | -------------------------------------------------------------------------------- /docs/about/bigdata.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageClass: custom-page-class 3 | layout: showtime 4 | --- 5 | 6 |
7 | -------------------------------------------------------------------------------- /docs/artical/artical.md: -------------------------------------------------------------------------------- 1 | # 技术文章集合 2 | 3 | ## kafka 4 | 5 | 1. kafka offset 介绍 [查看文章](https://blog.csdn.net/qq_37502106/article/details/80409748) 6 | 7 | 2. zookeeper + kafka 的leader机制 [查看文章](https://blog.csdn.net/qq_37502106/article/details/80260415) 8 | 9 | 3. kafka 一致性的重要机制 ISR,leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护。如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除. 当ISR中所有Replica都向Leader发送ACK时,leader才commit [查看文章](https://blog.csdn.net/qq_37502106/article/details/80271800) 10 | 11 | 12 | ## JAVA 13 | 14 | 1. ConcurentHashMap 在JDK7 和 JDK8中的区别。JDK7中采用**分段锁**的机制,JDK8中采用了 CAS算法。[查看文章](https://blog.csdn.net/woaiwym/article/details/80675789) 15 | 16 | 2. Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析 [查看文章](http://www.importnew.com/28263.html) 17 | 18 | ## Spark 19 | 20 | 0. Spark SQL 官方文章,必看。[查看文章](http://spark.apache.org/docs/1.6.3/sql-programming-guide.html) 21 | 22 | 1. Spark Sql 的二次排序取 Top N [查看文章](https://blog.csdn.net/wangpei1949/article/details/66978412) 23 | 24 | 2. Spark Sql 使用hiveContext [查看文章](https://blog.csdn.net/qq_41455420/article/details/79515511) 25 | 26 | 3. Spark 分组TopN [查看文章](https://blog.csdn.net/luofazha2012/article/details/80636858) 27 | 28 | 4. Spark中foreachPartition和mapPartitions的区别 [查看文章](https://blog.csdn.net/u010454030/article/details/78897150) 29 | 30 | 5. 谓词下推 [查看文章](https://blog.csdn.net/zxm1306192988/article/details/80255747) 31 | 32 | 6. Spark 数据本地化。 [查看文章](https://www.cnblogs.com/jxhd1/p/6702224.html?utm_source=itdadao&utm_medium=referral) 33 | 34 | ## HDFS 35 | 36 | 0. HDFS 的知识点总结 [查看文章](https://www.cnblogs.com/caiyisen/p/7395843.html) 37 | 38 | 1. 初识 HDFS [查看文章](https://www.cnblogs.com/wxplmm/p/7239342.html) 39 | 40 | 2. Hadoop 集群里面的端口 [查看文章](https://www.cnblogs.com/waterfish/articles/4533076.html) 41 | ## 系统 42 | 43 | 0. Ubuntu 18.04 网络配置,改动了。[查看文章](https://help.ubuntu.com/lts/serverguide/network-configuration.html.zh-CN) 44 | 45 | ## zookeeper 46 | 47 | 0. 查看集群的状态。[查看文章](https://blog.csdn.net/luoww1/article/details/76078772) 48 | -------------------------------------------------------------------------------- /docs/distribute/distribute.md: -------------------------------------------------------------------------------- 1 | # 分布式系统基础 2 | 3 | ## 定义 4 | 5 | > 分布式系统:利用多台计算机协同解决单台计算机无法解决的计算、存储问题 6 | 7 | 8 | 单个节点<----->网络<----->单个节点<----->网络<----->单个节点 9 | 10 | ## 常见的异常情况 11 | * 分布式系统的核心问题就是解决各种异常情况 12 | * __机器宕机__(机器出问题): 13 | * 最常见,一般需要人工介入 14 | * 节点重启后会丢失内存信息(部分分布式系统中,节点可以通过读取本地存储或其他节点的方式恢复内存信息) 15 | * __网络异常__(网络出问题): 16 | * 消息丢失 17 | * 在IP 网络中,网络层不保证数据报文的可靠传递,在发生网络拥塞,路由变动、设备异常等情况时,都可能发生数据丢失的问题 18 | * 如果某些节点正常,而某些节点始终无法正常通信,则称之为“网络分化” 19 | * 消息乱序 20 | * 消息和发送的顺序不一致 21 | * 应对方法:序列号机制 22 | * 数据错误 23 | * 比特错误 24 | * 应对方法:校验码 25 | * TCP协议下仍要考虑网络异常: 26 | * TCP 只能保证传输层可靠,而无法保证上层通信仍可靠 27 | 28 | > * 分布式系统三态: 29 | > \* 成功 30 | > \* 失败 31 | > \* 超时: 32 | > \* 某个节点宕机,无法返回成功信号 33 | > \* 网络异常,无法发出/收到相应信号 34 | 35 | * __存储数据丢失__ 36 | * 常见以硬盘作为存储介质,硬盘损坏导致 37 | * 对策:从其他节点读取,恢复存储 38 | * __其他异常__(半死不活类) 39 | * 磁盘故障导致的IO缓慢,时好时坏的,导致进程无响应 40 | * 网络不稳定 41 | * __异常一定会发生,分布式系统的样本一般较大:即 小概率 X 大样本 =大概率__ 42 | * 比如某系统中每天异常发生的概率为10 的-9 次方,但系统每天处理的请求为 10的8次方,最终这种异常出现的概率就成为了 10% 43 | 44 | ## 副本: 45 | > * 副本 是指在分布式系统中为数据或者服务提供的冗余 46 | > \* 数据副本是解决数据丢失异常的唯一手段 47 | > \* 服务副本,是指数个节点提供某种相同的服务,这种服务一般并不依赖节点本地的存储,其所需数据一般来自其他节点 48 | > \* 例如:GFS 的一个 chunk 的数个副本就是数据副本 49 | > \* 而 mapreduce 的 job worker则是服务副本 50 | 51 | * __副本一致性__:分布式系统通过副本控制协议,使得从系统外部读取系统内部的各个副本的数据在一定的约束提哦啊见下相同,称之为 副本一致性(consistency) 52 | * 强一致性(strong consistency): 53 | * 任何时刻,任何用户或者节点 都可以读到 最近一次成功更新后的副本数据。 54 | * 一致性最高,最难实现 55 | * 单调一致性(monotonic consistency): 56 | * 任何时刻,任何用户一旦读到某个数据在某次更新后的值,这个用户不会再读到比这个值更旧的值 57 | * 若于强一致,但是却非常实用;因为通常来说,用户只关心从己方视角观察到的一致性 58 | * 回话一致性(session consistency): 59 | * 任何时刻,任何用户在某一次回话中一旦读到某个数据在某次更新后的值,这个用户在这次回话中不会再读到比这个值更旧的值 60 | * 通过引入 会话 的概念,在单调一致的基础上更加放松约束。 61 | * 最终一致性(eventual consistency) 62 | * 要求一旦更新成功,各个副本上的数据最终将达到完全一致的状态,但是达到最终一致状态所需要的时间不能保障。 63 | * 对于最终一致的系统而言,一个用户只要始终读取某一个副本的数据,则可以实现类似单调一致的效果 64 | * 弱一致性(week consistency): 65 | * 一旦某个更新成功,用户无法在一个确定的时间内读到这次更新的值,且即使在某个副本上读到了新的值,也不能保证在其他副本上可以读到新的值。 66 | * 弱一致性在实际中很难使用,需要应用方做很多工作来保证系统可用。 67 | 68 | ## 衡量分布式系统的指标: 69 | * 性能(performance): 70 | * 系统的吞吐量: 指系统在某一时间可以处理的数据总量: 71 | * 系统的响应延迟:系统完成某一功能需要使用的时间 72 | * 系统的并发能力: 系统可以同时完成某一功能的能力: QPS(query per second) 73 | * 三个性能往往会相互制约 74 | * 可用性(avaiability) 75 | * 指在面对 各种异常情况,可以提供服务的能力 76 | * 可扩展性(scalability) 77 | * 指分布式系统通过扩展集群机器规模提高系统性能(吞吐、延迟、并发)、存储容量、计算能力的特性。 78 | * 一致性 79 | * 分布式系统为了提高可用性,总是不可避免的使用副本机制,从而引发副本一致性问题。 80 | * 越是强的一致性模型,对用户使用起来越简单。 81 | 82 | 83 | --- 84 | 85 | # 数据分布方式 86 | 87 | --- 88 | 89 | 90 | > ## 分布式VS单机: 91 | * 最大区别在于 问题的规模,即 计算、存储的区别 92 | * 无论是计算还是存储,其问题的输入对象都是数据,所以如何拆解分布式系统的输入数据成为分布式系统的基本问题,这里把这个问题叫做 数据分布方式: 93 | 94 | 1. __哈希方式__:按照数据的某一特征(如id)计算哈希值,并将哈希值与集群中的机器建立映射关系,从而将不同的哈希值的数据分布到不同的机器上。 95 | * 优点 96 | * 只要哈希函数的散列特性好,哈希方式可以比较为均匀的将数据分布到集群中去 97 | * 需要的元数据信息也简单,只要知道 哈希函数的计算方式及 模的服务器总量 即可 98 | * 缺点: 99 | * 可扩展性不高,一旦集群规模需要扩展,则几乎所有的数据都要被迁移 并重新分布 100 | * 工程中,往往在扩展时,将集群规模成本扩展,按照数据重新计算哈希,这样原本一台机器上的数据只需要迁移一半到对应的机器即可 101 | * 另一张思路是,将对应关系交由专门的元数据服务器来进行管理。 102 | * 一旦数据特征值的数据严重不均,容易出现“数据倾斜“(data skew)的问题。例如:若系统以某个id作为哈希分数据,当某个id数据量异常庞大时, 该用户的数据将始终由一台机器处理,造成数据缓慢的问题 103 | * 解决思路: 重新选取数据特征 104 | 2. __按照数据范围分布__:将数据按照特征值的值域范围划分为不同的区间(数据区间的数据大小和区间大大小时内有关系的)(动态划分区间) 105 | * 需要记录数据分布情况 106 | * 优点: 107 | * 扩展性比较好 108 | * 缺点: 109 | * 需要维护较为复杂的元数据 110 | 3. __按数据量分布__: 与具体的数据特征无关,将数据整体视为一个顺序增长的文件,然后将文件按照固定的大小分为若干的数据块,不同的数据块分到不同的服务器上 111 | * 优缺点与按数据范围类似 112 | 4. __一致性哈希__: 使用一个哈希函数计算数据或数据特征的哈希值,令该哈希函数的输出值域成为一个封闭的环,即哈希函数的输出的最大值是最小值的前序。 113 | * 优点: 114 | * 可以任意动态的添加、删除节点 115 | * 缺点: 116 | * 随机分布节点的方式使得其很难均匀的分布哈希值域 117 | * 改进算法是:引入 虚节点的概念(先均匀分布,添加节点时也新添加 虚节点) 118 | 119 | ## 副本与数据分布 120 | * 以机器为单位VS以文件段为单位: 121 | * 以文件段为单位的优点: 122 | * 数据恢复效率高,文件副本将在所有的机器上均有分布 123 | * 副本分布与机器无关,有利于集群扩展 124 | * 同样,文件段的方式也会引起元数据的开销过大 125 | * 本地化计算: 126 | * 尽量将计算和存储都放在同一台物理机上进行 127 | 128 | 工程应用: 129 | 130 | | 实例 | 数据分布方案 | 131 | | :--- | :--- | 132 | | GFS、HDFS | 按数据量分布 | 133 | | mapreduce | 本地化计算 | 134 | | big talbe /hbase | 按数据范围 | 135 | 136 | # 副本控制协议: 137 | > 指按照特定的协议流程控制副本数据的读写行为,使得副本满足一定的可用性和一致性要求的分布式协议。 138 | > 主要包含两大类:1、 中心化 2、 区中心化 139 | 140 | ## 中心化副本控制协议: 141 | * 由一个中心节点协调副本数据的更新、维护副本之间的一致性 142 | * 优点: 143 | * 协议简单,将分布式并发控制问题简化为单机并发控制问题 144 | * 缺点: 145 | * 存在停服时间: 系统的可用性依赖于中心化节点,当中心节点异常或与中心节点通信异常时,系统将失去某些服务。 146 | 147 | ### Primary-secondary协议 148 | > 在本协议中,副本被分为两大类,其中有且仅有一个副本作为primary副本,除primary的都时secondary副本。维护primary的节点作为中心节点,中心节点负责维护数据的更新、并发控制、协调副本的一致性。 149 | 150 | 1. 数据更新基本流程: 151 | 1. 外部节点将更新操作发给primary节点 152 | 2. primary节点进行并发控制即确定并发更新操作的先后顺序 153 | 3. primary节点将更新操作发送给secondary节点 154 | 4. primary 根据 secondary 节点的完成情况决定更新是否成功并将结果返回外部节点 155 | 2. 数据读取方式: 156 | 1. primary-secondary实现强一致性的几种思路: 157 | 1. 由于数据的更新流程都是由primary控制的,primary副本上的数据一定是最新的,所以如果始终只读primary副本的数据,可以实现强一致性 158 | 2. 由primary控制节点secondary节点的可用性 159 | 3. primary副本的确定与切换 160 | 1. 在 primary-secondary 类型的分布式系统中,哪个副本是 primary 这一信息都属于元信息,由专门的元数据服务器维护。执行更新操作时,首先查询元数据服务器获取副本的 primary 信息,从而进一步执行数据更新流程。 161 | 2. 切换副本的难点在于两个方面: 162 | 1. 如何确定节点的状态以发现原 primary 节点异常是一个较为复杂的问题 163 | 2. 切换 primary后,不能影响副本的一致性 164 | 3. 由于分布式系统中可靠的发现节点异常是需要一定的探测时间的,这样的探测时间通常是 10秒级别在这 10 秒时间内,系统不能提供更新服务。从这里可以看到,primary-backup 类副本协议的最大缺点就是由于 primary 切换带来的一定的停服务时间。 165 | 4. 数据同步 166 | 1. Primary-secondary 型协议一般都会遇到 secondary 副本与 primary 不一致的问题: 167 | 1. secondary 上的数据落后于 primary 上的数据。 168 | 1. 回放 primary 上的操作日志 169 | 2. secondary 上的数据有脏数据。 170 | 1. 以简单的直接丢弃有脏数据的副本,这样相当于副本没有数据。 171 | 2. 也可以设计一些基于 undo 日志的方式从而可以删除脏数据 172 | 3. secondary 是一个新增加的副本,完全没有数据: 173 | 1. 直接拷贝 primary 副本的数据:快照 + 回放日志 174 | 5. 工程应用: 175 | 1. GFS 系统的副本控制协议是典型的 Primary-Secondary 型协议,Primary 副本由 Master 指定,Primary 副本决定并发更新操作的顺序。虽然在 GFS 中,更新操作的数据由客户端提交,并在各个副本之间流式的传输,及由上一个副本传递到下一个副本,每个副本都即接受其他副本的更新,也向下更新另一个副本,但是数据的更新过程完全是由 primary 控制的,所以也可以认为数据是由primary 副本同步到 secondary 副本的。 176 | ## 去中心化副本控制协议 177 | > 与中心化副本系统协议最大的不同是,去中心化副本控制协议没有中心节点,协议中所有的节点都是完全对等的,节点之间通过平等协商达到一致 178 | * 优点: 179 | * 没有因为中心化节点异常而带来的停服务等问题 180 | * 缺点: 181 | * 协议过程通常比较复杂。尤其当去中心化协议需要实现强一致性时,协议流程变得复杂且不容易理解 182 | Paxos 是唯一在工程中得到应用的强一致性去中心化副本控制协议 183 | # Lease机制 184 | > Lease 机制是最重要的分布式协议,广泛应用于各种实际的分布式系统中 185 | 186 | ### 简单的例子: 187 | 1. 在一个分布式系统中,有一个中心服务器节点,中心服务器存储、维护着一些数据,这些数据是系统的元数据。系统中其他的节点通过访问中心服务器节点读取、修改其上的元数据(那么中心服务器节点的性能成为系统的瓶颈) 188 | 2. 心服务器在向各节点发送数据时同时向节点颁发一个 lease。每个 lease 具有一个有效期,和信用卡上的有效期类似,lease 上的有效期通常是一个明确的时间点 189 | 3. 在 lease 的有效期内,中心服务器保证不会修改对应数据的值(假设中心服务器与各节点的时钟是同步的) 190 | * lease 机制可以容错的关键是: 191 | * 服务器一旦发出数据及 lease,无论客户端是否收到,也无论后续客户端是否宕机,也无论后续网络是否正常,服务器只要等待 lease 超时,就可以保证对应的客户端节点不会再继续 cache 数据,从而可以放心的修改数据而不会破坏 cache 的一致性。 192 | ### lease机制的本质: 193 | * lease 的定义: 194 | * Lease 是由颁发者授予的在某一有效期内的承诺 195 | * Lease 机制具有很高的容错能力: 196 | * 通过引入有效期,Lease 机制能否非常好的容错网络异常: 单向通信 197 | * Lease 机制能较好的容错节点宕机 198 | * lease 机制不依赖于存储 199 | * Lease 机制依赖于有效期,这就要求颁发者和接收者的时钟是同步的: 200 | * 为防止出现时钟不同步,实践中的通常做法是将颁发者的有效期设置得比接收者的略大,只需大过时钟误差就可以避免对 lease 的有效性的影响 201 | ### 基于 lease 机制确定节点状态 202 | 🌰说明: 203 | 1. 在一个 primary-secondary 架构的系统中,有三个节点 A、B、C 互为副本,其中有一个节点为 primary,且同一时刻只能有一个 primary 节点。另有一个节点 Q 负责判断节点 A、B、C的状态,一旦 Q 发现 primary 异常,节点 Q 将选择另一个节点作为 primary。假设最开始时节点 A为 primary,B、C 为 secondary。节点 Q 需要判断节点 A、B、C 的状态是否正常。 204 | 205 | 2. 由中心节点向其他节点发送 lease,若某个节点持有有效的 lease,则认为该节点正常可以提供服务。用于例 2.3.1 中,节点 A、B、C 依然周期性的发送 heart beat 报告自身状态,节点 Q 收到 heart beat后发送一个 lease,表示节点 Q 确认了节点 A、B、C 的状态,并允许节点在 lease 有效期内正常工作。节点 Q 可以给 primary 节点一个特殊的 lease,表示节点可以作为 primary 工作。一旦节点 Q 希望切换新的 primary,则只需等前一个 primary 的 lease 过期,则就可以安全的颁发新的 lease 给新的primary 节点,而不会出现“双主”问题。 206 | 207 | 3. 在实际系统中,若用一个中心节点发送 lease 也有很大的风险,一旦该中心节点宕机或网络异常,则所有的节点没有 lease,从而造成系统高度不可用。为此,实际系统总是使用多个中心节点互为副本,成为一个小的集群,该小集群具有高可用性,对外提供颁发 lease 的功能。chubby 和 zookeeper都是基于这样的设计 208 | ### lease 的有效期时间选择: 209 | * Lease 的有效期虽然是一个确定的时间点,当颁发者在发布 lease 时通常都是将当前时间加上一个固定的时长从而计算出 lease 的有效期。 210 | * 工程中,常选择的 lease 时长是 10 秒级别,这是一个经过验证的经验值,实践中可以作为参考并综合选择合适的时长 211 | ### GFS 中的 Lease 212 | GFS 中使用 Lease 确定 Chuck 的 Primary 副本。Lease 由 Master 节点颁发给 primary 副本,持有Lease 的副本成为 primary 副本。Primary 副本控制该 chuck 的数据更新流量,确定并发更新操作在chuck 上的执行顺序。GFS 中的 Lease 信息由 Master 在响应各个节点的 HeartBeat 时附带传递(piggyback)。对于每一个 chuck,其上的并发更新操作的顺序在各个副本上是一致的,首先 master选择 primary 的顺序,即颁发 Lease 的顺序,在每一任的 primary 任期内,每个 primary 决定并发更新的顺序,从而更新操作的顺序最终全局一致。当 GFS 的 master 失去某个节点的 HeartBeat 时,只需待该节点上的 primary chuck 的 Lease 超时,就可以为这些 chuck 重新选择 primary 副本并颁发 lease。 213 | ### Chubby 与 Zookeeper 中的 Lease 214 | # Quorum 机制 215 | ## write-all-read-one 216 | > 是一种最简单的副本控制规则,顾名思义即在更新时写所有的副本,只有在所有的副本上更新成功,才认为更新成功,从而保证所有的副本一致,这样在读取数据时可以读任一副本上的数据。 217 | 其实现需要依赖一种假设: 218 | > 假设有一种 magic 的机制,当某次更新操作 wi 一旦在所有 N 个副本上都成功,此时全局都能知道这个信息,此后读取操作将指定读取数据版本为 vi 的数据,称在所有 N 个副本上都成功的更新操作为“成功提交的更新操作”,称对应的数据为“成功提交的数据”。在 WARO 中,如果某次更新操作 wi 在某个副本上失败,此时该副本的最新的数据只有 vi-1,由于不满足在所有 N 个副本上都成功,则 wi 不是一个“成功提交的更新操作”,此时,虽然其他 N-1 个副本上最新的数据是 vi,但 vi 不是一个“成功提交的数据”,最新的成功提交的数据只是 vi-1 219 | 这里需要特别强调的是,在工程实践中,这种 magic 的机制往往较难实现或效率较低。 220 | ## Quorum 定义 221 | > WARO 牺牲了更新服务的可用性,最大程度的增强读服务的可用性。下面将 WARO 的条件进 222 | > 行松弛,从而使得可以在读写服务可用性之间做折中,得出 Quorum 机制: 223 | > 在 Quorum 机制下,当某次更新操作 wi 一旦在所有 N 个副本中的 W 个副本上都成功,则就称该更新操作为“成功提交的更新操作”,称对应的数据为“成功提交的数据”。令 R>N-W,由于更新操作 wi 仅在 W 个副本上成功,所以在读取数据时,最多需要读取 R 个副本则一定能读到 wi 更新后的数据 vi 。如果某次更新 wi 在 W 个副本上成功,由于 W+R>N,任意 R 个副本组成的集合一定与成功的W个副本组成的集合有交集,所以读取R个副本一定能读到wi更新后的数据vi。如图 2-10,Quorum 机制的原理可以文森图表示。 224 | 225 | 226 | 227 | ![image.png | center | 411x191](https://cdn.yuque.com/yuque/0/2018/png/94821/1529219892350-684f2bac-e976-40c4-a23b-4ad8aaa4b62c.png "") 228 | 229 | 230 | 仅仅依赖 quorum 机制是无法保证强一致性的。因为仅有 quorum 机制时无法确定最新已成功提交的版本号,除非将最新已提交的版本号作为元数据由特定的元数据服务器或元数据集群管理,否则很难确定最新成功提交的版本号 231 | 232 | Quorum 机制的三个系统参数 N、W、R 控制了系统的可用性,也是系统对用户的服务承诺:数据最多有 N 个副本,但数据更新成功 W 个副本即返回用户成功。对于一致性要求较高的 Quorum 系统,系统还应该承诺任何时候不读取未成功提交的数据,即读取到的数据都是曾经在 W 个副本上成功的数据。 233 | ## quorum 机制下达到强一致系统 234 | 1. 限制提交的更新操作必须严格递增,即只有在前一个更新操作成功提交后才可以提交后一个更新操作,从而成功提交的数据版本号必须是连续增加的。 235 | 2. 读取 R 个副本,对于 R 个副本中版本号最高的数据, 236 | 1. 若已存在 W 个,则该数据为最新的成功提交的数据 237 | 2. 若存在个数据少于 W 个,假设为 X 个,则继续读取其他副本,直若成功读取到 W 个该版本的副本,则该数据为最新的成功提交的数据;如果在所有副本中该数据的个数肯定不满足 W 个,则 R 中版本号第二大的为最新的成功提交的副本。 238 | 3. __可以看出,在单纯使用 Quorum 机制时,若要确定最新的成功提交的版本,最多需要读取 R+(W-R-1)=N 个副本,当出现任一副本异常时,读最新的成功提交的版本这一功能都有可能不可用。__ 239 | ## 基于 Quorum 机制选择 primary 240 | 在 primary-secondary 协议中,当 primary 异常时,需要选择出一个新的 primary,之后 secondary副本与 primary 同步数据。通常情况下,选择新的 primary 的工作是由某一中心节点完成的,在引入quorum 机制后,常用的 primary 选择方式与读取数据的方式类似,即中心节点读取 R 个副本,选择R 个副本中版本号最高的副本作为新的 primary。新 primary 与至少 W 个副本完成数据同步后作为新的 primary 提供读写服务。首先,R 个副本中版本号最高的副本一定蕴含了最新的成功提交的数据。再者,虽然不能确定最高版本号的数是一个成功提交的数据,但新的 primary 在随后与 secondary 同步数据,使得该版本的副本个数达到 W,从而使得该版本的数据成为成功提交的数据。 241 | ### GFS 中的 Quorum 242 | GFS 使用 WARO 机制读写副本,即如果更新所有副本成功则认为更新成功,一旦更新成功,则可以任意选择一个副本读取数据;如果更新某个副本失败,则更显失败,副本之间处于不一致的状态。GFS 系统不保证异常状态时副本的一致性,GFS 系统需要上层应用通过 Checksum 等机制自行判断数据是否合法。值得注意的是 GFS 中的 append 操作,一旦 append 操作某个 chunck 的副本上失败,GFS 系统会自动新增一个 chunck 并尝试 append 操作,由于可以让新增的 chunck 在正常的机器上创建,从而解决了由于 WARO 造成的系统可用性下降问题。进而在 GFS 中,append 操作不保证一定在文件的结尾进行,由于在新增的 chunk 上重试 append,append 的数据可能会出现多份重复的现象,但每个 append 操作会返回用户最终成功的 offset 位置,在这个位置上,任意读取某个副本一定可以读到写入的数据。这种在新增 chunk 上进行尝试的思路,大大增大了系统的容错能力,提高了系统可用性,是一种非常值得借鉴的设计思路。 243 | 244 | 分布式系统原理介绍 245 | # 日志技术 246 | > 日志技术是宕机恢复的主要技术之一 247 | ## Redo Log: 248 | __Redo Log 更新流程__ 249 | 1. 将更新操作的结果(例如 Set K1=1,则记录 K1=1)以追加写(append)的方式写入磁盘的日志文件 250 | 2. 按更新操作修改内存中的数据 251 | 3. 返回更新成功 252 | 4. Redo 写入日志的是更新操作完成后的结果 253 | Redo Log 的宕机恢复 254 | 1. 从头读取日志文件中的每次更新操作的结果,用这些结果修改内存中的数据。 255 | > 从 Redo Log 的宕机恢复流程也可以看出,只有写入日志文件的更新结果才能在宕机后恢复。这也是为什么在 Redo Log 流程中需要先更新日志文件再更新内存中的数据的原因 256 | ## Check point: 257 | 宕机恢复流量的缺点是需要回放所有 redo 日志,效率较低,假如需要恢复的操作非常多,那么这个宕机恢复过程将非常漫长。解决这一问题的方法即引入 check point 技术。在简化的模型下,checkpoint 技术的过程即将内存中的数据以某种易于重新加载的数据组织方式完整的 dump 到磁盘,从而减少宕机恢复时需要回放的日志数据。 258 | 259 | # Paxos 协议: 260 | > Paxos 协议是少数在工程实践中证实的强一致性、高可用的去__中心化分布式协议__ 261 | > Paxos 协议的流程较为复杂,但其基本思想却不难理解,类似于人类社会的投票过程。Paxos 协议中,有一组完全对等的参与节点(称为 accpetor),这组节点各自就某一事件做出决议,如果某个决议获得了超过半数节点的同意则生效。Paxos 协议中只要有超过一半的节点正常,就可以工作,能很好对抗宕机、网络分化等异常情况。 262 | * 节点角色: Paxos 协议中,有三类节点: 263 | * Proposer:提案者。Proposer 可以有多个,Proposer 提出议案(value)。所谓 value,在工程中可以是任何操作,例如“修改某个变量的值为某个值”、“设置当前 primary 为某个节点”等等。Paxos协议中统一将这些操作抽象为 value。不同的 Proposer 可以提出不同的甚至矛盾的 value,例如某个Proposer 提议“将变量 X 设置为 1”,另一个 Proposer 提议“将变量 X 设置为 2”,但对同一轮 Paxos过程,最多只有一个 value 被批准。 264 | * Acceptor:批准者。Acceptor 有 N 个,Proposer 提出的 value 必须获得超过半数(N/2+1)的 Acceptor批准后才能通过。Acceptor 之间完全对等独立。 265 | * Learner:学习者。Learner 学习被批准的 value。所谓学习就是通过读取各个 Proposer 对 value的选择结果,如果某个 value 被超过半数 Proposer 通过,则 Learner 学习到了这个 value。回忆(2.4 )不难理解,这里类似 Quorum 机制,某个 value 需要获得 W=N/2 + 1 的 Acceptor 批准,从而学习者需要至少读取 N/2+1 个 Accpetor,至多读取 N 个 Acceptor 的结果后,能学习到一个通过的 value。 266 | 上述三类角色只是逻辑上的划分,实践中一个节点可以同时充当这三类角色。 267 | 268 | # CAP 理论 269 | ## CAP 理论的定义 270 | CAP 三个字母分别代表了分布式系统中三个相互矛盾的属性: 271 | * Consistency (一致性):CAP 理论中的副本一致性特指强一致性 272 | * Availiablity(可用性):指系统在出现异常时已经可以提供服务 273 | * Tolerance to the partition of network (分区容忍):指系统可以对网络分化这种异常情况进行容错处理; 274 | CAP 理论指出:无法设计一种分布式协议,使得同时完全具备 CAP 三个属性,即 275 | * 1)该种协议下的副本始终是强一致性 276 | * 2)服务始终是可用的 277 | * 3)协议可以容忍任何网络分区异常 278 | * 分布式系统协议只能在 CAP 这三者间所有折中 279 | ## CAP理论的意义: 280 | 热力学第二定律说明了永动机是不可能存在的,不要去妄图设计永动机。与之类似,CAP 理论的意义就在于明确提出了不要去妄图设计一种对 CAP 三大属性都完全拥有的完美系统,因为这种系统在理论上就已经被证明不存在。 281 | 282 | 283 | 参考:[分布式系统原理·刘杰] 284 | 285 | ## 评价交流 286 | 287 | > 欢迎留下的你的想法~ 288 | 289 | 290 | -------------------------------------------------------------------------------- /docs/liuyan/README.md: -------------------------------------------------------------------------------- 1 | ## 提高效率的工具 2 | 3 | > 工欲善其事必先利其器。 4 | 5 | | 工具用途 | 地址 | 6 | | --- | --- | 7 | | 矢量图标库 |[阿里矢量图标库](https://www.iconfont.cn/) | 8 | | 开发辅助工具 | [菜鸟工具](https://c.runoob.com/) | 9 | | ip 地址计算 | [ip地址计算](http://www.ab126.com/goju/1840.html) | 10 | | 电脑录屏 | [免费在线录屏](https://www.apowersoft.cn/free-online-screen-recorder)| 11 | | 云端办公软件 | [UZER.ME](https://uzer.me/) | 12 | | 开发者工具 | [开发者工具](http://www.ofmonkey.com/) | 13 | | 二维码美化 | [草料二维码](https://cli.im/img)| 14 | | 在线画图 | [在线画图](https://www.draw.io/) | 15 | | 时间转换 | [时间戳与日期互换](http://tool.chinaz.com/Tools/unixtime.aspx) | 16 | | web 前端导航 | [前端著名站点导航](http://www.alloyteam.com/nav/) | 17 | -------------------------------------------------------------------------------- /docs/ml/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 板块介绍 3 | 4 | 以后会把机器学习相关的文章记录在此。会分享以及一些文章以及一些资源。 5 | -------------------------------------------------------------------------------- /docs/ml/ml-guid.md: -------------------------------------------------------------------------------- 1 | 2 | # 从机器学习谈起 3 | 4 | ![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186576-780ddd32-8843-47a4-8397-41f0f7014386.jpeg#align=left&display=inline&height=1722&originHeight=1722&originWidth=554&size=0&status=done&width=554)
图| 维基百科
**版权说明:**本文系转载。****
**本文中的所有文字,图片,代码的版权都是属于作者和博客园共同所有。**
5 |
在本篇文章中,我将对机器学习做个概要的介绍。本文的目的是能让即便完全不了解机器学习的人也能了解机器学习,并且上手相关的实践。当然,本文也面对一般读者,不会对阅读有相关的前提要求。
在进入正题前,我想读者心中可能会有一个疑惑:机器学习有什么重要性,以至于要阅读完这篇非常长的文章呢?
6 |
我并不直接回答这个问题前。相反,我想请大家看两张图,下图是图一:
7 |
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186612-f5f6dfa7-a84c-477f-a5d7-022340307bfd.jpeg#align=left&display=inline&height=400&originHeight=509&originWidth=1074&size=0&status=done&width=844) 图1 机器学习界的执牛耳者与互联网界的大鳄的联姻
8 |
这幅图上上的三人是当今机器学习界的执牛耳者。中间的是Geoffrey Hinton, 加拿大多伦多大学的教授,如今被聘为“Google大脑”的负责人。右边的是Yann LeCun, 纽约大学教授,如今是Facebook人工智能实验室的主任。而左边的大家都很熟悉,Andrew Ng,中文名吴恩达,斯坦福大学副教授,如今也是“百度大脑”的负责人与百度首席科学家。这三位都是目前业界炙手可热的大牛,被互联网界大鳄求贤若渴的聘请,足见他们的重要性。而他们的研究方向,则全部都是机器学习的子类--深度学习。
9 | 10 | 吴恩达教授已于2017年3月20日从百度离职。现在在自己成立的人工智能公司 Landing.ai 担任首席执行官。 11 | 下图是图二:
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186594-c995cac5-7762-4d79-b2a9-cb754f224ac3.jpeg#align=left&display=inline&height=354&originHeight=304&originWidth=500&size=0&status=done&width=582)图2 语音助手产品
这幅图上描述的是什么?Windows Phone上的语音助手Cortana,名字来源于《光环》中士官长的助手。相比其他竞争对手,微软很迟才推出这个服务。Cortana背后的核心技术是什么,为什么它能够听懂人的语音?事实上,这个技术正是机器学习。机器学习是所有语音助手产品(包括Apple的siri与Google的Now)能够跟人交互的关键技术。
通过上面两图,我相信大家可以看出机器学习似乎是一个很重要的,有很多未知特性的技术。学习它似乎是一件有趣的任务。实际上,学习机器学习不仅可以帮助我们了解互联网界最新的趋势,同时也可以知道伴随我们的便利服务的实现技术。
12 |
机器学习是什么,为什么它能有这么大的魔力,这些问题正是本文要回答的。同时,本文叫做“从机器学习谈起”,因此会以漫谈的形式介绍跟机器学习相关的所有内容,包括学科(如数据挖掘、计算机视觉等),算法(神经网络,svm)等等。本文的主要目录如下:


1.一个故事说明什么是机器学习
2.机器学习的定义
3.机器学习的范围
4.机器学习的方法
5.机器学习的应用--大数据
6.机器学习的子类--深度学习
7.机器学习的父类--人工智能
9.总结
10.后记

13 | 14 | 15 | ## 1. 一个故事说明什么是机器学习 16 | **
机器学习这个词是让人疑惑的,首先它是英文名称Machine Learning(简称ML)的直译,在计算界Machine一般指计算机。这个名字使用了拟人的手法,说明了这门技术是让机器“学习”的技术。但是计算机是死的,怎么可能像人类一样“学习”呢?
传统上如果我们想让计算机工作,我们给它一串指令,然后它遵照这个指令一步步执行下去。有因有果,非常明确。但这样的方式在机器学习中行不通。机器学习根本不接受你输入的指令,相反,它接受你输入的数据! 也就是说,机器学习是一种让计算机利用数据而不是指令来进行各种工作的方法。这听起来非常不可思议,但结果上却是非常可行的。“统计”思想将在你学习“机器学习”相关理念时无时无刻不伴随,相关而不是因果的概念将是支撑机器学习能够工作的核心概念。你会颠覆对你以前所有程序中建立的因果无处不在的根本理念。
下面我通过一个故事来简单地阐明什么是机器学习。这个故事比较适合用在知乎上作为一个概念的阐明。在这里,这个故事没有展开,但相关内容与核心是存在的。如果你想简单的了解一下什么是机器学习,那么看完这个故事就足够了。如果你想了解机器学习的更多知识以及与它关联紧密的当代技术,那么请你继续往下看,后面有更多的丰富的内容。
这个例子来源于我真实的生活经验,我在思考这个问题的时候突然发现它的过程可以被扩充化为一个完整的机器学习的过程,因此我决定使用这个例子作为所有介绍的开始。这个故事称为“等人问题”。
我相信大家都有跟别人相约,然后等人的经历。现实中不是每个人都那么守时的,于是当你碰到一些爱迟到的人,你的时间不可避免的要浪费。我就碰到过这样的一个例子。
对我的一个朋友小Y而言,他就不是那么守时,最常见的表现是他经常迟到。当有一次我跟他约好3点钟在某个麦当劳见面时,在我出门的那一刻我突然想到一个问题:我现在出发合适么?我会不会又到了地点后,花上30分钟去等他?我决定采取一个策略解决这个问题。
要想解决这个问题,有好几种方法。第一种方法是采用知识:我搜寻能够解决这个问题的知识。但很遗憾,没有人会把如何等人这个问题作为知识传授,因此我不可能找到已有的知识能够解决这个问题。第二种方法是问他人:我去询问他人获得解决这个问题的能力。但是同样的,这个问题没有人能够解答,因为可能没人碰上跟我一样的情况。第三种方法是准则法:我问自己的内心,我有否设立过什么准则去面对这个问题?例如,无论别人如何,我都会守时到达。但我不是个死板的人,我没有设立过这样的规则。
事实上,我相信有种方法比以上三种都合适。我把过往跟小Y相约的经历在脑海中重现一下,看看跟他相约的次数中,迟到占了多大的比例。而我利用这来预测他这次迟到的可能性。如果这个值超出了我心里的某个界限,那我选择等一会再出发。假设我跟小Y约过5次,他迟到的次数是1次,那么他按时到的比例为80%,我心中的阈值为70%,我认为这次小Y应该不会迟到,因此我按时出门。如果小Y在5次迟到的次数中占了4次,也就是他按时到达的比例为20%,由于这个值低于我的阈值,因此我选择推迟出门的时间。这个方法从它的利用层面来看,又称为经验法。在经验法的思考过程中,我事实上利用了以往所有相约的数据。因此也可以称之为依据数据做的判断。
**依据数据所做的判断跟机器学习的思想根本上是一致的。**
刚才的思考过程我只考虑“频次”这种属性。在真实的机器学习中,这可能都不算是一个应用。一般的机器学习模型至少考虑两个量:一个是因变量,也就是我们希望预测的结果,在这个例子里就是小Y迟到与否的判断。另一个是自变量,也就是用来预测小Y是否迟到的量。假设我把时间作为自变量,譬如我发现小Y所有迟到的日子基本都是星期五,而在非星期五情况下他基本不迟到。于是我可以建立一个模型,来模拟小Y迟到与否跟日子是否是星期五的概率。见下图:
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186593-5dfbe889-0c13-4d6a-82b9-ca8c3aec909a.jpeg#align=left&display=inline&height=365&originHeight=435&originWidth=835&size=0&status=done&width=701)
图3 决策树模型
这样的图就是一个最简单的机器学习模型,称之为决策树。
当我们考虑的自变量只有一个时,情况较为简单。如果把我们的自变量再增加一个。例如小Y迟到的部分情况时是在他开车过来的时候(你可以理解为他开车水平较臭,或者路较堵)。于是我可以关联考虑这些信息。建立一个更复杂的模型,这个模型包含两个自变量与一个因变量。
再更复杂一点,小Y的迟到跟天气也有一定的原因,例如下雨的时候,这时候我需要考虑三个自变量。
如果我希望能够预测小Y迟到的具体时间,我可以把他每次迟到的时间跟雨量的大小以及前面考虑的自变量统一建立一个模型。于是我的模型可以预测值,例如他大概会迟到几分钟。这样可以帮助我更好的规划我出门的时间。在这样的情况下,决策树就无法很好地支撑了,因为决策树只能预测离散值。我们可以用节2所介绍的线型回归方法建立这个模型。
如果我把这些建立模型的过程交给电脑。比如把所有的自变量和因变量输入,然后让计算机帮我生成一个模型,同时让计算机根据我当前的情况,给出我是否需要迟出门,需要迟几分钟的建议。那么计算机执行这些辅助决策的过程就是机器学习的过程。
**机器学习方法是计算机利用已有的数据(经验),得出了某种模型(迟到的规律),并利用此模型预测未来(是否迟到)的一种方法。**
通过上面的分析,可以看出机器学习与人类思考的经验过程是类似的,不过它能考虑更多的情况,执行更加复杂的计算。事实上,机器学习的一个主要目的就是把人类思考归纳经验的过程转化为计算机通过对数据的处理计算得出模型的过程。经过计算机得出的模型能够以近似于人的方式解决很多灵活复杂的问题。
下面,我会开始对机器学习的正式介绍,包括定义、范围,方法、应用等等,都有所包含。
17 | 18 | 19 | ## 2. 机器学习的定义 20 | 从广义上来说,机器学习是一种能够赋予机器学习的能力以此让它完成直接编程无法完成的功能的方法。但从实践的意义上来说,机器学习是一种通过利用数据,训练出模型,然后使用模型预测的一种方法。
让我们具体看一个例子。
![](https://cdn.nlark.com/yuque/0/2019/png/199648/1553594186593-d1c11cc9-978f-4ca8-baa4-c1105818a38b.png#align=left&display=inline&height=368&originHeight=387&originWidth=742&size=0&status=done&width=705)
图4 房价的例子
拿国民话题的房子来说。现在我手里有一栋房子需要售卖,我应该给它标上多大的价格?房子的面积是100平方米,价格是100万,120万,还是140万?
很显然,我希望获得房价与面积的某种规律。那么我该如何获得这个规律?用报纸上的房价平均数据么?还是参考别人面积相似的?无论哪种,似乎都并不是太靠谱。
我现在希望获得一个合理的,并且能够最大程度的反映面积与房价关系的规律。于是我调查了周边与我房型类似的一些房子,获得一组数据。这组数据中包含了大大小小房子的面积与价格,如果我能从这组数据中找出面积与价格的规律,那么我就可以得出房子的价格。
对规律的寻找很简单,拟合出一条直线,让它“穿过”所有的点,并且与各个点的距离尽可能的小。
通过这条直线,我获得了一个能够最佳反映房价与面积规律的规律。这条直线同时也是一个下式所表明的函数:
上述中的a、b都是直线的参数。获得这些参数以后,我就可以计算出房子的价格。
假设a = 0.75,b = 50,则房价 = 100 * 0.75 + 50 = 125万。这个结果与我前面所列的100万,120万,140万都不一样。由于这条直线综合考虑了大部分的情况,因此从“统计”意义上来说,这是一个最合理的预测。
在求解过程中透露出了两个信息:
1.房价模型是根据拟合的函数类型决定的。如果是直线,那么拟合出的就是直线方程。如果是其他类型的线,例如抛物线,那么拟合出的就是抛物线方程。机器学习有众多算法,一些强力算法可以拟合出复杂的非线性模型,用来反映一些不是直线所能表达的情况。
2.如果我的数据越多,我的模型就越能够考虑到越多的情况,由此对于新情况的预测效果可能就越好。这是机器学习界“数据为王”思想的一个体现。一般来说(不是绝对),数据越多,最后机器学习生成的模型预测的效果越好。
通过我拟合直线的过程,我们可以对机器学习过程做一个完整的回顾。首先,我们需要在计算机中存储历史的数据。接着,我们将这些 数据通过机器学习算法进行处理,这个过程在机器学习中叫做“训练”,处理的结果可以被我们用来对新的数据进行预测,这个结果一般称之为“模型”。对新数据 的预测过程在机器学习中叫做“预测”。“训练”与“预测”是机器学习的两个过程,“模型”则是过程的中间输出结果,“训练”产生“模型”,“模型”指导 “预测”。
让我们把机器学习的过程与人类对历史经验归纳的过程做个比对。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186632-3463d95a-54b4-4255-877b-b9546e0edf68.jpeg#align=left&display=inline&height=383&originHeight=520&originWidth=1080&size=0&status=done&width=796)
图5 机器学习与人类思考的类比
人类在成长、生活过程中积累了很多的历史与经验。人类定期地对这些经验进行“归纳”,获得了生活的“规律”。当人类遇到未知的问题或者需要对未来进行“推测”的时候,人类使用这些“规律”,对未知问题与未来进行“推测”,从而指导自己的生活和工作。
机器学习中的“训练”与“预测”过程可以对应到人类的“归纳”和“推测”过程。通过这样的对应,我们可以发现,机器学习的思想并不复杂,仅仅是对人类在生活中学习成长的一个模拟。由于机器学习不是基于编程形成的结果,因此它的处理过程不是因果的逻辑,而是通过归纳思想得出的相关性结论。
21 |
这也可以联想到人类为什么要学习历史,历史实际上是人类过往经验的总结。有句话说得很好,“历史往往不一样,但历史总是惊人的相似”。通过学习历史,我们从历史中归纳出人生与国家的规律,从而指导我们的下一步工作,这是具有莫大价值的。当代一些人忽视了历史的本来价值,而是把其作为一种宣扬功绩的手段,这其实是对历史真实价值的一种误用。
22 | 23 | 24 | ## 3.机器学习的范围 25 | 上文虽然说明了机器学习是什么,但是并没有给出机器学习的范围。
26 |
其实,机器学习跟模式识别,统计学习,数据挖掘,计算机视觉,语音识别,自然语言处理等领域有着很深的联系。
27 |
从范围上来说,机器学习跟模式识别,统计学习,数据挖掘是类似的,同时,机器学习与其他领域的处理技术的结合,形成了计算机视觉、语音识别、自然语言处理等交叉学科。因此,一般说数据挖掘时,可以等同于说机器学习。同时,我们平常所说的机器学习应用,应该是通用的,不仅仅局限在结构化数据,还有图像,音频等应用。
在这节对机器学习这些相关领域的介绍有助于我们理清机器学习的应用场景与研究范围,更好的理解后面的算法与应用层次。
下图是机器学习所牵扯的一些相关范围的学科与研究领域。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186612-e0735347-dd37-460b-94c1-8b2d062bae32.jpeg#align=left&display=inline&height=491&originHeight=620&originWidth=1080&size=0&status=done&width=855)图6 机器学习与相关学科
**模式识别**
模式识别=机器学习。两者的主要区别在于前者是从工业界发展起来的概念,后者则主要源自计算机学科。在著名的《Pattern Recognition And Machine Learning》这本书中,Christopher M. Bishop在开头是这样说的“模式识别源自工业界,而机器学习来自于计算机学科。不过,它们中的活动可以被视为同一个领域的两个方面,同时在过去的10年间,它们都有了长足的发展”。
**数据挖掘**
数据挖掘=机器学习+数据库。这几年数据挖掘的概念实在是太耳熟能详。几乎等同于炒作。但凡说数据挖掘都会吹嘘数据挖掘如何如何,例如从数据中挖出金子,以及将废弃的数据转化为价值等等。但是,我尽管可能会挖出金子,但我也可能挖的是“石头”啊。这个说法的意思是,数据挖掘仅仅是一种思考方式,告诉我们应该尝试从数据中挖掘出知识,但不是每个数据都能挖掘出金子的,所以不要神话它。一个系统绝对不会因为上了一个数据挖掘模块就变得无所不能(这是IBM最喜欢吹嘘的),恰恰相反,一个拥有数据挖掘思维的人员才是关键,而且他还必须对数据有深刻的认识,这样才可能从数据中导出模式指引业务的改善。大部分数据挖掘中的算法是机器学习的算法在数据库中的优化。
**统计学习**
统计学习近似等于机器学习。统计学习是个与机器学习高度重叠的学科。因为机器学习中的大多数方法来自统计学,甚至可以认为,统计学的发展促进机器学习的繁荣昌盛。例如著名的支持向量机算法,就是源自统计学科。但是在某种程度上两者是有分别的,这个分别在于:统计学习者重点关注的是统计模型的发展与优化,偏数学,而机器学习者更关注的是能够解决问题,偏实践,因此机器学习研究者会重点研究学习算法在计算机上执行的效率与准确性的提升。
**计算机视觉**
计算机视觉=图像处理+机器学习。图像处理技术用于将图像处理为适合进入机器学习模型中的输入,机器学习则负责从图像中识别出相关的模式。计算机视觉相关的应用非常的多,例如百度识图、手写字符识别、车牌识别等等应用。这个领域是应用前景非常火热的,同时也是研究的热门方向。随着机器学习的新领域深度学习的发展,大大促进了计算机图像识别的效果,因此未来计算机视觉界的发展前景不可估量。
**语音识别**
语音识别=语音处理+机器学习。语音识别就是音频处理技术与机器学习的结合。语音识别技术一般不会单独使用,一般会结合自然语言处理的相关技术。目前的相关应用有苹果的语音助手siri等。
**自然语言处理**
自然语言处理=文本处理+机器学习。自然语言处理技术主要是让机器理解人类的语言的一门领域。在自然语言处理技术中,大量使用了编译原理相关的技术,例如词法分析,语法分析等等,除此之外,在理解这个层面,则使用了语义理解,机器学习等技术。作为唯一由人类自身创造的符号,自然语言处理一直是机器学习界不断研究的方向。按照百度机器学习专家余凯的说法“听与看,说白了就是阿猫和阿狗都会的,而只有语言才是人类独有的”。如何利用机器学习技术进行自然语言的的深度理解,一直是工业和学术界关注的焦点。
可以看出机器学习在众多领域的外延和应用。机器学习技术的发展促使了很多智能领域的进步,改善着我们的生活。
28 | 29 | 30 | ## 4.机器学习的方法 31 | 通过上节的介绍我们知晓了机器学习的大致范围,那么机器学习里面究竟有多少经典的算法呢?在这个部分我会简要介绍一下机器学习中的经典代表方法。这部分介绍的重点是这些方法内涵的思想,数学与实践细节不会在这讨论。 32 | 33 | ### 4.1 回归算法 34 | 在大部分机器学习课程中,回归算法都是介绍的第一个算法。原因有两个:一.回归算法比较简单,介绍它可以让人平滑地从统计学迁移到机器学习中。二.回归算法是后面若干强大算法的基石,如果不理解回归算法,无法学习那些强大的算法。回归算法有两个重要的子类:即线性回归和逻辑回归。
35 |
线性回归就是我们前面说过的房价求解问题。如何拟合出一条直线最佳匹配我所有的数据?一般使用“最小二乘法”来求解。“最小二乘法”的思想是这样的,假设我们拟合出的直线代表数据的真实值,而观测到的数据代表拥有误差的值。为了尽可能减小误差的影响,需要求解一条直线使所有误差的平方和最小。最小二乘法将最优问题转化为求函数极值问题。函数极值在数学上我们一般会采用求导数为0的方法。但这种做法并不适合计算机,可能求解不出来,也可能计算量太大。
36 |
计算机科学界专门有一个学科叫“数值计算”,专门用来提升计算机进行各类计算时的准确性和效率问题。例如,著名的“梯度下降”以及“牛顿法”就是数值计算中的经典算法,也非常适合来处理求解函数极值的问题。梯度下降法是解决回归模型中最简单且有效的方法之一。从严格意义上来说,由于后文中的神经网络和推荐算法中都有线性回归的因子,因此梯度下降法在后面的算法实现中也有应用。
逻辑回归是一种与线性回归非常类似的算法,但是,从本质上讲,线型回归处理的问题类型与逻辑回归不一致。线性回归处理的是数值问题,也就是最后预测出的结果是数字,例如房价。而逻辑回归属于分类算法,也就是说,逻辑回归预测结果是离散的分类,例如判断这封邮件是否是垃圾邮件,以及用户是否会点击此广告等等。
实现方面的话,逻辑回归只是对对线性回归的计算结果加上了一个Sigmoid函数,将数值结果转化为了0到1之间的概率(Sigmoid函数的图像一般来说并不直观,你只需要理解对数值越大,函数越逼近1,数值越小,函数越逼近0),接着我们根据这个概率可以做预测,例如概率大于0.5,则这封邮件就是垃圾邮件,或者肿瘤是否是恶性的等等。从直观上来说,逻辑回归是画出了一条分类线,见下图。
![](https://cdn.nlark.com/yuque/0/2019/png/199648/1553594186624-2b9aa342-44fe-4888-ba79-e34bd46ffef2.png#align=left&display=inline&height=425&originHeight=505&originWidth=1043&size=0&status=done&width=877)图7 逻辑回归的直观解释
假设我们有一组肿瘤患者的数据,这些患者的肿瘤中有些是良性的(图中的蓝色点),有些是恶性的(图中的红色点)。这里肿瘤的红蓝色可以被称作数据的“标签”。同时每个数据包括两个“特征”:患者的年龄与肿瘤的大小。我们将这两个特征与标签映射到这个二维空间上,形成了我上图的数据。
当我有一个绿色的点时,我该判断这个肿瘤是恶性的还是良性的呢?根据红蓝点我们训练出了一个逻辑回归模型,也就是图中的分类线。这时,根据绿点出现在分类线的左侧,因此我们判断它的标签应该是红色,也就是说属于恶性肿瘤。
逻辑回归算法划出的分类线基本都是线性的(也有划出非线性分类线的逻辑回归,不过那样的模型在处理数据量较大的时候效率会很低),这意味着当两类之间的界线不是线性时,逻辑回归的表达能力就不足。下面的两个算法是机器学习界最强大且重要的算法,都可以拟合出非线性的分类线。 37 | 38 | ### 4.2 神经网络 39 | 神经网络(也称之为人工神经网络,ANN)算法是80年代机器学习界非常流行的算法,不过在90年代中途衰落。现在,携着“深度学习”之势,神经网络重装归来,重新成为最强大的机器学习算法之一。
神经网络的诞生起源于对大脑工作机理的研究。早期生物界学者们使用神经网络来模拟大脑。机器学习的学者们使用神经网络进行机器学习的实验,发现在视觉与语音的识别上效果都相当好。在BP算法(加速神经网络训练过程的数值算法)诞生以后,神经网络的发展进入了一个热潮。BP算法的发明人之一是前面介绍的机器学习大牛Geoffrey Hinton(图1中的中间者)。
具体说来,神经网络的学习机理是什么?简单来说,就是分解与整合。在著名的Hubel-Wiesel试验中,学者们研究猫的视觉分析机理是这样的。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186622-87c0e481-1142-422f-a2c2-107dab258021.jpeg#align=left&display=inline&height=365&originHeight=412&originWidth=1080&size=0&status=done&width=956) 图8 Hubel-Wiesel试验与大脑视觉机理
比方说,一个正方形,分解为四个折线进入视觉处理的下一层中。四个神经元分别处理一个折线。每个折线再继续被分解为两条直线,每条直线再被分解为黑白两个面。于是,一个复杂的图像变成了大量的细节进入神经元,神经元处理以后再进行整合,最后得出了看到的是正方形的结论。这就是大脑视觉识别的机理,也是神经网络工作的机理。
让我们看一个简单的神经网络的逻辑架构。在这个网络中,分成输入层,隐藏层,和输出层。输入层负责接收信号,隐藏层负责对数据的分解与处理,最后的结果被整合到输出层。每层中的一个圆代表一个处理单元,可以认为是模拟了一个神经元,若干个处理单元组成了一个层,若干个层再组成了一个网络,也就是"神经网络"。
![](https://cdn.nlark.com/yuque/0/2019/png/199648/1553594186686-398313e9-8c91-44a1-9b6d-ae71addb8d54.png#align=left&display=inline&height=322&originHeight=322&originWidth=593&size=0&status=done&width=593)
图9 神经网络的逻辑架构
在神经网络中,每个处理单元事实上就是一个逻辑回归模型,逻辑回归模型接收上层的输入,把模型的预测结果作为输出传输到下一个层次。通过这样的过程,神经网络可以完成非常复杂的非线性分类。
下图会演示神经网络在图像识别领域的一个著名应用,这个程序叫做LeNet,是一个基于多个隐层构建的神经网络。通过LeNet可以识别多种手写数字,并且达到很高的识别精度与拥有较好的鲁棒性。
![](https://cdn.nlark.com/yuque/0/2019/gif/199648/1553594186633-82119abf-8cd6-4945-8cdd-1787168b64ac.gif#align=left&display=inline&height=305&originHeight=200&originWidth=320&size=0&status=done&width=488)
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186636-cb3b5e62-f11e-49c4-b904-ed3b2655dc9f.jpeg#align=left&display=inline&height=121&originHeight=80&originWidth=320&size=0&status=done&width=482)图10 LeNet的效果展示
右下方的方形中显示的是输入计算机的图像,方形上方的红色字样“answer”后面显示的是计算机的输出。左边的三条竖直的图像列显示的是神经网络中三个隐藏层的输出,可以看出,随着层次的不断深入,越深的层次处理的细节越低,例如层3基本处理的都已经是线的细节了。LeNet的发明人就是前文介绍过的机器学习的大牛Yann LeCun(图1右者)。
进入90年代,神经网络的发展进入了一个瓶颈期。其主要原因是尽管有BP算法的加速,神经网络的训练过程仍然很困难。因此90年代后期支持向量机(SVM)算法取代了神经网络的地位。
**4.3 SVM(支持向量机)**
支持向量机算法是诞生于统计学习界,同时在机器学习界大放光彩的经典算法。
支持向量机算法从某种意义上来说是逻辑回归算法的强化:通过给予逻辑回归算法更严格的优化条件,支持向量机算法可以获得比逻辑回归更好的分类界线。但是如果没有某类函数技术,则支持向量机算法最多算是一种更好的线性分类技术。
40 |
但是,通过跟高斯“核”的结合,支持向量机可以表达出非常复杂的分类界线,从而达成很好的的分类效果。“核”事实上就是一种特殊的函数,最典型的特征就是可以将低维的空间映射到高维的空间。
41 |
例如下图所示:
![](https://cdn.nlark.com/yuque/0/2019/png/199648/1553594186717-add9dac0-eab0-40d3-8d44-d04fd73f8239.png#align=left&display=inline&height=281&originHeight=188&originWidth=240&size=0&status=done&width=359)
_ _图11 支持向量机图例
我们如何在二维平面划分出一个圆形的分类界线?在二维平面可能会很困难,但是通过“核”可以将二维空间映射到三维空间,然后使用一个线性平面就可以达成类似效果。也就是说,二维平面划分出的非线性分类界线可以等价于三维平面的线性分类界线。于是,我们可以通过在三维空间中进行简单的线性划分就可以达到在二维平面中的非线性划分效果。![](https://cdn.nlark.com/yuque/0/2019/gif/199648/1553594186687-aef1cd7f-93fb-4ec4-8b17-ac54695a44c9.gif#align=left&display=inline&height=356&originHeight=356&originWidth=444&size=0&status=done&width=444)
_ _图12 三维空间的切割
支持向量机是一种数学成分很浓的机器学习算法(相对的,神经网络则有生物科学成分)。在算法的核心步骤中,有一步证明,即将数据从低维映射到高维不会带来最后计算复杂性的提升。于是,通过支持向量机算法,既可以保持计算效率,又可以获得非常好的分类效果。因此支持向量机在90年代后期一直占据着机器学习中最核心的地位,基本取代了神经网络算法。直到现在神经网络借着深度学习重新兴起,两者之间才又发生了微妙的平衡转变。 42 | 43 | 44 | ### 4.4 聚类算法 45 | 前面的算法中的一个显著特征就是我的训练数据中包含了标签,训练出的模型可以对其他未知数据预测标签。在下面的算法中,训练数据都是不含标签的,而算法的目的则是通过训练,推测出这些数据的标签。这类算法有一个统称,即无监督算法(前面有标签的数据的算法则是有监督算法)。无监督算法中最典型的代表就是聚类算法。
让我们还是拿一个二维的数据来说,某一个数据包含两个特征。我希望通过聚类算法,给他们中不同的种类打上标签,我该怎么做呢?简单来说,聚类算法就是计算种群中的距离,根据距离的远近将数据划分为多个族群。
聚类算法中最典型的代表就是K-Means算法。 46 | 47 | 48 | ### 4.5 降维算法 49 | 降维算法也是一种无监督学习算法,其主要特征是将数据从高维降低到低维层次。在这里,维度其实表示的是数据的特征量的大小,例如,房价包含房子的长、宽、面积与房间数量四个特征,也就是维度为4维的数据。可以看出来,长与宽事实上与面积表示的信息重叠了,例如面积=长 × 宽。通过降维算法我们就可以去除冗余信息,将特征减少为面积与房间数量两个特征,即从4维的数据压缩到2维。于是我们将数据从高维降低到低维,不仅利于表示,同时在计算上也能带来加速。
刚才说的降维过程中减少的维度属于肉眼可视的层次,同时压缩也不会带来信息的损失(因为信息冗余了)。如果肉眼不可视,或者没有冗余的特征,降维算法也能工作,不过这样会带来一些信息的损失。但是,降维算法可以从数学上证明,从高维压缩到的低维中最大程度地保留了数据的信息。因此,使用降维算法仍然有很多的好处。
降维算法的主要作用是压缩数据与提升机器学习其他算法的效率。通过降维算法,可以将具有几千个特征的数据压缩至若干个特征。另外,降维算法的另一个好处是数据的可视化,例如将5维的数据压缩至2维,然后可以用二维平面来可视。降维算法的主要代表是PCA算法(即主成分分析算法)。 50 | 51 | 52 | ### 4.6 推荐算法 53 | 推荐算法是目前业界非常火的一种算法,在电商界,如亚马逊,天猫,京东等得到了广泛的运用。推荐算法的主要特征就是可以自动向用户推荐他们最感兴趣的东西,从而增加购买率,提升效益。推荐算法有两个主要的类别:
一类是基于物品内容的推荐,是将与用户购买的内容近似的物品推荐给用户,这样的前提是每个物品都得有若干个标签,因此才可以找出与用户购买物品类似的物品,这样推荐的好处是关联程度较大,但是由于每个物品都需要贴标签,因此工作量较大。
另一类是基于用户相似度的推荐,则是将与目标用户兴趣相同的其他用户购买的东西推荐给目标用户,例如小A历史上买了物品B和C,经过算法分析,发现另一个与小A近似的用户小D购买了物品E,于是将物品E推荐给小A。
两类推荐都有各自的优缺点,在一般的电商应用中,一般是两类混合使用。推荐算法中最有名的算法就是协同过滤算法。 54 | 55 | 56 | ### 4.7 其他 57 | 除了以上算法之外,机器学习界还有其他的如高斯判别,朴素贝叶斯,决策树等等算法。但是上面列的六个算法是使用最多,影响最广,种类最全的典型。机器学习界的一个特色就是算法众多,发展百花齐放。
下面做一个总结,按照训练的数据有无标签,可以将上面算法分为监督学习算法和无监督学习算法,但推荐算法较为特殊,既不属于监督学习,也不属于非监督学习,是单独的一类。
**监督学习算法:**
线性回归,逻辑回归,神经网络,SVM
**无监督学习算法:**
聚类算法,降维算法
**特殊算法:**
推荐算法
除了这些算法以外,有一些算法的名字在机器学习领域中也经常出现。但他们本身并不算是一个机器学习算法,而是为了解决某个子问题而诞生的。你可以理解他们为以上算法的子算法,用于大幅度提高训练过程。其中的代表有:梯度下降法,主要运用在线型回归,逻辑回归,神经网络,推荐算法中;牛顿法,主要运用在线型回归中;BP算法,主要运用在神经网络中;SMO算法,主要运用在SVM中。
58 | 59 | 60 | 61 | ## 5.机器学习的应用--大数据 62 | 说完机器学习的方法,下面要谈一谈机器学习的应用了。无疑,在2010年以前,机器学习的应用在某些特定领域发挥了巨大的作用,如车牌识别,网络攻击防范,手写字符识别等等。但是,从2010年以后,随着大数据概念的兴起,机器学习大量的应用都与大数据高度耦合,几乎可以认为大数据是机器学习应用的最佳场景。
譬如,但凡你能找到的介绍大数据魔力的文章,都会说大数据如何准确准确预测到了某些事。例如经典的Google利用大数据预测了H1N1在美国某小镇的爆发。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186672-9c902034-659e-4efa-91d5-b4d4326aa34c.jpeg#align=left&display=inline&height=207&originHeight=207&originWidth=634&size=0&status=done&width=634)
图13 Google成功预测H1N1
百度预测2014年世界杯,从淘汰赛到决赛全部预测正确。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186660-40e4156f-e7b8-4209-82c5-e496561a5202.jpeg#align=left&display=inline&height=421&originHeight=421&originWidth=600&size=0&status=done&width=600)图14 百度世界杯成功预测了所有比赛结果
这些实在太神奇了,那么究竟是什么原因导致大数据具有这些魔力的呢?简单来说,就是机器学习技术。正是基于机器学习技术的应用,数据才能发挥其魔力。
63 |
大数据的核心是利用数据的价值,机器学习是利用数据价值的关键技术,对于大数据而言,机器学习是不可或缺的。相反,对于机器学习而言,越多的数据会越 可能提升模型的精确性,同时,复杂的机器学习算法的计算时间也迫切需要分布式计算与内存计算这样的关键技术。因此,机器学习的兴盛也离不开大数据的帮助。 大数据与机器学习两者是互相促进,相依相存的关系。
机器学习与大数据紧密联系。但是,必须清醒的认识到,大数据并不等同于机器学习,同理,机器学习也不等同于大数据。大数据中包含有分布式计算,内存数据库,多维分析等等多种技术。单从分析方法来看,大数据也包含以下四种分析方法:
1.**大数据,小分析:**即数据仓库领域的OLAP分析思路,也就是多维分析思想。
2.**大数据,大分析:**这个代表的就是数据挖掘与机器学习分析法。
3.**流式分析:**这个主要指的是事件驱动架构。
4.**查询分析:**经典代表是NoSQL数据库。
也就是说,机器学习仅仅是大数据分析中的一种而已。尽管机器学习的一些结果具有很大的魔力,在某种场合下是大数据价值最好的说明。但这并不代表机器学习是大数据下的唯一的分析方法。
机器学习与大数据的结合产生了巨大的价值。基于机器学习技术的发展,数据能够“预测”。对人类而言,积累的经验越丰富,阅历也广泛,对未来的判断越准确。例如常说的“经验丰富”的人比“初出茅庐”的小伙子更有工作上的优势,就在于经验丰富的人获得的规律比他人更准确。而在机器学习领域,根据著名的一个实验,有效的证实了机器学习界一个理论:即机器学习模型的数据越多,机器学习的预测的效率就越好。见下图:
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186716-9e85e725-db05-4c2d-bb80-6dd47cae2946.jpeg#align=left&display=inline&height=490&originHeight=490&originWidth=522&size=0&status=done&width=522)
![](https://cdn.nlark.com/yuque/0/2019/png/199648/1553594186682-8708e75e-558e-48ba-92b0-950edece6766.png#align=left&display=inline&height=111&originHeight=135&originWidth=1080&size=0&status=done&width=885)
图15 机器学习准确率与数据的关系
通过这张图可以看出,各种不同算法在输入的数据量达到一定级数后,都有相近的高准确度。于是诞生了机器学习界的名言:**成功的机器学习应用不是拥有最好的算法,而是拥有最多的数据!**
在大数据的时代,有好多优势促使机器学习能够应用更广泛。例如随着物联网和移动设备的发展,我们拥有的数据越来越多,种类也包括图片、文本、视频等非结构化数据,这使得机器学习模型可以获得越来越多的数据。同时大数据技术中的分布式计算Map-Reduce使得机器学习的速度越来越快,可以更方便的使用。种种优势使得在大数据时代,机器学习的优势可以得到最佳的发挥。
64 | 65 | 66 | 67 | ## 6. 机器学习的子类--深度学习 68 | 近来,机器学习的发展产生了一个新的方向,即“深度学习”。
虽然深度学习这四字听起来颇为高大上,但其理念却非常简单,就是传统的神经网络发展到了多隐藏层的情况。
在上文介绍过,自从90年代以后,神经网络已经消寂了一段时间。但是BP算法的发明人Geoffrey Hinton一直没有放弃对神经网络的研究。由于神经网络在隐藏层扩大到两个以上,其训练速度就会非常慢,因此实用性一直低于支持向量机。2006年,Geoffrey Hinton在科学杂志《Science》上发表了一篇文章,论证了两个观点:
**1.多隐层的神经网络具有优异的特征学习能力,学习得到的特征对数据有更本质的刻画,从而有利于可视化或分类;**
**2.深度神经网络在训练上的难度,可以通过“逐层初始化” 来有效克服。**
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186714-41f6f824-74ac-4711-9ea2-f37e17140ae2.jpeg#align=left&display=inline&height=383&originHeight=483&originWidth=1080&size=0&status=done&width=856)图16 Geoffrey Hinton与他的学生在Science上发表文章
通过这样的发现,不仅解决了神经网络在计算上的难度,同时也说明了深层神经网络在学习上的优异性。从此,神经网络重新成为了机器学习界中的主流强大学习技术。同时,具有多个隐藏层的神经网络被称为深度神经网络,基于深度神经网络的学习研究称之为深度学习。
由于深度学习的重要性质,在各方面都取得极大的关注,按照时间轴排序,有以下四个标志性事件值得一说:
2012年6月,《纽约时报》披露了Google Brain项目,这个项目是由Andrew Ng和Map-Reduce发明人Jeff Dean共同主导,用16000个CPU Core的并行计算平台训练一种称为“深层神经网络”的机器学习模型,在语音识别和图像识别等领域获得了巨大的成功。Andrew Ng就是文章开始所介绍的机器学习的大牛(图1中左者)。
2012年11月,微软在中国天津的一次活动上公开演示了一个全自动的同声传译系统,讲演者用英文演讲,后台的计算机一气呵成自动完成语音识别、英中机器翻译,以及中文语音合成,效果非常流畅,其中支撑的关键技术是深度学习;
2013年1月,在百度的年会上,创始人兼CEO李彦宏高调宣布要成立百度研究院,其中第一个重点方向就是深度学习,并为此而成立深度学习研究院(IDL)。
2013年4月,《麻省理工学院技术评论》杂志将深度学习列为2013年十大突破性技术(Breakthrough Technology)之首。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186682-6cceeb86-4b18-4f08-91a0-7f0f331a9479.jpeg#align=left&display=inline&height=339&originHeight=510&originWidth=1080&size=0&status=done&width=717)
图17 深度学习的发展热潮
文章开头所列的三位机器学习的大牛,不仅都是机器学习界的专家,更是深度学习研究领域的先驱。因此,使他们担任各个大型互联网公司技术掌舵者的原因不仅在于他们的技术实力,更在于他们研究的领域是前景无限的深度学习技术。
目前业界许多的图像识别技术与语音识别技术的进步都源于深度学习的发展,除了本文开头所提的Cortana等语音助手,还包括一些图像识别应用,其中典型的代表就是下图的百度识图功能。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186722-9b250727-eb2d-462c-acab-823dee5cc082.jpeg#align=left&display=inline&height=310&originHeight=517&originWidth=1078&size=0&status=done&width=646)
图18 百度识图
深度学习属于机器学习的子类。基于深度学习的发展极大的促进了机器学习的地位提高,更进一步地,推动了业界对机器学习父类人工智能梦想的再次重视。
69 |
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186722-713403ad-91d9-4369-851f-df62ae49fc1f.jpeg#align=left&display=inline&height=526&originHeight=762&originWidth=1080&size=0&status=done&width=746)
70 | 71 | 72 | 73 | ## 7. 机器学习的父类--人工智能 74 | 人工智能是机器学习的父类。深度学习则是机器学习的子类。如果把三者的关系用图来表明的话,则是下图:
![](https://cdn.nlark.com/yuque/0/2019/png/199648/1553594186738-19d8e922-dc3e-4f2e-8414-e2943bced8cc.png#align=left&display=inline&height=410&originHeight=511&originWidth=531&size=0&status=done&width=426)图19 深度学习、机器学习、人工智能三者关系
毫无疑问,人工智能(AI)是人类所能想象的科技界最突破性的发明了,某种意义上来说,人工智能就像游戏最终幻想的名字一样,是人类对于科技界的最终梦想。从50年代提出人工智能的理念以后,科技界,产业界不断在探索,研究。这段时间各种小说、电影都在以各种方式展现对于人工智能的想象。人类可以发明类似于人类的机器,这是多么伟大的一种理念!但事实上,自从50年代以后,人工智能的发展就磕磕碰碰,未有见到足够震撼的科学技术的进步。
总结起来,人工智能的发展经历了如下若干阶段,从早期的逻辑推理,到中期的专家系统,这些科研进步确实使我们离机器的智能有点接近了,但还有一大段距离。直到机器学习诞生以后,人工智能界感觉终于找对了方向。基于机器学习的图像识别和语音识别在某些垂直领域达到了跟人相媲美的程度。机器学习使人类第一次如此接近人工智能的梦想。
事实上,如果我们把人工智能相关的技术以及其他业界的技术做一个类比,就可以发现机器学习在人工智能中的重要地位不是没有理由的。
人类区别于其他物体,植物,动物的最主要区别,作者认为是**“智慧”**。而智慧的最佳体现是什么?
是计算能力么,应该不是,心算速度快的人我们一般称之为天才。
是反应能力么,也不是,反应快的人我们称之为灵敏。
是记忆能力么,也不是,记忆好的人我们一般称之为过目不忘。
是推理能力么,这样的人我也许会称他智力很高,类似“福尔摩斯”,但不会称他拥有智慧。
是知识能力么,这样的人我们称之为博闻广,也不会称他拥有智慧。
想想看我们一般形容谁有大智慧?圣人,诸如庄子,老子等。**智慧是对生活的感悟,是对人生的积淀与思考**,这与我们机器学习的思想何其相似?通过经验获取规律,指导人生与未来。没有经验就没有智慧。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186712-4a52ee1d-ed53-47ad-a040-29f86d3bfe33.jpeg#align=left&display=inline&height=350&originHeight=446&originWidth=1080&size=0&status=done&width=847)
图20 机器学习与智慧
那么,从计算机来看,以上的种种能力都有种种技术去应对。
75 |
例如计算能力我们有分布式计算,反应能力我们有事件驱动架构,检索能力我们有搜索引擎,知识存储能力我们有数据仓库,逻辑推理能力我们有专家系统,但是,唯有对应智慧中最显著特征的归纳与感悟能力,只有机器学习与之对应。这也是机器学习能力最能表征智慧的根本原因。
让我们再看一下机器人的制造,在我们具有了强大的计算,海量的存储,快速的检索,迅速的反应,优秀的逻辑推理后我们如果再配合上一个强大的智慧大脑,一个真正意义上的人工智能也许就会诞生,这也是为什么说在机器学习快速发展的现在,人工智能可能不再是梦想的原因。
人工智能的发展可能不仅取决于机器学习,更取决于前面所介绍的深度学习,深度学习技术由于深度模拟了人类大脑的构成,在视觉识别与语音识别上显著性的突破了原有机器学习技术的界限,因此极有可能是真正实现人工智能梦想的关键技术。无论是谷歌大脑还是百度大脑,都是通过海量层次的深度学习网络所构成的。也许借助于深度学习技术,在不远的将来,一个具有人类智能的计算机真的有可能实现。
最后再说一下题外话,由于人工智能借助于深度学习技术的快速发展,已经在某些地方引起了传统技术界达人的担忧。真实世界的“钢铁侠”,特斯拉CEO马斯克就是其中之一。最近马斯克在参加MIT讨论会时,就表达了对于人工智能的担忧。“人工智能的研究就类似于召唤恶魔,我们必须在某些地方加强注意。”
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186716-51777f1c-4471-4662-bc3f-ccc0d9454294.jpeg#align=left&display=inline&height=435&originHeight=508&originWidth=812&size=0&status=done&width=696)图21 马斯克与人工智能
尽管马斯克的担心有些危言耸听,但是马斯克的推理不无道理。“如果人工智能想要消除垃圾邮件的话,可能它最后的决定就是消灭人类。”马斯克认为预防此类现象的方法是引入政府的监管。在这里作者的观点与马斯克类似,在人工智能诞生之初就给其加上若干规则限制可能有效,也就是不应该使用单纯的机器学习,而应该是机器学习与规则引擎等系统的综合能够较好的解决这类问题。因为如果学习没有限制,极有可能进入某个误区,必须要加上某些引导。正如人类社会中,法律就是一个最好的规则,杀人者死就是对于人类在探索提高生产力时不可逾越的界限。
在这里,必须提一下这里的规则与机器学习引出的规律的不同,规律不是一个严格意义的准则,其代表的更多是概率上的指导,而规则则是神圣不可侵犯,不可修改的。规律可以调整,但规则是不能改变的。有效的结合规律与规则的特点,可以引导出一个合理的,可控的学习型人工智能。
76 | 77 | 78 | 79 | ## 8.总结 80 | 本文首先介绍了互联网界与机器学习大牛结合的趋势,以及使用机器学习的相关应用,接着以一个“等人故事”展开对机器学习的介绍。介绍中首先是机器学习的概念与定义,然后是机器学习的相关学科,机器学习中包含的各类学习算法,接着介绍机器学习与大数据的关系,机器学习的新子类深度学习,最后探讨了一下机器学习与人工智能发展的联系以及机器学习与潜意识的关联。经过本文的介绍,相信大家对机器学习技术有一定的了解,例如机器学习是什么,它的内核思想是什么(即统计和归纳),通过了解机器学习与人类思考的近似联系可以知晓机器学习为什么具有智慧能力的原因等等。其次,本文漫谈了机器学习与外延学科的关系,机器学习与大数据相互促进相得益彰的联系,机器学习界最新的深度学习的迅猛发展,以及对于人类基于机器学习开发智能机器人的一种展望与思考,最后作者简单谈了一点关于让计算机拥有潜意识的设想。
机器学习是目前业界最为Amazing与火热的一项技术,从网上的每一次淘宝的购买东西,到自动驾驶汽车技术,以及网络攻击抵御系统等等,都有机器学习的因子在内,同时机器学习也是最有可能使人类完成AI dream的一项技术,各种人工智能目前的应用,如微软小冰聊天机器人,到计算机视觉技术的进步,都有机器学习努力的成分。作为一名当代的计算机领域的开发或管理人员,以及身处这个世界,使用者IT技术带来便利的人们,最好都应该了解一些机器学习的相关知识与概念,因为这可以帮你更好的理解为你带来莫大便利技术的背后原理,以及让你更好的理解当代科技的进程。
81 | 82 | 83 | 84 | ### 9. 后记 85 | 本篇文章原作者写于 2014 年,写作周期两个多月。我对上述内容作了一点删改,请原作者见谅。对于想从事机器学习但无从下手的小伙伴来说,绝对是一篇很好的入门文章。
86 |
**参考文献:**
1.Andrew Ng Courera Machine Learning
2.LeNet Homepage
3.pluskid svm


(完)
87 | 88 | --- 89 | 90 |
大数据与人工智能的时代
你还在等什么?
   
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553594186687-bccfa864-d7d3-46c4-84d1-e324cf067394.jpeg#align=left&display=inline&height=452&originHeight=655&originWidth=1080&size=0&status=done&width=746) 91 | -------------------------------------------------------------------------------- /docs/ml/ml-term.md: -------------------------------------------------------------------------------- 1 | # 机器学习--术语 2 | 3 | 每个行业都有一些行话,外行人经常是刚接触就一头雾水,今天我们来捋一捋部分机器学习相关的术语。 4 | 在人工智能界有一种说法,认为> **机器学习**是人工智能领域中最能够体现智能的一个分支。我们或许每天都在不知不觉中使用了机器学习的算法每次,你打开谷歌、必应搜索到你需要的内容,正是因为他们有良好的学习算法。谷歌和微软实现了学习算法来排行网页每次,你用 > ****Facebook ****或苹果的图片分类程序他能认出你朋友的照片,这也是机器学习。每次您阅读您的电子邮件垃圾邮件筛选器,可以帮你过滤大量的垃圾邮件这也是一种学习算法。 5 | ![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553655951386-9cb9fc75-8ff4-44cb-989c-072ddffc1efa.jpeg#align=left&display=inline&height=479&originHeight=625&originWidth=974&size=0&status=done&width=746)
在进行机器学习研究之前,我们要有数据,假设我们有一组西瓜的数据,例如(色泽=青涩,敲声=清脆),(色泽=浅白,敲声=浑响)等等一组这样的数据,我们称之为**数据集(data set),**其中每一条记录是关于一个事件或对象的描述,称为一个示例(instance)或**样本(sample)**。反应事件或对象在某方面的表现和性质,例如“色泽”,“敲声”,称为**特征**(feature),特征的取值,例如“青涩”,“浅白”,称为特征值,属性张成的空间称为**样本空间**(sample space)。如果我们把特征当做坐标轴,可以构建一个多维空间,每个西瓜🍉都可以找到自己的坐标,这个点也可以使用向量表示,称为**特征向量(feature vector)**。
在这篇文章中,提到机器学习可以类比为人类的归纳总结过程。经验=数据,归纳=训练。         
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553655951408-35fb9992-a8a1-4b0a-9a83-c06a0d360cfb.jpeg#align=left&display=inline&height=359&originHeight=520&originWidth=1080&size=0&status=done&width=746)现在我们要拿着上文的数据,来训练我们选西瓜的模型。这个从数据到模型的过程,称之为**训练(training)**或**学习(learning)**。这个过程通过某个学习算法来完成的。训练过程使用的数据,成为训练数据(training data),其中每个样本称为一个**训练样本**(training sample)。训练样本组成的集合成为**训练集(training set)**。学习算法通常有参数设置,使用不用的参数值和训练数据,将产生不同的结果。
当然,要建立一个帮助我们买瓜的模型,仅有前面的数据是远远不够的,要建立这样的关于预测的模型,我们需要获得训练样本的结果信息,例如((色泽=青涩,敲声=浑响),好瓜),其中“好瓜称为**标记(lable)**,拥有标记信息的示例,则称为**样例(example)**。
前面,我们预测的是离散值,例如“好瓜”,“坏瓜”,此类学习任务称为**分类(classification)**。如果,我们想要预测连续值,比如西瓜成熟度 0.99 0.89 0.56 ,此类任务称为**回归(regression)**。对于只涉及两个分类的二分类(binary classfication),通常称其中一个类为**正类(positive class)**,另一个类为**负类(negative class)**,设计多个分类的任务,称为**多分类(multi-class calssfication)**。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553655951411-847a65b5-8ae7-4f9b-94b6-0f8df0a10e9d.jpeg#align=left&display=inline&height=1445&originHeight=1550&originWidth=800&size=0&status=done&width=746)
学得模型之后,使用这个模型进行预测的过程称为**测试(testing)**,被测试的样本称为**测试样本(testing sample)**。
其实,我们还可以对西瓜进行**聚类(clustering)**,也就是将训练集中的西瓜分成若干组。每一组称为一个“簇”(cluster)。聚类产生的分类,我们事前是不知道的,在学习过程中我们的训练集没有标记信息。
根据训练数据是否拥有标记信息,学习任务可大致划分为两大类:**监督学习(supervised learning)**和**无监督学习(unsupervised learning)**,分类和回归是前者的代表,而聚类是后者的代表。
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553655951398-2ea38815-b5b4-4199-813f-71e20017c316.jpeg#align=left&display=inline&height=367&originHeight=367&originWidth=640&size=0&status=done&width=640)
最后,需要注意的是,机器学习的目标是使学得的模型能很好地适用于“新样本”,而不是仅仅在训练集上工作得很好。学得的模型适用于新样本的能力,成为**泛化(generalization)**。泛化能力针对的其实是学习方法,它用于衡量该学习方法学习到的模型在整个样本空间上的表现。
回归问题往往会通过计算**误差(Error)**来确定模型的精确性。误差由于训练集和验证集的不同,会被分为训练误差(Training Error)和验证误差(Validation Error)。但值得注意的是,模型并不是误差越小就一定越好,因为如果仅仅基于误差,我们可能会得到一个**过拟合(Overfitting)**的模型;但是如果不考虑误差,我们可能会得到一个**欠拟合(Underfitting)**的模型,用图像来说的话大致可以这样理解:
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553655951416-2dfbe7b8-215a-4512-934a-46f694683f1b.jpeg#align=left&display=inline&height=140&originHeight=140&originWidth=555&size=0&status=done&width=555)
如果模型十分简单,往往会欠拟合,对于训练数据和测试数据的误差都会很大;但如果模型太过于复杂,往往会过拟合,那么训练数据的误差可能相当小,但是测试数据的误差会增大。好的模型应当平衡于这两者之间。
6 |
机器学习下面有很多算法,以及对应的应用场景,在接下来的文章,会慢慢啃完的。
7 |
推荐一个网站,它上面有很多数据集供我们选择,如图:


画外音:搜索 UCI


![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553655951424-950591bc-c8e0-41c6-84ec-af445d6305ba.jpeg#align=left&display=inline&height=379&originHeight=548&originWidth=1080&size=0&status=done&width=746)
8 |
以上只是冰山一角。
9 |
(完)
10 | 11 | --- 12 | 13 |
大数据与人工智能的时代
你还在等什么?
   
![](https://cdn.nlark.com/yuque/0/2019/jpeg/199648/1553655951414-71fb7c31-b74f-4fde-8a86-c99b308e96b3.jpeg#align=left&display=inline&height=452&originHeight=655&originWidth=1080&size=0&status=done&width=746)
14 |
你在看吗?

15 | > 本文参考: 16 | >   1.《机器学习》-- 周志华.清华大学出版社 17 | > 1.  吴恩达机器学习课程 18 | 19 |


20 | -------------------------------------------------------------------------------- /docs/ml/study-road.md: -------------------------------------------------------------------------------- 1 | # 超过8000星的「机器学习路线图」,福利。 2 | 3 | 昨天给大家分享了一个关于 AI 基础知识学习的两个网站和三本书。 4 | 5 | [给人工智能新手,两份不同阶段的资料](http://mp.weixin.qq.com/s?__biz=MzIwMjA2MTk4Ng==&mid=2247485464&idx=1&sn=56e67f8843094e46fcd2c3abea78f4ad&chksm=96e529d5a192a0c34ff8fa3844cb3ba9faaa285551677fa6d98b8a5182c49db091e819f7f3b8&scene=21#wechat_redirect) 6 | 7 | 今天,给大家安利一个另外一个机器学习的路线。

**持续更新,中英文都有,学习资料全部开源,免费。
** 8 | 9 | **机器学习路线如下:** 10 | 11 | **先决条件** 12 | * Python
13 | * Jupyter Notebook
14 | * 你需要的数学
15 | * 机器学习环境
16 | 17 | **使用 Scikit-Learn 进行机器学习** 18 | * 为什么是 Scikit-Learn?
19 | * 端到端机器学习项目
20 | * 线性回归
21 | * 分类
22 | * 训练模型
23 | * 支持向量机
24 | * 决策树
25 | * 集成学习和随机森林
26 | * 无监督学习
27 | * 结束并期待
28 | 29 |
**使用 TensorFlow 的神经网络** 30 | * 为什么选择 TensorFlow?
31 | * 启动和运行 TensorFlow
32 | * ANN - 人工神经网络
33 | * CNN - 卷积神经网络
34 | * RNN - 递归神经网络
35 | * 训练网络:最佳实践
36 | * 自动编码器
37 | * 强化学习
38 | * 下一步
39 | 40 | **实用工具** 41 | * 机器学习项目
42 | * 数据科学工具
43 | * 值得一看的 博客 / YouTube 频道 / 网站!
44 | 45 | **注意:上面提到的,**
**
**所有提到的资料课程,GitHub原文都有链接哦。** 46 | 47 | [戳这里](https://github.com/clone95/Virgilio/tree/master/zh-CN/LearningPaths/Machine%20Learning%20Engineer%20Career%20Path) 48 | 49 | --- 50 | **如果对您有帮助,欢迎点赞、关注、转发。**
51 |
52 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/199648/1555226590435-cbd4cfbc-d941-4781-ae61-2e2c4439b6d2.png#align=left&display=inline&height=262&name=image.png&originHeight=655&originWidth=1080&size=206333&status=done&width=432) 53 | -------------------------------------------------------------------------------- /docs/ml/study-website.md: -------------------------------------------------------------------------------- 1 | # 给人工智能新手,两份不同阶段的资料 2 | 3 | 4 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/199648/1555227126333-fe9c9cf7-607e-45fb-b85b-08e94754540d.png#align=left&display=inline&height=243&name=image.png&originHeight=608&originWidth=1018&size=301902&status=done&width=407)
图|编辑器 5 | 6 | 这篇文章,给大家推荐两个不错的网站。即使现在用不到,可以先收藏了。 7 | 8 | ## 偏理论 9 | 10 | **www.coursera.org 免费** 11 | 12 | AI 非技术通俗讲解 等级:初级 
https://www.coursera.org/learn/ai-for-everyone/ 13 | 14 | 机器学习理论基础
https://www.coursera.org/learn/machine-learning 15 | 16 | 神经网络与深度学习
https://www.coursera.org/learn/neural-networks-deep-learning 17 | 18 | 19 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/199648/1555227138315-c40da4f4-9616-4382-aaf3-dbc07da4c7d6.png#align=left&display=inline&height=302&name=image.png&originHeight=755&originWidth=1080&size=267801&status=done&width=432) 20 | 21 | ## 偏工程化 22 | 23 | **cn.udacity.com 收费**
**
机器学习、深度学习、计算机视觉、量化投资、自然语言处理、无人驾驶、数据科学家。 24 | 25 | 按照职业路径划分的学习科目,学过工科的都知道,项目为导向是占学习很大比重的,这上面的课程也是这样设计的,跟着项目学完,直接上手工作是没有什么问题的。 26 | 27 | 28 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/199648/1555227149170-54da008c-1f7d-4c9e-a1aa-3fceaacb71d4.png#align=left&display=inline&height=281&name=image.png&originHeight=703&originWidth=1080&size=357148&status=done&width=432) 29 | 30 | **建议** 31 | 32 | 如果你的目标是找到一份 AI 岗位的工作,那么 02 将会很适合你,优达学城的课程都是结合项目进行的,学完能够很快的上手工作。缺点是:有点贵。 33 | 34 | 如果你是在校的大学生,时间充足,建议从 01 开始,打好理论基础。 35 | 36 | ## 推荐书籍 37 | 38 | 39 | 不错的书籍如下: 40 | 41 | **统计学习方法 -- 李航**
**机器学习 -- 周志华(西瓜书)**
**深度学习 (花书)** 42 | 43 | --- 44 |
45 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/199648/1555227168300-0783bc05-f331-4e3d-a8ee-44a7f7b940c4.png#align=left&display=inline&height=262&name=image.png&originHeight=655&originWidth=1080&size=206333&status=done&width=432) 46 | -------------------------------------------------------------------------------- /docs/ziyuan01/BigDataIs.md: -------------------------------------------------------------------------------- 1 | # 简述大数据技术 2 | 3 | 大数据是什么?真的太宽泛了,从任何角度都可以回答这个问题。简单概述,大数据的几个表现就是海量数据,数据源多样,数据增速快。面临着数据存储的问题,数据计算的问题,所以大数据技术的出现就是为了解决上述问题。今天主要从数据流程方便介绍每个流程下的大数据技术以及应用。
4 | 5 | ## 数据流程 6 | 7 | **数据流程**
8 | 9 | 不管是传统的数据批处理还是实时流处理,数据端到端的过程都包含:数据产生、数据采集和传输、数据存储,处理、数据应用这几个过程。具体的流程图以及关键环节如下图 1-1  10 | 11 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/199648/1550899315204-3a859669-d295-483b-9a25-059fdba4180e.png#align=left&display=inline&height=416&linkTarget=_blank&name=image.png&originHeight=832&originWidth=1378&size=446120&status=done&width=689)
图 1-1
12 |
具体的细节这里不做介绍了,数据的处理由传统的数据库发展到数据仓库,由离线的批处理发展到实时的流处理,技术一直在变迁,但是上述流程大致是保持不变的,改变的只是技术细节。 13 | 14 | ## 开源技术 15 | 16 | **开源技术** 17 | 18 | 根据上述几个流程,下面介绍一下这些流程下都有哪些常用的开源技术,如图 1-2 所示 19 | 20 | ![image.png](https://cdn.nlark.com/yuque/0/2019/png/199648/1550899330971-dd7640ad-9ef6-4435-b5a4-39fb19beec64.png#align=left&display=inline&height=343&linkTarget=_blank&name=image.png&originHeight=686&originWidth=1400&size=233138&status=done&width=700)
图 1-2  21 | 22 | 并没有把所有的开源框架都列进去,而且商业软件上不在讨论范围之内。 23 | 24 | **数据采集和传输**主要用到的技术分为离线的批处理和实时采集数据,离线的批处理使用 Sqoop,下游主要是离线数据处理平台,比如说 Hive。实时的数据采集和传输主要使用 Flume + Kafka , 下游一般是实时流处理平台,比如 Spark, Storm, Flink 等。 25 | 26 | **数据处理** 主要用到的技术最为多,离线和准实时的工具主要包括 MapReduce、Hive 、Spark, 流处理的工具主要包含 Storm, Flink, Beam 等。 27 | 28 | 其中 MapReduce 抽象出 Map 和 Reduce 模型,使得不会分布式编程的程序员也可以编写分布式的程序在分布式集群上运行。 29 | 30 | Hive 是建立在 Hadoop 上的 SQL 抽象,降低了使用门槛,使得只会 SQL 的工程师不必编写程序就可以处理大数据的计算,Hive 会将 SQL 解析成 mapReduce 程序运行。尽管 MR + Hive 能够完成海量数据大多数批处理工作,但是延迟太大,不适合迭代计算和 DAG。 31 | 32 | 由于 Spark 具有可以伸缩性、基于内存等特点,能够较好的支持数据及时查询和迭代分析的需求。其中,Spark 还提供了了 SQL 接口来方便业务任务来处理和分析数据。此外,Spark 还有流处理的功能,提供了计算框架 spark streaming , 以微批处理的方式将实时数据分成小的时间片段(秒或者几百毫秒)来处理。 33 | 34 | Storm 是实时处理的开山鼻祖,提供了 Spout 和 Bolt 原语,底层是 Topology(拓扑) ,它会一直运行,除非 kill 它。 35 | 36 | Flink 把批处理和实时流处理计算整合起来了,将二者统一起来,Flink 完全支持流处理,批处理被看做是一种特殊的流处理,即它的输入数据被定义为有界。具体的细节这里不做展示,感兴趣的可以去官网看一下它的数据处理流程。 37 | 38 | Beam 不单单是相统一批流处理,还希望统一大数据处理范式和标准,该项目的重点是在于处理数据的编程范式和接口。Beam 主要是由 Beam SDK 和 Beam Runner 组成。 39 | 40 | **数据存储** 的主要技术,HDFS 是分布式文件系统,它是谷歌的 GFS 思想提出之后,Doug 受到启发而开发的一种类 GFS 的文件系统,高容错、高吞吐的特性使之非常适合大规模数据集。HBase 是构建在 HDFS 上的分布式、面向列的存储数据库,在需要实时读写和随机访问的超大规模数据集情况下时候。同样,HBase 是由 Google 的 BigTable 的思想发展而来的。还有其他如 Redis, Mysql 等存放一些结果数据或当做缓存来使用。  41 | 42 | 43 | --- 44 | 45 | 结束。 46 | -------------------------------------------------------------------------------- /docs/ziyuan01/MR.md: -------------------------------------------------------------------------------- 1 | # MapReduce 2 | 3 | ## MapReduce 是什么 4 | 5 | 它是一个__分布式__的__离线计算__框架。是一种编程模型,用于大规模(大于 TB)的并行计算,将自己的程序运行在分布式系统上,Map 是映射、Reduce 是归约。__可用于大规模的算法图形处理和文字处理__。 6 | 7 | ## MapReduce 的设计理念 8 | 9 | 1.分布式计算 10 | 2.移动计算到数据,计算向数据靠拢,也就是将计算程序移动到集群中的数据节点上运行。 11 | 12 | ## MapReduce 的计算框架组成 13 | 14 | 计算流程为:input --> map --> shuffle -->reduce --->output 15 | 16 | 下面来源于网络,觉得很详细。就以这个图作为依据来说明,mapreduce 的过程。 17 | 18 | 19 | 20 | ![image.png | left | 747x355](https://cdn.nlark.com/yuque/0/2018/png/199648/1542028905333-449256b7-1dd8-47ea-b440-490c647eeacf.png "") 21 | 22 | 23 | 24 | ## 深入理解 mapreduce 25 | MapReduce 底层到地址如何让运行的呢? 26 | 27 | 1.客户端要编写好 MapReduce 程序,配置好 MapReduce 的作业(job) 28 | 29 | 2.接下来提交 job 到 JobTracker 上。JobTracker 复杂构建这个 job , 具体的就是分配一个 job id , 接着检查输入输出目录是否存在等。 30 | 31 | ## MR架构 32 | 33 | __它是一主多从架构。__ 34 | 35 | __主是 JobTracker (RM)__ , 负责调度分配每一个子任务的运行于 TaskTracker 上。一个 Hadoop 集群一般只有一个 RM, 运行在 Matser 节点上。 36 | 37 | __从是 TaskTracker (NM)__, 主动联系 主 ,接收作业,并负责执行每一个任务。为了减少网络带宽,__TaskTrack__ 最好运行在 __HDFS__ 的 __DataNode__ 节点上。 38 | 39 | ## MR & Yarn 架构 40 | 41 | 特点: 42 | 1、分布式并行计算 43 | 2、主要核心功能:排序,默认的排序方式是按照key进行排序 44 | 概念定义: 45 | 1、MapReduce执行流程涉及到Client、ResourceManager、NodeManager、ApplicationMaster、Container、Task 46 | 2、其中Client是提交Mapreduce的机器 47 | 3、ApplicationMaster是负责该Job调度的进程,一个job一个applicationMaster 48 | 4、Container是资源表示形式 49 | 5、Task是运行在NodeManager上的进程,使用到资源就是Container  50 | 6、resourcemanager是管理整个集群的资源 51 | 52 | 7、nodemanager是单个节点的资源管理 53 | 54 | 55 | 56 | ![image.png | left | 747x323](https://cdn.nlark.com/yuque/0/2018/png/199648/1542031075758-2b5c3279-bff0-41ee-a3b3-1d80f7bcbf6b.png "") 57 | 58 | 59 | 60 | 61 | ![image.png | left | 747x393](https://cdn.nlark.com/yuque/0/2018/png/199648/1542031086462-6d139901-d15f-4051-9a19-b82f7d8579c9.png "") 62 | 63 | 64 | 提交流程: 65 | 1、Clinet向RM申请资源,RM上有所有NM的节点资源信息,RM将资源信息(NM的hostname、以及分配的内存和CPU大小)发送给Client 66 | 2、Client根据请求到资源信息发送到对应的NM,NM中产生Container对象,然后在Container对象中调用相关代码,启动AM  67 | 3、AM开始获取job相关设置信息,获得得到map task数量(由InputFormat的getSplits方法决定)和reduce task数量(由参数mapreduce.job.reduces影响) 68 | 4、然后AM向RM申请Map Task运行的资源(一个task就需要申请一个container),RM将分配的资源发送给AM,AM远程调用NM的相关方法启动对应的Container,并在Container中启动对应的Map Task 69 | 5、当一个Map Task执行完成后,会通知AM进程,当前Map Task执行完成;当总Map Task中有5%执行完成,AM向RM申请reduce task运行资源(一个task需要一个container) 70 | 6、RM将资源信息发送给AM,AM在对应的NM节点启动对应的Container,并在Container中运行对应的reduce task任务 71 | 7、当reduce task任务执行完成后,会AM进程,当所有的reduce task执行完成,AM通知client表示程序执行完成 72 | 73 | 具体执行流程: 74 | 75 | 1、runjar向resourcemanager申请提交一个job 76 | 2、resourcemanager返回job相关的资源提交的路径staging-dir和本job产生的job ID 77 | 3、runjar根据路径提交资源/tmp/hadoop-yarn/staging/job ID 78 | 4、runjar向resourcemanager回报提交结果 79 | 5、resourcemanager将job加入任务队列 80 | 6、nodemanager向resourcemanager领取任务 81 | 7、resourcemanager向nodemanager分配运行资源容器container 82 | 8、resourcemanager启动MRappMaster 83 | 9、MRappMaster向resourcemanager注册相关信息 84 | 10、启动map task 任务 85 | 11、启动reduce task 任务 86 | 12、job完成以后MRappMaster向resourcemanager注销自己 87 | 88 | 89 | ## 深入理解 shuffle 90 | 91 | __MapReduce的Shuffle过程介绍__ 92 | 93 | Shuffle的本义是__洗牌、混洗__,把一组有一定规则的数据尽量转换成一组无规则的数据,越随机越好。MapReduce中的Shuffle更像是洗牌的逆过程,__把一组无规则的数据尽量转换成一组具有一定规则的数__据。 94 | 95 | 为什么 MapReduce 计算模型需要 Shuffle 过程?我们都知道 MapReduce 计算模型一般包括两个重要的阶段:Map 是映射,负责数据的过滤分发;Reduce 是规约,负责数据的计算归并。Reduce 的数据来源于 Map,Map 的输出即是 Reduce 的输入,Reduce 则需要通过 Shuffle 来获取数据。 96 | 97 | 从Map输出到Reduce输入的整个过程可以广义地称为Shuffle。Shuffle横跨Map端和Reduce端,在Map端包括Spill过程,在Reduce端包括copy和sort过程,如图所示: 98 | 99 | 100 | 101 | ![image.png | left | 747x304](https://cdn.nlark.com/yuque/0/2018/png/199648/1542030867993-fb78d502-f22d-4f40-8288-25a6a6a48f6c.png "") 102 | 103 | 104 | 105 | __Spill过程__ 106 | 107 | Spill过程包括输出、排序、溢写、合并等步骤,如图所示: 108 | 109 | 110 | 111 | 112 | ![image.png | left | 620x176](https://cdn.nlark.com/yuque/0/2018/png/199648/1542030884941-b91931a9-223f-4cd4-a750-0048db8c9fa4.png "") 113 | 114 | 115 | __Collect__ 116 | 117 | 每个Map任务不断地以键值对的形式把数据输出到在内存中构造的一个环形数据结构中。使用环形数据结构是为了更有效地使用内存空间,在内存中放置尽可能多的数据。 118 | 119 | 这个数据结构其实就是个字节数组,叫Kvbuffer,名如其义,但是这里面不光放置了数据,还放置了一些索引数据,给放置索引数据的区域起了一个Kvmeta的别名,在Kvbuffer的一块区域上穿了一个IntBuffer(字节序采用的是平台自身的字节序)的马甲。数据区域和索引数据区域在Kvbuffer中是相邻不重叠的两个区域,用一个分界点来划分两者,分界点不是亘古不变的,而是每次Spill之后都会更新一次。初始的分界点是0,数据的存储方向是向上增长,索引数据的存储方向是向下增长,如图所示: 120 | 121 | 122 | ![image.png | left | 576x177](https://cdn.nlark.com/yuque/0/2018/png/199648/1542030922943-c5752822-78e1-442e-a1fc-610768bdc09c.png "") 123 | 124 | 125 | Kvbuffer的存放指针bufindex是一直闷着头地向上增长,比如bufindex初始值为0,一个Int型的key写完之后,bufindex增长为4,一个Int型的value写完之后,bufindex增长为8。 126 | 127 | 索引是对在kvbuffer中的索引,是个四元组,包括:value的起始位置、key的起始位置、partition值、value的长度,占用四个Int长度,Kvmeta的存放指针Kvindex每次都是向下跳四个“格子”,然后再向上一个格子一个格子地填充四元组的数据。比如Kvindex初始位置是-4,当第一个写完之后,(Kvindex+0)的位置存放value的起始位置、(Kvindex+1)的位置存放key的起始位置、(Kvindex+2)的位置存放partition的值、(Kvindex+3)的位置存放value的长度,然后Kvindex跳到-8位置,等第二个和索引写完之后,Kvindex跳到-32位置。 128 | 129 | Kvbuffer的大小虽然可以通过参数设置,但是总共就那么大,和索引不断地增加,加着加着,Kvbuffer总有不够用的那天,那怎么办?把数据从内存刷到磁盘上再接着往内存写数据,把Kvbuffer中的数据刷到磁盘上的过程就叫Spill,多么明了的叫法,内存中的数据满了就自动地spill到具有更大空间的磁盘。 130 | 131 | 关于Spill触发的条件,也就是Kvbuffer用到什么程度开始Spill,还是要讲究一下的。如果把Kvbuffer用得死死得,一点缝都不剩的时候再开始Spill,那Map任务就需要等Spill完成腾出空间之后才能继续写数据;如果Kvbuffer只是满到一定程度,比如80%的时候就开始Spill,那在Spill的同时,Map任务还能继续写数据,如果Spill够快,Map可能都不需要为空闲空间而发愁。两利相衡取其大,一般选择后者。 132 | 133 | Spill这个重要的过程是由Spill线程承担,Spill线程从Map任务接到“命令”之后就开始正式干活,干的活叫SortAndSpill,原来不仅仅是Spill,在Spill之前还有个颇具争议性的Sort。 134 | 135 | __Sort__ 136 | 137 | 先把Kvbuffer中的数据按照partition值和key两个关键字升序排序,移动的只是索引数据,排序结果是Kvmeta中数据按照partition为单位聚集在一起,同一partition内的按照key有序。 138 | 139 | __Spill__ 140 | 141 | Spill线程为这次Spill过程创建一个磁盘文件:从所有的本地目录中轮训查找能存储这么大空间的目录,找到之后在其中创建一个类似于“spill12.out”的文件。Spill线程根据排过序的Kvmeta挨个partition的把数据吐到这个文件中,一个partition对应的数据吐完之后顺序地吐下个partition,直到把所有的partition遍历完。一个partition在文件中对应的数据也叫段(segment)。 142 | 143 | 所有的partition对应的数据都放在这个文件里,虽然是顺序存放的,但是怎么直接知道某个partition在这个文件中存放的起始位置呢?强大的索引又出场了。有一个三元组记录某个partition对应的数据在这个文件中的索引:起始位置、原始数据长度、压缩之后的数据长度,一个partition对应一个三元组。然后把这些索引信息存放在内存中,如果内存中放不下了,后续的索引信息就需要写到磁盘文件中了:从所有的本地目录中轮训查找能存储这么大空间的目录,找到之后在其中创建一个类似于“spill12.out.index”的文件,文件中不光存储了索引数据,还存储了crc32的校验数据。(spill12.out.index不一定在磁盘上创建,如果内存(默认1M空间)中能放得下就放在内存中,即使在磁盘上创建了,和spill12.out文件也不一定在同一个目录下。) 144 | 145 | 每一次Spill过程就会最少生成一个out文件,有时还会生成index文件,Spill的次数也烙印在文件名中。索引文件和数据文件的对应关系如下图所示: 146 | 147 | 148 | 149 | ![image.png | left | 544x379](https://cdn.nlark.com/yuque/0/2018/png/199648/1542030940698-4b484c92-fa5f-4fde-8f3d-63ec914b54a8.png "") 150 | 151 | 152 | 在Spill线程如火如荼的进行SortAndSpill工作的同时,Map任务不会因此而停歇,而是一无既往地进行着数据输出。Map还是把数据写到kvbuffer中,那问题就来了:只顾着闷头按照bufindex指针向上增长,kvmeta只顾着按照Kvindex向下增长,是保持指针起始位置不变继续跑呢,还是另谋它路?如果保持指针起始位置不变,很快bufindex和Kvindex就碰头了,碰头之后再重新开始或者移动内存都比较麻烦,不可取。Map取kvbuffer中剩余空间的中间位置,用这个位置设置为新的分界点,bufindex指针移动到这个分界点,Kvindex移动到这个分界点的-16位置,然后两者就可以和谐地按照自己既定的轨迹放置数据了,当Spill完成,空间腾出之后,不需要做任何改动继续前进。分界点的转换如下图所示: 153 | 154 | 155 | ![image.png | left | 591x376](https://cdn.nlark.com/yuque/0/2018/png/199648/1542030948718-dd318e24-e3cb-4df0-a407-5a6c1bfbb64c.png "") 156 | 157 | 158 | 159 | Map任务总要把输出的数据写到磁盘上,即使输出数据量很小在内存中全部能装得下,在最后也会把数据刷到磁盘上。 160 | 161 | __Merge__ 162 | 163 | Map任务如果输出数据量很大,可能会进行好几次Spill,out文件和Index文件会产生很多,分布在不同的磁盘上。最后把这些文件进行合并的merge过程闪亮登场。 164 | 165 | Merge过程怎么知道产生的Spill文件都在哪了呢?从所有的本地目录上扫描得到产生的Spill文件,然后把路径存储在一个数组里。Merge过程又怎么知道Spill的索引信息呢?没错,也是从所有的本地目录上扫描得到Index文件,然后把索引信息存储在一个列表里。到这里,又遇到了一个值得纳闷的地方。在之前Spill过程中的时候为什么不直接把这些信息存储在内存中呢,何必又多了这步扫描的操作?特别是Spill的索引数据,之前当内存超限之后就把数据写到磁盘,现在又要从磁盘把这些数据读出来,还是需要装到更多的内存中。之所以多此一举,是因为这时kvbuffer这个内存大户已经不再使用可以回收,有内存空间来装这些数据了。(对于内存空间较大的土豪来说,用内存来省却这两个io步骤还是值得考虑的。) 166 | 167 | 然后为merge过程创建一个叫file.out的文件和一个叫file.out.Index的文件用来存储最终的输出和索引。 168 | 169 | 一个partition一个partition的进行合并输出。对于某个partition来说,从索引列表中查询这个partition对应的所有索引信息,每个对应一个段插入到段列表中。也就是这个partition对应一个段列表,记录所有的Spill文件中对应的这个partition那段数据的文件名、起始位置、长度等等。 170 | 171 | 然后对这个partition对应的所有的segment进行合并,目标是合并成一个segment。当这个partition对应很多个segment时,会分批地进行合并:先从segment列表中把第一批取出来,以key为关键字放置成最小堆,然后从最小堆中每次取出最小的输出到一个临时文件中,这样就把这一批段合并成一个临时的段,把它加回到segment列表中;再从segment列表中把第二批取出来合并输出到一个临时segment,把其加入到列表中;这样往复执行,直到剩下的段是一批,输出到最终的文件中。 172 | 173 | 最终的索引数据仍然输出到Index文件中。 174 | 175 | 176 | ![image.png | left | 590x235](https://cdn.nlark.com/yuque/0/2018/png/199648/1542030970368-39564007-a353-4126-a142-d2a7a63242af.png "") 177 | 178 | 179 | Map端的Shuffle过程到此结束。 180 | 181 | __Copy__ 182 | 183 | Reduce任务通过HTTP向各个Map任务拖取它所需要的数据。每个节点都会启动一个常驻的HTTP server,其中一项服务就是响应Reduce拖取Map数据。当有MapOutput的HTTP请求过来的时候,HTTP server就读取相应的Map输出文件中对应这个Reduce部分的数据通过网络流输出给Reduce。 184 | 185 | Reduce任务拖取某个Map对应的数据,如果在内存中能放得下这次数据的话就直接把数据写到内存中。Reduce要向每个Map去拖取数据,在内存中每个Map对应一块数据,当内存中存储的Map数据占用空间达到一定程度的时候,开始启动内存中merge,把内存中的数据merge输出到磁盘上一个文件中。 186 | 187 | 如果在内存中不能放得下这个Map的数据的话,直接把Map数据写到磁盘上,在本地目录创建一个文件,从HTTP流中读取数据然后写到磁盘,使用的缓存区大小是64K。拖一个Map数据过来就会创建一个文件,当文件数量达到一定阈值时,开始启动磁盘文件merge,把这些文件合并输出到一个文件。 188 | 189 | 有些Map的数据较小是可以放在内存中的,有些Map的数据较大需要放在磁盘上,这样最后Reduce任务拖过来的数据有些放在内存中了有些放在磁盘上,最后会对这些来一个全局合并。 190 | 191 | __Merge Sort__ 192 | 193 | 这里使用的Merge和Map端使用的Merge过程一样。Map的输出数据已经是有序的,Merge进行一次合并排序,所谓Reduce端的sort过程就是这个合并的过程。一般Reduce是一边copy一边sort,即copy和sort两个阶段是重叠而不是完全分开的。 194 | 195 | Reduce端的Shuffle过程至此结束。 196 | -------------------------------------------------------------------------------- /docs/ziyuan01/README.md: -------------------------------------------------------------------------------- 1 | # HDFS 2 | 3 | ## 如何学习大数据? 4 | 5 | 大数据生态有什么? 6 | ![image.png | left | 747x434](https://cdn.nlark.com/yuque/0/2018/png/199648/1541505323203-074507a4-3e06-44ed-99bb-bda10f11f9f5.png "") 7 | 8 | 共同点是:都是分布式的。 9 | __分布式就是将庞大的数据,复杂的业务分发到不同的计算机节点和服务器上进行处理。__ 10 | 11 | 为什么使用分布式呢?要从大数据的角度思考🤔 :tada: 12 | 1.效率高,处理快 13 | 2.单点限制,给单机解压 14 | 3.可扩展,添加更多的节点 15 | 4.安全稳定,一个节点宕机,另一个节点可以接替。 16 | 17 | __大数据架构层面看问题,。__ 18 | 19 | __分布式计算就是将庞大的数据,复杂的业务分发到不同的计算机节点和服务器上进行处理。__ 20 | 发展:03年Google 发表三篇论文,分别是 GFS,mapreduce,bigtable的思想,分别对应后来出现的 HDFS,mapreduce,Hbase 21 | 06年 __Docu cutting__ 推出 Hadoop,现在就职于 __Cloudra__ 公司。 22 | 阿里飞天2.0完成重大如破!!! 23 | 24 | 等等上述这些都是大数据的基石,这个时代研究大数据是很有意义的。时代在进步... 25 | 26 | ## HDFS 是什么? 27 | 28 | 它是一个__分布式文件存储系统,__全称是 __Hadoop Distributed File System 。 __为什么会出现这个呢?想象一个场景,数据随着业务的增长飞快的积累,达到了PB甚至更大的量级,这时候受网络带宽和单机节点的资源限制的影响,海量的数据无法进行存储,如何存储,万一数据丢了怎么办?这一系列问题,在 HDFS 中都将迎刃而解。__HDFS 就是解决海量数据的存储问题__。百度网盘就是一个现实的例子,它的成功离不开 分布式存储技术。 29 | 30 | 科普: 31 | PB = 1024TB 32 | TB = 1024GB 33 | GB = 1024MB 34 | MB = 1024KB 35 | 36 | 假设这里有 一个带宽为 4000M 的网络,带宽是以 bit(比特)表示,而电信,联通,移动等运营商在推广的时候往往忽略了这个单位。 37 | 所以实际处理处理速度是 4000/8=500m/s 38 | 39 | 传输 __1TB__ 的数据,大概是 2097秒 约等于 34 分钟。 40 | 传输 1PB 的数据 ,大概是 596 小时 约等于 24 天。这还是在没有其他因素的影响下。注意 PB 级别的数据是大数据的入门级别的数据。可见处理海量数据,单机是很难高效的进行的,需要利用分布式来存储和计算海量数据。 41 | 42 | ## HDFS 的优缺点 43 | 44 | 优点: 45 | * 处理海量数据,TB , PB ... 46 | * 支持处理百万规模以上的文件数量, __10k+节点__ 47 | * __适合批处理__ ,__移动计算而非数据,数据位置暴露给计算框架__ 48 | * 可构建在廉价机器上 49 | * 可靠性高,多个副本 50 | * 自动创建多个副本,副本丢失后自动恢复,高容错性 51 | 52 | 缺点: 53 | * 不支持毫秒级别 54 | * 吞吐量达但受限于延迟 55 | * 不允许修改文件(其实本身支持,但不这么做,为了性能) 56 | 57 | ## HDFS 1.x 架构图 58 | 59 | 60 | 61 | ![image.png | left | 747x488](https://cdn.nlark.com/yuque/0/2018/png/199648/1541508154349-d5d9e29b-922d-4cfa-8ade-9cac1b2cbfb8.png "") 62 | 63 | 64 | 上述红字部分是这个架构存在的问题,后面还提到__ fsimage__ 太大之后消耗内存的问题。 65 | 66 | 由上图可见,HDFS主要的部分有 __NameNode , DateNode , Secondeary Namenode__ 67 | 68 | ## HDFS 功能模块详解 69 | 70 | #### __HDFS 数据存储模型(逻辑上)__ 71 | 72 | 文件被__线性切分__为固定大小的 block 块。通过偏移量(offset)进行标记。 73 | 1.x版本的默认 block 块为 64 M 74 | 2.x版本的默认 block 块为 128 M 75 | 76 | #### __存储方式__ 77 | 78 | 按__固定大小__切分为若干个 __block__ ,存储到不同的节点上。 79 | 默认情况下,每个 block 都有额外的两个副本,共三份。 80 | 副本数不能大于节点数,(同一个节点就出现相同的副本了,没有意义) 81 | 82 | 文件上传时,可以通过 HDFS 客户端设置 文件的副本数和 block 大小,该文件一旦上传之后,副本数可以修改,但是 block 的大小就不能改了。 83 | 84 | 85 | 86 | ![image.png | left | 747x496](https://cdn.nlark.com/yuque/0/2018/png/199648/1541508828396-0347d0da-bbcb-40dd-87b6-afdd4b8d24b4.png "") 87 | 88 | 89 | #### __NameNode__ 90 | 91 | 简称 NN , 主要是干什么的呢? 92 | 93 | 观察之前的结构图,也可以看出,不管是文件的读取和写入都是要经过 NameNode 节点的,而且 NameNode 节点接受来自 DataNode 的心跳数据,而且还会存储文件的元数据(文件大小,归属,权限,偏移量列表即说明一个完整的文件包含哪些 block ) 94 | 95 | 综上,NameNode 主要干这样事情: 96 | 97 | * 接受客户端的读、写 98 | * 接收 DN 汇报 block 列表 99 | * 保存元数据,元数据是基于内存的。数据包括文件的大小,归属,偏移量列表等 100 | * block 的位置信息,在启动的时候上报给 NN,并且动态更新。一般是3秒一次 101 | 102 | 说明: 103 | 104 | NameNode 如果10分钟没有收到 DN 的汇报,就会认为这个节点 lost 了,然后把缺失的副本在别的节点上增加一份。 105 | 106 | NameNode 启动之后,会加载元数据到内存中,也会创建一份镜像文件 fsimage. 107 | 108 | block 位置信息不会保存到 fsimage 109 | 110 | eidts 记录对元数据的操作日志。 111 | 112 | #### __Secondary NameNode__ 113 | 114 | 它的主要工作主要是帮助NN合并 edits 和元数据,减少 NN 启动时间。 115 | 116 | 执行合并的时间和机制: 117 | 118 | * 根据配置文件配置时间间隔:fs.checkppint.period , 默认为 3600 秒 119 | * 根据配置文件设置 edits log 的大小,fs.checkpoint.size 默认为 64 MB 120 | 121 | 合并流程:图示 122 | 123 | 124 | 125 | ![image.png | left | 747x590](https://cdn.nlark.com/yuque/0/2018/png/199648/1541512723822-4962dfa8-4d5a-43ff-b44e-aa9f64760674.png "") 126 | 127 | 128 | 步骤一:__SSN __在一个 checkpoint 时间点和 __NameNode __进行通信,请求 NameNode 停止使用 edits 文件记录相关操作而是暂时将新的 Write 操作写到新的文件 edits.new 来。 129 | 130 | 步骤二:SSN 通过 HTTP GET 的方式从 __NameNode __中将 __fsimage__和__edits __文件下载回来本地目录中。 131 | 132 | 步骤三:SSN中合并 edits 和 fsimage 。SSN 将从 NameNode 中下载回来的 fsimage 加载到内存中,然后逐条 执行 edits 文件中的各个操作项,使得加载到内存中的 fsimage 中包含 edits 中的操作,这个过程就是所谓的合并了。 133 | 134 | 步骤四:在 SSN 中合并完 fsimage 和 edits 文件后,需要将新的 fsimage 回传到 NameNode 上,这个是通过__HTTP POST __方式进行的。 135 | 136 | 步骤五:__NameNode __将从 SSN 接收到的新的 fsimage 替换掉旧的 fsimage 。同时将 edits.new 文件转换为通常的 edits 文件,这样 edits 文件的大小就得到减少了。SSN 整个合并以及和 NameNode 的交互过程到这里已经结束。 137 | 138 | 139 | 一个亿的 block 的元数据会占用138G的内存。可见,元数据太大,会大量占用服务器的内存资源 140 | 141 | 142 | #### __DataNode __ 143 | 144 | 就是用来存储数据的。逻辑上分为各个block 块 145 | 146 | 这里有一个 block 块的放置策略: 147 | * 第一个副本,如果在集群内部会放到上传的这台服务器的 DN 上。如果是集群外,则会随机找一个空闲的机器。 148 | * 第二个副本,放置在与第一个副本 不同机架 的节点上。 149 | * 第三个副本,放置在与第二个副本 相同机架 的 不同 节点上。 150 | * 更多 副本,随机 151 | 152 | 153 | 154 | ![image.png | left | 747x540](https://cdn.nlark.com/yuque/0/2018/png/199648/1541513477121-1a141885-bde2-4246-8822-d3af5ed0bc75.png "") 155 | 156 | 157 | 机架,这种结构的多为功能型服务器。机房里面可以见到,不同网段,Hadoop源码里面提供了感知机架(机架感应)的代码。 158 | 159 | #### __HDFS的读流程__ 160 | 161 | 162 | 163 | ![image.png | left | 747x447](https://cdn.nlark.com/yuque/0/2018/png/199648/1541823943595-589f23e3-efe0-4146-bd92-e9e4be512996.png "") 164 | 165 | 166 | 角色:HDFS Client , NameNode, DateNode 167 | 168 | 1,首先 HDFS 调用 FileSystem 对象的 Open 方法获取一个 DistributedFileSystem 实例; 169 | 2,DistributedFileSystem 通过 __RPC协议__ 获取第一批 block localtions(第一批 block 块的位置),__同一个 block 和副本都会返回位置信息,这些位置信息按照 hadoop 的拓扑结构给这些位置排序,就近原则。__ 170 | 3.前两部会生成一个 FSDataInputStream ,该对象会被封装 DFSInputStream 对象,DFSInputStream 可以方便的管理 datanode 和 namenode 数据流。客户端调用 read 方法,DFSInputStream 最会找出离客户端最近的 datanode 并连接。 171 | 4.__数据从 datanode 源源不断的流向客户端__。这些操作对客户端来说是透明的,客户端的角度看来只 172 | 是读一个持续不断的流。 173 | 174 | 5.如果第一批 block 都读完了, DFSInputStream 就会去 namenode 拿下一批 block 的 locations,然后继续读,如果所有的块都读完,这时就会关闭掉所有的流。如果在读数据的时候, __DFSInputStream 和 datanode的通讯发生异常,就会尝试正在读的 block 的排序第二近的datanode,并且会记录哪个 datanode 发生错误,剩余的blocks 读的时候就会直接跳过该 datanode。__ 175 | DFSInputStream 也会检查 block 数据校验和,如果发现一个坏的 block ,就会先报告到 namenode 节点,然后DFSInputStream 在其他的 datanode 上读该 block 的镜像。该设计就是客户端直接连接 datanode 来检索数据并且namenode 来负责为每一个 block 提供最优的 datanode,namenode 仅仅处理 block location 的请求,这些信息都加载在 namenode 的内存中,hdfs 通过 datanode 集群可以承受大量客户端的并发访问。 176 | 177 | RPC 跨越了传输层应用层RPC 使得开发包括网络分布式多程序在内的应用程序更加容易。 178 | 179 | #### __HDFS的写流程__ 180 | 181 | 182 | 183 | ![image.png | left | 747x540](https://cdn.nlark.com/yuque/0/2018/png/199648/1541825912702-70ffd0db-65ff-44fc-9007-d8d7f0ff5c7e.png "") 184 | 185 | 1.客户端通过调用 DistributedFileSystem 的 create 方法创建新文件。 186 | 187 | 2.DistributedFileSystem 通过 RPC 调用 namenode 去创建一个没有 blocks 关联的新文件,创建前, namenode 会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过, namenode 就会记录下新文件,否则就会抛出IO异常。 188 | 189 | 3.前两步结束后,会返回 FSDataOutputStream 的对象,与读文件的时候相似,FSDataOutputStream 被封装成DFSOutputStream。 190 | DFSOutputStream 可以协调 namenode 和 datanode。客户端开始写数据到 DFSOutputStream,DFSOutputStream 会把数 191 | 据切成一个个小的 packet然后排成队列 data quene。 192 | 193 | 4.DataStreamer 会去处理接受 data quene,它先询问namenode 这个新的 block 最适合存储的在哪几个 datanode。 194 | 195 | 里(比如重复数是 3,那么就找到 3 个最适合的 datanode),把他们排成一个管道 pipeline 输出。DataStreamer 把packet 按队列输出到管道的第一个 datanode 中,第一个datanode 又把 packet 输出到第二个 datanode 中,以此类推。 196 | 197 | 5.DFSOutputStream 还有一个对列叫 ack quene,也是由 packet 组成等待 datanode 的收到响应,当 pipeline 中的 datanode 都表示已经收到数据的时候,这时 ack quene才会把对应的 packet 包移除掉。 如果在写的过程中某个datanode 发生错误,会采取以下几步: 198 | 199 | * pipeline 被关闭掉; 200 | 201 | * 为了防止防止丢包。ack quene 里的 packet 会同步到 data quene 里;创建新的 pipeline 管道怼到其他正常 DN上 202 | 203 | * 剩下的部分被写到剩下的两个正常的 datanode 中; 204 | 205 | * namenode 找到另外的 datanode 去创建这个块的复制。当然,这些操作对客户端来说是无感知的。 206 | 207 | 6.客户端完成写数据后调用 close 方法关闭写入流。深入 DFSOutputStream 内部原理 208 | 209 | 打开一个 DFSOutputStream 流,Client 会写数据到流内部的一个缓冲区中,然后数据被分解成多个 Packet,每个Packet 大小为 64k 字节,每个 Packet 又由一组 chunk 和这组 chunk 对应的 checksum 数据组成,默认 chunk 大小为 512字节,每个 checksum 是对 512 字节数据计算的校验和数据。 210 | 211 | ===》当 Client 写入的字节流数据达到一个 Packet 的长度,这个 Packet 会被构建出来,然后会被放到队列dataQueue 中,接着 DataStreamer 线程会不断地从dataQueue 队列中取出 Packet,发送到复制 Pipeline 中的第一个 DataNode 上,并将该 Packet 从 dataQueue 队列中移到 ackQueue 队列中。ResponseProcessor 线程接收从Datanode 发送过来的 ack,如果是一个成功的 ack,表示复制 Pipeline 中的所有 Datanode 都已经接收到这个 Packet,ResponseProcessor 线程将 packet 从队列 ackQueue 中删除。 212 | 213 | ====》 在发送过程中,如果发生错误,错误的数据节点会被移除掉,ackqueue 数据块同步到 dataqueue 中,然后重新创建一个新的 Pipeline,排除掉出错的那些 DataNode节点,接着 DataStreamer 线程继续从 dataQueue 队列中发送 Packet。 214 | 215 | 下面是 DFSOutputStream 的结构及其原理,如图所示: 216 | 217 | 218 | ![image.png | left | 726x374](https://cdn.nlark.com/yuque/0/2018/png/199648/1541522815542-7b1d4d60-3ad1-404a-a44a-4d8ad04e1ed2.png "") 219 | 220 | 注意:客户端执行 write 操作后,写完的 block 才是可见的,正在写的 block 对客户端是不可见的,只有调用 sync 方法,客户端才确保该文件的写操作已经全部完成,当客户端调用 close 方法时,会默认调用 sync 方法。是否需要手动调用取决你根据程序需要在数据健壮性和吞吐率之间的权衡。 221 | 222 | ## HDFS 的文件权限和安全模式 223 | 224 | __安全模式__ 225 | 226 | * namenode 启动的时候,首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各 项操作。 227 | * 一旦在内存中成功建立文件系统元数据的映射,则创建一个新的fsimage 文件(这个操作不需要 SecondaryNameNode)和一个空的编辑日志。 228 | * 此刻 namenode 运行在安全模式。即 namenode 的文件系统对于客服端来说是只读的。(显示 目录,显示文件内容等。写、删除、重命名都会失败)。 229 | * 在此阶段 Namenode 收集各个 datanode 的报告,当数据块达到最小副本数以上时,会被认为是“安全”的, 在一定比例(可设置)的数据块被确定为“安全”后,再过若干时间,安全模式结束 230 | * 当检测到副本数不足的数据块时,该块会被复制直到达到最小副本数,系统中数据块的位 置并不是由 namenode 维护的,而是以块列表形式存储在 datanode 中 231 | 232 | 233 | ![image.png | left | 747x220](https://cdn.nlark.com/yuque/0/2018/png/199648/1541523023852-0f767a2f-1df2-4fe4-9ce2-50102276741d.png "") 234 | 235 | 236 | 退出安全模式:__hdfs namenode -safemode leave__ 237 | 238 | ## HDFS 命令操作 239 | 240 | 241 | `hdfs dfs -put file /input` 242 | file是要上传的文件,/input 是文件上传到 hdfs 的路径。 243 | 244 | `hdfs dfs -rm -rf file /input` 245 | 删除文件 246 | 247 | `hdfs dfs -mkdir -p /input` 248 | 创建目录 249 | 250 | 其他的可以查看 help 251 | -------------------------------------------------------------------------------- /docs/ziyuan01/distribute.md: -------------------------------------------------------------------------------- 1 | # 分布式系统基础 2 | 3 | ## 定义 4 | 5 | > 分布式系统:利用多台计算机协同解决单台计算机无法解决的计算、存储问题 6 | 7 | 8 | 单个节点<----->网络<----->单个节点<----->网络<----->单个节点 9 | 10 | ## 常见的异常情况 11 | * 分布式系统的核心问题就是解决各种异常情况 12 | * __机器宕机__(机器出问题): 13 | * 最常见,一般需要人工介入 14 | * 节点重启后会丢失内存信息(部分分布式系统中,节点可以通过读取本地存储或其他节点的方式恢复内存信息) 15 | * __网络异常__(网络出问题): 16 | * 消息丢失 17 | * 在IP 网络中,网络层不保证数据报文的可靠传递,在发生网络拥塞,路由变动、设备异常等情况时,都可能发生数据丢失的问题 18 | * 如果某些节点正常,而某些节点始终无法正常通信,则称之为“网络分化” 19 | * 消息乱序 20 | * 消息和发送的顺序不一致 21 | * 应对方法:序列号机制 22 | * 数据错误 23 | * 比特错误 24 | * 应对方法:校验码 25 | * TCP协议下仍要考虑网络异常: 26 | * TCP 只能保证传输层可靠,而无法保证上层通信仍可靠 27 | 28 | > * 分布式系统三态: 29 | > \* 成功 30 | > \* 失败 31 | > \* 超时: 32 | > \* 某个节点宕机,无法返回成功信号 33 | > \* 网络异常,无法发出/收到相应信号 34 | 35 | * __存储数据丢失__ 36 | * 常见以硬盘作为存储介质,硬盘损坏导致 37 | * 对策:从其他节点读取,恢复存储 38 | * __其他异常__(半死不活类) 39 | * 磁盘故障导致的IO缓慢,时好时坏的,导致进程无响应 40 | * 网络不稳定 41 | * __异常一定会发生,分布式系统的样本一般较大:即 小概率 X 大样本 =大概率__ 42 | * 比如某系统中每天异常发生的概率为10 的-9 次方,但系统每天处理的请求为 10的8次方,最终这种异常出现的概率就成为了 10% 43 | 44 | ## 副本: 45 | > * 副本 是指在分布式系统中为数据或者服务提供的冗余 46 | > \* 数据副本是解决数据丢失异常的唯一手段 47 | > \* 服务副本,是指数个节点提供某种相同的服务,这种服务一般并不依赖节点本地的存储,其所需数据一般来自其他节点 48 | > \* 例如:GFS 的一个 chunk 的数个副本就是数据副本 49 | > \* 而 mapreduce 的 job worker则是服务副本 50 | 51 | * __副本一致性__:分布式系统通过副本控制协议,使得从系统外部读取系统内部的各个副本的数据在一定的约束提哦啊见下相同,称之为 副本一致性(consistency) 52 | * 强一致性(strong consistency): 53 | * 任何时刻,任何用户或者节点 都可以读到 最近一次成功更新后的副本数据。 54 | * 一致性最高,最难实现 55 | * 单调一致性(monotonic consistency): 56 | * 任何时刻,任何用户一旦读到某个数据在某次更新后的值,这个用户不会再读到比这个值更旧的值 57 | * 若于强一致,但是却非常实用;因为通常来说,用户只关心从己方视角观察到的一致性 58 | * 回话一致性(session consistency): 59 | * 任何时刻,任何用户在某一次回话中一旦读到某个数据在某次更新后的值,这个用户在这次回话中不会再读到比这个值更旧的值 60 | * 通过引入 会话 的概念,在单调一致的基础上更加放松约束。 61 | * 最终一致性(eventual consistency) 62 | * 要求一旦更新成功,各个副本上的数据最终将达到完全一致的状态,但是达到最终一致状态所需要的时间不能保障。 63 | * 对于最终一致的系统而言,一个用户只要始终读取某一个副本的数据,则可以实现类似单调一致的效果 64 | * 弱一致性(week consistency): 65 | * 一旦某个更新成功,用户无法在一个确定的时间内读到这次更新的值,且即使在某个副本上读到了新的值,也不能保证在其他副本上可以读到新的值。 66 | * 弱一致性在实际中很难使用,需要应用方做很多工作来保证系统可用。 67 | 68 | ## 衡量分布式系统的指标: 69 | * 性能(performance): 70 | * 系统的吞吐量: 指系统在某一时间可以处理的数据总量: 71 | * 系统的响应延迟:系统完成某一功能需要使用的时间 72 | * 系统的并发能力: 系统可以同时完成某一功能的能力: QPS(query per second) 73 | * 三个性能往往会相互制约 74 | * 可用性(avaiability) 75 | * 指在面对 各种异常情况,可以提供服务的能力 76 | * 可扩展性(scalability) 77 | * 指分布式系统通过扩展集群机器规模提高系统性能(吞吐、延迟、并发)、存储容量、计算能力的特性。 78 | * 一致性 79 | * 分布式系统为了提高可用性,总是不可避免的使用副本机制,从而引发副本一致性问题。 80 | * 越是强的一致性模型,对用户使用起来越简单。 81 | 82 | 83 | --- 84 | 85 | # 数据分布方式 86 | 87 | --- 88 | 89 | 90 | > ## 分布式VS单机: 91 | * 最大区别在于 问题的规模,即 计算、存储的区别 92 | * 无论是计算还是存储,其问题的输入对象都是数据,所以如何拆解分布式系统的输入数据成为分布式系统的基本问题,这里把这个问题叫做 数据分布方式: 93 | 94 | 1. __哈希方式__:按照数据的某一特征(如id)计算哈希值,并将哈希值与集群中的机器建立映射关系,从而将不同的哈希值的数据分布到不同的机器上。 95 | * 优点 96 | * 只要哈希函数的散列特性好,哈希方式可以比较为均匀的将数据分布到集群中去 97 | * 需要的元数据信息也简单,只要知道 哈希函数的计算方式及 模的服务器总量 即可 98 | * 缺点: 99 | * 可扩展性不高,一旦集群规模需要扩展,则几乎所有的数据都要被迁移 并重新分布 100 | * 工程中,往往在扩展时,将集群规模成本扩展,按照数据重新计算哈希,这样原本一台机器上的数据只需要迁移一半到对应的机器即可 101 | * 另一张思路是,将对应关系交由专门的元数据服务器来进行管理。 102 | * 一旦数据特征值的数据严重不均,容易出现“数据倾斜“(data skew)的问题。例如:若系统以某个id作为哈希分数据,当某个id数据量异常庞大时, 该用户的数据将始终由一台机器处理,造成数据缓慢的问题 103 | * 解决思路: 重新选取数据特征 104 | 2. __按照数据范围分布__:将数据按照特征值的值域范围划分为不同的区间(数据区间的数据大小和区间大大小时内有关系的)(动态划分区间) 105 | * 需要记录数据分布情况 106 | * 优点: 107 | * 扩展性比较好 108 | * 缺点: 109 | * 需要维护较为复杂的元数据 110 | 3. __按数据量分布__: 与具体的数据特征无关,将数据整体视为一个顺序增长的文件,然后将文件按照固定的大小分为若干的数据块,不同的数据块分到不同的服务器上 111 | * 优缺点与按数据范围类似 112 | 4. __一致性哈希__: 使用一个哈希函数计算数据或数据特征的哈希值,令该哈希函数的输出值域成为一个封闭的环,即哈希函数的输出的最大值是最小值的前序。 113 | * 优点: 114 | * 可以任意动态的添加、删除节点 115 | * 缺点: 116 | * 随机分布节点的方式使得其很难均匀的分布哈希值域 117 | * 改进算法是:引入 虚节点的概念(先均匀分布,添加节点时也新添加 虚节点) 118 | 119 | ## 副本与数据分布 120 | * 以机器为单位VS以文件段为单位: 121 | * 以文件段为单位的优点: 122 | * 数据恢复效率高,文件副本将在所有的机器上均有分布 123 | * 副本分布与机器无关,有利于集群扩展 124 | * 同样,文件段的方式也会引起元数据的开销过大 125 | * 本地化计算: 126 | * 尽量将计算和存储都放在同一台物理机上进行 127 | 128 | 工程应用: 129 | 130 | | 实例 | 数据分布方案 | 131 | | :--- | :--- | 132 | | GFS、HDFS | 按数据量分布 | 133 | | mapreduce | 本地化计算 | 134 | | big talbe /hbase | 按数据范围 | 135 | 136 | # 副本控制协议: 137 | > 指按照特定的协议流程控制副本数据的读写行为,使得副本满足一定的可用性和一致性要求的分布式协议。 138 | > 主要包含两大类:1、 中心化 2、 区中心化 139 | 140 | ## 中心化副本控制协议: 141 | * 由一个中心节点协调副本数据的更新、维护副本之间的一致性 142 | * 优点: 143 | * 协议简单,将分布式并发控制问题简化为单机并发控制问题 144 | * 缺点: 145 | * 存在停服时间: 系统的可用性依赖于中心化节点,当中心节点异常或与中心节点通信异常时,系统将失去某些服务。 146 | 147 | ### Primary-secondary协议 148 | > 在本协议中,副本被分为两大类,其中有且仅有一个副本作为primary副本,除primary的都时secondary副本。维护primary的节点作为中心节点,中心节点负责维护数据的更新、并发控制、协调副本的一致性。 149 | 150 | 1. 数据更新基本流程: 151 | 1. 外部节点将更新操作发给primary节点 152 | 2. primary节点进行并发控制即确定并发更新操作的先后顺序 153 | 3. primary节点将更新操作发送给secondary节点 154 | 4. primary 根据 secondary 节点的完成情况决定更新是否成功并将结果返回外部节点 155 | 2. 数据读取方式: 156 | 1. primary-secondary实现强一致性的几种思路: 157 | 1. 由于数据的更新流程都是由primary控制的,primary副本上的数据一定是最新的,所以如果始终只读primary副本的数据,可以实现强一致性 158 | 2. 由primary控制节点secondary节点的可用性 159 | 3. primary副本的确定与切换 160 | 1. 在 primary-secondary 类型的分布式系统中,哪个副本是 primary 这一信息都属于元信息,由专门的元数据服务器维护。执行更新操作时,首先查询元数据服务器获取副本的 primary 信息,从而进一步执行数据更新流程。 161 | 2. 切换副本的难点在于两个方面: 162 | 1. 如何确定节点的状态以发现原 primary 节点异常是一个较为复杂的问题 163 | 2. 切换 primary后,不能影响副本的一致性 164 | 3. 由于分布式系统中可靠的发现节点异常是需要一定的探测时间的,这样的探测时间通常是 10秒级别在这 10 秒时间内,系统不能提供更新服务。从这里可以看到,primary-backup 类副本协议的最大缺点就是由于 primary 切换带来的一定的停服务时间。 165 | 4. 数据同步 166 | 1. Primary-secondary 型协议一般都会遇到 secondary 副本与 primary 不一致的问题: 167 | 1. secondary 上的数据落后于 primary 上的数据。 168 | 1. 回放 primary 上的操作日志 169 | 2. secondary 上的数据有脏数据。 170 | 1. 以简单的直接丢弃有脏数据的副本,这样相当于副本没有数据。 171 | 2. 也可以设计一些基于 undo 日志的方式从而可以删除脏数据 172 | 3. secondary 是一个新增加的副本,完全没有数据: 173 | 1. 直接拷贝 primary 副本的数据:快照 + 回放日志 174 | 5. 工程应用: 175 | 1. GFS 系统的副本控制协议是典型的 Primary-Secondary 型协议,Primary 副本由 Master 指定,Primary 副本决定并发更新操作的顺序。虽然在 GFS 中,更新操作的数据由客户端提交,并在各个副本之间流式的传输,及由上一个副本传递到下一个副本,每个副本都即接受其他副本的更新,也向下更新另一个副本,但是数据的更新过程完全是由 primary 控制的,所以也可以认为数据是由primary 副本同步到 secondary 副本的。 176 | ## 去中心化副本控制协议 177 | > 与中心化副本系统协议最大的不同是,去中心化副本控制协议没有中心节点,协议中所有的节点都是完全对等的,节点之间通过平等协商达到一致 178 | * 优点: 179 | * 没有因为中心化节点异常而带来的停服务等问题 180 | * 缺点: 181 | * 协议过程通常比较复杂。尤其当去中心化协议需要实现强一致性时,协议流程变得复杂且不容易理解 182 | Paxos 是唯一在工程中得到应用的强一致性去中心化副本控制协议 183 | # Lease机制 184 | > Lease 机制是最重要的分布式协议,广泛应用于各种实际的分布式系统中 185 | 186 | ### 简单的例子: 187 | 1. 在一个分布式系统中,有一个中心服务器节点,中心服务器存储、维护着一些数据,这些数据是系统的元数据。系统中其他的节点通过访问中心服务器节点读取、修改其上的元数据(那么中心服务器节点的性能成为系统的瓶颈) 188 | 2. 心服务器在向各节点发送数据时同时向节点颁发一个 lease。每个 lease 具有一个有效期,和信用卡上的有效期类似,lease 上的有效期通常是一个明确的时间点 189 | 3. 在 lease 的有效期内,中心服务器保证不会修改对应数据的值(假设中心服务器与各节点的时钟是同步的) 190 | * lease 机制可以容错的关键是: 191 | * 服务器一旦发出数据及 lease,无论客户端是否收到,也无论后续客户端是否宕机,也无论后续网络是否正常,服务器只要等待 lease 超时,就可以保证对应的客户端节点不会再继续 cache 数据,从而可以放心的修改数据而不会破坏 cache 的一致性。 192 | ### lease机制的本质: 193 | * lease 的定义: 194 | * Lease 是由颁发者授予的在某一有效期内的承诺 195 | * Lease 机制具有很高的容错能力: 196 | * 通过引入有效期,Lease 机制能否非常好的容错网络异常: 单向通信 197 | * Lease 机制能较好的容错节点宕机 198 | * lease 机制不依赖于存储 199 | * Lease 机制依赖于有效期,这就要求颁发者和接收者的时钟是同步的: 200 | * 为防止出现时钟不同步,实践中的通常做法是将颁发者的有效期设置得比接收者的略大,只需大过时钟误差就可以避免对 lease 的有效性的影响 201 | ### 基于 lease 机制确定节点状态 202 | 🌰说明: 203 | 1. 在一个 primary-secondary 架构的系统中,有三个节点 A、B、C 互为副本,其中有一个节点为 primary,且同一时刻只能有一个 primary 节点。另有一个节点 Q 负责判断节点 A、B、C的状态,一旦 Q 发现 primary 异常,节点 Q 将选择另一个节点作为 primary。假设最开始时节点 A为 primary,B、C 为 secondary。节点 Q 需要判断节点 A、B、C 的状态是否正常。 204 | 205 | 2. 由中心节点向其他节点发送 lease,若某个节点持有有效的 lease,则认为该节点正常可以提供服务。用于例 2.3.1 中,节点 A、B、C 依然周期性的发送 heart beat 报告自身状态,节点 Q 收到 heart beat后发送一个 lease,表示节点 Q 确认了节点 A、B、C 的状态,并允许节点在 lease 有效期内正常工作。节点 Q 可以给 primary 节点一个特殊的 lease,表示节点可以作为 primary 工作。一旦节点 Q 希望切换新的 primary,则只需等前一个 primary 的 lease 过期,则就可以安全的颁发新的 lease 给新的primary 节点,而不会出现“双主”问题。 206 | 207 | 3. 在实际系统中,若用一个中心节点发送 lease 也有很大的风险,一旦该中心节点宕机或网络异常,则所有的节点没有 lease,从而造成系统高度不可用。为此,实际系统总是使用多个中心节点互为副本,成为一个小的集群,该小集群具有高可用性,对外提供颁发 lease 的功能。chubby 和 zookeeper都是基于这样的设计 208 | ### lease 的有效期时间选择: 209 | * Lease 的有效期虽然是一个确定的时间点,当颁发者在发布 lease 时通常都是将当前时间加上一个固定的时长从而计算出 lease 的有效期。 210 | * 工程中,常选择的 lease 时长是 10 秒级别,这是一个经过验证的经验值,实践中可以作为参考并综合选择合适的时长 211 | ### GFS 中的 Lease 212 | GFS 中使用 Lease 确定 Chuck 的 Primary 副本。Lease 由 Master 节点颁发给 primary 副本,持有Lease 的副本成为 primary 副本。Primary 副本控制该 chuck 的数据更新流量,确定并发更新操作在chuck 上的执行顺序。GFS 中的 Lease 信息由 Master 在响应各个节点的 HeartBeat 时附带传递(piggyback)。对于每一个 chuck,其上的并发更新操作的顺序在各个副本上是一致的,首先 master选择 primary 的顺序,即颁发 Lease 的顺序,在每一任的 primary 任期内,每个 primary 决定并发更新的顺序,从而更新操作的顺序最终全局一致。当 GFS 的 master 失去某个节点的 HeartBeat 时,只需待该节点上的 primary chuck 的 Lease 超时,就可以为这些 chuck 重新选择 primary 副本并颁发 lease。 213 | ### Chubby 与 Zookeeper 中的 Lease 214 | # Quorum 机制 215 | ## write-all-read-one 216 | > 是一种最简单的副本控制规则,顾名思义即在更新时写所有的副本,只有在所有的副本上更新成功,才认为更新成功,从而保证所有的副本一致,这样在读取数据时可以读任一副本上的数据。 217 | 其实现需要依赖一种假设: 218 | > 假设有一种 magic 的机制,当某次更新操作 wi 一旦在所有 N 个副本上都成功,此时全局都能知道这个信息,此后读取操作将指定读取数据版本为 vi 的数据,称在所有 N 个副本上都成功的更新操作为“成功提交的更新操作”,称对应的数据为“成功提交的数据”。在 WARO 中,如果某次更新操作 wi 在某个副本上失败,此时该副本的最新的数据只有 vi-1,由于不满足在所有 N 个副本上都成功,则 wi 不是一个“成功提交的更新操作”,此时,虽然其他 N-1 个副本上最新的数据是 vi,但 vi 不是一个“成功提交的数据”,最新的成功提交的数据只是 vi-1 219 | 这里需要特别强调的是,在工程实践中,这种 magic 的机制往往较难实现或效率较低。 220 | ## Quorum 定义 221 | > WARO 牺牲了更新服务的可用性,最大程度的增强读服务的可用性。下面将 WARO 的条件进 222 | > 行松弛,从而使得可以在读写服务可用性之间做折中,得出 Quorum 机制: 223 | > 在 Quorum 机制下,当某次更新操作 wi 一旦在所有 N 个副本中的 W 个副本上都成功,则就称该更新操作为“成功提交的更新操作”,称对应的数据为“成功提交的数据”。令 R>N-W,由于更新操作 wi 仅在 W 个副本上成功,所以在读取数据时,最多需要读取 R 个副本则一定能读到 wi 更新后的数据 vi 。如果某次更新 wi 在 W 个副本上成功,由于 W+R>N,任意 R 个副本组成的集合一定与成功的W个副本组成的集合有交集,所以读取R个副本一定能读到wi更新后的数据vi。如图 2-10,Quorum 机制的原理可以文森图表示。 224 | 225 | 226 | 227 | ![image.png | center | 411x191](https://cdn.yuque.com/yuque/0/2018/png/94821/1529219892350-684f2bac-e976-40c4-a23b-4ad8aaa4b62c.png "") 228 | 229 | 230 | 仅仅依赖 quorum 机制是无法保证强一致性的。因为仅有 quorum 机制时无法确定最新已成功提交的版本号,除非将最新已提交的版本号作为元数据由特定的元数据服务器或元数据集群管理,否则很难确定最新成功提交的版本号 231 | 232 | Quorum 机制的三个系统参数 N、W、R 控制了系统的可用性,也是系统对用户的服务承诺:数据最多有 N 个副本,但数据更新成功 W 个副本即返回用户成功。对于一致性要求较高的 Quorum 系统,系统还应该承诺任何时候不读取未成功提交的数据,即读取到的数据都是曾经在 W 个副本上成功的数据。 233 | ## quorum 机制下达到强一致系统 234 | 1. 限制提交的更新操作必须严格递增,即只有在前一个更新操作成功提交后才可以提交后一个更新操作,从而成功提交的数据版本号必须是连续增加的。 235 | 2. 读取 R 个副本,对于 R 个副本中版本号最高的数据, 236 | 1. 若已存在 W 个,则该数据为最新的成功提交的数据 237 | 2. 若存在个数据少于 W 个,假设为 X 个,则继续读取其他副本,直若成功读取到 W 个该版本的副本,则该数据为最新的成功提交的数据;如果在所有副本中该数据的个数肯定不满足 W 个,则 R 中版本号第二大的为最新的成功提交的副本。 238 | 3. __可以看出,在单纯使用 Quorum 机制时,若要确定最新的成功提交的版本,最多需要读取 R+(W-R-1)=N 个副本,当出现任一副本异常时,读最新的成功提交的版本这一功能都有可能不可用。__ 239 | ## 基于 Quorum 机制选择 primary 240 | 在 primary-secondary 协议中,当 primary 异常时,需要选择出一个新的 primary,之后 secondary副本与 primary 同步数据。通常情况下,选择新的 primary 的工作是由某一中心节点完成的,在引入quorum 机制后,常用的 primary 选择方式与读取数据的方式类似,即中心节点读取 R 个副本,选择R 个副本中版本号最高的副本作为新的 primary。新 primary 与至少 W 个副本完成数据同步后作为新的 primary 提供读写服务。首先,R 个副本中版本号最高的副本一定蕴含了最新的成功提交的数据。再者,虽然不能确定最高版本号的数是一个成功提交的数据,但新的 primary 在随后与 secondary 同步数据,使得该版本的副本个数达到 W,从而使得该版本的数据成为成功提交的数据。 241 | ### GFS 中的 Quorum 242 | GFS 使用 WARO 机制读写副本,即如果更新所有副本成功则认为更新成功,一旦更新成功,则可以任意选择一个副本读取数据;如果更新某个副本失败,则更显失败,副本之间处于不一致的状态。GFS 系统不保证异常状态时副本的一致性,GFS 系统需要上层应用通过 Checksum 等机制自行判断数据是否合法。值得注意的是 GFS 中的 append 操作,一旦 append 操作某个 chunck 的副本上失败,GFS 系统会自动新增一个 chunck 并尝试 append 操作,由于可以让新增的 chunck 在正常的机器上创建,从而解决了由于 WARO 造成的系统可用性下降问题。进而在 GFS 中,append 操作不保证一定在文件的结尾进行,由于在新增的 chunk 上重试 append,append 的数据可能会出现多份重复的现象,但每个 append 操作会返回用户最终成功的 offset 位置,在这个位置上,任意读取某个副本一定可以读到写入的数据。这种在新增 chunk 上进行尝试的思路,大大增大了系统的容错能力,提高了系统可用性,是一种非常值得借鉴的设计思路。 243 | 244 | 分布式系统原理介绍 245 | # 日志技术 246 | > 日志技术是宕机恢复的主要技术之一 247 | ## Redo Log: 248 | __Redo Log 更新流程__ 249 | 1. 将更新操作的结果(例如 Set K1=1,则记录 K1=1)以追加写(append)的方式写入磁盘的日志文件 250 | 2. 按更新操作修改内存中的数据 251 | 3. 返回更新成功 252 | 4. Redo 写入日志的是更新操作完成后的结果 253 | Redo Log 的宕机恢复 254 | 1. 从头读取日志文件中的每次更新操作的结果,用这些结果修改内存中的数据。 255 | > 从 Redo Log 的宕机恢复流程也可以看出,只有写入日志文件的更新结果才能在宕机后恢复。这也是为什么在 Redo Log 流程中需要先更新日志文件再更新内存中的数据的原因 256 | ## Check point: 257 | 宕机恢复流量的缺点是需要回放所有 redo 日志,效率较低,假如需要恢复的操作非常多,那么这个宕机恢复过程将非常漫长。解决这一问题的方法即引入 check point 技术。在简化的模型下,checkpoint 技术的过程即将内存中的数据以某种易于重新加载的数据组织方式完整的 dump 到磁盘,从而减少宕机恢复时需要回放的日志数据。 258 | 259 | # Paxos 协议: 260 | > Paxos 协议是少数在工程实践中证实的强一致性、高可用的去__中心化分布式协议__ 261 | > Paxos 协议的流程较为复杂,但其基本思想却不难理解,类似于人类社会的投票过程。Paxos 协议中,有一组完全对等的参与节点(称为 accpetor),这组节点各自就某一事件做出决议,如果某个决议获得了超过半数节点的同意则生效。Paxos 协议中只要有超过一半的节点正常,就可以工作,能很好对抗宕机、网络分化等异常情况。 262 | * 节点角色: Paxos 协议中,有三类节点: 263 | * Proposer:提案者。Proposer 可以有多个,Proposer 提出议案(value)。所谓 value,在工程中可以是任何操作,例如“修改某个变量的值为某个值”、“设置当前 primary 为某个节点”等等。Paxos协议中统一将这些操作抽象为 value。不同的 Proposer 可以提出不同的甚至矛盾的 value,例如某个Proposer 提议“将变量 X 设置为 1”,另一个 Proposer 提议“将变量 X 设置为 2”,但对同一轮 Paxos过程,最多只有一个 value 被批准。 264 | * Acceptor:批准者。Acceptor 有 N 个,Proposer 提出的 value 必须获得超过半数(N/2+1)的 Acceptor批准后才能通过。Acceptor 之间完全对等独立。 265 | * Learner:学习者。Learner 学习被批准的 value。所谓学习就是通过读取各个 Proposer 对 value的选择结果,如果某个 value 被超过半数 Proposer 通过,则 Learner 学习到了这个 value。回忆(2.4 )不难理解,这里类似 Quorum 机制,某个 value 需要获得 W=N/2 + 1 的 Acceptor 批准,从而学习者需要至少读取 N/2+1 个 Accpetor,至多读取 N 个 Acceptor 的结果后,能学习到一个通过的 value。 266 | 上述三类角色只是逻辑上的划分,实践中一个节点可以同时充当这三类角色。 267 | 268 | # CAP 理论 269 | ## CAP 理论的定义 270 | CAP 三个字母分别代表了分布式系统中三个相互矛盾的属性: 271 | * Consistency (一致性):CAP 理论中的副本一致性特指强一致性 272 | * Availiablity(可用性):指系统在出现异常时已经可以提供服务 273 | * Tolerance to the partition of network (分区容忍):指系统可以对网络分化这种异常情况进行容错处理; 274 | CAP 理论指出:无法设计一种分布式协议,使得同时完全具备 CAP 三个属性,即 275 | * 1)该种协议下的副本始终是强一致性 276 | * 2)服务始终是可用的 277 | * 3)协议可以容忍任何网络分区异常 278 | * 分布式系统协议只能在 CAP 这三者间所有折中 279 | ## CAP理论的意义: 280 | 热力学第二定律说明了永动机是不可能存在的,不要去妄图设计永动机。与之类似,CAP 理论的意义就在于明确提出了不要去妄图设计一种对 CAP 三大属性都完全拥有的完美系统,因为这种系统在理论上就已经被证明不存在。 281 | 282 | 283 | 参考:[分布式系统原理·刘杰] 284 | -------------------------------------------------------------------------------- /docs/ziyuan01/jianjie.md: -------------------------------------------------------------------------------- 1 | # 大数据Hadoop生态简介 2 | 3 | ## 大数据带来的挑战 4 | 5 | 数据量越来越大,种类越来越多,产生的速度越来越快 6 | 7 | > 声明:参考华为的一篇文章 8 | 9 | ## Hadoop 10 | * Apache开源项目,超始于2005年 11 | * 针对解决数据量大,各类多,产生数据快的问题 12 | * 强大的开源社区支持 13 | * 日益丰富的生态系统 14 | 15 | ## HDFS 16 | 17 | ![image.png | left | 826x313](https://cdn.nlark.com/yuque/0/2018/png/134439/1543470987426-a944fef0-159d-4e00-9480-3a0cd432a50f.png "") 18 | 19 | * HDFS是基于Google发布的GFS论文进行设计开发,运行在通用硬件上的分布式文件系统 20 | * HDFS的特点 21 | * 高容错性:认为硬件总是不可靠的,所以每份数据都有备份文件 22 | * 高吞吐量:为大量数据访问的应用提供高吞吐量支持 23 | * 大文件存储:支持存储TB-PB级别的数据 24 | 25 | 26 | ![image.png | left | 747x326](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471327213-f151620e-0248-4a8d-9a8b-528d9f208299.png "") 27 | 28 | ## MapReduce 29 | * MapReduce基于Google发布的分布式计算框架Map/Reduce论文设计开发,用于大规模数据的并行运算 30 | * 特点 31 | * 易于编程:程序员仅需描述做什么,具体怎么做交由系统的执行框架处理 32 | 33 | 34 | ![image.png | left | 747x395](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471566082-1f67a146-6ffc-4253-bb76-e0ca55c0e892.png "") 35 | 36 | ## Yarn 37 | * Yarn是Hadoop2.0中的资源管理系统,它是一个通用的资源管理模块,可为各类应用程序进行资源管理和作业调度,除了提供MapReduce框架,还可以支持其他框架,比如Saprk、Storm等 38 | * 特点 39 | * 良好的扩展性:可通过添加节点以扩展集群能力 40 | * 高容错性:通过计算迁移策略提高中集群的容错性 41 | 42 | 43 | ![image.png | left | 747x357](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471582455-a2a52dd5-6fa4-467f-bd97-01fa37930f72.png "") 44 | 45 | ## Hive 46 | * Hive是基于Hadoop的数据仓库软件,可以查询和管理PB级别的分布式数据。提供类SQL的HiveSQL语言将SQL查询转换为MapReduce任务实现数据处理 47 | ### 常见场景 48 | * 数据清洗:数据抽取,数据加载,数据切换 49 | * 非实时分析:日志分析,文本分析等 50 | * 数据挖掘:用户行为分析,兴趣分析等 51 | 52 | 53 | ![image.png | left | 747x470](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471595113-e246a226-8bc3-41e7-927c-5dbc47eff04a.png "") 54 | 55 | ## HBase 56 | * HBase是一个高可靠性、高性能、面向列、可伸缩的分布式数据库,提供少量数据存储功能,用来解决关系型数据库在处理海量数据时的局限性 57 | ### 常见场景 58 | * 存储大表数据:表的规划可达到数十亿行以及数百万列 59 | * 高效的随机读取 60 | * 同时处理结构化和非结构化的数据 61 | 62 | 63 | ![image.png | left | 747x211](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471606753-1647c79c-941b-4495-bed4-63a14d767ae1.png "") 64 | 65 | ## Spark 66 | * Spark是一种通用的高性能集群计算系统。既有类似于MR的颁式内存计算框架,也有类似Hive的类SQL查询,还提供了实时数据的处理引擎和机器学习的算法库 67 | ### 常见场景 68 | * 快速的数据处理,ETL(抽取,转换,加载) 69 | * 实时数据分析 70 | * 数据挖掘和机器学习 71 | 72 | 73 | ![image.png | left | 747x294](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471618690-ac664bd2-2740-4598-8fa9-aa1195e87c5d.png "") 74 | 75 | ## Kafka 76 | * Kafkaa是一个高吞吐、分布式、基于发布订阅的消息系统,利用Kafka技术可在廉价的机器上搭建起大规模消息系统,适用于离线和在线的消息消费 77 | ### 常见场景 78 | * 常规的消息收集 79 | * 网站活性跟踪 80 | * 聚合统计系统运营数据:如监控数据 81 | 82 | 83 | ![image.png | left | 747x330](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471631733-abdf5d38-610a-489c-a165-f525099de57c.png "") 84 | 85 | ## Strom 86 | * Storm是一个分布式、实时计算框架,具有高度容错、低时延的优点 87 | ### 常见场景 88 | * 实时分析:如实时处理日志处理、交通流量分析等 89 | * 实时统计:如网站的实时访问统计、排序等 90 | * 实时推荐:如实时广告定位、事件营销等 91 | 92 | 93 | ![image.png | left | 747x370](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471655106-240d2236-afec-405a-abc9-82148428a737.png "") 94 | 95 | ## Flume 96 | * Flume是一个分布式、可靠和高可用的少量日志聚合的系统。支持在系统中定制各类数据发送方,用于收集数据然后写到各种数据接收方的能力。用户几乎不必进行任何额外开发即可使用 97 | ### 常见场景 98 | * 从固定目录下采集日志信息到目的地:HDFS,HBase,Kafka 99 | * 实时采集日志信息到目的地 100 | 101 | 102 | ![image.png | left | 747x207](https://cdn.nlark.com/yuque/0/2018/png/134439/1543471670387-c52db064-58e9-4c94-9277-d32dcf2c5a68.png "") 103 | -------------------------------------------------------------------------------- /docs/ziyuan02/MRyuanli.md: -------------------------------------------------------------------------------- 1 | # MR的原理和运行流程 2 | 3 | ## Map的运行过程 4 | 5 | 以HDFS上的文件作为默认输入源为例(MR也可以有其他的输入源) 6 | 7 | 8 | 9 | ![Map运行过程 | left](https://github.com/jiaoqiyuan/163-bigdate-note/raw/master/%E6%97%A5%E5%BF%97%E8%A7%A3%E6%9E%90%E5%8F%8A%E8%AE%A1%E7%AE%97%EF%BC%9AMR/img/Map%E8%BF%90%E8%A1%8C%E8%BF%87%E7%A8%8B.png "") 10 | 11 | 12 | * block是HDFS上的文件块,split是文件的分片(逻辑划分,不包含具体数据,只包含这些数据的位置信息)。 13 | 14 | * 一个split包含一个或多个block,默认是一对一的关系。 15 | * 一个split不包含两个文件的block, 不会跨越file边界,也就是说一个split是不会跨文件进行划分的。 16 | * 当分片完成后,MR程序会将split中的数据以K/V(key/value)的形式读取出来,然后将这些数据交给用户自定义的Map函数进行处理。 17 | 18 | * 一个Map处理一个split。 19 | * 用户用Map函数处理完数据后将处理后,同样将结果以K/V的形式交给MR的计算框架。 20 | * MR计算框架会将不同的数据划分成不同的partition,数据相同的多个partition最后会分到同一个reduce节点上面进行处理,也就是说一类partition对应一个reduce。 21 | * Map默认使用Hash算法对key值进行Hash计算,这样保证了相同key值的数据能够划分到相同的partition中,同时也保证了不同的partition之间的数据量时大致相当的,[参考链接](https://zhuanlan.zhihu.com/p/42864264) 22 | * 一个程序中Map和Reduce的数量是有split和partition的数据决定的。 23 | 24 | ## Reduce处理过程 25 | 26 | 27 | 28 | ![Reduce处理过程 | left](https://github.com/jiaoqiyuan/163-bigdate-note/raw/master/%E6%97%A5%E5%BF%97%E8%A7%A3%E6%9E%90%E5%8F%8A%E8%AE%A1%E7%AE%97%EF%BC%9AMR/img/Reduce%E5%A4%84%E7%90%86%E8%BF%87%E7%A8%8B.png "") 29 | 30 | 31 | * Map处理完后,reduce处理程序在各个Map节点将属于自己的数据拷贝到自己的内存缓冲区中 32 | * 最后将这些数据合并成一个大的数据集,并且按照key值进行聚合,把聚合后的value值作为一个迭代器给用户使用。 33 | * 用户使用自定义的reduce函数处理完迭代器中的数据后,一般把结果以K/V的格式存储到HDFS上的文件中。 34 | 35 | ## Shuffle过程 36 | 37 | * 在上面介绍的MR过程中,还存在一个shuffle过程,发生与Map和Reduce之中。 38 | 39 | 40 | 41 | ![shuffle | left](https://github.com/jiaoqiyuan/163-bigdate-note/raw/master/%E6%97%A5%E5%BF%97%E8%A7%A3%E6%9E%90%E5%8F%8A%E8%AE%A1%E7%AE%97%EF%BC%9AMR/img/shuffle%E8%BF%87%E7%A8%8B.png "") 42 | 43 | __Map中的shuffle__ 44 | 45 | * Collec阶段键数据放在环形缓冲区,唤醒缓冲区分为数据区和索引区。 46 | * sort阶段对在统一partition内的索引按照key值排序。 47 | * spill(溢写)阶段根据拍好序的索引将数据按顺序写到文件中。 48 | * Merge阶段将Spill生成的小文件分批合并排序成一个大文件。 49 | * Reduce中的shuffle 50 | * Copy阶段将Map段的数据分批拷贝到Reduce的缓冲区。 51 | * Spill阶段将内存缓冲区的数据按照顺序写到文件中。 52 | * Merge阶段将溢出文件合并成一个排好序的数据集。 53 | * Combine优化 54 | * 整个过程中可以提前对聚合好的value值进行计算,这个过程就叫Combine。 55 | * Combine在Map端发生时间 56 | * 在数据排序后,溢写到磁盘前,相同key值的value是紧挨在一起的,可以进行聚合运算,运行一次combiner。 57 | * 再合并溢出文件输出到磁盘前,如果存在至少3个溢出文件,则运行combiner,可以通过min.num.spills.for.combine设置阈值。 58 | * Reduce端 59 | 60 | * 在合并溢出文件输出到磁盘前,运行combiner。 61 | * Combiner不是任何情况下都适用的,需要根据业务需要进行设置。 62 | ## MR运行过程 63 | 64 | 65 | 66 | ![MR运行过程 | left](https://github.com/jiaoqiyuan/163-bigdate-note/raw/master/%E6%97%A5%E5%BF%97%E8%A7%A3%E6%9E%90%E5%8F%8A%E8%AE%A1%E7%AE%97%EF%BC%9AMR/img/MR%E8%BF%90%E8%A1%8C%E8%BF%87%E7%A8%8B.png "") 67 | 68 | 69 | * 一个文件分成多个split数据片。 70 | * 每个split由多一个map进行处理。 71 | * Map处理完一个数据就把处理结果放到一个环形缓冲区内存中。 72 | * 环形缓冲区满后里面的数据会被溢写到一个个小文件中。 73 | * 小文件会被合并成一个大文件,大文件会按照partition进行排序。 74 | * reduce节点将所有属于自己的数据从partition中拷贝到自己的缓冲区中,并进行合并。 75 | * 最后合并后的数据交给reduce处理程序进行处理。 76 | * 处理后的结果存放到HDFS上。 77 | * MR运行在集群上:YARN(Yet Another Resource Negotiator) 78 | 79 | 80 | 81 | ![YARN结构 | left](https://github.com/jiaoqiyuan/163-bigdate-note/raw/master/%E6%97%A5%E5%BF%97%E8%A7%A3%E6%9E%90%E5%8F%8A%E8%AE%A1%E7%AE%97%EF%BC%9AMR/img/YARN%E6%A1%86%E6%9E%B6.png "") 82 | __ ResourceManager负责调度和管理整个集群的资源__ 83 | 84 | * 主要职责是调度,对应用程序的整体进行资源分配 85 | * Nodemanager负责节点上的计算资源,内部包含Container, App Master,管理Container生命周期,资源使用情况,节点健康状况,并将这些信息回报给RM。 86 | * Container中包含一些资源信息,如cpu核数,内存大小 87 | * 一个应用程序由一个App Master管理,App Master负责将应用程序运行在各个节点的Container中,App Master与RM协商资源分配的问题。 88 | * ## MapReduce On Yarn 89 | 90 | 91 | 92 | ![MR on YARN | left](https://github.com/jiaoqiyuan/163-bigdate-note/raw/master/%E6%97%A5%E5%BF%97%E8%A7%A3%E6%9E%90%E5%8F%8A%E8%AE%A1%E7%AE%97%EF%BC%9AMR/img/MRonYarn.png "") 93 | 94 | 95 | * MR程序在客户端启动,客户端会向RM发送一个请求。 96 | * RM收到请求后返回一个AppID给客户端。 97 | * 然后客户端拿着AppID,用户名,队列,令牌向RM发出资源请求。 98 | * 客户端这时会将程序用到的jar包,资源文件,程序运行中需要的数据等传送到HDFS上。 99 | * RM接收到客户端的资源请求后,分配一个container0的资源包,由NodeManager启动一个AppMaster。 100 | * RM将集群的容量信息发送给AppMaster,AppMaster计算这个程序需要的资源量后,根据需要想RM请求更多的container。 101 | * 最后由各个NodeManager在节点上启动MapTask和ReduceTask。 102 | 103 | ## Yarn && Job 104 | 105 | 上面的 Yarn 管理 MR 任务是不是比较粗略,下面我将介绍比较详细的处理流程: 106 | 107 | 这也是今日头条的一个面试题,引发的思考: 108 | 109 | > MR 任务为例,讲一下 Yarn 的整个过程。 110 | 111 | Yarn 中的主要组件包括:Resourcemanager,ApplicationMaster, NodeManager。 112 | 113 | __Resourcemanager__:每个Hadoop集群只会有一个ResourceManager(如果是HA的话会存在两个,但是有且只有一个处于active状态),启动每一个 Job 所属的 ApplicationMaster,另外监控ApplicationMaster 以及NodeManager 的存在情况,并且负责协调计算节点上计算资源的分配。ResourceManager 内部主要有两个组件: 114 | 115 | * __Scheduler__:这个组件完全是插拔式的,用户可以根据自己的需求实现不同的调度器,目前YARN提供了FIFO、容量以及公平调度器。这个组件的唯一功能就是给提交到集群的应用程序分配资源,并且对可用的资源和运行的队列进行限制。Scheduler并不对作业进行监控; 116 | * __ApplicationsManager__ :这个组件用于管理整个集群应用程序的 application masters,负责接收应用程序的提交;为application master启动提供资源;监控应用程序的运行进度以及在应用程序出现故障时重启它。 117 | 118 | __ApplicationMaster__:每个 Job 都有对应一个 ApplicationMaster ,并且负责运行 mapreduce 任务,并负责报告任务的状态。ApplicationMaster是应用程序级别的,每个ApplicationMaster管理运行在YARN上的应用程序。YARN 将 ApplicationMaster看做是第三方组件,ApplicationMaster负责和ResourceManager scheduler协商资源,并且和NodeManager通信来运行相应的task。ResourceManager 为 ApplicationMaster 分配容器,这些容器将会用来运行task。ApplicationMaster 也会追踪应用程序的状态,监控容器的运行进度。当容器运行完成, ApplicationMaster 将会向 ResourceManager 注销这个容器;如果是整个作业运行完成,其也会向 ResourceManager 注销自己,这样这些资源就可以分配给其他的应用程序使用了。 119 | 120 | __NodeManager__:负责启动和管理节点的容器。NodeManager是YARN中每个节点上的代理,它管理Hadoop集群中单个计算节点,根据相关的设置来启动容器的。NodeManager会定期向ResourceManager发送心跳信息来更新其健康状态。同时其也会监督Container的生命周期管理,监控每个Container的资源使用(内存、CPU等)情况,追踪节点健康状况,管理日志和不同应用程序用到的附属服务(auxiliary service)。 121 | 122 | __Container__: Container是与特定节点绑定的,其包含了内存、CPU磁盘等逻辑资源。不过在现在的容器实现中,这些资源只包括了内存和CPU。容器是由 ResourceManager scheduler 服务动态分配的资源构成。容器授予 ApplicationMaster 使用特定主机的特定数量资源的权限。ApplicationMaster 也是在容器中运行的,其在应用程序分配的第一个容器中运行。 123 | 124 | 125 | > 必须牢记yarn只是一个资源管理的框架,并不是一个计算框架,计算框架可以运行在yarn上。我们所能做的就是向RM申请container,然后配合NM一起来启动container。 126 | 127 | 下面是请求资源和分配资源的流程: 128 | 129 | 1.客户端向 ResourceManager 发送 job 请求,客户端产生的 RunJar 进程与 ResourceManager 通过 RPC 通信。 130 | 2.ResourceManager 向客户端返回 job 相关资源的提交路径以及 jobID。 131 | 3.客户端将 job 相关的资源提交到相应的共享文件夹下。 132 | 4.客户端向 ResourceManager 提交 job 133 | 5.ResourceManager 通过__调度器__在 NodeManager 创建一个容器,并且在容器中启用MRAppmaster 进程,该进程由 ResourceManager 启动。 134 | 6.该 MRAppmaster 进程对作业进行初始化,创建多个对象对作业进行跟踪。 135 | 7.MRAppmaster 从文件系统获取计算得到输入分片,只获取切片信息,不需要jar等资源,为每个分片创建一个 map 以及指定数量的 reduce 对象,之后 MRAppmaster 决定如何运行构成 mapreduce 的各个任务。 136 | 8.若作业很大,MRAppmaster 为所有的 map 任务和reduce 任务向 ResourceManger 发起申请容器的请求,请求中包含 map 任务的数据本地化信息以及数据分片等信息。 137 | 9.ResourceManager 为任务分配了容器之后,MRAppmaster 就通过 与 NodeManger 通信启动容器,由 MRAppmaster 负责分配在哪些 NodeManager 负责分配在哪些 NodeManager 上运行map (即 yarnchild 进程)和reduce 任务。 138 | 10.运行 mao 和 reduce 任务的 NodeManager 从共享系统中获取 job 的相关县,包括 jar 文件,配置文件等。 139 | 11.关于查询状态,不经过 reourcemanager ,而是任务周期性的 MRAppmaster 汇报状态以及进度,客户端每秒通过查询一次 MRAppmaster 来更新状态和信息。 140 | 141 | 上面可以很乱,重点是辅助理解细节,认知到位了,无关细节了吧。 142 | 143 | 下面总结一下,大概的流程: 144 | 145 | 146 | 147 | ![image.png | left | 604x397](https://cdn.nlark.com/yuque/0/2018/png/199648/1544534623921-5cd78e48-8181-404c-a67b-41898ca4574a.png "") 148 | 149 | 流程大致如下: 150 | · client客户端向yarn集群(resourcemanager)提交任务 151 | · resourcemanager选择一个node创建appmaster 152 | · appmaster根据任务向rm申请资源 153 | · rm返回资源申请的结果 154 | · appmaster去对应的node上创建任务需要的资源(container形式,包括内存和CPU) 155 | · appmaster负责与nodemanager进行沟通,监控任务运行 156 | · 最后任务运行成功,汇总结果。 157 | 其中Resourcemanager里面一个很重要的东西,就是调度器Scheduler,调度规则可以使用官方提供的,也可以自定义。 158 | 159 | 160 | ## 评价交流 161 | 162 | > 欢迎留下的你的想法~ 163 | 164 | 165 | -------------------------------------------------------------------------------- /docs/ziyuan02/NameNodeDetail.md: -------------------------------------------------------------------------------- 1 | # HDFS的 NameNode 内存解析 2 | 3 | ## 概述 4 | 从整个HDFS系统架构上看,NameNode是其中最重要、最复杂也是最容易出现问题的地方,而且一旦NameNode出现故障,整个Hadoop集群就将处于不可服务的状态,同时随着数据规模和集群规模地持续增长,很多小量级时被隐藏的问题逐渐暴露出来。所以,从更高层次掌握NameNode的内部结构和运行机制尤其重要。除特别说明外,本文基于社区版本Hadoop-2.4.1[1][2],虽然2.4.1之后已经有多次版本迭代,但是基本原理相同。 5 | 6 | NameNode管理着整个HDFS文件系统的元数据。从架构设计上看,元数据大致分成两个层次:Namespace管理层,负责管理文件系统中的树状目录结构以及文件与数据块的映射关系;块管理层,负责管理文件系统中文件的物理块与实际存储位置的映射关系BlocksMap,如图1所示[1]。Namespace管理的元数据除内存常驻外,也会周期Flush到持久化设备上FsImage文件;BlocksMap元数据只在内存中存在;当NameNode发生重启,首先从持久化设备中读取FsImage构建Namespace,之后根据DataNode的汇报信息重新构造BlocksMap。这两部分数据结构是占据了NameNode大部分JVM Heap空间。 7 | 8 | ![image.png | center | 592x416](https://cdn.nlark.com/yuque/0/2018/png/199648/1544578308842-6b685f51-0fb4-48fd-879a-901f60fc5e3e.png "") 9 | 10 |
11 |
12 |
图1 HDFS结构图
13 |
14 | 15 | 16 | 除了对文件系统本身元数据的管理之外,NameNode还需要维护整个集群的机架及DataNode的信息、Lease管理以及集中式缓存引入的缓存管理等等。这几部分数据结构空间占用相对固定,且占用较小。 17 | 18 | 测试数据显示,Namespace目录和文件总量到2亿,数据块总量到3亿后,常驻内存使用量超过90GB。 19 | 20 | ## NameNode 内存全景 21 | 22 | 23 | 24 | ![image.png | left | 747x374](https://cdn.nlark.com/yuque/0/2018/png/199648/1544578398487-312330d4-8315-4706-be23-43fc9871e0d7.png "") 25 | 26 | 27 |
28 |
图2 NameNode内存全景图 29 |
30 |
31 | 32 | Namespace:维护整个文件系统的目录树结构及目录树上的状态变化; 33 | BlockManager:维护整个文件系统中与数据块相关的信息及数据块的状态变化; 34 | NetworkTopology:维护机架拓扑及DataNode信息,机架感知的基础; 35 | 其它: 36 | LeaseManager:读写的互斥同步就是靠Lease实现,支持HDFS的Write-Once-Read-Many的核心数据结构; 37 | CacheManager:Hadoop 2.3.0引入的集中式缓存新特性,支持集中式缓存的管理,实现memory-locality提升读性能; 38 | SnapshotManager:Hadoop 2.1.0引入的Snapshot新特性,用于数据备份、回滚,以防止因用户误操作导致集群出现数据问题; 39 | DelegationTokenSecretManager:管理HDFS的安全访问; 40 | 另外还有临时数据信息、统计信息metrics等等。 41 | 42 | NameNode常驻内存主要被Namespace和BlockManager使用,二者使用占比分别接近50%。其它部分内存开销较小且相对固定,与Namespace和BlockManager相比基本可以忽略。 43 | 44 | 45 | ## 模块细节 46 | ### Namespace 47 | 48 | 与单机文件系统相似,HDFS对文件系统的目录结构也是按照树状结构维护,Namespace保存了目录树及每个目录/文件节点的属性。除在内存常驻外,这部分数据会定期flush到持久化设备上,生成一个新的FsImage文件,方便NameNode发生重启时,从FsImage及时恢复整个Namespace。图3所示为Namespace内存结构。前述集群中目录和文件总量即整个Namespace目录树中包含的节点总数,可见Namespace本身其实是一棵非常巨大的树。 49 | 50 |
51 |
52 |
53 | 54 |
55 | 56 |
57 |
58 | 59 |
60 |
图3 Namespace内存结构 61 |
62 |
63 | 64 | 在整个Namespace目录树中存在两种不同类型的INode数据结构:INodeDirectory和INodeFile。其中INodeDirectory标识的是目录树中的目录,INodeFile标识的是目录树中的文件。由于二者均继承自INode,所以具备大部分相同的公共信息INodeWithAdditionalFields,除常用基础属性外,其中还提供了扩展属性features,如Quota、Snapshot等均通过Feature增加,如果以后出现新属性也可通过Feature方便扩展。不同的是,INodeFile特有的标识副本数和数据块大小组合的header(2.6.1之后又新增了标识存储策略ID的信息)及该文件包含的有序Blocks数组;INodeDirectory则特有子节点的列表children。这里需要特别说明children是默认大小为5的ArrayList,按照子节点name有序存储,虽然在插入时会损失一部分写性能,但是可以方便后续快速二分查找提高读性能,对一般存储系统,读操作比写操作占比要高。具体的继承关系见图4所示。 65 | 66 |
67 |
68 | 69 |
70 | 71 |
图4 INode继承关系
72 |
73 | 74 | 75 | ### BlockManager 76 | BlocksMap在NameNode内存空间占据很大比例,由BlockManager统一管理,相比Namespace,BlockManager管理的这部分数据要复杂的多。Namespace与BlockManager之间通过前面提到的INodeFile有序Blocks数组关联到一起。图5所示BlockManager管理的内存结构。 77 | 78 |
79 | 80 |
81 | 82 |
83 |
84 | 85 |
86 |
图5 BlockManager管理的内存结构
87 |
88 | 89 | 每一个INodeFile都会包含数量不等的Block,具体数量由文件大小及每一个Block大小(默认为64M)比值决定,这些Block按照所在文件的先后顺序组成BlockInfo数组,如图5所示的BlockInfo[A~K],BlockInfo维护的是Block的元数据,结构如图6所示,数据本身是由DataNode管理,所以BlockInfo需要包含实际数据到底由哪些DataNode管理的信息,这里的核心是名为triplets的Object数组,大小为3\*replicas,其中replicas是Block副本数量。triplets包含的信息: 90 | 91 | * triplets[i]:Block所在的DataNode; 92 | * triplets[i+1]:该DataNode上前一个Block; 93 | * triplets[i+2]:该DataNode上后一个Block; 94 | 95 | 其中i表示的是Block的第i个副本,i取值[0,replicas)。 96 | 97 |
98 | 99 |
100 | 101 |
102 | 103 |
图6 BlockInfo继承关系
104 |
105 | 106 | 107 | 从前面描述可以看到BlockInfo几块重要信息:文件包含了哪些Block,这些Block分别被实际存储在哪些DataNode上,DataNode上所有Block前后链表关系。 108 | 109 | 如果从信息完整度来看,以上数据足够支持所有关于HDFS文件系统的正常操作,但还存在一个使用场景较多的问题:不能通过blockid快速定位Block,所以引入了BlocksMap。 110 | 111 | BlocksMap底层通过LightWeightGSet实现,本质是一个链式解决冲突的哈希表。为了避免rehash过程带来的性能开销,初始化时,索引空间直接给到了整个JVM可用内存的2%,并且不再变化。集群启动过程,DataNode会进行BR(BlockReport),根据BR的每一个Block计算其HashCode,之后将对应的BlockInfo插入到相应位置逐渐构建起来巨大的BlocksMap。前面在INodeFile里也提到的BlockInfo集合,如果我们将BlocksMap里的BlockInfo与所有INodeFile里的BlockInfo分别收集起来,可以发现两个集合完全相同,事实上BlocksMap里所有的BlockInfo就是INodeFile中对应BlockInfo的引用;通过Block查找对应BlockInfo时,也是先对Block计算HashCode,根据结果快速定位到对应的BlockInfo信息。至此涉及到HDFS文件系统本身元数据的问题基本上已经解决了。 112 | 113 | 前面提到部分都属于静态数据部分,NameNode内存中所有数据都要随读写情况发生变化,BlockManager当然也需要管理这部分动态数据。主要是当Block发生变化不符合预期时需要及时调整Blocks的分布。这里涉及几个核心的数据结构: 114 | 115 | excessReplicateMap:若某个Block实际存储的副本数多于预设副本数,这时候需要删除多余副本,这里多余副本会被置于excessReplicateMap中。excessReplicateMap是从DataNode的StorageID到Block集合的映射集。 116 | neededReplications:若某个Block实际存储的副本数少于预设副本数,这时候需要补充缺少副本,这里哪些Block缺少多少个副本都统一存在neededReplications里,本质上neededReplications是一个优先级队列,缺少副本数越多的Block之后越会被优先处理。 117 | invalidateBlocks:若某个Block即将被删除,会被置于invalidateBlocks中。invalidateBlocks是从DataNode的StorageID到Block集合的映射集。如某个文件被客户端执行了删除操作,该文件所属的所有Block会先被置于invalidateBlocks中。 118 | corruptReplicas:有些场景Block由于时间戳/长度不匹配等等造成Block不可用,会被暂存在corruptReplicas中,之后再做处理。 119 | 120 | 前面几个涉及到Block分布情况动态变化的核心数据结构,这里的数据实际上是过渡性质的,BlockManager内部的ReplicationMonitor线程(图5标识Thread/Monitor)会持续从其中取出数据并通过逻辑处理后分发给具体的DatanodeDescriptor对应数据结构(3.3 NetworkTopology里会有简单介绍),当对应DataNode的心跳过来之后,NameNode会遍历DatanodeDescriptor里暂存的数据,将其转换成对应指令返回给DataNode,DataNode收到任务并执行完成后再反馈回NameNode,之后DatanodeDescriptor里对应信息被清除。如BlockB预设副本数为3,由于某种原因实际副本变成4(如之前下线的DataNode D重新上线,其中B正好有BlockB的一个副本数据),BlockManager能及时发现副本变化,并将多余的DataNode D上BlockB副本放置到excessReplicateMap中,ReplicationMonitor线程定期检查时发现excessReplicateMap中数据后将其移到DataNode D对应DatanodeDescriptor中invalidateBlocks里,当DataNode D下次心跳过来后,随心跳返回删除Block B的指令,DataNode D收到指令实际删除其上的Block B数据并反馈回NameNode,此后BlockManager将DataNode D上的Block B从内存中清除,至此Block B的副本符合预期,整个流程如图7所示。 121 | 122 |
123 |
124 |
125 | 126 |
127 | 128 | 图7 副本数异常时处理过程 129 |
130 |
131 | 132 | ### NetworkTopology 133 | 134 | 前面多次提到Block与DataNode之间的关联关系,事实上NameNode确实还需要管理所有DataNode,不仅如此,由于数据写入前需要确定数据块写入位置,NameNode还维护着整个机架拓扑NetworkTopology。图8所示内存中机架拓扑图。 135 | 136 |
137 |
138 |
139 | 140 |
141 | 142 | 图8 NetworkTopology内存结构 143 |
144 |
145 | 146 | 从图8可以看出这里包含两个部分:机架拓扑结构NetworkTopology和DataNode节点信息。其中树状的机架拓扑是根据机架感知(一般都是外部脚本计算得到)在集群启动完成后建立起来,整个机架的拓扑结构在NameNode的生命周期内一般不会发生变化;另一部分是比较关键的DataNode信息,BlockManager已经提到每一个DataNode上的Blocks集合都会形成一个双向链表,更准确的应该是DataNode的每一个存储单元DatanodeStorageInfo上的所有Blocks集合会形成一个双向链表,这个链表的入口就是机架拓扑结构叶子节点即DataNode管理的DatanodeStorageInfo。此外由于上层应用对数据的增删查随时发生变化,随之DatanodeStorageInfo上的Blocks也会动态变化,所以NetworkTopology上的DataNode对象还会管理这些动态变化的数据结构,如replicateBlocks/recoverBlocks/invalidateBlocks,这些数据结构正好和BlockManager管理的动态数据结构对应,实现了数据的动态变化由BlockManager传达到DataNode内存对象最后通过指令下达到物理DataNode实际执行的流动过程,流程在3.2 BlockManager已经介绍。 147 | 148 | 这里存在一个问题,为什么DatanodeStorageInfo下所有Block之间会以双向链表组织,而不是其它数据结构?如果结合实际场景就不难发现,对每一个DatanodeStorageInfo下Block的操作集中在快速增加/删除(Block动态增减变化)及顺序遍历(BlockReport期间),所以双向链表是非常合适的数据结构。 149 | 150 | ### LeaseManager 151 | 152 | Lease 机制是重要的分布式协议,广泛应用于各种实际的分布式系统中。HDFS支持Write-Once-Read-Many,对文件写操作的互斥同步靠Lease实现。Lease实际上是时间约束锁,其主要特点是排他性。客户端写文件时需要先申请一个Lease,一旦有客户端持有了某个文件的Lease,其它客户端就不可能再申请到该文件的Lease,这就保证了同一时刻对一个文件的写操作只能发生在一个客户端。NameNode的LeaseManager是Lease机制的核心,维护了文件与Lease、客户端与Lease的对应关系,这类信息会随写数据的变化实时发生对应改变。 153 | 154 | 155 | ![9.png | center | 747x447](https://cdn.nlark.com/yuque/0/2018/png/199648/1544867768625-2b7f5ebe-79bb-4d3a-a086-5e28ade0eb01.png "") 156 | 157 |
158 |
9 LeaseManager的内存数据结构 159 |
160 |
161 | 162 | 图9所示为LeaseManager内存结构,包括以下三个主要核心数据结构: 163 | 164 | * sortedLeases:Lease集合,按照时间先后有序组织,便于检查Lease是否超时; 165 | * leases:客户端到Lease的映射关系; 166 | * sortedLeasesByPath:文件路径到Lease的映射关系; 167 | 168 | 其中每一个写数据的客户端会对应一个Lease,每个Lease里包含至少一个标识文件路径的Path。Lease本身已经维护了其持有者(客户端)及该Lease正在操作的文件路径集合,之所以增加了leases和sortedLeasesByPath为提高通过Lease持有者或文件路径快速索引到Lease的性能。 169 | 170 | 由于Lease本身的时间约束特性,当Lease发生超时后需要强制回收,内存中与该Lease相关的内容要被及时清除。超时检查及超时后的处理逻辑由LeaseManager.Monitor统一执行。LeaseManager中维护了两个与Lease相关的超时时间:软超时(softLimit)和硬超时(hardLimit),使用场景稍有不同。 171 | 172 | 正常情况下,客户端向集群写文件前需要向NameNode的LeaseManager申请Lease;写文件过程中定期更新Lease时间,以防Lease过期,周期与softLimit相关;写完数据后申请释放Lease。整个过程可能发生两类问题:(1)写文件过程中客户端没有及时更新Lease时间;(2)写完文件后没有成功释放Lease。两个问题分别对应为softLimit和hardLimit。两种场景都会触发LeaseManager对Lease超时强制回收。如果客户端写文件过程中没有及时更新Lease超过softLimit时间后,另一客户端尝试对同一文件进行写操作时触发Lease软超时强制回收;如果客户端写文件完成但是没有成功释放Lease,则会由LeaseManager的后台线程LeaseManager.Monitor检查是否硬超时后统一触发超时回收。不管是softLimit还是hardLimit超时触发的强制Lease回收,处理逻辑都一样:FSNamesystem.internalReleaseLease,逻辑本身比较复杂,这里不再展开,简单的说先对Lease过期前最后一次写入的Block进行检查和修复,之后释放超时持有的Lease,保证后面其它客户端的写入能够正常申请到该文件的Lease。 173 | 174 | NameNode内存数据结构非常丰富,这里对几个重要的数据结构进行了简单的描述,除了前面罗列之外,其实还有如SnapShotManager/CacheManager等,由于其内存占用有限且有一些特性还尚未稳定,这里不再展开。 175 | 176 | ## 建议 177 | 178 | > 测试数据显示,Namespace目录和文件总量到2亿,数据块总量到3亿后,常驻内存使用量超过90GB。 179 | 180 | 尽管社区和业界均对NameNode内存瓶颈有成熟的解决方案,但是不一定适用所有的场景,尤其是中小规模集群。结合实践过程和集群规模发展期可能遇到的NameNode内存相关问题这里有几点建议: 181 | 182 | * 合并小文件。正如前面提到,目录/文件和Block均会占用NameNode内存空间,大量小文件会降低内存使用效率;另外,小文件的读写性能远远低于大文件的读写,主要原因对小文件读写需要在多个数据源切换,严重影响性能。 183 | * 调整合适的BlockSize。主要针对集群内文件较大的业务场景,可以通过调整默认的Block Size大小(参数:dfs.blocksize,默认128M),降低NameNode的内存增长趋势。 184 | * HDFS Federation方案。当集群和数据均达到一定规模时,仅通过垂直扩展NameNode已不能很好的支持业务发展,可以考虑HDFS Federation方案实现对NameNode的水平扩展,在解决NameNode的内存问题的同时通过Federation可以达到良好的隔离性,不会因为单一应用压垮整集群。 185 | 186 | 187 | 188 | ## 评价交流 189 | 190 | > 欢迎留下的你的想法~ 191 | 192 | 193 | -------------------------------------------------------------------------------- /docs/ziyuan02/README.md: -------------------------------------------------------------------------------- 1 | # Hadoop HA 机制 2 | 3 | Hadoop 2.x 架构在 NameNode 上的改变,解决了单点问题和主备切换的问题,元数据信息同步的问题,结合 ZK 来实现的,但是 ZK 是如何把主备的 NameNode 节点进行切换的呢?是如何让保证元数据信息同步的呢? 4 | 5 | 带着这些疑问,开始今天的文章。 6 | 7 | 先来看一下官方提供的配置:Apache 官方的例子,熟悉的同学可以略过... 8 | 9 | ## 配置 10 | 11 | 先是配置 hdfs-site.xml 文件。 12 | 13 | hdfs集群服务名字: 14 | ```xml 15 | 16 | dfs.nameservices 17 | mycluster 18 | 19 | ``` 20 | 21 | NameNode 别名 22 | ```xml 23 | 24 | dfs.ha.namenodes.mycluster 25 | nn1,nn2, nn3 26 | 27 | ``` 28 | 29 | NameNode 的 PRC 监听端口: 30 | 31 | ```xml 32 | 33 | dfs.namenode.rpc-address.mycluster.nn1 34 | machine1.example.com:8020 35 | 36 | 37 | dfs.namenode.rpc-address.mycluster.nn2 38 | machine2.example.com:8020 39 | 40 | 41 | dfs.namenode.rpc-address.mycluster.nn3 42 | machine3.example.com:8020 43 | 44 | ``` 45 | 46 | NameNode 的 HTTP 监听端口: 47 | 48 | ```xml 49 | 50 | dfs.namenode.http-address.mycluster.nn1 51 | machine1.example.com:9870 52 | 53 | 54 | dfs.namenode.http-address.mycluster.nn2 55 | machine2.example.com:9870 56 | 57 | 58 | dfs.namenode.http-address.mycluster.nn3 59 | machine3.example.com:9870 60 | 61 | ``` 62 | 63 | This is where one configures the addresses of the JournalNodes which provide the shared edits storage, written to by the Active nameNode and read by the Standby NameNode to stay up-to-date with all the file system changes the Active NameNode makes.dfs.namenode.shared.edits.dirqjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/mycluster 64 | 65 | the Java class that HDFS clients use to contact the Active NameNode.dfs.client.failover.proxy.provider.myclusterorg.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider 66 | 67 | Importantly, when using the Quorum Journal Manager, only one NameNode will ever be allowed to write to the JournalNodes, so there is no potential for corrupting the file system metadata from a split-brain scenario. 只有允许同一时刻有一个 NameNode 往JNS 里面写数据。因为会出现过时的 NameNode 好保持着 Active 状态,或者出现假死状态。 68 | 69 | 下面配置了 sshfence 用于通过 ssh 连接目标机器,运行 kill 命令。 70 | 71 | ```xml 72 | 73 | dfs.ha.fencing.methods 74 | sshfence 75 | 76 | 77 | 78 | dfs.ha.fencing.ssh.private-key-files 79 | /home/exampleuser/.ssh/id_rsa 80 | 81 | ``` 82 | 83 | JNS : 84 | ```xml 85 | 86 | dfs.namenode.shared.edits.dir 87 | qjournal://node1.example.com:8485;node2.example.com:8485;node3.example.com:8485/mycluster 88 | 89 | ``` 90 | 91 | core-site.xml 文件: 92 | 93 | 配置 ZK 集群: 94 | ```xml 95 | 96 | ha.zookeeper.quorum 97 | node01:2181,node02:2181,node03:2181 98 | 99 | ``` 100 | 101 | 配置 HDFS 集群的服务域名: 102 | 103 | ```xml 104 | 105 | fs.defaultFS 106 | hdfs://mycluster 107 | 108 | ``` 109 | 110 | 以上都配置了什么呢? 111 | 112 | 有这几个核心点: 113 | 114 | NameNode 主备,ZK 集群,JNS 集群,sshfence 一把利剑。 115 | 116 | 原汁原味的文档,官方文档地址:[地址](http://hadoop.apache.org/docs/r3.0.2/hadoop-project-dist/hadoop-hdfs/HDFSHighAvailabilityWithQJM.html#Configuration\_details) 117 | 118 | ## 主备切换 119 | 120 | __NameNode 的主备切换主要是由 ZKFailerController, HeathMonitor, ActiveStandbyElector 三个组件协同实现的。__ 121 | 122 | 123 | ![image.png | left | 747x379](https://cdn.nlark.com/yuque/0/2018/png/199648/1544107639749-0beff873-44ab-49ba-9491-5981a76bb731.png "") 124 | 125 | 126 | 其中,__ZKFailerController__ 在 NameNode 上作为独立的进行启动,(在系统上可以看到 zkfc 这个后台进程)。启动的时候,会创建 HeathMonitor 和 ActiveStandbyElector 这两个内部组件,ZKFailoverController 在创建 HealthMonitor 和 ActiveStandbyElector 的同时,也会向 HealthMonitor 和 ActiveStandbyElector 注册相应的回调方法。 127 | 128 | HealthMonitor 初始化完成之后会启动内部的线程来定时调用对应 NameNode 的 HAServiceProtocol RPC 接口的方法,对 NameNode 的健康状态进行检测。 HealthMonitor 如果检测到 NameNode 的健康状态发生变化,会回调 ZKFailoverController 注册的相应方法进行处理。 129 | 130 | 如果 ZKFailoverController 判断需要进行主备切换,会首先使用 ActiveStandbyElector 来进行自动的主备选举。 ActiveStandbyElector 与 Zookeeper 进行交互完成自动的主备选举。 ActiveStandbyElector 在主备选举完成后,会回调 ZKFailoverController 的相应方法来通知当前的NameNode 成为主 NameNode 或备 NameNode。 131 | 132 | ZKFailoverController 调用对应 NameNode 的 HAServiceProtocol RPC 接口的方法将 NameNode 转换为 Active 状态或 Standby 状态。 133 | 134 | ## 具体的细节:(重要) 135 | 136 | __健康检查:HeathMonitor__ 137 | 138 | __ZKFailoverController__ 在初始化的时候会创建 __HealthMonitor__,HealthMonitor 在内部会启动一个线程来循环调用 NameNode 的 HAServiceProtocol RPC 接口的方法来检测 NameNode 的状态,并将状态的变化通过回调的方式来通知 ZKFailoverController。 139 | 140 | HealthMonitor 主要检测 NameNode 的两类状态,分别是 HealthMonitor.State 和 HAServiceStatus。HealthMonitor.State 是通过 HAServiceProtocol RPC 接口的 monitorHealth 方法来获取的,反映了 NameNode 节点的健康状况,主要是磁盘存储资源是否充足。 141 | 142 | HealthMonitor.State 包括下面几种状态: 143 | 144 | ```plain 145 | NITIALIZING:HealthMonitor 在初始化过程中,还没有开始进行健康状况检测; 146 | SERVICE_HEALTHY:NameNode 状态正常; 147 | SERVICE_NOT_RESPONDING:调用 NameNode 的 monitorHealth 方法调用无响应或响应超时; 148 | SERVICE_UNHEALTHY:NameNode 还在运行,但是 monitorHealth 方法返回状态不正常,磁盘存储资源不足; 149 | HEALTH_MONITOR_FAILED:HealthMonitor 自己在运行过程中发生了异常,不能继续检测 NameNode 的健康状况,会导致 ZKFailoverController 进程退出; 150 | HealthMonitor.State 在状态检测之中起主要的作用,在 HealthMonitor.State 发生变化的时候,HealthMonitor 会回调 ZKFailoverController 的相应方法来进行处理,具体处理见后文 ZKFailoverController 部分所述。 151 | ``` 152 | 153 | 而 HAServiceStatus 则是通过 HAServiceProtocol RPC 接口的 getServiceStatus 方法来获取的,主要反映的是 NameNode 的 HA 状态,包括: 154 | 155 | ```plain 156 | INITIALIZING:NameNode 在初始化过程中; 157 | ACTIVE:当前 NameNode 为主 NameNode; 158 | STANDBY:当前 NameNode 为备 NameNode; 159 | STOPPING:当前 NameNode 已停止; 160 | ``` 161 | 162 | HAServiceStatus 在状态检测之中只是起辅助的作用,在 HAServiceStatus 发生变化时,HealthMonitor 也会回调 ZKFailoverController 的相应方法来进行处理,具体处理见后文 ZKFailoverController 部分所述。 163 | 164 | __主备选举:ActiveStandbyElector__ 165 | 166 | __Namenode(包括 YARN ResourceManager) 的主备选举是通过 ActiveStandbyElector 来完成的,ActiveStandbyElector 主要是利用了 Zookeeper 的写一致性和临时节点机制__,具体的主备选举实现如下: 167 | 168 | __创建锁节点__ ,如果 HealthMonitor 检测到对应的 NameNode 的状态正常,那么表示这个 NameNode 有资格参加 Zookeeper 的主备选举。如果目前还没有进行过主备选举的话,那么相应的 ActiveStandbyElector 就会发起一次主备选举,尝试在 Zookeeper 上创建一个路径为/hadoop-ha//ActiveStandbyElectorLock 的临时节点,__Zookeeper 的写一致性会保证最终只会有一个 ActiveStandbyElector 创建成功__,那么创建成功的 ActiveStandbyElector 对应的 NameNode 就会成为主 NameNode,ActiveStandbyElector 会回调 ZKFailoverController 的方法进一步将对应的 NameNode 切换为 Active 状态。而创建失败的 ActiveStandbyElector 对应的 NameNode 成为备 NameNode,ActiveStandbyElector 会回调 ZKFailoverController 的方法进一步将对应的 NameNode 切换为 Standby 状态。 169 | 170 | __Watcher 监听:__ 171 | 172 | __不管创建 /hadoop-ha//ActiveStandbyElectorLock 节点是否成功,ActiveStandbyElector 随后都会向 Zookeeper 注册一个 Watcher 来监听这个节点的状态变化事件__,ActiveStandbyElector 主要关注这个节点的 NodeDeleted 事件。(所有的 NameNode 都会有一个 Watcher 监听)。 173 | 174 | __自动触发主备选举__ ,如果 Active NameNode 对应的 HealthMonitor 检测到 NameNode 的状态异常时, ZKFailoverController 会主动删除当前在 Zookeeper 上建立的临时节点/hadoop-ha//ActiveStandbyElectorLock,这样处于 Standby 状态的 NameNode 的 ActiveStandbyElector 注册的监听器就会收到这个节点的 NodeDeleted 事件。收到这个事件之后,会马上再次进入到创建/hadoop-ha//ActiveStandbyElectorLock 节点的流程,如果创建成功,这个本来处于 Standby 状态的 NameNode 就选举为主 NameNode 并随后开始切换为 Active 状态。 175 | 176 | 当然,如果是 Active 状态的 NameNode 所在的机器整个宕掉的话,那么根据 Zookeeper 的临时节点特性,/hadoop-ha/\${dfs.nameservices}/ActiveStandbyElectorLock 节点会自动被删除,从而也会自动进行一次主备切换。 177 | 178 | __脑裂:__ 179 | 180 | Zookeeper 在工程实践的过程中经常会发生的一个现象就是 Zookeeper 客户端“假死”,__所谓的“假死”是指如果 Zookeeper 客户端机器负载过高或者正在进行 JVM Full GC,那么可能会导致 Zookeeper 客户端到 Zookeeper 服务端的心跳不能正常发出,一旦这个时间持续较长,超过了配置的 Zookeeper Session Timeout 参数的话,Zookeeper 服务端就会认为客户端的 session 已经过期从而将客户端的 Session 关闭。“假死”有可能引起分布式系统常说的双主或脑裂 (brain-split) 现象。__ 181 | 182 | 具体到本文所述的 NameNode,假设 NameNode1 当前为 Active 状态,NameNode2 当前为 Standby 状态。如果某一时刻 NameNode1 对应的 ZKFailoverController 进程发生了“假死”现象,那么 Zookeeper 服务端会认为 NameNode1 挂掉了,根据前面的主备切换逻辑,NameNode2 会替代 NameNode1 进入 Active 状态。但是此时 NameNode1 可能仍然处于 Active 状态正常运行,即使随后 NameNode1 对应的 ZKFailoverController 因为负载下降或者 Full GC 结束而恢复了正常,感知到自己和 Zookeeper 的 Session 已经关闭,但是由于网络的延迟以及 CPU 线程调度的不确定性,仍然有可能会在接下来的一段时间窗口内 NameNode1 认为自己还是处于 Active 状态。这样 NameNode1 和 NameNode2 都处于 Active 状态,都可以对外提供服务。这种情况对于 NameNode 这类对数据一致性要求非常高的系统来说是灾难性的,数据会发生错乱且无法恢复。Zookeeper 社区对这种问题的解决方法叫做 fencing,中文翻译为隔离,也就是__想办法把旧的 Active NameNode 隔离起来,使它不能正常对外提供服务。__ 183 | 184 |
185 |
ActiveStandbyElector 为了实fencing,会在成功创建 Zookeeper 节点 hadoop-ha//ActiveStandbyElectorLock从而成为 Active NameNode 之后,创建另外一个路径为/hadoop-ha//ActiveBreadCrumb 的持久节点,这个节点里面保存了这个 Active NameNode 的地址信息。Active NameNode 的 ActiveStandbyElector 186 | 在正常的状态下关闭 Zookeeper Session 的时候 (注意由于/hadoop-ha//ActiveStandbyElectorLock 是临时节点,也会随之删除),会一起删除节点/hadoop-ha//ActiveBreadCrumb。但是如果 ActiveStandbyElector 在异常的状态下 Zookeeper Session 关闭 (比如前述的Zookeeper 假死),那么由于/hadoop-ha//ActiveBreadCrumb 是持久节点,会一直保留下来。后面当另一个 NameNode 选主成功之后,会注意到上一个 Active NameNode 遗留下来的这个节点,从而会回调 ZKFailoverController 的方法对旧的 Active NameNode 进行 fencing!!!!!!
187 |
188 | 189 | 190 |
191 |
如果 ActiveStandbyElector 选主成功之后,发现了上一个 Active NameNode 遗留下来的/hadoop-ha//ActiveBreadCrumb 节点 ,那么 ActiveStandbyElector 会首先回调 ZKFailoverController 注册的 fenceOldActive 方法,尝试对旧的 Active NameNode 进行 fencing,在进行 fencing 的时候,会执行以下的操作:
192 |
193 | 194 |
195 |
196 |
197 | 198 | 首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它转换为 Standby 状态。 如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施,Hadoop 目前主要提供两种隔离措施,通常会选择 sshfence: sshfence:通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死; shellfence:执行一个用户自定义的 shell 脚本来将对应的进程隔离; 199 | 200 | 只有在成功地执行完成 fencing 之后,选主成功的 ActiveStandbyElector 才会回调 ZKFailoverController 的 becomeActive 方法将对应的 NameNode 转换为 Active 状态,开始对外提供服务。 201 | 202 | ## JQM数据同步机制 203 | 204 | ActiveNameNode和StandbyNameNode使用 JouranlNode 集群来进行数据同步的过程如图所示,Active NameNode 首先把 EditLog 提交到 JournalNode 集群,然后 Standby NameNode 再从 JournalNode 集群定时同步 EditLog: 205 | 206 | 基于 QJM 的共享存储的数据同步机制 207 | 208 | 209 | ![image.png | left | 627x259](https://cdn.nlark.com/yuque/0/2018/png/199648/1544107625915-afc7be3d-0e13-4424-86bb-80d8d8224d7c.png "") 210 | 211 | __Active NameNode 提交 EditLog 到 JournalNode 集群,当处于 Active 状态的 NameNode 调用 FSEditLog 类的 logSync 方法来提交 EditLog 的时候,会通过 JouranlSet 同时向本地磁盘目录和 JournalNode 集群上的共享存储目录写入 EditLog。__写入 JournalNode 集群是通过__并行调用__每一个 JournalNode 的 QJournalProtocol RPC 接口的 journal 方法实现的,如果对大多数 JournalNode 的 journal 方法调用成功,那么就认为提交 EditLog 成功,否则 NameNode 就会认为这次提交 EditLog 失败。提交 EditLog 失败会导致 Active NameNode 关闭 JournalSet 之后退出进程,留待处于 Standby 状态的 NameNode 接管之后进行数据恢复。 212 | 213 | 从上面的叙述可以看出,__Active NameNode 提交 EditLog 到 JournalNode 集群的过程实际上是同步阻塞的__,但是并不需要所有的 JournalNode 都调用成功,只要__大多数 JournalNode 调用成功__就可以了。如果无法形成大多数,那么就认为提交 EditLog 失败,NameNode 停止服务退出进程。如果对应到分布式系统的 CAP 理论的话,虽然采用了 Paxos 的“大多数”思想对 C(consistency,一致性) 和 A(availability,可用性) 进行了折衷,但还是可以认为 NameNode 选择了 C 而放弃了 A,这也符合 NameNode 对数据一致性的要求。 214 | 215 |
216 |
当 NameNode 进入 Standby 状态之后,会启动一个 EditLogTailer 线程。这个线程会定期调用 EditLogTailer 类的 doTailEdits 方法从 JournalNode 集群上同步 EditLog,然后把同步的 EditLog 回放到内存之中的文件系统镜像上 (并不会同时把 EditLog 写入到本地磁盘上)。这里需要关注的是:从 JournalNode 集群上同步的 EditLog 都是处于 finalized 状态的 EditLog Segment。“NameNode 的元数据存储概述”一节说过 EditLog Segment 实际上有两种状态,处于 in-progress 状态的 Edit Log 当前正在被写入,被认为是处于不稳定的中间态,有可能会在后续的过程之中发生修改,比如被截断。Active NameNode 在完成一个 EditLog Segment 的写入之后,就会向 218 | JournalNode 集群发送 finalizeLogSegment RPC 请求,将完成写入的 EditLog Segment finalized,然后开始下一个新的 EditLog Segment。一旦 finalizeLogSegment 方法在大多数的 JournalNode 上调用成功,表明这个 EditLog Segment 已经在大多数的 JournalNode 上达成一致。一个 EditLog Segment 处于 finalized 状态之后,可以保证它再也不会变化。
219 |
220 | 221 | 222 | ## 评价交流 223 | 224 | > 欢迎留下的你的想法~ 225 | 226 | 227 | -------------------------------------------------------------------------------- /docs/ziyuan02/kafka-01.md: -------------------------------------------------------------------------------- 1 | # Kafka 2 | 3 | 4 | ## 1.Kafka 零拷贝 5 | 6 | 7 | ![image.png | left | 400x336.9649805447471](https://cdn.nlark.com/yuque/0/2018/png/199648/1544258929376-66038419-ae8e-4069-b064-3d161af50a0f.png "") 8 | 9 | 每次数据遍历用户内核边界时,都必须进行复制,这会消耗CPU周期和内存带宽。幸运的是,您可以通过一种称为“适当地 - 零拷贝”的技术来消除这些副本。内核使用零拷贝的应用程序要求__内核直接将数据从磁盘文件复制到套接字,而不通过应用程序__。零拷贝大大提高了应用程序的性能,减少了内核和用户模式之间的上下文切换次数 10 | 11 | __减少内核态到用户态的转换。__ 12 | 13 | 如果使用sendfile,只需要一次拷贝就行:允许操作系统将数据直接从页缓存发送到网络上。所以在这个优化的路径中,只有最后一步将数据拷贝到网卡缓存中是需要的。 14 | 15 | 16 | 17 | ![image.png | left | 509x432](https://cdn.nlark.com/yuque/0/2018/png/199648/1544256974771-8671bf16-85be-43fc-989a-0256b69f11f7.png "") 18 | 19 | 20 | __kafka 的使用场景__: 21 | 22 | 消息消费的时候,包括外部Consumer以及Follower 从partiton Leader同步数据,都是如此。简单描述就是: 23 | 24 | Consumer从Broker获取文件数据的时候,直接通过下面的方法进行channel到channel的数据传输。 25 | 26 | ```java 27 | java.nio.FileChannel.transferTo(long position, long count, 28 | WritableByteChannel target) 29 | ``` 30 | 31 | 也就是说你的数据源是一个Channel,数据接收端也是一个Channel(SocketChannel),则通过该方式进行数据传输,是直接在内核态进行的,__避免拷贝数据导致的内核态和用户态的多次切换__。 32 | 33 | Kafka设计哲学上基本观点是认为数据时时刻刻都在流动,虽然数据在磁盘中,但因为基于内核进行交换,获得了数据近乎是存储在内存中的速度。没有必要放在用户空间中 34 | 35 | Kafka作为一个集大成者的消息中间件,有三个很重要的特征: 36 | 37 | 1. __分布式__,为大规模消息打下基础 38 | 2. 可以对消息进行__持久化__,默认会存放7天,意味可以重复消费 39 | 3. 既支持__队列方__式,也支持__发布-订阅模式__ 40 | 41 | 由于基于集群设计,又提供了非常强的__持久化和容错能力__。我们可以认为它是类似一个增加了消息处理能力的HDFS。 42 | 43 | kafka从整体角度讲,所有数据存储被抽象为topic,topic表明了不同数据类型,在broker中可以有很多个topic,producer发出消息给broker,consumer订阅一个或者多个topic,从broker拿数据。从broker拿数据和存数据都需要编码和解码,只有数据特殊时,才需要自己的解码器。 44 | 45 | consumer订阅了topic之后,它可以有很多的分组,sparkStreaming采用迭代器进行处理。生产者发布消息时,会具体到topic的分区中,broker会在分区的后面追加,所以就有时间的概念,当发布的消息达成一定阀值后写入磁盘,写完后消费者就可以收到这个消息了。 46 | 47 | 最后,想说,在中kafka里没有消息的id,只有offset,而且kafka本身是无状态的,offset只对consumer有意义。 48 | 49 | ## 2.kafka 如何做到1秒发布百万级条消息 50 | 51 | kafak 提供的生产端的API发布消息到一个 topic 或者多个 topic 的一个分区(保证消息的顺序性)或多个分区(并行处理,不能保证消息的顺序性)。topic 可以理解为数据的类别,是一个逻辑概念。 52 | 53 | 维护一个Topic中的分区log,以顺序追加的方式向各个分区中写入消,每一个分区都是不可变的消息队列,数据由 k , v 组成,k 是 offset :一个64位整型的唯一标识,offset代表了Topic分区中所有消息流中该消息的起始字节位置。 v 是就是实际的消息。 54 | 55 | 56 | 写入快: 57 | 1.顺序写入(随机写入会增加寻址过程),__批量__发送写入磁盘。 58 | 2.降低字节复制带来的开销,producer,broker,comsumer 三者使用共享的二进制消息格式。 59 | 60 | KAFKA这种消息队列在生产端和消费端分别采取的push和pull的方式,也就是你生产端可以认为KAFKA是个无底洞,有多少数据可以使劲往里面推送,消费端则是根据自己的消费能力,需要多少数据,你自己过来KAFKA这里拉取,KAFKA能保证只要这里有数据,消费端需要多少,都尽可以自己过来拿。 61 | 62 | 写出快: 63 | 1.零拷贝 FileChannel.transferTo ,页缓存sendfile组合,意味着KAFKA集群的消费者大多数都完全从缓存消费消息,而磁盘没有任何读取活动。 64 | 65 | 假设一个Topic有多个消费者的情况, 并使用上面的零拷贝优化,数据被复制到页缓存中一次,并在每个消费上重复使用,而不是存储在存储器中,也不在每次读取时复制到用户空间。 这使得以接近网络连接限制的速度消费消息。 66 | 67 | 2.__批量压缩__,即将多个消息一起压缩而不是单个消息压缩。 68 | 69 | 70 | ## 3.kafka 数据可靠性深度解读 71 | 72 | kafka发送消息的时候支持:同步和异步,默认是同步的、可通过producer.type属性进行配置。 73 | 74 | kafka的消息确认机制默认是 1 :leader收到会返回一个 ack 75 | 0的话是不返回任何 ack 76 | -1的话是所有的 follower 都与leader保持同步之后,返回zck 77 | 78 | 配置项:request.required.acks属性来确认消息的生产。 79 | 80 | __综合以上,可以检出 acks=0 和acks=1 的时候,都会发生数据丢失的情况。__ 81 | 82 | __解决办法:__ 83 | 84 | 针对消息丢失: 85 | 同步模式下,确认机制设置为-1,即让消息写入Leader和Follower之后再确认消息发送成功; 86 | 异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态; 87 | ``` 88 | queue.enqueue.timeout.ms = -1 89 | ``` 90 | 91 | ![image.png | left | 827x332](https://cdn.nlark.com/yuque/0/2018/png/199648/1545056253490-80974070-8209-4bdf-9a12-c377af41ad68.png "") 92 | 93 | 94 | 95 | 针对消息重复:将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可。 96 | 97 | kafka 的消息保存在Topic中,Topic可分为多个分区,为保证数据的安全性,每个分区又有多个Replia。 98 | 99 | 多分区的设计的特点:  100 | 1.为了并发读写,加快读写速度;  101 | 2.是利用多分区的存储,利于数据的均衡;  102 | 3.是为了加快数据的恢复速率,一但某台机器挂了,整个集群只需要恢复一部分数据,可加快故障恢复的时间。 103 | 104 | 每个Partition分为多个Segment,每个Segment有.log和.index 两个文件,每个log文件承载具体的数据,每条消息都有一个递增的offset,Index文件是对log文件的索引,Consumer查找offset时使用的是二分法根据文件名去定位到哪个Segment,然后解析msg,匹配到对应的offset的msg。 105 | 106 | kafka处理的数据量很大,可以说有多少个partition就有多少个leader,  所以简化一些管理逻辑,可以节省很多资源消耗。  kafka会将"leader"均衡的分散在每个实例上,可确保整体的性能稳定. 107 | 108 | ## 4.kafak 分区 leader 机制 109 | 110 | kafka在引入Replication之后,同一个Partition可能会有多个Replica,而这时需要在这些Replication之间选出一个Leader。 111 | 112 | Kafka将每个Topic进行分区Patition,以提高消息的并行处理,同时为保证高可用性,每个分区都有一定数量的副本 Replica,这样当部分服务器不可用时副本所在服务器就可以接替上来,保证系统可用性。在Leader上负责读写,Follower负责数据的同步。当一个Leader发生故障如何从Follower中选择新Leader呢? 113 | 114 | Kafka在Zookeeper上针对每个Topic都维护了一个ISR(in-sync replica---已同步的副本)的集合,集合的增减Kafka都会更新该记录。如果某分区的Leader不可用,Kafka就从ISR集合中选择一个副本作为新的Leader。这样就可以容忍的失败数比较高,假如某Topic有N+1个副本,则可以容忍N个服务器不可用! 115 | 116 | 基于上面的分区 leader 机制,Producer和Consumer只与这个Leader交互,其它Replica作为Follower从Leader中复制数据。因为需要保证同一个Partition的多个Replica之间的数据一致性(其中一个宕机后其它Replica必须要能继续服务并且即不能造成数据重复也不能造成数据丢失)。如果没有一个Leader,所有Replica都可同时读/写数据,那就需要保证多个Replica之间互相(N×N条通路)同步数据,数据的一致性和有序性非常难保证,大大增加了Replication实现的复杂性,同时也增加了出现异常的几率。而引入Leader后,只有Leader负责数据读写,Follower只向Leader顺序Fetch数据(N条通路),系统更加简单且高效。 117 | 118 | 119 | ## 5.kafka 重复消费数据 120 | 121 | > kafka重复消费都是由于未正常提交offset,故修改配置,正常提交offset即可解决 122 | 123 | 消费者速度很慢,导致一个session周期(0.1版本是默认30s)内未完成消费。导致心跳机制检测报告出问题。 124 | 125 | 导致消费了的数据未及时提交offset.配置由可能是自动提交 126 | 127 | 问题场景: 128 | 1.offset为自动提交,正在消费数据,kill消费者线程,下次重复消费 129 | 130 | 2.设置自动提交,关闭kafka,close之前,调用consumer.unsubscribed()则由可能部分offset没有提交。 131 | 132 | 3.消费程序和业务逻辑在一个线程,导致offset提交超时 133 | 134 | 参考:[https://www.cnblogs.com/huiandong/p/9402409.html](https://www.cnblogs.com/huiandong/p/9402409.html) 135 | 136 | ## 评论交流 137 | 138 | > say you want... 139 | 140 | 141 | -------------------------------------------------------------------------------- /docs/ziyuan02/secondarySort.md: -------------------------------------------------------------------------------- 1 | # MR 二次排序 2 | 3 | ## 规则 4 | 在一个数据文件中,首先按照key排序。  5 | 在key相同的情况下,按照value大小排序的情况称为二次排序。 6 | 7 | * __自定义key__ :NewKey实现比较规则 8 | * __自定义GroupingComparator方法__ 9 | 10 | 11 | 12 | ![image.png | left | 746x379](https://cdn.nlark.com/yuque/0/2018/png/199648/1544530369151-2c799756-df4a-436c-9765-ad9a4294c8e6.png "") 13 | 14 | ## 原理 15 | 16 | 在map阶段,使 17 | 用job.setInputFormatClass定义的InputFormat将输入的数据集分割成小数据块splites,同时InputFormat提供一个RecordReder的实现。比如使用的是TextInputFormat,它提供的RecordReder会将文本的一行的行号作为key,这一行的文本作为value。这就是自定义Map的输入是<LongWritable, Text>的原因。然后调用自定义Map的map方法,将一个个<LongWritable, Text>对输入给Map的map方法。注意输出应该符合自定义Map中定义的输出<IntPair, IntWritable>。最终是生成一个List<IntPair, IntWritable>。在map阶段的最后,会先调用job.setPartitionerClass对这个List进行分区,每个分区映射到一个reducer。每个分区内又调用job.setSortComparatorClass设置的key比较函数类排序。可以看到,这本身就是一个二次排序。如果没有通过job.setSortComparatorClass设置key比较函数类,则使用key的实现的compareTo方法。 18 | 19 | 在reduce阶段,reducer接收到所有映射到这个reducer的map输出后,也是会调用job.setSortComparatorClass设置的key比较函数类对所有数据对排序。然后开始构造一个key对应的value迭代器。这时就要用到分组,使用job.setGroupingComparatorClass设置的分组函数类。只要这个比较器比较的两个key相同,他们就属于同一个组,它们的value放在一个value迭代器,而这个迭代器的key使用属于同一个组的所有key的第一个key。最后就是进入Reducer的reduce方法,reduce方法的输入是所有的(key和它的value迭代器)。同样注意输入与输出的类型必须与自定义的Reducer中声明的一致。  20 | 21 | ## 参数 22 | 23 | 主要是下面这几个配置参数: 24 | 25 | job.setPartitionerClass(Partitioner p); 26 | 27 | job.setSortComparatorClass(RawComparator c); 28 | 29 | job.setGroupingComparatorClass(RawComparator c); 30 | 31 | ## 评价交流 32 | 33 | > 欢迎留下的你的想法~ 34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/ziyuan02/skewindata.md: -------------------------------------------------------------------------------- 1 | # 数据倾斜 2 | ## 什么是数据倾斜 3 | 4 | > 数据在集群上处理时,会被分配到各个节点上,当数据分配不均匀时,个别节点的数据量特别多,会导致整个任务变慢,甚至出现内存溢出程序失败的情况。 5 | 6 | ## Map端倾斜(比较少见) 7 | 8 | * Map端每个节点处理的数据量由InputFormat决定. 9 | * 对于输入数据是HDFS上的文件,FileInputFormat已经做好了分片. 10 | * 自定义的InputFormat注意要在getSplit方法中将数据均匀分片. 11 | 12 | ## Reduce端倾斜 13 | 14 | * Map端输出的几个Key值的数据量特别大 15 | 16 | * 根据业务场景,过滤掉一些没有用的Key值,比如空值. 17 | * 当Reduce端要聚合成一个值的运算(比如累加),可以先在Map端设置Combine操作,提前将数据聚合. 18 | * 自定义Partition类,编写特定的getPartition函数使Key值分配均匀. 19 | 20 | ## 关联Join数据倾斜 21 | 22 | * 将Reducer Join转化为Map Join. 23 | 24 | 25 | 26 | ![ReduceJoin转MapJoin | left](https://github.com/jiaoqiyuan/163-bigdate-note/raw/master/%E6%97%A5%E5%BF%97%E8%A7%A3%E6%9E%90%E5%8F%8A%E8%AE%A1%E7%AE%97%EF%BC%9AMR/img/ReduceJoin%E8%BD%ACMapJoin.png "") 27 | 28 | 29 | > 注意只有在一个小表和一个大表做Join的时候才能这样优化. 30 | 31 | * 将Key值加上N以内的随机数前缀进行扩容(比较常用的方法) 32 | 33 | 34 | 35 | ![关联数据倾斜2 | left](https://github.com/jiaoqiyuan/163-bigdate-note/raw/master/%E6%97%A5%E5%BF%97%E8%A7%A3%E6%9E%90%E5%8F%8A%E8%AE%A1%E7%AE%97%EF%BC%9AMR/img/Key%E5%80%BC%E5%8A%A0N%E4%BB%A5%E5%86%85%E9%9A%8F%E6%9C%BA%E6%95%B0.png "") 36 | 37 | 38 | ## 解决 39 | 40 | __补充:__ 41 | 42 | 1.参数hive.groupby.skewindata = true,解决数据倾斜的万能钥匙,查询计划会有两个 MR [Job](http://www.verydemo.com/demo_c152_i9269.html)。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。 43 | 2.MapJoin会把小表全部读入内存中,在map阶段直接拿另外一个表的数据和内存中表数据做匹配。hive的where条件本身就是在map阶段进行的操作,where的条件写在join里面,使得减少join的数量。 44 | 45 | ## 评价交流 46 | 47 | > 欢迎留下的你的想法~ 48 | 49 | 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blog", 3 | "version": "1.0.0", 4 | "description": "测试vue生产静态网页", 5 | "main": "index.js", 6 | "dependencies": { 7 | "leancloud-storage": "^3.10.0", 8 | "valine": "^1.3.3", 9 | "vuepress": "^0.14.4" 10 | }, 11 | "devDependencies": { 12 | "@vuepress/plugin-back-to-top": "^1.0.0-alpha.0", 13 | "@vuepress/plugin-blog": "^1.0.0-alpha.0" 14 | }, 15 | "scripts": { 16 | "docs:dev": "vuepress dev docs", 17 | "docs:build": "vuepress build docs" 18 | }, 19 | "author": "", 20 | "license": "ISC" 21 | } 22 | -------------------------------------------------------------------------------- /push.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | #确保脚本抛出遇到的错误 4 | set -e 5 | 6 | git init 7 | git add -A 8 | git commit -m '更新文章内容' 9 | 10 | # 如果发布到 https://.github.io 11 | # git push -f git@github.com:/.github.io.git master 12 | 13 | # 如果发布到 https://.github.io/ 14 | 15 | git push -f git@github.com:aikuyun/bigdata-doc.git master 16 | -------------------------------------------------------------------------------- /testfine.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import os 3 | 4 | # from heavy import special_commit 5 | 6 | 7 | def modify(): 8 | file = open('zero.md', 'r') 9 | flag = int(file.readline()) == 0 10 | file.close() 11 | file = open('zero.md', 'w+') 12 | if flag: 13 | file.write('1') 14 | else: 15 | file.write('0') 16 | file.close() 17 | 18 | 19 | def commit(): 20 | os.system('git commit -a -m testfine > /dev/null 2>&1') 21 | 22 | 23 | def set_sys_time(year, month, day): 24 | os.system('date -s %04d%02d%02d' % (year, month, day)) 25 | 26 | 27 | def trick_commit(year, month, day): 28 | set_sys_time(year, month, day) 29 | modify() 30 | commit() 31 | 32 | 33 | def daily_commit(start_date, end_date): 34 | for i in range((end_date - start_date).days + 1): 35 | cur_date = start_date + datetime.timedelta(days=i) 36 | trick_commit(cur_date.year, cur_date.month, cur_date.day) 37 | 38 | 39 | if __name__ == '__main__': 40 | daily_commit(datetime.date(2017, 3, 31), datetime.date(2018, 1, 28)) 41 | --------------------------------------------------------------------------------