From 12b7346d65ed805d65332fa44d1bfd59e382a136 Mon Sep 17 00:00:00 2001 From: min <40719512+collabH@users.noreply.github.com> Date: Thu, 28 Apr 2022 20:02:19 +0800 Subject: [PATCH] Revert "GitBook: [#6] No subject" This reverts commit 8d964517a2860272c5548fb66d8f681733c4195d. --- README.md | 280 +++++++------- SUMMARY.md | 225 ----------- base/README.md | 2 - base/algorithm/README.md | 2 - base/datastructure/README.md | 2 - ...60\346\215\256\347\273\223\346\236\204.md" | 361 +++++++++--------- base/fen-bu-shi-li-lun/README.md | 2 - base/java/README.md | 2 - base/java/bing-fa-bian-cheng/README.md | 2 - ...66\345\217\221\347\274\226\347\250\213.md" | 140 +++---- base/ji-suan-ji-li-lun/README.md | 2 - base/scala/README.md | 2 - ...03\345\274\217\346\236\266\346\236\204.md" | 106 +++-- bigdata/README.md | 2 - bigdata/cache/README.md | 2 - bigdata/cache/alluxio/README.md | 2 - bigdata/collect/README.md | 2 - bigdata/collect/canal/README.md | 2 - bigdata/collect/debezium/DebeziumOverView.md | 187 +++++---- bigdata/collect/debezium/README.md | 2 - bigdata/collect/flume/README.md | 2 - bigdata/collect/sqoop/README.md | 2 - bigdata/datalake/README.md | 2 - bigdata/datalake/hudi/README.md | 2 - bigdata/datalake/iceberg/README.md | 2 - bigdata/engine/README.md | 2 - bigdata/engine/flink/README.md | 2 - bigdata/engine/flink/books/README.md | 2 - .../README.md | 2 - bigdata/engine/flink/connector/README.md | 2 - .../Checkpoint\346\234\272\345\210\266.md" | 97 ++--- bigdata/engine/flink/core/README.md | 2 - bigdata/engine/flink/core/TableSQLOverview.md | 163 ++++---- bigdata/engine/flink/feature/README.md | 2 - bigdata/engine/flink/monitor/README.md | 2 - bigdata/engine/flink/practice/README.md | 2 - ...15\345\216\213\351\227\256\351\242\230.md" | 94 +++-- bigdata/engine/flink/sourcecode/README.md | 2 - ...37\347\220\206\346\267\261\345\205\245.md" | 112 +++--- bigdata/engine/spark/README.md | 2 - bigdata/engine/spark/practice/README.md | 2 - .../engine/spark/spark sql/SparkSQL API.md | 104 ++--- ...30\345\214\226\345\210\206\346\236\220.md" | 0 ...Streaming\346\225\264\345\220\210Flume.md" | 56 ++- bigdata/engine/spark/spark-sql/README.md | 2 - .../engine/spark/spark-streaming/README.md | 2 - bigdata/engine/spark/yuan-ma-fen-xi/README.md | 2 - .../yuan-ma-fen-xi/spark-he-xin-dui-xiang.md | 87 ----- ...05\345\255\230\347\256\241\347\220\206.md" | 125 +++--- ...70\345\277\203\345\257\271\350\261\241.md" | 86 +++++ ...04\344\273\266\351\200\232\344\277\241.md" | 7 +- ...222\214Shuffle\350\247\243\346\236\220.md" | 191 ++++----- ...50\347\275\262\346\265\201\347\250\213.md" | 42 +- bigdata/graph/README.md | 2 - ...53\351\200\237\345\205\245\351\227\250.md" | 87 ++--- bigdata/graph/nebula-graph/README.md | 2 - ...06\347\276\244\347\256\241\347\220\206.md" | 0 ...47\345\210\253\351\205\215\347\275\256.md" | 0 ...37\347\220\206\345\211\226\346\236\220.md" | 0 ...23\345\207\272\345\211\226\346\236\220.md" | 0 ...0\206\346\241\206\346\236\266MapReduce.md" | 0 bigdata/hadoop/README.md | 2 - bigdata/hadoop/hdfs/README.md | 2 - bigdata/hadoop/mapreduce/README.md | 2 - bigdata/hadoop/yarn/README.md | 2 - bigdata/kvstore/README.md | 2 - ...se\350\277\207\346\273\244\345\231\250.md" | 140 ++++--- bigdata/kvstore/hbase/README.md | 2 - bigdata/kvstore/rocksdb/README.md | 2 - ...04\344\273\266\346\217\217\350\277\260.md" | 184 ++++----- bigdata/mq/README.md | 2 - bigdata/mq/kafka/README.md | 2 - .../mq/kafka/kafka-quan-wei-zhi-nan/README.md | 2 - .../2.\345\256\211\350\243\205Kafka.md" | 0 ...ka\347\224\237\344\272\247\350\200\205.md" | 0 ...ka\346\266\210\350\264\271\350\200\205.md" | 0 .../5.\346\267\261\345\205\245Kafka.md" | 0 ...10\346\201\257\344\274\240\350\276\223.md" | 0 ...60\346\215\256\347\256\241\351\201\223.md" | 0 ...60\346\215\256\351\225\234\345\203\217.md" | 0 .../9.\347\256\241\347\220\206Kafka.md" | 0 .../mq/kafka/shen-ru-li-jie-kafka/README.md | 2 - ...53\351\200\237\345\205\245\351\227\250.md" | 290 +++++++------- ...06\344\270\216\345\256\236\350\267\265.md" | 105 ++--- bigdata/mq/pulsar/README.md | 2 - bigdata/olap/README.md | 2 - bigdata/olap/clickhouse/README.md | 2 - bigdata/olap/druid/README.md | 2 - bigdata/olap/hive/README.md | 2 - .../hive/hive-bian-cheng-zhi-nan/README.md | 2 - ...50\344\273\245\345\217\212\351\224\201.md" | 0 .../11.HCatalog.md" | 0 ...07\344\273\266\346\240\274\345\274\217.md" | 0 .../3.HiveQL\347\233\270\345\205\263.md" | 0 .../4.\347\264\242\345\274\225.md" | 0 ...41\345\274\217\350\256\276\350\256\241.md" | 0 ...13\347\274\251\346\226\271\346\263\225.md" | 0 ...75\346\225\260\345\274\200\345\217\221.md" | 0 ...\217\212Thrift\346\234\215\345\212\241.md" | 0 bigdata/olap/impala/README.md | 2 - ...37\347\220\206\345\210\206\346\236\220.md" | 102 ++--- bigdata/olap/kudu/README.md | 2 - bigdata/olap/kudu/paper/README.md | 2 - bigdata/olap/kylin/README.md | 2 - bigdata/olap/presto/README.md | 2 - bigdata/scheduler/README.md | 2 - bigdata/tools/README.md | 2 - bigdata/tools/sqltree/README.md | 2 - bigdata/tools/sqltree/calcite/README.md | 2 - bigdata/zookeeper/README.md | 2 - ...34\344\270\216\351\203\250\347\275\262.md" | 177 ++++----- datawarehouse/README.md | 6 - datawarehouse/fang-an-shi-jian/README.md | 2 - datawarehouse/li-lun/README.md | 2 - .../shu-ju-zhong-tai-mo-kuai-she-ji/README.md | 2 - ...55\345\217\260\350\256\276\350\256\241.md" | 0 ...60\344\273\223\345\273\272\350\256\276.md" | 0 ...23\345\272\223\345\256\236\346\210\230.md" | 0 devops/README.md | 2 - devops/maven/README.md | 2 - mac/README.md | 6 - mac/iterm2/README.md | 2 - servicemonitor/README.md | 2 - servicemonitor/prometheus/README.md | 2 - 124 files changed, 1672 insertions(+), 2030 deletions(-) delete mode 100644 SUMMARY.md delete mode 100644 base/README.md delete mode 100644 base/algorithm/README.md delete mode 100644 base/datastructure/README.md delete mode 100644 base/fen-bu-shi-li-lun/README.md delete mode 100644 base/java/README.md delete mode 100644 base/java/bing-fa-bian-cheng/README.md rename base/java/bing-fa-bian-cheng/ren-shi-bing-fa-bian-cheng.md => "base/java/\345\271\266\345\217\221\347\274\226\347\250\213/\350\256\244\350\257\206\345\271\266\345\217\221\347\274\226\347\250\213.md" (88%) delete mode 100644 base/ji-suan-ji-li-lun/README.md delete mode 100644 base/scala/README.md rename base/fen-bu-shi-li-lun/fen-bu-shi-jia-gou.md => "base/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272/\345\210\206\345\270\203\345\274\217\346\236\266\346\236\204.md" (90%) delete mode 100644 bigdata/README.md delete mode 100644 bigdata/cache/README.md delete mode 100644 bigdata/cache/alluxio/README.md delete mode 100644 bigdata/collect/README.md delete mode 100644 bigdata/collect/canal/README.md delete mode 100644 bigdata/collect/debezium/README.md delete mode 100644 bigdata/collect/flume/README.md delete mode 100644 bigdata/collect/sqoop/README.md delete mode 100644 bigdata/datalake/README.md delete mode 100644 bigdata/datalake/hudi/README.md delete mode 100644 bigdata/datalake/iceberg/README.md delete mode 100644 bigdata/engine/README.md delete mode 100644 bigdata/engine/flink/README.md delete mode 100644 bigdata/engine/flink/books/README.md delete mode 100644 bigdata/engine/flink/books/flink-nei-he-yuan-li-yu-shi-xian/README.md delete mode 100644 bigdata/engine/flink/connector/README.md delete mode 100644 bigdata/engine/flink/core/README.md delete mode 100644 bigdata/engine/flink/feature/README.md delete mode 100644 bigdata/engine/flink/monitor/README.md delete mode 100644 bigdata/engine/flink/practice/README.md delete mode 100644 bigdata/engine/flink/sourcecode/README.md delete mode 100644 bigdata/engine/spark/README.md delete mode 100644 bigdata/engine/spark/practice/README.md rename bigdata/engine/spark/spark-sql/sparksql-you-hua-fen-xi.md => "bigdata/engine/spark/spark sql/SparkSQL\344\274\230\345\214\226\345\210\206\346\236\220.md" (100%) delete mode 100644 bigdata/engine/spark/spark-sql/README.md delete mode 100644 bigdata/engine/spark/spark-streaming/README.md delete mode 100644 bigdata/engine/spark/yuan-ma-fen-xi/README.md delete mode 100644 bigdata/engine/spark/yuan-ma-fen-xi/spark-he-xin-dui-xiang.md create mode 100644 "bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\346\240\270\345\277\203\345\257\271\350\261\241.md" rename bigdata/engine/spark/yuan-ma-fen-xi/spark-zu-jian-tong-xin.md => "bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\347\273\204\344\273\266\351\200\232\344\277\241.md" (74%) rename bigdata/engine/spark/yuan-ma-fen-xi/spark-tiao-du-he-shuffle-jie-xi.md => "bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\350\260\203\345\272\246\345\222\214Shuffle\350\247\243\346\236\220.md" (63%) rename bigdata/engine/spark/yuan-ma-fen-xi/yarn-de-bu-shu-liu-cheng.md => "bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/yarn\347\232\204\351\203\250\347\275\262\346\265\201\347\250\213.md" (85%) delete mode 100644 bigdata/graph/README.md rename bigdata/graph/nebula-graph/2.-kuai-su-ru-men.md => "bigdata/graph/nebula graph/2.\345\277\253\351\200\237\345\205\245\351\227\250.md" (70%) delete mode 100644 bigdata/graph/nebula-graph/README.md rename bigdata/hadoop/hdfs/hdfs-ji-qun-guan-li.md => "bigdata/hadoop/HDFS/HDFS\351\233\206\347\276\244\347\256\241\347\220\206.md" (100%) rename bigdata/hadoop/yarn/hadoop-xiang-guan-zu-jian-sheng-chan-ji-bie-pei-zhi.md => "bigdata/hadoop/Hadoop\347\233\270\345\205\263\347\273\204\344\273\266\347\224\237\344\272\247\347\272\247\345\210\253\351\205\215\347\275\256.md" (100%) rename bigdata/hadoop/mapreduce/mapreduce-de-gong-zuo-yuan-li-pou-xi.md => "bigdata/hadoop/MapReduce/MapReduce\347\232\204\345\267\245\344\275\234\345\216\237\347\220\206\345\211\226\346\236\220.md" (100%) rename bigdata/hadoop/mapreduce/mapreduce-shu-ru-shu-chu-pou-xi.md => "bigdata/hadoop/MapReduce/MapReduce\350\276\223\345\205\245\350\276\223\345\207\272\345\211\226\346\236\220.md" (100%) rename bigdata/hadoop/mapreduce/fen-bu-shi-chu-li-kuang-jia-mapreduce.md => "bigdata/hadoop/MapReduce/\345\210\206\345\270\203\345\274\217\345\244\204\347\220\206\346\241\206\346\236\266MapReduce.md" (100%) delete mode 100644 bigdata/hadoop/README.md delete mode 100644 bigdata/hadoop/hdfs/README.md delete mode 100644 bigdata/hadoop/mapreduce/README.md delete mode 100644 bigdata/hadoop/yarn/README.md delete mode 100644 bigdata/kvstore/README.md delete mode 100644 bigdata/kvstore/hbase/README.md delete mode 100644 bigdata/kvstore/rocksdb/README.md delete mode 100644 bigdata/mq/README.md delete mode 100644 bigdata/mq/kafka/README.md delete mode 100644 bigdata/mq/kafka/kafka-quan-wei-zhi-nan/README.md rename bigdata/mq/kafka/kafka-quan-wei-zhi-nan/2.-an-zhuang-kafka.md => "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/2.\345\256\211\350\243\205Kafka.md" (100%) rename bigdata/mq/kafka/kafka-quan-wei-zhi-nan/3.kafka-sheng-chan-zhe.md => "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/3.Kafka\347\224\237\344\272\247\350\200\205.md" (100%) rename bigdata/mq/kafka/kafka-quan-wei-zhi-nan/4.kafka-xiao-fei-zhe.md => "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/4.Kafka\346\266\210\350\264\271\350\200\205.md" (100%) rename bigdata/mq/kafka/kafka-quan-wei-zhi-nan/5.-shen-ru-kafka.md => "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/5.\346\267\261\345\205\245Kafka.md" (100%) rename bigdata/mq/kafka/kafka-quan-wei-zhi-nan/6.-ke-kao-de-xiao-xi-chuan-shu.md => "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/6.\345\217\257\351\235\240\347\232\204\346\266\210\346\201\257\344\274\240\350\276\223.md" (100%) rename bigdata/mq/kafka/kafka-quan-wei-zhi-nan/7.-gou-jian-shu-ju-guan-dao.md => "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/7.\346\236\204\345\273\272\346\225\260\346\215\256\347\256\241\351\201\223.md" (100%) rename bigdata/mq/kafka/kafka-quan-wei-zhi-nan/8.-kua-ji-qun-shu-ju-jing-xiang.md => "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/8.\350\267\250\351\233\206\347\276\244\346\225\260\346\215\256\351\225\234\345\203\217.md" (100%) rename bigdata/mq/kafka/kafka-quan-wei-zhi-nan/9.-guan-li-kafka.md => "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/9.\347\256\241\347\220\206Kafka.md" (100%) delete mode 100644 bigdata/mq/kafka/shen-ru-li-jie-kafka/README.md delete mode 100644 bigdata/mq/pulsar/README.md delete mode 100644 bigdata/olap/README.md delete mode 100644 bigdata/olap/clickhouse/README.md delete mode 100644 bigdata/olap/druid/README.md delete mode 100644 bigdata/olap/hive/README.md delete mode 100644 bigdata/olap/hive/hive-bian-cheng-zhi-nan/README.md rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/10.-cun-chu-he-an-quan-yi-ji-suo.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/10.\345\255\230\345\202\250\345\222\214\345\256\211\345\205\250\344\273\245\345\217\212\351\224\201.md" (100%) rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/11.hcatalog.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/11.HCatalog.md" (100%) rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/2.-shu-ju-lei-xing-he-wen-jian-ge-shi.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/2.\346\225\260\346\215\256\347\261\273\345\236\213\345\222\214\346\226\207\344\273\266\346\240\274\345\274\217.md" (100%) rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/3.hiveql-xiang-guan.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/3.HiveQL\347\233\270\345\205\263.md" (100%) rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/4.-suo-yin.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/4.\347\264\242\345\274\225.md" (100%) rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/5.-mo-shi-she-ji.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/5.\346\250\241\345\274\217\350\256\276\350\256\241.md" (100%) rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/7.-qi-ta-wen-jian-ge-shi-he-ya-suo-fang-fa.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/7.\345\205\266\344\273\226\346\226\207\344\273\266\346\240\274\345\274\217\345\222\214\345\216\213\347\274\251\346\226\271\346\263\225.md" (100%) rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/8.-han-shu-kai-fa.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/8.\345\207\275\346\225\260\345\274\200\345\217\221.md" (100%) rename bigdata/olap/hive/hive-bian-cheng-zhi-nan/9.-wen-jian-he-ji-lu-ge-shi-yi-ji-thrift-fu-wu.md => "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/9.\346\226\207\344\273\266\345\222\214\350\256\260\345\275\225\346\240\274\345\274\217\344\273\245\345\217\212Thrift\346\234\215\345\212\241.md" (100%) delete mode 100644 bigdata/olap/impala/README.md delete mode 100644 bigdata/olap/kudu/README.md delete mode 100644 bigdata/olap/kudu/paper/README.md delete mode 100644 bigdata/olap/kylin/README.md delete mode 100644 bigdata/olap/presto/README.md delete mode 100644 bigdata/scheduler/README.md delete mode 100644 bigdata/tools/README.md delete mode 100644 bigdata/tools/sqltree/README.md delete mode 100644 bigdata/tools/sqltree/calcite/README.md delete mode 100644 bigdata/zookeeper/README.md delete mode 100644 datawarehouse/README.md delete mode 100644 datawarehouse/fang-an-shi-jian/README.md delete mode 100644 datawarehouse/li-lun/README.md delete mode 100644 datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/README.md rename datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/shu-ju-zhong-tai-she-ji.md => "datawarehouse/\346\225\260\346\215\256\344\270\255\345\217\260\346\250\241\345\235\227\350\256\276\350\256\241/\346\225\260\346\215\256\344\270\255\345\217\260\350\256\276\350\256\241.md" (100%) rename datawarehouse/fang-an-shi-jian/ji-yu-flink-de-shi-shi-shu-cang-jian-she.md => "datawarehouse/\346\226\271\346\241\210\345\256\236\350\267\265/\345\237\272\344\272\216Flink\347\232\204\345\256\236\346\227\266\346\225\260\344\273\223\345\273\272\350\256\276.md" (100%) rename datawarehouse/li-lun/shu-ju-cang-ku-shi-zhan.md => "datawarehouse/\347\220\206\350\256\272/\346\225\260\346\215\256\344\273\223\345\272\223\345\256\236\346\210\230.md" (100%) delete mode 100644 devops/README.md delete mode 100644 devops/maven/README.md delete mode 100644 mac/README.md delete mode 100644 mac/iterm2/README.md delete mode 100644 servicemonitor/README.md delete mode 100644 servicemonitor/prometheus/README.md diff --git a/README.md b/README.md index 8e1beca7..7d655920 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,68 @@ ---- -description: 仓库概览 ---- +![img.png](./img/logo.png) -# 概览 - -![img.png](img/logo.png) - -## repository +# repository [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT/) -[![Stargazers over time](https://starchart.cc/collabH/repository.svg)](./) +[![Stargazers over time](https://starchart.cc/collabH/repository.svg)](#) -### 概述 +## 概述 * 个人学习知识库涉及到数据仓库建模、实时计算、大数据、Java、算法等。 * [在线文档](https://repository-1.gitbook.io/bigdata-growth/) -### RoadMap +## RoadMap -![roadMap](roadmap/roadmap.jpg) +![roadMap](./roadmap/roadmap.jpg) -### 基础能力 +## 基础能力 -#### 数据结构 +### 数据结构 -#### 分布式理论 +### 分布式理论 -* [分布式架构](base/fen-bu-shi-li-lun/fen-bu-shi-jia-gou.md) +* [分布式架构](base/分布式理论/分布式架构.md) -#### 计算机理论 +### 计算机理论 * [LSM存储模型](base/计算机理论/LSM存储模型.md) -#### Scala +### Scala -* [ScalaOverView](base/scala/ScalaOverView.md) +* [ScalaOverView](./base/scala/ScalaOverView.md) -#### JVM +### JVM -#### Java +### Java -**并发编程** +#### 并发编程 -* [认识并发编程](base/java/bing-fa-bian-cheng/ren-shi-bing-fa-bian-cheng.md) +* [认识并发编程](base/java/并发编程/认识并发编程.md) * [并发工具包](base/java/并发编程/并发工具类concurrent.md) -**JDK源码** +#### JDK源码 -**todo** +#### todo -### 算法 +## 算法 * [算法题解](base/algorithm/算法题解.md) -### BigData +## BigData -#### cache +### cache **数据编排技术** -**alluxio** +#### alluxio * [Alluxio概览](bigdata/cache/alluxio/AlluxioOverView.md) * [Alluxio部署](bigdata/cache/alluxio/AlluxioDeployment.md) * [Alluxio整合计算引擎](bigdata/cache/alluxio/AlluxioWithEngine.md) -#### datalake +### datalake -**hudi** +#### hudi * [Hudi概览](bigdata/datalake/hudi/hudiOverview.md) * [Hudi整合Spark](bigdata/datalake/hudi/hudiWithSpark.md) @@ -77,26 +71,26 @@ description: 仓库概览 * [Hudi原理分析](bigdata/datalake/hudi/hudi原理分析.md) * [hudi数据湖实践](bigdata/datalake/hudi/hudi数据湖实践.md) -**iceberg** +#### iceberg * [IceBerg概览](bigdata/datalake/iceberg/icebergOverview.md) * [IceBerg整合Flink](bigdata/datalake/iceberg/icebergWithFlink.md) * [IceBerg整合Hive](bigdata/datalake/iceberg/icebergWithHive.md) * [IceBerg整合Spark](bigdata/datalake/iceberg/IcebergWithSpark.md) -#### kvstore +### kvstore **K-V结构存储,如Hbase、RocksDb(内嵌KV存储)等** -**rocksDB** +#### rocksDB * [rocksDB概述](bigdata/kvstore/rocksdb/RocksdbOverview.md) * [rocksDB配置](bigdata/kvstore/rocksdb/Rocksdb配置.md) * [rocksDB组件描述](bigdata/kvstore/rocksdb/Rocksdb组件描述.md) -* [rocksdb on flink]() +* [rocksdb on flink](bigdata/kvstore/rocksdb/RocksDB%20On%20Flink.md) * [rocksdb API](bigdata/kvstore/rocksdb/RocksDB%20API.xmind) -#### HBase +### HBase * [HBase概览](bigdata/kvstore/hbase/HBaseOverview.md) * [HBaseShell](bigdata/kvstore/hbase/HBase%20Shell.xmind) @@ -104,47 +98,47 @@ description: 仓库概览 * [HBase整合MapReduce](bigdata/kvstore/hbase/HBase整合第三方组件.md) * [HBase过滤器](bigdata/kvstore/hbase/Hbase过滤器.md) -#### Hadoop +### Hadoop **广义上的Hadoop生态圈的学习笔记,主要记录HDFS、MapReduce、Yarn相关读书笔记及源码分析等。** -**HDFS** +#### HDFS -* [Hadoop快速入门](bigdata/hadoop/Hadoop%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B.xmind) +* [Hadoop快速入门](bigdata/hadoop/Hadoop快速开始.xmind) * [HDFSOverView](bigdata/hadoop/HDFS/HDFSOverView.xmind) -* [Hadoop广义生态系统](bigdata/hadoop/Hadoop%E5%B9%BF%E4%B9%89%E7%94%9F%E6%80%81%E7%B3%BB%E7%BB%9F.xmind) +* [Hadoop广义生态系统](bigdata/hadoop/Hadoop广义生态系统.xmind) * [Hadoop高可用配置](bigdata/hadoop/Hadoop高可用配置.md) -* [HadoopCommon分析](bigdata/hadoop/HDFS/HadoopCommon%E5%8C%85%E5%88%86%E6%9E%90.pdf) -* [HDFS集群相关管理](bigdata/hadoop/hdfs/hdfs-ji-qun-guan-li.md) -* [HDFS Shell]() +* [HadoopCommon分析](bigdata/hadoop/HDFS/HadoopCommon包分析.pdf) +* [HDFS集群相关管理](bigdata/hadoop/HDFS/HDFS集群管理.md) +* [HDFS Shell](bigdata/hadoop/HDFS/HDFS%20Shell命令.md) -**MapReduce** +#### MapReduce -* [分布式处理框架MapReduce](bigdata/hadoop/mapreduce/fen-bu-shi-chu-li-kuang-jia-mapreduce.md) +* [分布式处理框架MapReduce](bigdata/hadoop/MapReduce/分布式处理框架MapReduce.md) * [MapReduce概览](bigdata/hadoop/MapReduce/MapReduceOverView.xmind) -* [MapReduce调优](bigdata/hadoop/MapReduce/MapReduce%E8%B0%83%E4%BC%98.xmind) +* [MapReduce调优](bigdata/hadoop/MapReduce/MapReduce调优.xmind) * [MapReduce数据相关操作](bigdata/hadoop/MapReduce/MapReduce数据操作.md) -* [MapReduce输入输出剖析](bigdata/hadoop/mapreduce/mapreduce-shu-ru-shu-chu-pou-xi.md) -* [MapReduce的工作机制](bigdata/hadoop/mapreduce/mapreduce-de-gong-zuo-yuan-li-pou-xi.md) +* [MapReduce输入输出剖析](bigdata/hadoop/MapReduce/MapReduce输入输出剖析.md) +* [MapReduce的工作机制](bigdata/hadoop/MapReduce/MapReduce的工作原理剖析.md) -**Yarn** +#### Yarn * [Yarn快速入门](bigdata/hadoop/Yarn/YARN快速入门.md) -**生产配置** +#### 生产配置 * [Hadoop高可用配置](bigdata/hadoop/Hadoop高可用配置.md) -* [Hadoop生产相关配置](bigdata/hadoop/yarn/hadoop-xiang-guan-zu-jian-sheng-chan-ji-bie-pei-zhi.md) +* [Hadoop生产相关配置](bigdata/hadoop/Hadoop相关组件生产级别配置.md) -#### Engine +### Engine **计算引擎相关,主要包含Flink、Spark等** -**Flink** +#### Flink * 主要包含对Flink文档阅读的总结和相关Flink源码的阅读,以及Flink新特性记录等等 -**Core** +##### Core * [FlinkOverView](bigdata/engine/flink/core/FlinkOverview.md) * [CheckPoint机制](bigdata/engine/flink/core/Checkpoint机制.md) @@ -159,7 +153,7 @@ description: 仓库概览 * [Flink Function](bigdata/engine/flink/core/Flink%20Function.xmind) * [DataSource API](bigdata/engine/flink/core/Data%20Source.xmind) -**SourceCode** +##### SourceCode * [FlinkCheckpoint源码分析](bigdata/engine/flink/sourcecode/FlinkCheckpoint源码分析.md) * [FlinkSQL源码解析](bigdata/engine/flink/sourcecode/FlinkSQL源码解析.md) @@ -170,110 +164,110 @@ description: 仓库概览 * [Flink运行环境源码解析](bigdata/engine/flink/sourcecode/Flink运行环境源码解析.md) * [FlinkTimerService机制分析](bigdata/engine/flink/sourcecode/FlinkTimerService机制分析.md) * [StreamSource源解析](bigdata/engine/flink/sourcecode/StreamSource源解析.md) -* [Flink状态管理与检查点机制](bigdata/engine/flink/sourcecode/Flink%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86%E4%B8%8E%E6%A3%80%E6%9F%A5%E7%82%B9%E6%9C%BA%E5%88%B6.xmind) +* [Flink状态管理与检查点机制](bigdata/engine/flink/sourcecode/Flink状态管理与检查点机制.xmind) -**Book** +##### Book -**Flink内核原理与实现** +###### Flink内核原理与实现 -* [1-3章读书笔记](bigdata/engine/flink/books/Flink%E5%86%85%E6%A0%B8%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/1-3%E7%AB%A0%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.xmind) -* [第4章时间与窗口](bigdata/engine/flink/books/Flink%E5%86%85%E6%A0%B8%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/%E7%AC%AC4%E7%AB%A0%E6%97%B6%E9%97%B4%E4%B8%8E%E7%AA%97%E5%8F%A3.xmind) -* [5-6章读书笔记](bigdata/engine/flink/books/Flink%E5%86%85%E6%A0%B8%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/5-6%E7%AB%A0%E7%B1%BB%E5%9E%8B%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.xmind) -* [第7章状态原理](bigdata/engine/flink/books/Flink%E5%86%85%E6%A0%B8%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/%E7%AC%AC7%E7%AB%A0%E7%8A%B6%E6%80%81%E5%8E%9F%E7%90%86.xmind) -* [第8章作业提交](bigdata/engine/flink/books/Flink%E5%86%85%E6%A0%B8%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/%E7%AC%AC8%E7%AB%A0%E4%BD%9C%E4%B8%9A%E6%8F%90%E4%BA%A4.xmind) -* [第9章资源管理](bigdata/engine/flink/books/Flink%E5%86%85%E6%A0%B8%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/%E7%AC%AC9%E7%AB%A0%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86.xmind) -* [第10章作业调度](bigdata/engine/flink/books/Flink%E5%86%85%E6%A0%B8%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/%E7%AC%AC10%E7%AB%A0%E4%BD%9C%E4%B8%9A%E8%B0%83%E5%BA%A6.xmind) +* [1-3章读书笔记](bigdata/engine/flink/books/Flink内核原理与实现/1-3章读书笔记.xmind) +* [第4章时间与窗口](bigdata/engine/flink/books/Flink内核原理与实现/第4章时间与窗口.xmind) +* [5-6章读书笔记](bigdata/engine/flink/books/Flink内核原理与实现/5-6章类型序列化和内存管理读书笔记.xmind) +* [第7章状态原理](bigdata/engine/flink/books/Flink内核原理与实现/第7章状态原理.xmind) +* [第8章作业提交](bigdata/engine/flink/books/Flink内核原理与实现/第8章作业提交.xmind) +* [第9章资源管理](bigdata/engine/flink/books/Flink内核原理与实现/第9章资源管理.xmind) +* [第10章作业调度](bigdata/engine/flink/books/Flink内核原理与实现/第10章作业调度.xmind) * [第11-13章Task执行数据交换等](bigdata/engine/flink/books/Flink内核原理与实现/第11-13章Task执行数据交换等.md) -**Feature** +##### Feature * [Flink1.12新特性](bigdata/engine/flink/feature/Flink1.12新特性.md) * [Flink1.13新特性](bigdata/engine/flink/feature/Flink1.13新特性.md) * [Flink1.14新特性](bigdata/engine/flink/feature/Flink1.14新特性.md) -**Practice** +##### Practice -* [Flink踩坑指南](bigdata/engine/flink/practice/Flink%E8%B8%A9%E5%9D%91.xmind) +* [Flink踩坑指南](bigdata/engine/flink/practice/Flink踩坑.xmind) * [记录一次Flink反压问题](bigdata/engine/flink/practice/记录一次Flink反压问题.md) -* [Flink SQL实践调优](bigdata/engine/flink/practice/Flink%20SQL%E8%B0%83%E4%BC%98.xmind) -* [Flink On K8s实践]() +* [Flink SQL实践调优](bigdata/engine/flink/practice/Flink%20SQL调优.xmind) +* [Flink On K8s实践](bigdata/engine/flink/practice/Flink%20On%20K8s.md) -**Connector** +##### Connector * [自定义Table Connector](bigdata/engine/flink/connector/自定义TableConnector.md) -**monitor** +##### monitor * [搭建Flink任务指标监控系统](bigdata/engine/flink/monitor/搭建Flink任务指标监控系统.md) -**Spark** +#### Spark **主要包含Spark相关书籍读书笔记、Spark核心组件分析、Spark相关API实践以及Spark生产踩坑等。** -* [Spark基础入门](bigdata/engine/spark/Spark%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8.xmind) +* [Spark基础入门](bigdata/engine/spark/Spark基础入门.xmind) * [SparkOnDeploy](bigdata/engine/spark/SparkOnDeploy.md) * [Spark调度系统](bigdata/engine/spark/Spark调度系统.md) * [Spark计算引擎和Shuffle](bigdata/engine/spark/Spark计算引擎和Shuffle.md) * [Spark存储体系](bigdata/engine/spark/Spark存储体系.md) -* [Spark大数据处理读书笔记](bigdata/engine/spark/Spark%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0.xmind) +* [Spark大数据处理读书笔记](bigdata/engine/spark/Spark大数据处理读书笔记.xmind) -**Spark Core** +##### Spark Core * [SparkCore](bigdata/engine/spark/spark%20core/Spark%20Core.xmind) * [SparkOperator](bigdata/engine/spark/spark%20core/Spark%20Operator.xmind) * [SparkConnector](bigdata/engine/spark/spark%20core/Spark%20Connector.xmind) -**Spark SQL** +##### Spark SQL * [SparkSQLAPI](bigdata/engine/spark/spark%20sql/Spark%20SQL%20API.xmind) * [SparkSQL](bigdata/engine/spark/spark%20sql/Spark%20SQL.xmind) -* [SparkSQL API]() -* [SparkSQL优化分析](bigdata/engine/spark/spark-sql/sparksql-you-hua-fen-xi.md) +* [SparkSQL API](bigdata/engine/spark/spark%20sql/SparkSQL%20API.md) +* [SparkSQL优化分析](bigdata/engine/spark/spark%20sql/SparkSQL优化分析.md) -**Spark Practice** +##### Spark Practice * [Spark生产实践](bigdata/engine/spark/practice/Spark生产实践.md) -**Spark Streaming** +##### Spark Streaming * [SparkStreaming](bigdata/engine/spark/spark%20streaming/Spark%20Steaming.xmind) -* [SparkStreaming整合Flume]() +* [SparkStreaming整合Flume](bigdata/engine/spark/spark%20streaming/SparkStreaming整合Flume.md) -**源码解析** +##### 源码解析 * [从浅到深剖析Spark源码](bigdata/engine/spark/从浅到深剖析Spark源码.md) -* [源码分析系列](bigdata/engine/spark/%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/) +* [源码分析系列](bigdata/engine/spark/源码分析) -#### Collect +### Collect **数据采集框架,主要包含Binlog增量与SQL快照方式框架** -#### Canal +### Canal * [CanalOverView](bigdata/collect/canal/CanalOverView.md) -#### Debezium +### Debezium * [DebeziumOverView](bigdata/collect/debezium/DebeziumOverView.md) -* [Debezium踩坑](bigdata/collect/debezium/Debezium%E8%B8%A9%E5%9D%91.xmind) +* [Debezium踩坑](bigdata/collect/debezium/Debezium踩坑.xmind) * [Debezium监控系统搭建](bigdata/collect/debezium/Debezium监控系统搭建.md) * [Debezium使用改造](bigdata/collect/debezium/Debezium使用改造.md) -**Flume** +#### Flume * [Flume快速入门](bigdata/collect/flume/FlumeOverwrite.md) * [Flume对接Kafka](bigdata/collect/flume/Flume对接Kafka.md) -**Sqoop** +#### Sqoop * [SqoopOverview](bigdata/collect/sqoop/SqoopOverview.md) * [Sqoop实战操作](bigdata/collect/sqoop/Sqoop实战操作.md) -#### MQ +### MQ **消息中间件相关,主要包含大数据中使用比较多的Kafka和Pulsar** -**Kafka** +#### Kafka * [kafka概览](bigdata/mq/kafka/KafkaOverView.xmind) * [基本概念](bigdata/mq/kafka/基本概念.md) @@ -281,150 +275,150 @@ description: 仓库概览 * [生产者源码剖析](bigdata/mq/kafka/生产者源码剖析.md) * [消费者源码剖析](bigdata/mq/kafka/消费者源码剖析.md) * [kafkaShell](bigdata/mq/kafka/KafkaShell.xmind) -* [kafka权威指南读书笔记](bigdata/mq/kafka/kafka%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97/) -* [深入理解Kafka读书笔记](bigdata/mq/kafka/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Kafka/) +* [kafka权威指南读书笔记](bigdata/mq/kafka/kafka权威指南) +* [深入理解Kafka读书笔记](bigdata/mq/kafka/深入理解Kafka) -**Pulsar** +#### Pulsar * [快速入门](bigdata/mq/pulsar/1.快速入门.md) * [原理与实践](bigdata/mq/pulsar/2.原理与实践.md) -#### Zookeeper +### Zookeeper * [Zookeeper原理和参数配置](bigdata/zookeeper/ZookeeperOverView.md) * [Zookeeper操作与部署](bigdata/zookeeper/Zookeeper操作与部署.md) -#### schedule +### schedule -**Azkaban** +#### Azkaban * [Azkaban生产实践](bigdata/scheduler/Azkaban生产实践.md) -**DolphinScheduler** +#### DolphinScheduler * [DolphinScheduler快速开始](bigdata/scheduler/DolphinScheduler快速开始.md) -#### olap +### olap **主要核心包含Kudu、Impala相关Olap引擎,生产实践及论文记录等。** -**Hive** +#### Hive * [HiveOverwrite](bigdata/olap/hive/HiveOverwrite.md) * [Hive SQL](bigdata/olap/hive/Hive%20SQL.xmind) -* [Hive调优指南](bigdata/olap/hive/Hive%E8%B0%83%E4%BC%98%E6%8C%87%E5%8D%97.xmind) -* [Hive踩坑解决方案](bigdata/olap/hive/Hive%E8%B8%A9%E5%9D%91%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88.xmind) -* [Hive编程指南读书笔记](bigdata/olap/hive/hive%E7%BC%96%E7%A8%8B%E6%8C%87%E5%8D%97/) -* [Hive Shell Beeline]() +* [Hive调优指南](bigdata/olap/hive/Hive调优指南.xmind) +* [Hive踩坑解决方案](bigdata/olap/hive/Hive踩坑解决方案.xmind) +* [Hive编程指南读书笔记](bigdata/olap/hive/hive编程指南) +* [Hive Shell Beeline](bigdata/olap/hive/Hive%20Shell和Beeline命令.md) * [Hive分区表和分桶表](bigdata/olap/hive/Hive分区表和分桶表.md) -**Presto** +#### Presto * [presto概述](bigdata/olap/presto/PrestoOverview.md) -**clickhouse** +#### clickhouse * [ClickHouse快速入门](bigdata/olap/clickhouse/ClickHouseOverView.md) -* [ClickHouse表引擎](bigdata/olap/clickhouse/ClickHouse%E8%A1%A8%E5%BC%95%E6%93%8E.xmind) +* [ClickHouse表引擎](bigdata/olap/clickhouse/ClickHouse表引擎.xmind) -**Druid** +#### Druid * [Druid概述](bigdata/olap/druid/DruidOverView.md) -**Kylin** +#### Kylin * [Kylin概述](bigdata/olap/kylin/KylinOverWrite.md) -**Kudu** +#### Kudu * [KuduOverView](bigdata/olap/kudu/KuduOverView.md) * [Kudu表和Schema设计](bigdata/olap/kudu/KuduSchemaDesgin.md) * [KuduConfiguration](bigdata/olap/kudu/KuduConfiguration.md) * [Kudu原理分析](bigdata/olap/kudu/Kudu原理分析.md) -* [Kudu踩坑](bigdata/olap/kudu/Kudu%E8%B8%A9%E5%9D%91.xmind) -* [Kudu存储结构架构图](bigdata/olap/kudu/Kudu%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84/) +* [Kudu踩坑](bigdata/olap/kudu/Kudu踩坑.xmind) +* [Kudu存储结构架构图](bigdata/olap/kudu/Kudu存储结构) * [Kudu生产实践](bigdata/olap/kudu/Kudu生产实践.md) -**paper** +##### paper * [Kudu论文阅读](bigdata/olap/kudu/paper/KuduPaper阅读.md) -**Impala** +#### Impala * [ImpalaOverView](bigdata/olap/impala/ImpalaOverView.md) * [ImpalaSQL](bigdata/olap/impala/Impala%20SQL.xmind) * [Impala操作KUDU](bigdata/olap/impala/使用Impala查询Kudu表.md) * [Impala生产实践](bigdata/olap/impala/Impala生产实践.md) -#### graph +### graph **图库相关** -**nebula graph** +#### nebula graph -* [1.简介]() -* [2.快速入门](bigdata/graph/nebula-graph/2.-kuai-su-ru-men.md) +* [1.简介](bigdata/graph/nebula%20graph/1.简介.md) +* [2.快速入门](bigdata/graph/nebula%20graph/2.快速入门.md) -#### tools +### tools **工具集相关,包含计算平台、sql语法Tree等** -**zeppelin** +#### zeppelin * [zeppelin](bigdata/tools/zeppelin/Zeppelin.xmind) -**SQL语法树** +#### SQL语法树 -**calcite** +##### calcite * [ApacheCalciteOverView](bigdata/tools/sqltree/calcite/CalciteOverView.md) -### 数据仓库建设 +## 数据仓库建设 -#### 理论 +### 理论 * [数据建模](datawarehouse/理论/DataModeler.md) -* [数据仓库建模](datawarehouse/%E7%90%86%E8%AE%BA/%E6%95%B0%E6%8D%AE%E4%BB%93%E5%BA%93%E5%BB%BA%E6%A8%A1.xmind) -* [数据仓库](datawarehouse/li-lun/shu-ju-cang-ku-shi-zhan.md) +* [数据仓库建模](datawarehouse/理论/数据仓库建模.xmind) +* [数据仓库](datawarehouse/理论/数据仓库实战.md) -#### 数据中台设计 +### 数据中台设计 -* [数据中台设计](datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/shu-ju-zhong-tai-she-ji.md) +* [数据中台设计](datawarehouse/数据中台模块设计/数据中台设计.md) * [thoth自研元数据平台设计](datawarehouse/数据中台模块设计/thoth自研元数据平台设计.md) -#### 方案实践 +### 方案实践 * [Kudu数据冷备](datawarehouse/方案实践/Kudu数据冷备方案.md) -* [基于Flink的实时数仓建设](datawarehouse/fang-an-shi-jian/ji-yu-flink-de-shi-shi-shu-cang-jian-she.md) +* [基于Flink的实时数仓建设](datawarehouse/方案实践/基于Flink的实时数仓建设.md) -#### 读书笔记 +### 读书笔记 * [数据中台读书笔记](datawarehouse/数据中台读书笔记.md) -### devops +## devops -* [shell命令](devops/Shell%E5%AD%A6%E4%B9%A0.xmind) -* [Linux命令](devops/Linux%E5%AD%A6%E4%B9%A0.xmind) +* [shell命令](devops/Shell学习.xmind) +* [Linux命令](devops/Linux学习.xmind) * [openshift基础命令](devops/k8s-openshift客户端命令使用.md) -### maven +## maven * [maven骨架制作](devops/maven/制作maven骨架.md) * [maven命令](devops/maven/Maven命令.md) -### 服务监控 +## 服务监控 * [Prometheus](servicemonitor/Prometheus/Prometheus实战.md) -### mac +## mac -* [iterm2](mac/iterm2/) +* [iterm2](mac/iterm2) -## 贡献方式 +# 贡献方式 * 欢迎通过[Gitter](https://gitter.im/collabH-repository/community)参与贡献 -* [贡献者指南](CONTRIBUTING.md) +* [贡献者指南](./CONTRIBUTING.md) -## 技术分享 +# 技术分享 -![](img/公众号.jpeg) +![](./img/公众号.jpeg) diff --git a/SUMMARY.md b/SUMMARY.md deleted file mode 100644 index 3f6e4f61..00000000 --- a/SUMMARY.md +++ /dev/null @@ -1,225 +0,0 @@ -# Table of contents - -* [概览](README.md) -* [bigdata](bigdata/README.md) - * [hadoop](bigdata/hadoop/README.md) - * [Hadoop高可用配置](bigdata/hadoop/Hadoop高可用配置.md) - * [HDFS](bigdata/hadoop/hdfs/README.md) - * [HDFS shell 命令]() - * [HDFS集群管理](bigdata/hadoop/hdfs/hdfs-ji-qun-guan-li.md) - * [MapReduce](bigdata/hadoop/mapreduce/README.md) - * [MapReduce数据操作](bigdata/hadoop/MapReduce/MapReduce数据操作.md) - * [MapReduce输入输出剖析](bigdata/hadoop/mapreduce/mapreduce-shu-ru-shu-chu-pou-xi.md) - * [MapReduce的工作原理剖析](bigdata/hadoop/mapreduce/mapreduce-de-gong-zuo-yuan-li-pou-xi.md) - * [分布式处理框架MapReduce](bigdata/hadoop/mapreduce/fen-bu-shi-chu-li-kuang-jia-mapreduce.md) - * [Yarn](bigdata/hadoop/yarn/README.md) - * [YARN快速入门](bigdata/hadoop/Yarn/YARN快速入门.md) - * [Yarn生产配置](bigdata/hadoop/yarn/hadoop-xiang-guan-zu-jian-sheng-chan-ji-bie-pei-zhi.md) - * [scheduler](bigdata/scheduler/README.md) - * [Azkaban生产实践](bigdata/scheduler/Azkaban生产实践.md) - * [系统架构](bigdata/scheduler/DolphinScheduler快速开始.md) - * [zookeeper](bigdata/zookeeper/README.md) - * [ZookeeperOverView](bigdata/zookeeper/ZookeeperOverView.md) - * [Zookeeper操作与部署](bigdata/zookeeper/Zookeeper操作与部署.md) - * [cache](bigdata/cache/README.md) - * [alluxio](bigdata/cache/alluxio/README.md) - * [AlluxioConfiguration](bigdata/cache/alluxio/AlluxioConfiguration.md) - * [AlluxioDeployment](bigdata/cache/alluxio/AlluxioDeployment.md) - * [AlluxioOverView](bigdata/cache/alluxio/AlluxioOverView.md) - * [AlluxioWithEngine](bigdata/cache/alluxio/AlluxioWithEngine.md) - * [collect](bigdata/collect/README.md) - * [canal](bigdata/collect/canal/README.md) - * [CanalOverView](bigdata/collect/canal/CanalOverView.md) - * [debezium](bigdata/collect/debezium/README.md) - * [DebeziumOverView](bigdata/collect/debezium/DebeziumOverView.md) - * [Debezium使用改造](bigdata/collect/debezium/Debezium使用改造.md) - * [Debezium监控系统搭建](bigdata/collect/debezium/Debezium监控系统搭建.md) - * [flume](bigdata/collect/flume/README.md) - * [FlumeOverwrite](bigdata/collect/flume/FlumeOverwrite.md) - * [Flume对接Kafka](bigdata/collect/flume/Flume对接Kafka.md) - * [sqoop](bigdata/collect/sqoop/README.md) - * [SqoopOverview](bigdata/collect/sqoop/SqoopOverview.md) - * [Sqoop实战操作](bigdata/collect/sqoop/Sqoop实战操作.md) - * [datalake](bigdata/datalake/README.md) - * [hudi](bigdata/datalake/hudi/README.md) - * [Flink基于Apache Hudi+Alluxio的数据湖实践]() - * [hudiOverview](bigdata/datalake/hudi/hudiOverview.md) - * [hudiWithFlink](bigdata/datalake/hudi/hudiWithFlink.md) - * [hudiWithSpark](bigdata/datalake/hudi/hudiWithSpark.md) - * [hudi原理分析](bigdata/datalake/hudi/hudi原理分析.md) - * [hudi数据湖实践](bigdata/datalake/hudi/hudi数据湖实践.md) - * [hudi调优实践](bigdata/datalake/hudi/hudi调优实践.md) - * [iceberg](bigdata/datalake/iceberg/README.md) - * [IcebergWithSpark](bigdata/datalake/iceberg/IcebergWithSpark.md) - * [icebergOverview](bigdata/datalake/iceberg/icebergOverview.md) - * [icebergWithFlink](bigdata/datalake/iceberg/icebergWithFlink.md) - * [icebergWithHive](bigdata/datalake/iceberg/icebergWithHive.md) - * [engine](bigdata/engine/README.md) - * [spark](bigdata/engine/spark/README.md) - * [SparkOnDeploy](bigdata/engine/spark/SparkOnDeploy.md) - * [SparkOverwrite](bigdata/engine/spark/SparkOverwrite.md) - * [Spark存储体系](bigdata/engine/spark/Spark存储体系.md) - * [Spark计算引擎和Shuffle](bigdata/engine/spark/Spark计算引擎和Shuffle.md) - * [Spark调优](bigdata/engine/spark/Spark调优.md) - * [Spark调度系统](bigdata/engine/spark/Spark调度系统.md) - * [Spark部署模式](bigdata/engine/spark/Spark部署模式.md) - * [从浅到深剖析Spark源码](bigdata/engine/spark/从浅到深剖析Spark源码.md) - * [practice](bigdata/engine/spark/practice/README.md) - * [Spark实践](bigdata/engine/spark/practice/Spark生产实践.md) - * [spark sql](bigdata/engine/spark/spark-sql/README.md) - * [SparkSQL API]() - * [SparkSQL优化分析](bigdata/engine/spark/spark-sql/sparksql-you-hua-fen-xi.md) - * [spark streaming](bigdata/engine/spark/spark-streaming/README.md) - * [SparkStreaming整合Flume]() - * [源码分析](bigdata/engine/spark/yuan-ma-fen-xi/README.md) - * [Spark内存管理](bigdata/engine/spark/源码分析/Spark内存管理.md) - * [yarn的部署流程](bigdata/engine/spark/yuan-ma-fen-xi/yarn-de-bu-shu-liu-cheng.md) - * [Spark核心对象](bigdata/engine/spark/yuan-ma-fen-xi/spark-he-xin-dui-xiang.md) - * [Spark通信架构](bigdata/engine/spark/yuan-ma-fen-xi/spark-zu-jian-tong-xin.md) - * [Spark调度和Shuffle解析](bigdata/engine/spark/yuan-ma-fen-xi/spark-tiao-du-he-shuffle-jie-xi.md) - * [flink](bigdata/engine/flink/README.md) - * [connector](bigdata/engine/flink/connector/README.md) - * [自定义TableConnector](bigdata/engine/flink/connector/自定义TableConnector.md) - * [core](bigdata/engine/flink/core/README.md) - * [Checkpoint机制剖析](bigdata/engine/flink/core/Checkpoint机制.md) - * [FlinkOverview](bigdata/engine/flink/core/FlinkOverview.md) - * [状态处理API](bigdata/engine/flink/core/StateProcessingAPI.md) - * [TableSQLOverview](bigdata/engine/flink/core/TableSQLOverview.md) - * [feature](bigdata/engine/flink/feature/README.md) - * [Flink1.12新特性](bigdata/engine/flink/feature/Flink1.12新特性.md) - * [Flink1.13新特性](bigdata/engine/flink/feature/Flink1.13新特性.md) - * [Flink1.14新特性](bigdata/engine/flink/feature/Flink1.14新特性.md) - * [monitor](bigdata/engine/flink/monitor/README.md) - * [Flink运维监控](bigdata/engine/flink/monitor/Flink运维监控.md) - * [搭建Flink任务指标监控系统](bigdata/engine/flink/monitor/搭建Flink任务指标监控系统.md) - * [practice](bigdata/engine/flink/practice/README.md) - * [Flink On K8s]() - * [记录一次Flink反压问题](bigdata/engine/flink/practice/记录一次Flink反压问题.md) - * [sourcecode](bigdata/engine/flink/sourcecode/README.md) - * [Flink Kafka Connector源码分析]() - * [FlinkCheckpoint源码分析](bigdata/engine/flink/sourcecode/FlinkCheckpoint源码分析.md) - * [Blink Planner](bigdata/engine/flink/sourcecode/FlinkSQL源码解析.md) - * [FlinkTimerService机制分析](bigdata/engine/flink/sourcecode/FlinkTimerService机制分析.md) - * [Flink内核源码分析](bigdata/engine/flink/sourcecode/Flink内核源码分析.md) - * [Flink窗口实现应用原理](bigdata/engine/flink/sourcecode/Flink窗口实现应用原理.md) - * [Flink网络流控及反压](bigdata/engine/flink/sourcecode/Flink网络流控及反压.md) - * [Flink运行环境源码解析](bigdata/engine/flink/sourcecode/Flink运行环境源码解析.md) - * [StreamSource源解析](bigdata/engine/flink/sourcecode/StreamSource源解析.md) - * [TaskExecutor内存模型原理深入](bigdata/engine/flink/sourcecode/TaskExecutor内存模型原理深入.md) - * [books](bigdata/engine/flink/books/README.md) - * [Flink内核原理与实现](bigdata/engine/flink/books/flink-nei-he-yuan-li-yu-shi-xian/README.md) - * [第11-13章Task执行数据交换等](bigdata/engine/flink/books/Flink内核原理与实现/第11-13章Task执行数据交换等.md) - * [graph](bigdata/graph/README.md) - * [nebula graph](bigdata/graph/nebula-graph/README.md) - * [1.简介]() - * [2.快速入门](bigdata/graph/nebula-graph/2.-kuai-su-ru-men.md) - * [kvstore](bigdata/kvstore/README.md) - * [hbase](bigdata/kvstore/hbase/README.md) - * [HBaseOverview](bigdata/kvstore/hbase/HBaseOverview.md) - * [HBase整合第三方组件](bigdata/kvstore/hbase/HBase整合第三方组件.md) - * [Hbase 过滤器详解](bigdata/kvstore/hbase/Hbase过滤器.md) - * [rocksdb](bigdata/kvstore/rocksdb/README.md) - * [RocksDB On Flink]() - * [RocksdbOverview](bigdata/kvstore/rocksdb/RocksdbOverview.md) - * [Rocksdb组件描述](bigdata/kvstore/rocksdb/Rocksdb组件描述.md) - * [Rocksdb配置](bigdata/kvstore/rocksdb/Rocksdb配置.md) - * [mq](bigdata/mq/README.md) - * [kafka](bigdata/mq/kafka/README.md) - * [Kafka Eagle](bigdata/mq/kafka/Kafka监控.md) - * [Kafka概念](bigdata/mq/kafka/基本概念.md) - * [消费者源码剖析](bigdata/mq/kafka/消费者源码剖析.md) - * [生产者源码剖析](bigdata/mq/kafka/生产者源码剖析.md) - * [kafka权威指南](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/README.md) - * [1.kafka入门](bigdata/mq/kafka/kafka权威指南/1.kafka入门.md) - * [2.安装Kafka](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/2.-an-zhuang-kafka.md) - * [3.Kafka生产者](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/3.kafka-sheng-chan-zhe.md) - * [4.Kafka消费者](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/4.kafka-xiao-fei-zhe.md) - * [5.深入Kafka](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/5.-shen-ru-kafka.md) - * [6.可靠的消息传输](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/6.-ke-kao-de-xiao-xi-chuan-shu.md) - * [7.构建数据管道](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/7.-gou-jian-shu-ju-guan-dao.md) - * [8.跨集群数据镜像](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/8.-kua-ji-qun-shu-ju-jing-xiang.md) - * [9.管理Kafka](bigdata/mq/kafka/kafka-quan-wei-zhi-nan/9.-guan-li-kafka.md) - * [深入理解Kafka](bigdata/mq/kafka/shen-ru-li-jie-kafka/README.md) - * [深入理解Kafka读书笔记](bigdata/mq/kafka/深入理解Kafka/深入理解Kafka读书笔记.md) - * [pulsar](bigdata/mq/pulsar/README.md) - * [1.快速入门](bigdata/mq/pulsar/1.快速入门.md) - * [2.原理与实践](bigdata/mq/pulsar/2.原理与实践.md) - * [olap](bigdata/olap/README.md) - * [clickhouse](bigdata/olap/clickhouse/README.md) - * [ClickHouseOverView](bigdata/olap/clickhouse/ClickHouseOverView.md) - * [druid](bigdata/olap/druid/README.md) - * [概述](bigdata/olap/druid/DruidOverView.md) - * [hive](bigdata/olap/hive/README.md) - * [Hive Shell和Beeline命令]() - * [HiveOverwrite](bigdata/olap/hive/HiveOverwrite.md) - * [Hive分区表和分桶表](bigdata/olap/hive/Hive分区表和分桶表.md) - * [hive编程指南](bigdata/olap/hive/hive-bian-cheng-zhi-nan/README.md) - * [1.基础知识](bigdata/olap/hive/hive编程指南/1.基础知识.md) - * [2.数据类型和文件格式](bigdata/olap/hive/hive-bian-cheng-zhi-nan/2.-shu-ju-lei-xing-he-wen-jian-ge-shi.md) - * [3.HiveQL相关](bigdata/olap/hive/hive-bian-cheng-zhi-nan/3.hiveql-xiang-guan.md) - * [4.索引](bigdata/olap/hive/hive-bian-cheng-zhi-nan/4.-suo-yin.md) - * [5.模式设计](bigdata/olap/hive/hive-bian-cheng-zhi-nan/5.-mo-shi-she-ji.md) - * [7.其他文件格式和压缩方法](bigdata/olap/hive/hive-bian-cheng-zhi-nan/7.-qi-ta-wen-jian-ge-shi-he-ya-suo-fang-fa.md) - * [8.函数开发](bigdata/olap/hive/hive-bian-cheng-zhi-nan/8.-han-shu-kai-fa.md) - * [9.文件和记录格式以及Thrift服务](bigdata/olap/hive/hive-bian-cheng-zhi-nan/9.-wen-jian-he-ji-lu-ge-shi-yi-ji-thrift-fu-wu.md) - * [11.HCatalog](bigdata/olap/hive/hive-bian-cheng-zhi-nan/11.hcatalog.md) - * [10.存储和安全以及锁](bigdata/olap/hive/hive-bian-cheng-zhi-nan/10.-cun-chu-he-an-quan-yi-ji-suo.md) - * [impala](bigdata/olap/impala/README.md) - * [ImpalaOverView](bigdata/olap/impala/ImpalaOverView.md) - * [Impala Script](bigdata/olap/impala/Impala生产实践.md) - * [使用Impala查询Kudu表](bigdata/olap/impala/使用Impala查询Kudu表.md) - * [kudu](bigdata/olap/kudu/README.md) - * [KuduConfiguration](bigdata/olap/kudu/KuduConfiguration.md) - * [KuduOverView](bigdata/olap/kudu/KuduOverView.md) - * [表和模式设计](bigdata/olap/kudu/KuduSchemaDesgin.md) - * [Kudu原理分析](bigdata/olap/kudu/Kudu原理分析.md) - * [Kudu生产实践](bigdata/olap/kudu/Kudu生产实践.md) - * [paper](bigdata/olap/kudu/paper/README.md) - * [KuduPaper阅读](bigdata/olap/kudu/paper/KuduPaper阅读.md) - * [kylin](bigdata/olap/kylin/README.md) - * [概述](bigdata/olap/kylin/KylinOverWrite.md) - * [presto](bigdata/olap/presto/README.md) - * [PrestoOverview](bigdata/olap/presto/PrestoOverview.md) - * [tools](bigdata/tools/README.md) - * [sqltree](bigdata/tools/sqltree/README.md) - * [calcite](bigdata/tools/sqltree/calcite/README.md) - * [快速入门](bigdata/tools/sqltree/calcite/CalciteOverView.md) -* [datawarehouse](datawarehouse/README.md) - * [数据中台读书笔记](datawarehouse/数据中台读书笔记.md) - * [数据中台模块设计](datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/README.md) - * [thoth](datawarehouse/数据中台模块设计/thoth自研元数据平台设计.md) - * [数据中台设计](datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/shu-ju-zhong-tai-she-ji.md) - * [方案实践](datawarehouse/fang-an-shi-jian/README.md) - * [Kudu数据冷备方案](datawarehouse/方案实践/Kudu数据冷备方案.md) - * [基于Flink的实时数仓建设](datawarehouse/fang-an-shi-jian/ji-yu-flink-de-shi-shi-shu-cang-jian-she.md) - * [理论](datawarehouse/li-lun/README.md) - * [数据仓库概念](datawarehouse/理论/DataModeler.md) - * [数据仓库实战](datawarehouse/li-lun/shu-ju-cang-ku-shi-zhan.md) -* [devops](devops/README.md) - * [k8s-openshift客户端命令使用](devops/k8s-openshift客户端命令使用.md) - * [maven](devops/maven/README.md) - * [Maven命令](devops/maven/Maven命令.md) - * [制作maven骨架](devops/maven/制作maven骨架.md) -* [base](base/README.md) - * [algorithm](base/algorithm/README.md) - * [算法题解](base/algorithm/算法题解.md) - * [datastructure](base/datastructure/README.md) - * [数据结构](base/datastructure/数据结构.md) - * [scala](base/scala/README.md) - * [Scala基础](base/scala/ScalaOverView.md) - * [分布式理论](base/fen-bu-shi-li-lun/README.md) - * [Raft一致性算法](base/分布式理论/Raft一致性算法.md) - * [分布式架构](base/fen-bu-shi-li-lun/fen-bu-shi-jia-gou.md) - * [计算机理论](base/ji-suan-ji-li-lun/README.md) - * [LSM存储模型](base/计算机理论/LSM存储模型.md) - * [java](base/java/README.md) - * [并发编程](base/java/bing-fa-bian-cheng/README.md) - * [并发工具类concurrent](base/java/并发编程/并发工具类concurrent.md) - * [认识并发编程](base/java/bing-fa-bian-cheng/ren-shi-bing-fa-bian-cheng.md) -* [mac os](mac/README.md) - * [iterm2](mac/iterm2/README.md) - * [多tab操作](mac/iterm2/iterm2快捷键.md) -* [servicemonitor](servicemonitor/README.md) - * [Prometheus](servicemonitor/prometheus/README.md) - * [安装](servicemonitor/Prometheus/Prometheus实战.md) -* [贡献者指南](CONTRIBUTING.md) diff --git a/base/README.md b/base/README.md deleted file mode 100644 index d4cc1a2a..00000000 --- a/base/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# base - diff --git a/base/algorithm/README.md b/base/algorithm/README.md deleted file mode 100644 index a54b0a7e..00000000 --- a/base/algorithm/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# algorithm - diff --git a/base/datastructure/README.md b/base/datastructure/README.md deleted file mode 100644 index 2f7687bd..00000000 --- a/base/datastructure/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# datastructure - diff --git "a/base/datastructure/\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/base/datastructure/\346\225\260\346\215\256\347\273\223\346\236\204.md" index d7c391e7..ac4d97a4 100644 --- "a/base/datastructure/\346\225\260\346\215\256\347\273\223\346\236\204.md" +++ "b/base/datastructure/\346\225\260\346\215\256\347\273\223\346\236\204.md" @@ -1,28 +1,26 @@ -# 数据结构 +# 数组 -## 数组 - -### 基础概念 +## 基础概念 * 将数据码成一排进行存放。 * 连续的,支持根据`index`快速检索数据(随机访问),但是对于任意位置添加或任意位置删除时间复杂度存在最好和最坏的情况。 -## 栈 +# 栈 * 栈是一种线性结构,相比数组,栈对应的操作是数组的子集,只能从一端添加元素,也只能从一端取出元素,此端为栈顶。 * 后进先出的数据结构,Last In First Out(LIFO) * 程序调用的系统栈,A方法调用B方法调用C方法,AB都在系统栈中,当C方法调用完后,下次调用的是B方法。 * 可以利用数组和队列来实现,利用数组实现可以向数组尾部插入数据,并且从尾部读取数据。 -## 队列 +# 队列 * 队列是一种线性结构,底层可以用动态数组实现,是一种FIFO(先进先出)的数据结构。 -### 循环队列 +## 循环队列 * 利用index来记录队头和队位标示来记录队列的情况。 -![](img/循环队列.jpg) +![](./img/循环队列.jpg) ```java public class LoopQueue implements Queue { @@ -135,7 +133,7 @@ public class LoopQueue implements Queue { } ``` -## 链表 +# 链表 * 线性结构,动态的数据结构不需要处理固定容量的问题,更深入的理解引入(或者指针)。 * 深入理解的递归结构,数据存储在"节点(Node)"中 @@ -278,9 +276,9 @@ public class LinkedList { } ``` -## 二分搜索树(非线性数据结构) +# 二分搜索树(非线性数据结构) -### 特点 +## 特点 * 二叉树具有天然的递归结构 * 每个节点的左子树也是二叉树 @@ -289,9 +287,9 @@ public class LinkedList { * 二分搜索数的每个节点的值都大于其左子树所有节点的值,小于其右子树的所有节点的值。 * 每颗子树也是一个二分搜索树 -### 遍历 +## 遍历 -#### 前序遍历 +### 前序遍历 ```java //根 左 右 @@ -322,9 +320,10 @@ private void preOrderNR() { } } } + ``` -#### 中序遍历 +### 中序遍历 * 中序遍历后得到的元素是顺序排列的 @@ -340,7 +339,7 @@ private void preOrderNR() { } ``` -#### 后序遍历 +### 后序遍历 ```java // 左 右 根 @@ -353,7 +352,7 @@ public void postOrder(Node node) { } ``` -#### 层序遍历 +### 层序遍历 * 根据树的深度去遍历(BFS,广度优先遍历),利用队列一层一层进行遍历,能够更快的找到搜索的元素 * 解决算法设计中的最短路径问题 @@ -377,9 +376,9 @@ public void bfs() { } ``` -### 删除节点 +## 删除节点 -#### 删除左右都有孩子的节点d +### 删除左右都有孩子的节点d * 找到s=min(d->right),s是d的后继 * s->right=delMin(d->right) @@ -523,28 +522,28 @@ public void bfs() { } ``` -## 堆和优先队列 +# 堆和优先队列 -### 优先队列 +## 优先队列 * 普通队列:先进先出;后进后出 * 出队顺序和入队顺序无关;和优先级相关 -### 堆 +## 堆 * 二叉堆是一颗满二叉树 * 堆中某节点的值总是不大于其父节点的值,最大堆 * 父亲节点index是`(childIndex-1)/2`,`LeftChildIndex=parentIndex * 2+1`,`RightChildIndex=parentIndex * 2+2` -## 线段树 +# 线段树 * 每个节点表示的都是一个区间内的数据,子节点都是区间的拆分,直到最终的子叶节点存储的为**一个长度的区间**。 * 线段树不是完全二叉树,也不一定是满二叉树 * 线段树是平衡二叉树,可以用数组来表示。 -## 字典树 +# 字典树 -![](img/trie.jpg) +![](./img/trie.jpg) * 每个节点有26个指向下个节点的指针 @@ -617,78 +616,78 @@ public class Trie { } ``` -## 并查集 +# 并查集 -### 概念 +## 概念 * 由孩子节点指向父亲节点。 * 主要用于解决俩个节点的连接问题。 -### 实现 +## 实现 -#### Quick Union Find +### Quick Union Find -```java + ```java public class QuickUnionFind implements UnionFind { - // 每个数据对应的集合的编号 - private int[] id; + // 每个数据对应的集合的编号 + private int[] id; - public QuickUnionFind(int size) { - id = new int[size]; - // 将集合编号指向自己 - for (int i = 0; i < id.length; i++) { - id[i] = i; - } - } + public QuickUnionFind(int size) { + id = new int[size]; + // 将集合编号指向自己 + for (int i = 0; i < id.length; i++) { + id[i] = i; + } + } - @Override - public int getSize() { - return id.length; - } + @Override + public int getSize() { + return id.length; + } - @Override - public boolean isConnected(int p, int q) { - // p和q所存储的集合编号是否相等 - return find(p) == find(q); - } + @Override + public boolean isConnected(int p, int q) { + // p和q所存储的集合编号是否相等 + return find(p) == find(q); + } - /** - * O n - * - * @param p - * @param q - */ - @Override - public void unionElements(int p, int q) { - int rootP = find(p); - int rootQ = find(q); - if (rootP == rootQ) { - return; - } - for (int i = 0; i < id.length; i++) { - if (id[i] == rootP) { - id[i] = rootQ; - } - } - } + /** + * O n + * + * @param p + * @param q + */ + @Override + public void unionElements(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) { + return; + } + for (int i = 0; i < id.length; i++) { + if (id[i] == rootP) { + id[i] = rootQ; + } + } + } - /** - * 查找元素p对应的编号 - * - * @param p - * @return - */ - private int find(int p) { - if (p < 0 || p >= id.length) { - throw new IllegalArgumentException(); - } - return id[p]; - } + /** + * 查找元素p对应的编号 + * + * @param p + * @return + */ + private int find(int p) { + if (p < 0 || p >= id.length) { + throw new IllegalArgumentException(); + } + return id[p]; + } } -``` + ``` -#### Tree Uinon Find +### Tree Uinon Find ```java public class TreeUnionFind implements UnionFind { @@ -750,7 +749,7 @@ public class TreeUnionFind implements UnionFind { } ``` -#### rank和路径压缩 +### rank和路径压缩 ```java public class TreeUnionFind1 implements UnionFind { @@ -813,22 +812,22 @@ public class TreeUnionFind1 implements UnionFind { } ``` -## AVL树 +# AVL树 -### 平衡二叉树 +## 平衡二叉树 * 对于任意一个节点,左子树和右子树的高度差不能为超过1。 * 标注节点的高度,计算平衡因子,平衡因子为高度差,一旦其绝对值大于等于2则不为平衡二叉树。 -### 左旋转和右旋转 +## 左旋转和右旋转 * 添加节点后,沿着节点向上维护平衡性。 -#### 右旋转 +### 右旋转 * LL:新插入节点在left子树的left -![](img/AVL右旋转.jpg) +![](./img/AVL右旋转.jpg) * 插入的元素在不平衡的节点的左侧的左侧,此时可以使用右旋转保证树的平衡 * 将原本的根节点y顺时针旋转到x的右子树,x为新的根节点。 @@ -854,9 +853,10 @@ return rightRotate(node); } ``` -#### 左旋转 +### 左旋转 * RR:插入的节点在right树的right + * 插入的元素在不平衡的节点的右侧的右侧 ```java @@ -889,13 +889,14 @@ private Node leftRotate(Node y) { x.height=Math.max(getHeight(x.left),getHeight(x.right))+1; return x; } + ``` -#### LR +### LR -!\[]\(./img/AVL LR.jpg) +![](./img/AVL LR.jpg) -!\[]\(./img/AVL LR2.jpg) +![](./img/AVL LR2.jpg) * 新插入的节点在Left子树的Right侧。 * 先对x节点左旋转后,转换为LL,在进行右旋转。 @@ -909,11 +910,13 @@ private Node leftRotate(Node y) { } ``` -#### RL -!\[]\(./img/AVL RL1.jpg) -!\[]\(./img/AVL RL2.jpg) +### RL + +![](./img/AVL RL1.jpg) + +![](./img/AVL RL2.jpg) * 先对x节点进行右旋转,转换成了RR,再进行左旋转。 @@ -925,9 +928,9 @@ private Node leftRotate(Node y) { } ``` -## 红黑树 +# 红黑树 -### 特点 +## 特点 * 每个节点或者是红色或者是黑色 * 根节点是黑色 @@ -1196,58 +1199,59 @@ public class RBTree, V> { System.out.println(); } } + ``` -## 2-3树 +# 2-3树 -### 特点 +## 特点 * 满足二分搜索树的基本性质 * 节点可以存放一个元素或者两个元素 -![](img/2-3树.jpg) +![](./img/2-3树.jpg) -### 红黑树和2-3树 +## 红黑树和2-3树 -![](img/红黑树和2-3树.jpg) +![](./img/红黑树和2-3树.jpg) * 每个三节点都会产生一个红色节点。 -## 哈希表 +# 哈希表 -### 哈希函数的设计 +## 哈希函数的设计 * 整型 * 小范围正整数直接使用 * 小范围负整数进行偏移 -#### 原则 +### 原则 * 一致性:如果a==b,hash(a)==hash(b) * 高效性:计算高效简便 * 均匀性:哈希值均匀分布 -### 解决哈希冲突 +## 解决哈希冲突 -#### 链表地址法 +### 链表地址法 -![](img/哈希冲突链表法.jpg) +![](./img/哈希冲突链表法.jpg) -## BitMap分析1 +# BitMap分析1 -### 实现原理 +## 实现原理 -​ 在java中,一个int类型占32个比特,我们用一个int数组来表示时未new int\[32],总计占用内存32\*32bit,现假如我们用int字节码的每一位表示一个数字的话,那么32个数字只需要一个int类型所占内存空间大小就够了,这样在大数据量的情况下会节省很多内存。 +​ 在java中,一个int类型占32个比特,我们用一个int数组来表示时未new int[32],总计占用内存32*32bit,现假如我们用int字节码的每一位表示一个数字的话,那么32个数字只需要一个int类型所占内存空间大小就够了,这样在大数据量的情况下会节省很多内存。 **具体思路:** - 1个int占4字节即4\*8=32位,那么我们只需要申请一个int数组长度为 int tmp\[1+N/32]即可存储完这些数据,其中N代表要进行查找的总数,tmp中的每个元素在内存在占32位可以对应表示十进制数0\~31,所以可得到BitMap表: + 1个int占4字节即4*8=32位,那么我们只需要申请一个int数组长度为 int tmp[1+N/32]即可存储完这些数据,其中N代表要进行查找的总数,tmp中的每个元素在内存在占32位可以对应表示十进制数0~31,所以可得到BitMap表: -    tmp\[0]:可表示0\~31 +    tmp[0]:可表示0~31 -    tmp\[1]:可表示32\~63 +    tmp[1]:可表示32~63 -    tmp\[2]可表示64\~95 +    tmp[2]可表示64~95     ....... @@ -1257,7 +1261,7 @@ public class RBTree, V> { ![bitMap.jpg](https://sustblog.oss-cn-beijing.aliyuncs.com/blog/2018/algorithm/bitMap.jpg) -  如何判断int数字在tmp数组的哪个下标,这个其实可以通过直接`除以32取整数部分`,例如:`整数8除以32取整等于0,那么8就在tmp[0]上`。另外,我们如何知道了8在tmp\[0]中的32个位中的哪个位,这种情况直接`mod上32`就ok,又如整数8,在tmp\[0]中的第8mod上32等于8,那么整数8就在tmp\[0]中的第八个bit位(从右边数起)。 +  如何判断int数字在tmp数组的哪个下标,这个其实可以通过直接`除以32取整数部分`,例如:`整数8除以32取整等于0,那么8就在tmp[0]上`。另外,我们如何知道了8在tmp[0]中的32个位中的哪个位,这种情况直接`mod上32`就ok,又如整数8,在tmp[0]中的第8mod上32等于8,那么整数8就在tmp[0]中的第八个bit位(从右边数起)。 ```java private long length; @@ -1321,34 +1325,34 @@ public class RBTree, V> { } ``` -### bitMap应用 +## bitMap应用 -1. 看个小场景 > 在3亿个整数中找出不重复的整数,限制内存不足以容纳3亿个整数。 +1. 看个小场景 > 在3亿个整数中找出不重复的整数,限制内存不足以容纳3亿个整数。 - > 对于这种场景我可以采用2-BitMap来解决,即为每个整数分配2bit,用不同的0、1组合来标识特殊意思,如00表示此整数没有出现过,01表示出现一次,11表示出现过多次,就可以找出重复的整数了,其需要的内存空间是正常BitMap的2倍,为:`3亿*2/8/1024/1024=71.5MB`。 + > 对于这种场景我可以采用2-BitMap来解决,即为每个整数分配2bit,用不同的0、1组合来标识特殊意思,如00表示此整数没有出现过,01表示出现一次,11表示出现过多次,就可以找出重复的整数了,其需要的内存空间是正常BitMap的2倍,为:`3亿*2/8/1024/1024=71.5MB`。   **具体的过程如下:** > 扫描着3亿个整数,组BitMap,先查看BitMap中的对应位置,如果00则变成01,是01则变成11,是11则保持不变,当将3亿个整数扫描完之后也就是说整个BitMap已经组装完毕。最后查看BitMap将对应位为11的整数输出即可。 -1. 已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。 +2. 已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。 - > 8位最多99 999 999,大概需要99 999 999个bit,大概10几m字节的内存即可。 (可以理解为从0-99 999 999的数字,每个数字对应一个Bit位,所以只需要99M个Bit==1.2MBytes,这样,就用了小小的1.2M左右的内存表示了所有的8位数的电话)   + > 8位最多99 999 999,大概需要99 999 999个bit,大概10几m字节的内存即可。 (可以理解为从0-99 999 999的数字,每个数字对应一个Bit位,所以只需要99M个Bit==1.2MBytes,这样,就用了小小的1.2M左右的内存表示了所有的8位数的电话)   -### bitMap问题 +## bitMap问题  BitMap 的思想在面试的时候还是可以用来解决不少问题的,然后在很多系统中也都会用到,算是一种不错的解决问题的思路。   但是 BitMap 也有一些局限,因此会有其它一些基于 BitMap 的算法出现来解决这些问题。 -* 数据碰撞。比如将字符串映射到 BitMap 的时候会有碰撞的问题,那就可以考虑用 **Bloom Filter** 来解决,Bloom Filter 使用多个 Hash 函数来减少冲突的概率。 -* 数据稀疏。又比如要存入(10,8887983,93452134)这三个数据,我们需要建立一个 99999999 长度的 BitMap ,但是实际上只存了3个数据,这时候就有很大的空间浪费,碰到这种问题的话,可以通过引入 Roaring BitMap 来解决。 +- 数据碰撞。比如将字符串映射到 BitMap 的时候会有碰撞的问题,那就可以考虑用 **Bloom Filter** 来解决,Bloom Filter 使用多个 Hash 函数来减少冲突的概率。 +- 数据稀疏。又比如要存入(10,8887983,93452134)这三个数据,我们需要建立一个 99999999 长度的 BitMap ,但是实际上只存了3个数据,这时候就有很大的空间浪费,碰到这种问题的话,可以通过引入 Roaring BitMap 来解决。 -## BitMap分析2 +# BitMap分析2 -* bitMap是位图,其实准确的来说,翻译成基于位的映射,举一个例子,有一个无序有界int数组{1,2,5,7},初步估计占用内存4\*4=16字节,这倒是没什么奇怪的,但是假如有10亿个这样的数呢,_10亿_4字节/(1024 \* 1024 \* 1024)=3.72G左右(1GB=1024MB 、1MB=1024KB 、1KB=1024B 、1B=8b)。如果这样的一个大的数据做查找和排序,那估计内存也崩溃了,有人说,这些数据可以不用一次性加载,那就是要存盘了,存盘必然消耗IO。我们提倡的是高性能,这个方案直接不考虑。 +* bitMap是位图,其实准确的来说,翻译成基于位的映射,举一个例子,有一个无序有界int数组{1,2,5,7},初步估计占用内存4*4=16字节,这倒是没什么奇怪的,但是假如有10亿个这样的数呢,*10亿*4字节/(1024 * 1024 * 1024)=3.72G左右(1GB=1024MB 、1MB=1024KB 、1KB=1024B 、1B=8b)。如果这样的一个大的数据做查找和排序,那估计内存也崩溃了,有人说,这些数据可以不用一次性加载,那就是要存盘了,存盘必然消耗IO。我们提倡的是高性能,这个方案直接不考虑。 -### 问题分析 +## 问题分析   如果用BitMap思想来解决的话,就好很多,解决方案如下: @@ -1356,22 +1360,22 @@ public class RBTree, V> { ![img](https://img2018.cnblogs.com/blog/885859/201905/885859-20190517120520403-950577854.png) -* 现在假如10亿的数据所需的空间就是3.72G/32了吧,一个占用32bit的数据现在只占用了1bit,节省了不少的空间,排序就更不用说了,一切显得那么顺利。这样的数据之间没有关联性,要是读取的,你可以用多线程的方式去读取。时间复杂度方面也是O(Max/n),其中Max为byte\[]数组的大小,n为线程大小。 +* 现在假如10亿的数据所需的空间就是3.72G/32了吧,一个占用32bit的数据现在只占用了1bit,节省了不少的空间,排序就更不用说了,一切显得那么顺利。这样的数据之间没有关联性,要是读取的,你可以用多线程的方式去读取。时间复杂度方面也是O(Max/n),其中Max为byte[]数组的大小,n为线程大小。 -### 应用与代码实现 +## 应用与代码实现 -* 一个数怎么快速定位它的索引号,也就是说搞清楚byte\[index]的index是多少,position是哪一位。举个例子吧,例如add(14)。14已经超出byte\[0]的映射范围,在byte\[1]范围之类。那么怎么快速定位它的索引呢。如果找到它的索引号,又怎么定位它的位置呢。Index(N)代表N的索引号,Position(N)代表N的所在的位置号。 +* 一个数怎么快速定位它的索引号,也就是说搞清楚byte[index]的index是多少,position是哪一位。举个例子吧,例如add(14)。14已经超出byte[0]的映射范围,在byte[1]范围之类。那么怎么快速定位它的索引呢。如果找到它的索引号,又怎么定位它的位置呢。Index(N)代表N的索引号,Position(N)代表N的所在的位置号。 ```java Index(N) = N/8 = N >> 3; Position(N) = N%8 = N & 0x07; ``` -#### add(int num) +### add(int num) * 你要向bitmap里add数据该怎么办呢,不用担心,很简单,也很神奇。上面已经分析了,add的目的是为了将所在的位置从0变成1.其他位置不变. -![img](https://upload-images.jianshu.io/upload\_images/1131487-943daf7a2ec3330e.png) +![img](https://upload-images.jianshu.io/upload_images/1131487-943daf7a2ec3330e.png) ```java public void add(int num){ @@ -1386,9 +1390,9 @@ public void add(int num){ } ``` -#### clear(int num) +### clear(int num) -* 对1进行左移,然后取反,最后与byte\[index]作与操作。 +* 对1进行左移,然后取反,最后与byte[index]作与操作。 ![img](https://img2018.cnblogs.com/blog/885859/201905/885859-20190517120639536-80524395.png) @@ -1406,7 +1410,7 @@ public void clear(int num){ } ``` -#### contain(int num) +### contain(int num) ![img](https://img2018.cnblogs.com/blog/885859/201905/885859-20190517120710462-1406459234.png) @@ -1418,7 +1422,7 @@ public boolean contain(int num){ // num/8得到byte[]的index } ``` -#### 完整代码 +### 完整代码 ```java public class BitMap { @@ -1485,18 +1489,18 @@ public class BitMap { } ``` -## 稀疏位图 +# 稀疏位图 位图(bitmap/bitset)在工程中应用广泛(如搜索引擎中的posting list),同时也是面试中一个重要考点 -位图在求交(introsect),求并(union)计算时有很好的性能,但如果数据集分布稀疏时,也会浪费较多空间。例如,当数据取值范围为\[0, 2^32-1],数据个数在1000w左右时,位图占用512MB,其中0的个数只占0.2%,空间浪费相当严重。 +位图在求交(introsect),求并(union)计算时有很好的性能,但如果数据集分布稀疏时,也会浪费较多空间。例如,当数据取值范围为[0, 2^32-1],数据个数在1000w左右时,位图占用512MB,其中0的个数只占0.2%,空间浪费相当严重。 -为了解决空间浪费,显然位图需要进行压缩。Daniel Lemire的[Roaring Bitmaps](https://link.zhihu.com/?target=http%3A//roaringbitmap.org)是众多压缩(稀疏)位图实现中的性能最好的一种: +为了解决空间浪费,显然位图需要进行压缩。Daniel Lemire的[Roaring Bitmaps](https://link.zhihu.com/?target=http%3A//roaringbitmap.org/)是众多压缩(稀疏)位图实现中的性能最好的一种: 1. 支持动态修改位图(静态的位图有其它压缩方式) 2. 利用SIMD加速位图操作 -### Roaring的数据结构 +## Roaring的数据结构 * Roaring Bitmaps明确面向稀疏位图,使用之前你要明确数据的分布/数量,否则Roaring可能占用比未压缩位图更多的空间和耗费更多的操作时间。 * Roaring Bitmaps思路是将取值范围分片,每片大小相同,可容纳2^16个数。根据范围内实际数据的个数/分布选择最紧凑的存储形式: @@ -1504,65 +1508,67 @@ public class BitMap { * 区间内数据较少,且分布零散,则选择使用有序数组 * 区间内数据连续分布,则选择用Run Length Encoding编码 -![img](https://pic2.zhimg.com/80/v2-e7384332a42b13b96a7285d1e04dbee1\_1440w.jpg) +![img](https://pic2.zhimg.com/80/v2-e7384332a42b13b96a7285d1e04dbee1_1440w.jpg) * Roaring将Bitmap从一层的连续存储,转换为一个二级的存储结构 * 第一层称之为Chunk,每个Chunk表示该区间取值范围的base(n2^16, 0<= n < 2^16),如果该取值范围内没有数据则Chunk不会创建 * 第二层称之为Container,会依据数据分布进行创建(Container内的值实际是区间内的offset) * Roaring并不是一种静态数据结构,随着数据的增删,Container选择的存储格式也会随之自动调整 + * 这里另一个重要参数是如何依据数据个数来判定使用位图还是数组。Roaring 选择**4096**作为阈值。 * 当区间内数量少于4096时,数组占用更紧凑;多于4096,则使用位图更经济 -![img](https://pic2.zhimg.com/80/v2-2533c2773f30ed6bd7aee51b071c8e35\_1440w.jpg) +![img](https://pic2.zhimg.com/80/v2-2533c2773f30ed6bd7aee51b071c8e35_1440w.jpg) Roaring 提供O(logn)的查找性能: -* 首先二分查找key值的高16位是否在分片`(chunk)`中 -* 如果分片存在,则查找分片对应的Container是否存在 - * 如果Bitmap Container,查找性能是O(1) - * 其它两种Container,需要进行二分查找 +- 首先二分查找key值的高16位是否在分片`(chunk)`中 +- 如果分片存在,则查找分片对应的Container是否存在 + - 如果Bitmap Container,查找性能是O(1) + - 其它两种Container,需要进行二分查找 -### **Roaring的性能** + +## **Roaring的性能** * Roaring使用了SIMD对操作进行了加速,但是SIMD是一门相当trick的技术,有机会可以单独再分析SIMD对性能的影响(尤其是introset/union)。 * Lucene在5.0中引入了Roaring缓存filter,并对Roaring进行了[性能评测](https://link.zhihu.com/?target=https%3A//www.elastic.co/blog/frame-of-reference-and-roaring-bitmaps),得到了一些有趣的结论。 -#### 遍历 +### 遍历 -![img](https://pic3.zhimg.com/80/v2-52e5fb62c9e5bfa5783ec5f6847905ea\_1440w.jpg) +![img](https://pic3.zhimg.com/80/v2-52e5fb62c9e5bfa5783ec5f6847905ea_1440w.jpg) -* 当数据比较稀疏时,Roaring相比Bitmap性能更好,但弱于int数组 -* 当数据比较密集时,Roaring性能会比Bitmap差,最好的是int数组 +- 当数据比较稀疏时,Roaring相比Bitmap性能更好,但弱于int数组 +- 当数据比较密集时,Roaring性能会比Bitmap差,最好的是int数组 -#### **内存占用** +### **内存占用** -![img](https://pic4.zhimg.com/80/v2-377b8fd58c61be1f08c9a318606e45d7\_1440w.jpg) +![img](https://pic4.zhimg.com/80/v2-377b8fd58c61be1f08c9a318606e45d7_1440w.jpg) -* 数据极其稀疏时,int数组更节省空间,但到了某一拐点后,Roaring会更加节省空间 -* 数据很密集时,int数组占用空间呈线性增长会快速超越Bitmap占用,而Roaring甚至可能由于其紧凑的编码格式(Run Length Encoding)反而比Bitmap更节省空间 +- 数据极其稀疏时,int数组更节省空间,但到了某一拐点后,Roaring会更加节省空间 +- 数据很密集时,int数组占用空间呈线性增长会快速超越Bitmap占用,而Roaring甚至可能由于其紧凑的编码格式(Run Length Encoding)反而比Bitmap更节省空间 -#### **构建时间** +### **构建时间** -![img](https://pic3.zhimg.com/80/v2-57b59eb85c0988bc86c6b21c1d436cba\_1440w.jpg) +![img](https://pic3.zhimg.com/80/v2-57b59eb85c0988bc86c6b21c1d436cba_1440w.jpg) -* 当数据极其稀疏时,位图构建速度远远快过int数组和Roaring -* 当数据超过全量1%时,Roaring成为构建速度更快的格式 +- 当数据极其稀疏时,位图构建速度远远快过int数组和Roaring +- 当数据超过全量1%时,Roaring成为构建速度更快的格式 -## skiplist +# skiplist * skiplist就是普通单向链表的基础上增加了分层索引,可以快速地根据分层索引定位数据。 -![](img/skiplist.jpeg) +![](./img/skiplist.jpeg) -### 查找 +## 查找 * 比如我们要查找key为19的结点,那么我们不需要逐个遍历,而是按照如下步骤: - * 从header出发,从高到低的level进行查找,先索引到9这个结点,发现9 < 19,继续查找(然后在level==2这层),查找到21这个节点,由于21 > 19, 所以结点不往前走,而是level由2降低到1 - * 然后索引到17这个节点,由于17 < 19, 所以继续往后,索引到21这个结点,发现21>19, 所以level由1降低到0 - * 在结点17上,level==0索引到19,查找完毕。 - * 如果在level==0这层没有查找到,那么说明不存在key为19的节点,查找失败 + - 从header出发,从高到低的level进行查找,先索引到9这个结点,发现9 < 19,继续查找(然后在level==2这层),查找到21这个节点,由于21 > 19, 所以结点不往前走,而是level由2降低到1 + - 然后索引到17这个节点,由于17 < 19, 所以继续往后,索引到21这个结点,发现21>19, 所以level由1降低到0 + - 在结点17上,level==0索引到19,查找完毕。 + - 如果在level==0这层没有查找到,那么说明不存在key为19的节点,查找失败 -``` +```c++ # Node节点 //forward declaration template @@ -1630,16 +1636,16 @@ Node *SkipList::search(const K key) const { }; ``` -### 插入 +## 插入 -![](img/skiplist插入1.jpeg) +![](./img/skiplist插入1.jpeg) * 其实插入节点的关键就是找到合适的插入位置,即从所有小于待插入节点key值的节点中,找出最大的那个,所以插入节点的过程如下: - * 查找合适的插入位置,比如上图中要插入key为17的结点,就需要一路查找到12,由于12 < 17,而12的下一个结点19 > 17,因而满足条件 - * 创建新结点,并且产生一个在1\~MAX\_LEVEL之间的随机level值作为该结点的level - * 调整指针指向 + - 查找合适的插入位置,比如上图中要插入key为17的结点,就需要一路查找到12,由于12 < 17,而12的下一个结点19 > 17,因而满足条件 + - 创建新结点,并且产生一个在1~MAX_LEVEL之间的随机level值作为该结点的level + - 调整指针指向 -``` +```c++ template bool SkipList::insert(K key, V value) { Node *update[MAX_LEVEL]; @@ -1678,16 +1684,16 @@ bool SkipList::insert(K key, V value) { }; ``` -### 移除 +## 移除 -![](img/skiplist删除1.jpeg) +![](./img/skiplist删除1.jpeg) * 移除结点其实很简单,就分以下3步: - * 查找到指定的结点,如果没找到则返回 - * 调整指针指向 - * 释放结点空间 + - 查找到指定的结点,如果没找到则返回 + - 调整指针指向 + - 释放结点空间 -``` +```c++ template bool SkipList::remove(K key, V &value) { Node *update[MAX_LEVEL]; @@ -1723,3 +1729,4 @@ bool SkipList::remove(K key, V &value) { return true; }; ``` + diff --git a/base/fen-bu-shi-li-lun/README.md b/base/fen-bu-shi-li-lun/README.md deleted file mode 100644 index 742d3952..00000000 --- a/base/fen-bu-shi-li-lun/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 分布式理论 - diff --git a/base/java/README.md b/base/java/README.md deleted file mode 100644 index a8687f1d..00000000 --- a/base/java/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# java - diff --git a/base/java/bing-fa-bian-cheng/README.md b/base/java/bing-fa-bian-cheng/README.md deleted file mode 100644 index ff845679..00000000 --- a/base/java/bing-fa-bian-cheng/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 并发编程 - diff --git a/base/java/bing-fa-bian-cheng/ren-shi-bing-fa-bian-cheng.md "b/base/java/\345\271\266\345\217\221\347\274\226\347\250\213/\350\256\244\350\257\206\345\271\266\345\217\221\347\274\226\347\250\213.md" similarity index 88% rename from base/java/bing-fa-bian-cheng/ren-shi-bing-fa-bian-cheng.md rename to "base/java/\345\271\266\345\217\221\347\274\226\347\250\213/\350\256\244\350\257\206\345\271\266\345\217\221\347\274\226\347\250\213.md" index 8d2e0edf..9b76a8bd 100644 --- a/base/java/bing-fa-bian-cheng/ren-shi-bing-fa-bian-cheng.md +++ "b/base/java/\345\271\266\345\217\221\347\274\226\347\250\213/\350\256\244\350\257\206\345\271\266\345\217\221\347\274\226\347\250\213.md" @@ -1,8 +1,6 @@ -# 认识并发编程 +# 并发编程缺点 -## 并发编程缺点 - -### 频繁的上下文切换 +## 频繁的上下文切换 * 时间片是CPU分配给各个线程的时间,CPU通过不断切换线程让我觉得多个线程同时执行,时间片一般是几十毫秒。而每次切换时,需要保存当前线程状态,以便能够进行恢复先前状态,而这个切换时非常消耗性能,过于频繁反而无法发挥出多线程编程的优势。通常减少上下文切换可以采用无锁并发编程,CAS算法,使用最少的线程和使用协程。 * 无锁并发编程:可以参照concurrentHashMap锁分段的思想,不同的线程处理不同段的数据,这样在多线程竞争的条件下,可以减少上下文切换的时间。 @@ -10,39 +8,39 @@ * 使用最少线程:避免创建不需要的线程,比如任务很少,但是创建了很多的线程,这样会造成大量的线程都处于等待状态 * 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换 -### 线程安全问题 +## 线程安全问题 * 多线程程序中需要考虑线程安全问题,防止死锁导致程序不可用。 -#### 打破死锁 +### 打破死锁 1. 避免一个线程同时获取多个锁 2. 避免一个线程在锁内部占有多个资源,尽量保证每个锁只占用一个资源 3. 尝试使用定时锁,使用lock.tryLock(timeOut),当超时等待时当前线程不会阻塞; 4. 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况 -## 线程状态转换 +# 线程状态转换 -### Java线程状态 +## Java线程状态 * java程序天生就是一个多线程程序,包含了:(1)分发处理发送给JVM信号的线程;(2)调用对象的finalize方法的线程;(3)清除Reference的线程;(4)main线程,用户程序的入口。 -#### 线程状态 +### 线程状态 * NEW:初始状态,线程被构建,但是没有调用`start`方法 * RUNNABLE:运行状态,Java线程将操作系统中的就绪和运行两种状态笼统地称为"运行中" * BLOCKED:阻塞状态,表示线程阻塞 * WAITING:等待状态,表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) -* TIME\_WAITING:超时等待状态 +* TIME_WAITING:超时等待状态 * TERMINATED:终止状态,表示当前线程已经执行完毕。 -![](../并发编程/img/线程状态切换.jpg) +![](./img/线程状态切换.jpg) -### interrupted +## interrupted * 线程中断可以理解为线程的一个标志位,它表示了一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了一个招呼。其他线程可以调用该线程的interrupt()方法对其进行中断操作,同时该线程可以调用 isInterrupted()来感知其他线程对其自身的中断操作,从而做出响应。另外,同样可以调用Thread的静态方法 interrupted()对当前线程进行中断操作,该方法会清除中断标志位。**需要注意的是,当抛出InterruptedException时候,会清除中断标志位,也就是说在调用isInterrupted会返回false。** -### Join +## Join * join方法可以看做是线程间协作的一种方式,很多时候,一个线程的输入可能非常依赖于另一个线程的输出,这就像两个好基友,一个基友先走在前面突然看见另一个基友落在后面了,这个时候他就会在原处等一等这个基友,等基友赶上来后,就两人携手并进。其实线程间的这种协作方式也符合现实生活。在软件开发的过程中,从客户那里获取需求后,需要经过需求分析师进行需求分解后,这个时候产品,开发才会继续跟进。如果一个线程实例A执行了threadB.join(),其含义是:当前线程A会等待threadB线程终止后threadA才会继续执行。 @@ -52,49 +50,49 @@ public final synchronized void join(long millis, int nanos) public final void join() throws InterruptedException ``` -### sleep +## sleep -#### sleep vs wait +### sleep vs wait 1. sleep()方法是Thread的静态方法,而wait是Object实例方法 2. wait()方法必须要在同步方法或者同步块中调用,也就是必须已经获得对象锁。而sleep()方法没有这个限制可以在任何地方种使用。另外,wait()方法会释放占有的对象锁,使得该线程进入等待池中,等待下一次获取资源。而sleep()方法只是会让出CPU并不会释放掉对象锁; 3. sleep()方法在休眠时间达到后如果再次获得CPU时间片就会继续执行,而wait()方法必须等待Object.notifty/Object.notifyAll通知后,才会离开等待池,并且再次获得CPU时间片才会继续执行。 -### yield +## yield * 一旦执行,它会是当前线程让出CPU,但是,需要注意的是,让出的CPU并不是代表当前线程不再运行了,如果在下一次竞争中,又获得了CPU时间片当前线程依然会继续运行。另外,让出的时间片只会分配**给当前线程相同优先级**的线程。 * 现代操作系统基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程会分配到若干时间片,当前时间片用完后就会发生线程调度,并等待这下次分配。线程分配到的时间多少也就决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要或多或少分配一些处理器资源的线程属性。 -* 在Java程序中,通过一个**整型成员变量Priority**来控制优先级,优先级的范围从1\~10.在构建线程的时候可以通过\*\*setPriority(int)\*\*方法进行设置,默认优先级为5,优先级高的线程相较于优先级低的线程优先获得处理器时间片。需要注意的是在不同JVM以及操作系统上,线程规划存在差异,有些操作系统甚至会忽略线程优先级的设定。 +* 在Java程序中,通过一个**整型成员变量Priority**来控制优先级,优先级的范围从1~10.在构建线程的时候可以通过**setPriority(int)**方法进行设置,默认优先级为5,优先级高的线程相较于优先级低的线程优先获得处理器时间片。需要注意的是在不同JVM以及操作系统上,线程规划存在差异,有些操作系统甚至会忽略线程优先级的设定。 * 另外需要注意的是,`sleep()和yield()`方法,同样都是当前线程会交出处理器资源,而它们不同的是,sleep()交出来的时间片其他线程都可以去竞争,也就是说都有机会获得当前线程让出的时间片。而yield()方法只允许与当前线程具有相同优先级的线程能够获得释放出来的CPU时间片。 -### 守护线程Daemon +## 守护线程Daemon * `守护线程`是一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默地守护一些系统服务,比如垃圾回收线程,JIT线程就可以理解守护线程。与之对应的就是用户线程,用户线程就可以认为是系统的工作线程,它会完成整个系统的业务操作。用户线程完全结束后就意味着整个系统的业务任务全部结束了,因此系统就没有对象需要守护的了,守护线程自然而然就会退。当一个Java应用,只有守护线程的时候,虚拟机就会自然退出。 -## 内存模型和happens-before +# 内存模型和happens-before -### 内存模型 +## 内存模型 -#### 共享变量 +### 共享变量 * 在java程序中所有**实例域,静态域和数组元素**都是放在`堆内存`中(所有线程均可访问到,是可以共享的),而局部变量,方法定义参数和异常处理器参数不会在线程间共享。共享数据会出现线程安全的问题,而非共享数据不会出现线程安全的问题。 -#### JMM抽象结构模型 +### JMM抽象结构模型 * 我们知道CPU的`处理速度和主存的读写速度`不是一个量级的,为了平衡这种巨大的差距,每个CPU都会有缓存。因此,共享变量会先放在主存中,每个线程都有属于自己的工作内存,并且会把位于主存中的共享变量拷贝到自己的工作内存,之后的读写操作均使用位于工作内存的变量副本,并在某个时刻将工作内存的变量副本写回到主存中去。JMM就从抽象层次定义了这种方式,并且JMM决定了一个线程对共享变量的写入何时对其他线程是可见的。 -![](../并发编程/img/JMM内存模型.jpg) +![](./img/JMM内存模型.jpg) -* 如图为JMM抽象示意图,线程A和线程B之间要完成通信的话,要经历如下两步: +* 如图为JMM抽象示意图,线程A和线程B之间要完成通信的话,要经历如下两步: - 1. 线程A从主内存中将共享变量读入线程A的工作内存后并进行操作,之后将数据重新写回到主内存中; - 2. 线程B从主存中读取最新的共享变量 + 1. 线程A从主内存中将共享变量读入线程A的工作内存后并进行操作,之后将数据重新写回到主内存中; + 2. 线程B从主存中读取最新的共享变量 - 从横向去看看,线程A和线程B就好像通过共享变量在进行隐式通信。这其中有很有意思的问题,如果线程A更新后数据并没有及时写回到主存,而此时线程B读到的是过期的数据,这就出现了“脏读”现象。可以通过`同步机制(控制不同线程间操作发生的相对顺序)`来解决或者通过`volatile关键字`使得每次volatile变量都能够强制刷新到主存,从而对每个线程都是可见的。 + 从横向去看看,线程A和线程B就好像通过共享变量在进行隐式通信。这其中有很有意思的问题,如果线程A更新后数据并没有及时写回到主存,而此时线程B读到的是过期的数据,这就出现了“脏读”现象。可以通过`同步机制(控制不同线程间操作发生的相对顺序)`来解决或者通过`volatile关键字`使得每次volatile变量都能够强制刷新到主存,从而对每个线程都是可见的。 -### happens-before规则 +## happens-before规则 -#### happens-before定义 +### happens-before定义 happens-before的概念最初由Leslie Lamport在其一篇影响深远的论文(《Time,Clocks and the Ordering of Events in a Distributed System》)中提出,有兴趣的可以google一下。JSR-133使用happens-before的概念来指定两个操作之间的执行顺序。由于这两个操作可以在一个线程之内,也可以是在不同线程之间。因此,**JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证**(如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见)。具体的定义为: @@ -114,7 +112,7 @@ happens-before的概念最初由Leslie Lamport在其一篇影响深远的论文 2. as-if-serial语义给编写单线程程序的程序员创造了一个幻境:单线程程序是按程序的顺序来执行的。happens-before关系给编写正确同步的多线程程序的程序员创造了一个幻境:正确同步的多线程程序是按happens-before指定的顺序来执行的。 3. as-if-serial语义和happens-before这么做的目的,都是为了在不改变程序执行结果的前提下,尽可能地提高程序执行的并行度。 -#### 具体规则 +### 具体规则 1. 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。 2. 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。 @@ -125,17 +123,19 @@ happens-before的概念最初由Leslie Lamport在其一篇影响深远的论文 7. 程序中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。 8. 对象finalize规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。 -## synchronized介绍 +# synchronized介绍 -### 原理 +## 原理 -#### 对象锁机制 +### 对象锁机制 * 执行同步代码块后首先要先执行**monitorenter**指令,退出的时候**monitorexit**指令。通过分析之后可以看出,使用`Synchronized`进行同步,其关键就是必须要对对象的监视器monitor进行获取,当线程获取monitor后才能继续往下执行,否则就只能等待。而这个获取的过程是**互斥**的,即同一时刻只有一个线程能够获取到monitor。在执行完同步代码块之后紧接着再会去执行一个静态同步方法,而这个方法锁的对象依然就这个类对象,那么这个正在执行的线程还需要获取该锁吗?答案是不必的,从上图中就可以看出来,执行静态同步方法的时候就只有一条monitorexit指令,并没有monitorenter获取锁的指令。这就是**锁的重入性**,即在同一锁程中,线程不需要再次获取同一把锁。Synchronized先天具有重入性。**每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加一,释放锁后就会将计数器减一**。 -* 任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取该对象的监视器才能进入同步块和同步方法,如果没有获取到监视器的线程将会被阻塞在同步块和同步方法的入口处,进入到BLOCKED状态 ![](../并发编程/img/对象监视器同步队列和线程状态的关系.jpg) +* 任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取该对象的监视器才能进入同步块和同步方法,如果没有获取到监视器的线程将会被阻塞在同步块和同步方法的入口处,进入到BLOCKED状态 +![](./img/对象监视器同步队列和线程状态的关系.jpg) + * 任意线程对Object的访问,首先要获得Object的监视器,如果获取失败,该线程就进入同步状态,线程状态变为BLOCKED,当Object的监视器占有者释放后,在同步队列中得线程就会有机会重新获取该监视器。 -#### synchronized的happens-before关系 +### synchronized的happens-before关系 ```java public class MonitorDemo { @@ -153,72 +153,75 @@ public class MonitorDemo { * 如果A happens-before B,则A的执行结果对B可见,并且A的执行顺序先于B。线程A先对共享变量A进行加一,由2 happens-before 5关系可知线程A的执行结果对线程B可见即线程B所读取到的a的值为1。 -#### 锁获取和锁释放内存语义 +### 锁获取和锁释放内存语义 -![](../并发编程/img/线程A写共享变量.jpg) - -* 线程A首先从主内存中读取共享变量a=0的值然后将该变量拷贝到自己的本地内存,进行加1操作后,再将该值刷新到主内存,整个过程即为线程A 加锁-->执行临界区代码-->释放锁相对应的内存语义。 ![](../并发编程/img/线程B读共享变量.png) +![](./img/线程A写共享变量.jpg) +* 线程A首先从主内存中读取共享变量a=0的值然后将该变量拷贝到自己的本地内存,进行加1操作后,再将该值刷新到主内存,整个过程即为线程A 加锁-->执行临界区代码-->释放锁相对应的内存语义。 +![](./img/线程B读共享变量.png) * 线程B获取锁的时候同样会从主内存中共享变量a的值,这个时候就是最新的值1,然后将该值拷贝到线程B的工作内存中去,释放锁的时候同样会重写到主内存中。从整体上来看,线程A的执行结果(a=1)对线程B是可见的,实现原理为:释放锁的时候会将值刷新到主内存中,其他线程获取锁时会强制从主内存中获取最新的值 -### synchronized优化 +## synchronized优化 * (1)CAS操作 (2)Java对象头 -#### CAS操作 +### CAS操作 * CAS比较交换的过程可以通俗的理解为CAS(V,O,N),包含三个值分别为:**V 内存地址存放的实际值;O 预期的值(旧值);N 更新的新值**。当V和O相同时,也就是说旧值和内存中实际的值相同表明该值没有被其他线程更改过,即该旧值O就是目前来说最新的值了,自然而然可以将新值N赋值给V。反之,V和O不相同,表明该值已经被其他线程改过了则该旧值O不是最新版本的值了,所以不能将新值N赋给V,返回V即可。当多个线程使用CAS操作一个变量是,只有一个线程会成功,并成功更新,其余会失败。失败的线程会重新尝试,当然也可以选择挂起线程。 * CAS的实现需要硬件指令集的支撑,在JDK1.5后虚拟机才可以使用处理器提供的**CMPXCHG**指令实现。 -**CAS的问题** +#### CAS的问题 * **ABA问题** 因为CAS会检查旧值有没有变化,这里存在这样一个有意思的问题。比如一个旧值A变为了成B,然后再变成A,刚好在做CAS时检查发现旧值并没有变化依然为A,但是实际上的确发生了变化。解决方案可以沿袭数据库中常用的乐观锁方式,添加一个版本号可以解决。原来的变化路径A->B->A就变成了1A->2B->3C。java这么优秀的语言,当然在java 1.5后的atomic包中提供了`AtomicStampedReference`来解决ABA问题,解决思路就是这样的。 * 使用CAS时非阻塞同步,也就是说不会将线程挂起,会自旋(无非就是一个死循环)进行下一次尝试,如果这里自旋时间过长对性能是很大的消耗。如果JVM能支持处理器提供的pause指令,那么在效率上会有一定的提升。 * 当对一个共享变量执行操作时CAS能保证其原子性,如果对多个共享变量进行操作,CAS就不能保证其原子性。有一个解决方案是利用对象整合多个共享变量,即一个类中的成员变量就是这几个共享变量。然后将这个对象做CAS操作就可以保证其原子性。atomic中提供了`AtomicReference`来保证引用对象之间的原子性。 -#### 对象头 +### 对象头 -![](<../并发编程/img/Mark Word存储结构.png>) +![](./img/Mark%20Word存储结构.png) -* 如图在Mark Word会默认存放hasdcode,年龄值以及锁标志位等信息。 +* 如图在Mark Word会默认存放hasdcode,年龄值以及锁标志位等信息。 - Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:**无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态**,这几个状态会随着竞争情况逐渐升级。**锁可以升级但不能降级**,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。对象的MarkWord变化为下图: ![](<../并发编程/img/Mark Word状态变化.png>) + Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:**无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态**,这几个状态会随着竞争情况逐渐升级。**锁可以升级但不能降级**,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。对象的MarkWord变化为下图: + ![](./img/Mark%20Word状态变化.png) + + -**偏向锁** +#### 偏向锁 -**偏向锁获取** +##### 偏向锁获取 * 当一个线程访问同步块并获取锁时,会在**对象头**和**栈帧中的锁记录**里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程 -**偏向锁的撤销** +##### **偏向锁的撤销** * 偏向锁使用了一种**等到竞争出现才释放锁**的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。 -![](../并发编程/img/偏向锁撤销流程.png) +![](./img/偏向锁撤销流程.png) * 如图,偏向锁的撤销,需要等待**全局安全点**(在这个时间点上没有正在执行的字节码)。它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态;如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word**要么**重新偏向于其他线程,**要么**恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程。 -**关闭偏向锁** +##### 关闭偏向锁 * 偏向锁在Java 6和Java 7里是默认启用的,但是它在应用程序启动几秒钟之后才激活,如有必要可以使用JVM参数来关闭延迟:**-XX:BiasedLockingStartupDelay=0**。如果你确定应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数关闭偏向锁:**-XX:-UseBiasedLocking=false**,那么程序默认会进入轻量级锁状态 -**轻量级锁** +#### 轻量级锁 -**加锁** +##### **加锁** * 线程在执行同步块之前,JVM会先在当前线程的栈桢中**创建用于存储锁记录的空间**,并将对象头中的Mark Word复制到锁记录中,官方称为**Displaced Mark Word**。然后线程尝试使用CAS**将对象头中的Mark Word替换为指向锁记录的指针**。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。 -**解锁** +##### **解锁** * 轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。下图是两个线程同时争夺锁,导致锁膨胀的流程图。 * 因为自旋会消耗CPU,为了避免无用的自旋(比如获得锁的线程被阻塞住了),一旦锁升级成重量级锁,就不会再恢复到轻量级锁状态。当锁处于这个状态下,其他线程试图获取锁时,都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮的夺锁之争。 -![](../并发编程/img/各种锁的对比.png) +![](./img/各种锁的对比.png) -## volatile +# volatile * **被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象。** -### 实现原理 +## 实现原理 ```java instance = new Instancce() //instance是volatile变量 @@ -253,25 +256,25 @@ public class VolatileExample { # 线程A操作writer要先与线程B操作reader ``` -### volatile内存语义 +## volatile内存语义 * 假设线程A先执行writer方法,线程B随后执行reader方法,初始时线程的本地内存中flag和a都是初始状态,下图是线程A执行volatile写后的状态图。 -![](../并发编程/img/volatile内存语义.png) +![](./img/volatile内存语义.png) * 当volatile变量写后,线程中本地内存中共享变量就会置为失效的状态,因此线程B再需要读取从主内存中去读取该变量的最新值。下图就展示了线程B读取同一个volatile变量的内存变化示意图。 -![](../并发编程/img/volatile读内存语义.png) +![](./img/volatile读内存语义.png) -#### 内存语义实现方式 +### 内存语义实现方式 * 内存屏障方式实现,防止指令序列重排序。 -![](../并发编程/img/JMM内存屏障.png) +![](./img/JMM内存屏障.png) * java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。为了实现volatile的内存语义,JMM会限制特定类型的编译器和处理器重排序,JMM会针对编译器制定volatile重排序规则表: -![](../并发编程/img/volatile重排序规则.png) +![](./img/volatile重排序规则.png) * "NO"表示禁止重排序。为了实现volatile内存语义时,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的**处理器重排序**。对于编译器来说,发现一个最优布置来最小化插入屏障的总数几乎是不可能的,为此,JMM采取了保守策略: @@ -290,9 +293,9 @@ public class VolatileExample { **LoadStore屏障**:禁止下面所有的普通写操作和上面的volatile读重排序 -## 三大性质 +# 三大性质 -### 原子性 +## 原子性 * 原子性是指**一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉**。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。我们先来看看哪些是原子操作,哪些不是原子操作,有一个直观的印象: @@ -314,7 +317,7 @@ a = a+1; //4 7. store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送给主内存中以便随后的write操作使用; 8. write(操作):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。 -### 有序性 +## 有序性 ```java public class Singleton { @@ -336,10 +339,11 @@ public class Singleton { * 这条语句实际上包含了三个操作:1.分配对象的内存空间;2.初始化对象;3.设置instance指向刚分配的内存地址。但由于存在重排序的问题,可能有以下的执行顺序: -![](../并发编程/img/有序性.png) +![](./img/有序性.png) * 如果2和3进行了重排序的话,线程B进行判断if(instance==null)时就会为false,而实际上这个instance并没有初始化成功,显而易见对线程B来说之后的操作就会是错得。而**用volatile修饰**的话就可以禁止2和3操作重排序,从而避免这种情况。**volatile包含禁止指令重排序的语义,其具有有序性**。 -### 可见性 +## 可见性 * 可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改。通过之前对[synchronzed](https://juejin.im/post/5ae6dc04f265da0ba351d3ff)内存语义进行了分析,当线程获取锁时会从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。从而,**synchronized具有可见性**。同样的在[volatile分析中](https://juejin.im/post/5ae9b41b518825670b33e6c4),会通过在指令中添加**lock指令**,以实现内存可见性。因此, **volatile具有可见性** + diff --git a/base/ji-suan-ji-li-lun/README.md b/base/ji-suan-ji-li-lun/README.md deleted file mode 100644 index 20b17ed5..00000000 --- a/base/ji-suan-ji-li-lun/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 计算机理论 - diff --git a/base/scala/README.md b/base/scala/README.md deleted file mode 100644 index 17d6ec9d..00000000 --- a/base/scala/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# scala - diff --git a/base/fen-bu-shi-li-lun/fen-bu-shi-jia-gou.md "b/base/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272/\345\210\206\345\270\203\345\274\217\346\236\266\346\236\204.md" similarity index 90% rename from base/fen-bu-shi-li-lun/fen-bu-shi-jia-gou.md rename to "base/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272/\345\210\206\345\270\203\345\274\217\346\236\266\346\236\204.md" index b70905f7..83a2e1a8 100644 --- a/base/fen-bu-shi-li-lun/fen-bu-shi-jia-gou.md +++ "b/base/\345\210\206\345\270\203\345\274\217\347\220\206\350\256\272/\345\210\206\345\270\203\345\274\217\346\236\266\346\236\204.md" @@ -1,46 +1,44 @@ -# 分布式架构 +# 分布式理论 -## 分布式理论 +## ACID -### ACID +![ACID隔离级别](./img/ACID.jpg) -![ACID隔离级别](../分布式理论/img/ACID.jpg) - -### CAP +## CAP * CAP:Consistency:一致性、Availability:可用性、Partition tolerance:分区容错性 * 最多只能同时满足两项 -#### 一致性 +### 一致性 * 多个副本之间是否能够保持`数据一致`的特性,即一个节点数据更新后,另一个节点的数据也应到更新,这是一种`强一致性`。 -#### 可用性 +### 可用性 * 可用性是指系统提供的服务必须一直处于可用状态,对于用户的每一个操作请求都可以在有限的时间内返回结果。 -#### 分区容错性 +### 分区容错性 * 分区容错性约束了一个分布式系统具有的特性,分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障。 -### BASE +## BASE * Basically Availabe(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性),是对CAP中一致性和可用性权衡的结果。 -#### 基本可用 +### 基本可用 * 响应时间上的损失:正常来说响应时0.5s,发生故障时可以维持在2~3s左右 * 功能上的损失:服务降级方式保证基本可用 -#### 软状态 +### 软状态 * 运行系统中的数据存在中间状态,并认为该中间状态的存在不会影响整个系统的可用性,即运行系统在不同节点上的数据副本之间进行数据同步的过程存在延时。 -#### 最终一致性 +### 最终一致性 * 系统中所有的数据副本在经过一段时间的同步后,最终能够达到一个一致的状态。 -**最终一致性的变体** +#### 最终一致性的变体 * 因果一致性:如果进程A在更新完某个数据后通知了进程B,那么进程B之后对该数据的访问都应该能够获取A更新后的最新值,并且如果进程B要对该数据进行更新的话,务必基于进程A更新后的最新值。 * 读己之所写:进程A更新一个数据项之后,它自己总是能够访问到更新过的最新值,而不会看到旧值。 @@ -48,22 +46,22 @@ * 单调读一致性:如果一个进程从系统中读取出一个数据的某值后,那么系统对于该进程后续的任何数据访问都不应该返回更旧的值。 * 单调写一致性:一个系统需要能保证来自同一个进程的写操作被顺序执行。 -## 一致性协议 +# 一致性协议 -### 2PC +## 2PC * Two-Phase Commit二阶段提交为了使分布式系统架构下所有节点在进行事务处理过程中能够保证原子性和一致性设计的算法。 -#### 2PC阶段 +### 2PC阶段 -**阶段一:提交事务请求(投票阶段)** +#### 阶段一:提交事务请求(投票阶段) * 事务询问:协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参入者的响应。 * 执行事务:各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中。 * 各参与者向协调者反馈事务询问的响应 * 如果参与者成功执行了事务操作,那么就反馈给协调者"Yes"响应,表示事务可以执行,如果参与者没有成功执行事务,就返回"NO"响应,表示事务不可以执行。 -**阶段二:执行事务提交** +#### 阶段二:执行事务提交 * 协调者更加各个参与者反馈情况来决定最终是否可以进行事务提交操作,正常来说存在俩种情况。 * 执行事务提交,所有参入者都反馈了"YES"响应 @@ -72,7 +70,7 @@ * 反馈事务提交结果:参与者在完成事务提交后,向协调者发送Ack消息。 * 完成事务:协调者接收到所有参与者反馈的Ack消息后,完成事务。 -![2PC事务提交](../%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/img/2PC%E4%BA%8B%E5%8A%A1%E6%8F%90%E4%BA%A4.jpg) +![2PC事务提交](./img/2PC事务提交.jpg) * 中断事务,有参与者反馈了"No"响应 * 发送回滚请求:协调者向所有参与者发送Rollback请求。 @@ -80,105 +78,105 @@ * 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送Ack消息。 * 中断事务:协调者接收到所有参与者反馈到Ack消息后,完成事务中断。 -![2PC事务提交](../%E5%88%86%E5%B8%83%E5%BC%8F%E7%90%86%E8%AE%BA/img/2PC%E4%BA%8B%E5%8A%A1%E4%B8%AD%E6%96%AD.jpg) +![2PC事务提交](./img/2PC事务中断.jpg) -#### 优缺点 +### 优缺点 * 优点:原理简单,实现方便。 * 缺点:同步阻塞、单点问题、脑裂。 -**同步阻塞** +#### 同步阻塞 * 2PC存在同步阻塞问题,所有参与该事务操作的逻辑都处于阻塞状态,各个参与者都在等待对方返回响应给协调者,然后在等待协调者给相应的事务操作。 -**单点问题** +#### 单点问题 * 协调者存在单点问题,一旦协调者挂掉就会导致事务无法提交并且锁定事务资源的状态。 -**数据不一致性** +#### 数据不一致性 * 在协调者发送commit请求出现宕机导致部分参与者没有接收到commit请求,最终一部分参与者提交了事务,一部分参与者没有提交事务,导致数据不一致性问题。 -**容错不健全** +#### 容错不健全 -### 3PC +## 3PC * Three-Phase Commit三阶段提交,是2PC的改进版,将2PC的"投票阶段"分为CanCommit、PreCommit。 -![3Pc](../分布式理论/img/3PC.jpg) +![3Pc](./img/3PC.jpg) -#### 阶段一:CanCommit +### 阶段一:CanCommit -**事务询问** +#### 事务询问 * 协调者向所有参与者发送一个包含事务内容的`canCommit`请求,询问`是否可以执行事务提交操作`,并开始`等待各参与者的响应` -**各参与者向协调者反馈事务询问的响应** +#### 各参与者向协调者反馈事务询问的响应 * 参与者在接收到协调者的canCommit请求后,正常情况下,如果其自身认为可以顺利执行事务,那么会反馈`Yes`响应,进入预备状态,否则反馈No响应。 -#### 阶段二:PreCommit +### 阶段二:PreCommit * 协调者会根据各个参与者的反馈情况来决定是否可以进行事务的`PreCommit`操作,存在俩种情况。 -**执行事务预提交** +#### 执行事务预提交 * 发送预提交请求:协调者向所有参与者节点发送`preCommit`的请求,并进入`Prepared`阶段。 * 事务预提交:参与者接收到`preCommit`请求后,会执行事务操作,并将`Undo和Redo信息记录到事务日志`中。 * 各个参与者向协调者反馈事务执行的响应:如果参与者成功执行事务操作,那么就会反馈给协调者Ack响应,同时等待最终的指令:提交(commit)或中止(abort) -**中断事务** +#### 中断事务 * 如果有任何一个参与者反馈`No`响应,则中断事务。 * 发送中断请求:协调者向所有参与者节点发送abort请求 * 中断事务:无论是`收到来自协调组的abort请求或是等待协调者请求过程中出现超时`,参与者都会中断事务。 -#### 阶段三:doCommit +### 阶段三:doCommit -**执行提交** +#### 执行提交 * 发送提交请求:假设协调者工作正常,并且它接收到了来自所有参与者的Ack响应,那么它将从"预提交"状态转换到"提交"状态,并向所有参与者发送doCommit请求。 * 事务提交:参与者接收到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。 * 反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack消息 * 完成事务:协调者接收到所有参与者反馈的Ack消息后,完成事务。 -**中断事务** +#### 中断事务 * 发送中断请求:协调者先所有参与者节点发送abort请求 * 事务回滚:参与者接收到abort请求后,会利用其在阶段二中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。 * 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送Ack消息。 * 中断事务:协调者接收到所有参与者的Ack消息后,中断事务。 -#### 优缺点 +### 优缺点 * 优点:降低参与者的`阻塞范围`,添加超时机制 * 缺点:仍然存在`数据一致性`问题 -## Zookeeper和Paxos +# Zookeeper和Paxos -### Zookeeper +## Zookeeper -#### 介绍 +### 介绍 * 组成Zookeeper集群的每台机器都会在`内存中维护当前的服务器状态`,并且`每台机器之间都相互保持通信`,只有`存在半数以上`的服务器才能对外提供服务。 * 顺序访问,每个客户端的每个更新请求,Zookeeper都会为其分配一个`全局唯一的递增编号`,其反映了所有事务操作的先后顺序。 * 高性能,Zookeeper将`全量数据存储在内存中`,并且直接服务于客户端的所有非事务请求,因此适用于`以读操作为主`的应用场景。 -### ZAB协议 +## ZAB协议 * Zookeeper Atomic Broadcast(ZAB,Zookeeper原子消息广播协议)的协议作为数据一致性的核心算法。 * Zookeeper使用一个`单一的主进程来接收并处理客户端的所有事务请求`,并采用ZAB的原子广播协议,将服务器数据的状态变更以事务`Proposal`的形式广播到所有的副本进程上去。 * ZAB协议的主备模型保证在同一时刻集群只能有一个`主进程来广播服务器的状态的变更`,ZAB还会保证一个状态变更以及被处理后,所有Follower的状态变更都应该提前被处理掉了。 -#### ZAB核心概念 +### ZAB核心概念 ``` 所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为Leader服务器,而余下的其他服务器则称为Follower服务器。Leader服务器负责将一个客户端事务请求转换成一个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器。之后Leader服务器需要等到所有Follower服务器的反馈,一旦超过半数的Follower服务器进行正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前一个Proposal进行提交。 ``` -#### ZAB基本模式 +### ZAB基本模式 -**崩溃恢复** +#### 崩溃恢复 * 当整个服务框架启动过程中或Leader服务器`出现网络中断、崩溃退出或重启等异常情况时`,ZAB协议就会进入恢复模式并选举`产生新的Leader服务器`,当选举产生了新的Leader服务器,`同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后`,ZAB协议就退出恢复模式。 * 让Leader选举算法保证新选举出来的Leader服务器拥有集群中`所有机器最高编号(ZXID最大)的事务Proposal`或者最大的(SID(Myid)),可以保证新选举出来的Leader一定具有所有已经提交的事务Proposal,这样也省去Leader服务器检查Proposal的提交和丢弃工作。 @@ -186,23 +184,23 @@ * ZXID是64位其中低32为单调递增的计数器高32位标示Leader周期epoch的编号,每产生一个Leader服务器就会从本地日志最大事务Proposal的ZXID并从中解析出来epoch,并将低32位置0. * 如果数据只在Leader接收后立刻发送崩溃恢复就可能导致这部分数据丢失 -**消息广播** +#### 消息广播 -![](../分布式理论/img/消息广播.jpg) +![](./img/消息广播.jpg) -* 当一台服务器启动加入集群时,此时集群已经存在Leader服务器在负责进行消息广播,新加入的服务器会自觉进行数据恢复模式\`:找到Leader所在服务器,并与其进行数据同步,然后一切参与到消息广播流程中。 +* 当一台服务器启动加入集群时,此时集群已经存在Leader服务器在负责进行消息广播,新加入的服务器会自觉进行数据恢复模式`:找到Leader所在服务器,并与其进行数据同步,然后一切参与到消息广播流程中。 * 消息广播是类似于2PC的过程,针对客户端的事务请求,Leader服务器会使其`生成对应的事务Proposal`,并将其发送到集群中其余所有的机器,然后在分别收集各自的选票,最后进行事务提交。 * 与2PC的不同是消息广播移除了中断逻辑,Follower服务器要么正常反馈Leader提出的事务Proposal,要么就抛弃Leader服务器,`移除中断逻辑后在Leader服务器收到半数Follower服务器的ACK后就可以提交事务`。 * 移除中断逻辑后无法解决数据一致性问题,因此引入了崩溃恢复模式来解决问题,消息广播协议基于`具有FIFO特性的TCP协议来进行网络通信`,因此可以保证消息广播过程中消息接收与发送的顺序性。Leader服务器会为每个事务请求生成对应的`Proposal来进行广播`,并且在广播事务Proposal之前,Leader服务器会首先为其分配一个全局递增的唯一ID,称为ZXID。ZAB协议需要保证每个消息严格的因果关系,因此必须将每一个事务Proposal按照其ZXID的先后顺序来进行排序和处理。 * 在消息广播过程中,Leader服务器会为每一个Follower服务器都各自分配一个单独的队列,然后将需要广播的事务Proposal依次放入队列汇总,并且根据FIFO策略进行消息发送,每个Follower在接收到这个事务Proposal之后,`会首先将其以事务日志的形式写入本地磁盘中,并且在成功写入后反馈给Leader服务器一个ACK响应`。当Leader服务器收到半数之上的Follower的ack响应后就会广播一个Commit消息给所有的Follower服务器以通知其进行事务提交,同时Leader自身也会完成对事务的提交。 -![](../分布式理论/img/消息广播底层原理.jpg) +![](./img/消息广播底层原理.jpg) -#### ZAB算法阶段 +### ZAB算法阶段 -![](../分布式理论/img/ZAB算法.jpg) +![](./img/ZAB算法.jpg) -### ZAB与Paxos算法的联系与区别 +## ZAB与Paxos算法的联系与区别 * 两者都存在一个类似于Leader进程的角色,由其负责协调多个Follower进程的运行。 * Leader进程都会等待半数的Follower作出正确的反馈后,才会将一个提案进行提交。 @@ -213,6 +211,6 @@ 在Paxos的基础上,ZAB协议额外添加了一个同步阶段,在同步阶段之前,ZAB协议也存在一个和Paxos算法中的读阶段类似的过程,称为发现(Discovery)阶段。在同步阶段汇总,新的Leader会确保存在过半的Follower已经提交了之前Leader周期中所有事务Proposal。同步阶段的引入可以有效的保证Leader在新的周期提出事务Proposal之前,所有的进程都已经完成了对之前所有事务Proposal的提交。一旦同步阶段完成后,ZAB就会执行和Paxos算法类似的写阶段。 ``` -### ZAB和Paxos本质区别 +## ZAB和Paxos本质区别 -* 设计目标不同,ZAB协议主要用于构建一个高可用的分布式数据主备系统,例如Zookeeper,而Paxos算法则是用于构建一个分布式的一致性状态机系统。 +* 设计目标不同,ZAB协议主要用于构建一个高可用的分布式数据主备系统,例如Zookeeper,而Paxos算法则是用于构建一个分布式的一致性状态机系统。 \ No newline at end of file diff --git a/bigdata/README.md b/bigdata/README.md deleted file mode 100644 index b68d9ae5..00000000 --- a/bigdata/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# bigdata - diff --git a/bigdata/cache/README.md b/bigdata/cache/README.md deleted file mode 100644 index 37b23231..00000000 --- a/bigdata/cache/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# cache - diff --git a/bigdata/cache/alluxio/README.md b/bigdata/cache/alluxio/README.md deleted file mode 100644 index 9b2cd8cf..00000000 --- a/bigdata/cache/alluxio/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# alluxio - diff --git a/bigdata/collect/README.md b/bigdata/collect/README.md deleted file mode 100644 index ac1cc6d3..00000000 --- a/bigdata/collect/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# collect - diff --git a/bigdata/collect/canal/README.md b/bigdata/collect/canal/README.md deleted file mode 100644 index e71c9bc6..00000000 --- a/bigdata/collect/canal/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# canal - diff --git a/bigdata/collect/debezium/DebeziumOverView.md b/bigdata/collect/debezium/DebeziumOverView.md index 68b8dcf3..8fc8f664 100644 --- a/bigdata/collect/debezium/DebeziumOverView.md +++ b/bigdata/collect/debezium/DebeziumOverView.md @@ -1,21 +1,19 @@ -# DebeziumOverView - -## 架构设计 +# 架构设计 * source connector从debezium发送消息到kafka * sink connector将记录从kafka中发送到其他系统接收器 -![Debezium Architecture](https://debezium.io/documentation/reference/1.4/\_images/debezium-architecture.png) +![Debezium Architecture](https://debezium.io/documentation/reference/1.4/_images/debezium-architecture.png) -### Debezium Server +## Debezium Server * 使用Debezium Server部署Debezium,这个Server是可配置的。 -![Debezium Architecture](https://debezium.io/documentation/reference/1.4/\_images/debezium-server-architecture.png) +![Debezium Architecture](https://debezium.io/documentation/reference/1.4/_images/debezium-server-architecture.png) -## Configuration +# Configuration -### Avro序列化 +## Avro序列化 * Debezium的序列化根据Kafka配置的序列化方式 @@ -24,18 +22,18 @@ key.converter=org.apache.kafka.connect.json.JsonConverter value.converter=org.apache.kafka.connect.json.JsonConverter ``` -#### 关闭schemas的传递 +### 关闭schemas的传递 * `key.converter.schemas.enable`:设置为false关闭key的schema传递 * `value.converter.schemas.enable`:设置为false关闭value的schema传递 -#### Avro序列化的优势 +### Avro序列化的优势 * Avro二进制格式是紧凑和有效的,并且可以保证每条记录的数据格式的正确。 * 这对于Debezium连接器非常重要,它将动态生成每条记录的模式,以匹配已更改的数据库表的结构。 * 变更的事件记录写入相同的topic可能有相同的schema不同的版本,avro更容易适用于变更的记录schema -#### APIicurio API Schema Registry使用 +### APIicurio API Schema Registry使用 * 使用avro序列化必须部署一个schema registry为了管理Avro消息schemas和他们的版本。 @@ -52,7 +50,7 @@ value.converter.apicurio.registry.url=http://apicurio:8080/api value.converter.apicurio.registry.global-id=io.apicurio.registry.utils.serde.strategy.GetOrCreateIdStrategy ``` -#### Confluent Schema Registry +### Confluent Schema Registry * debezium connector配置 @@ -74,7 +72,7 @@ docker run -it --rm --name schema-registry \ -p 8181:8181 confluentinc/cp-schema-registry ``` -### Topic路由 +## Topic路由 * 每个kafka记录包含一个数据变更事件有一个默认定义的topic。如果需要重新发送到其他topic需要在在记录到kafkaConnector之前指定一个topic。 * debezium提供的topic路由是单独消息转换,配置这个转换器在debezium的kafkaConnect配置中 @@ -82,12 +80,12 @@ docker run -it --rm --name schema-registry \ * 一个解析到目标主题的表达式 * 如何确保重新路由到目标主题的记录之间有唯一的密钥 -#### Use case +### Use case * 默认debezium提供的topic的名字为:`debeziumname.database.tablename` -* 对于PG的分区表关闭添加唯一键行为:`key.enforce.uniqueness=false` +* 对于PG的分区表关闭添加唯一键行为:`key.enforce.uniqueness=false` -#### reroute配置 +### reroute配置 ```properties transforms=Reroute @@ -97,23 +95,23 @@ transforms.Reroute.topic.regex=(.*)customers_shard(.*) transforms.Reroute.topic.replacement=$1customers_all_shards ``` -**topic.regex** +#### topic.regex * 指定将转换应用于每个更改事件记录的正则表达式,以确定是否应将其路由到特定主题。 -**topic.replacement** +#### topic.replacement * 指定表示目标主题名称的正则表达式。转换将每个匹配的记录路由到由该表达式标识的主题。 -#### 满足唯一键 +### 满足唯一键 * 一个debezium变更事件的key使用的是表的列作为表的主键,对于分库的表来说可能debezium的key是重复的。为了满足每个相同的key发送到相同的partition,topic路由转换插入一个字段`__dbz__physicalTableIdentifier`来保证,其默认为目标topic名称。 * `transforms.Reroute.key.field.name=shard_id`设置其他唯一key名称。 * 如果表包含全局唯一键,并且不需要更改键结构,则可以设置`key.enforcement.uniqueness`选项为false -### 新记录状态提取 +## 新记录状态提取 -#### 改变时间结构 +### 改变时间结构 * Debezium生成的数据变更事件是一个复杂的结构,每个事件包含三个部分 * Metadata:进行更改的操作\数据源信息比如database、table名称/变更时间/可选的转换信息等 @@ -138,7 +136,7 @@ transforms.Reroute.topic.replacement=$1customers_all_shards } ``` -#### 配置 +### 配置 * [相关配置](https://debezium.io/documentation/reference/1.3/configuration/event-flattening.html) @@ -164,7 +162,7 @@ transforms.unwrap.add.fields=table,lsn } ``` -* `transforms.unwrap.add.fields` 在metadata中添加字段,包括`table`/`lsn`等 +* `transforms.unwrap.add.fields` 在metadata中添加字段,包括`table`/`lsn`等 ```json { @@ -177,19 +175,19 @@ transforms.unwrap.add.fields=table,lsn } ``` -### 自定义Topic自动创建 +## 自定义Topic自动创建 * topic动态为offsets,connector status,config storge和history topics创建内部topics。目标topics为了捕获表将会动态创建一个默认的配置当kafka brokers的配置`auto.create.topics.enable`设置为`true`时 * 发送`POST`请求的请求体配置 -#### 配置Kafka Connect +### 配置Kafka Connect ```properties # 开启动态topic创建 auto.topic.creation.enable = true ``` -**默认group配置** +#### 默认group配置 ```json { @@ -204,7 +202,7 @@ auto.topic.creation.enable = true } ``` -**自定义group配置** +#### 自定义group配置 ```json { @@ -229,7 +227,7 @@ auto.topic.creation.enable = true } ``` -**注册自定义group** +#### 注册自定义group ```json { @@ -241,7 +239,7 @@ auto.topic.creation.enable = true } ``` -**完整的配置** +#### 完整的配置 ```json { @@ -265,19 +263,19 @@ auto.topic.creation.enable = true } ``` -## Connector +# Connector -### MySQL Connector +## MySQL Connector * 由于通常将MySQL设置为在指定的时间段后清除binlog,因此MySQL连接器会对每个数据库执行初始的一致快照。 MySQL连接器从创建快照的位置读取binlog。 * 当连接器崩溃或正常停止后重新启动时,连接器从特定位置(即从特定时间点)开始读取binlog。连接器通过读取数据库`历史Kafka topic`并`解析所有DDL语句`,重新构建了此时存在的表结构,直到binlog中连接器开始的位置。 * database的history topic仅提供给connector使用,connnctor可以选择性的生成schema变更事件对于不同的topic提供给应用程序使用。 -#### 执行database快照 +### 执行database快照 * 当连接器第一次启动时,它会对你的数据库执行一个初始一致的快照。 -**初始化快照执行流程** +#### 初始化快照执行流程 1. 获取阻止其他数据库客户端写操作的全局读锁。 2. 使用可重复读语义启动事务,以确保事务内的所有后续读取都针对一致快照执行。 @@ -289,20 +287,20 @@ auto.topic.creation.enable = true 8. 提交事务 9. 在连接器偏移中记录完成的快照。 -**connector初始化过程失败** +#### connector初始化过程失败 * 如果连接器发生故障、停止或在生成初始快照时重新平衡,则连接器在重新启动后将创建一个新快照。一旦初始快照完成,Debezium MySQL连接器就会从binlog中的相同位置重新启动,这样它就不会错过任何更新。 * 如果connector停止或者中断过程,mysql的binlog被清空,那么就会在发生一次初始化快照的过程。 -**全局读锁** +#### 全局读锁 * 有些环境不允许全局读锁。如果Debezium MySQL连接器检测到不允许全局读锁,连接器将使用表级锁,并使用该方法执行快照。 -#### 暴露Schema变更 +### 暴露Schema变更 * 通过配置Debezium MySQL连接器去提供schema变更事件,其中包括应用于MySQL服务器数据库的所有DDL语句这个连接器写入这些事件到名为"serverName"的kafka topic中,serverName通过`database.server.name`配置 -**schema变更topic结构** +#### schema变更topic结构 * schema变更topic的key @@ -443,9 +441,9 @@ auto.topic.creation.enable = true } ``` -### 事件 +## 事件 -#### create event +### create event ```json { @@ -631,7 +629,7 @@ auto.topic.creation.enable = true } ``` -#### update event +### update event ```json { @@ -672,7 +670,7 @@ auto.topic.creation.enable = true } ``` -#### delete event +### delete event ```json { @@ -707,72 +705,72 @@ auto.topic.creation.enable = true } ``` -**墓碑event** +#### 墓碑event -* 当一行被删除时,delete事件值仍然在日志压缩中工作,因为Kafka可以删除所有具有相同键的早期消息。然而,Kafka删除所有具有相同密钥的消息,消息值必须为空。为了实现这一点,Debezium MySQL连接器发出delete事件后,连接器发出一个特殊的tombstone事件,该事件具有相同的键,但为空值。 +* 当一行被删除时,delete事件值仍然在日志压缩中工作,因为Kafka可以删除所有具有相同键的早期消息。然而,Kafka删除所有具有相同密钥的消息,消息值必须为空。为了实现这一点,Debezium MySQL连接器发出delete事件后,连接器发出一个特殊的tombstone事件,该事件具有相同的键,但为空值。 -### 映射数据类型 +## 映射数据类型 * **literal type** : 值如何表示使用Kafka连接schema类型 * **semantic type** : Kafka连接模式如何捕获字段(schema名)的含义 -| MySQL type | Literal type | Semantic type | -| ------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `BOOLEAN, BOOL` | `BOOLEAN` | _n/a_ | -| `BIT(1)` | `BOOLEAN` | _n/a_ | -| `BIT(>1)` | `BYTES` | `io.debezium.data.Bits`The `length` schema parameter contains an integer that represents the number of bits. The `byte[]` contains the bits in _little-endian_ form and is sized to contain the specified number of bits.example (where n is bits)`numBytes = n/8 + (n%8== 0 ? 0 : 1)` | -| `TINYINT` | `INT16` | _n/a_ | -| `SMALLINT[(M)]` | `INT16` | _n/a_ | -| `MEDIUMINT[(M)]` | `INT32` | _n/a_ | -| `INT, INTEGER[(M)]` | `INT32` | _n/a_ | -| `BIGINT[(M)]` | `INT64` | _n/a_ | -| `REAL[(M,D)]` | `FLOAT32` | _n/a_ | -| `FLOAT[(M,D)]` | `FLOAT64` | _n/a_ | -| `DOUBLE[(M,D)]` | `FLOAT64` | _n/a_ | -| `CHAR(M)]` | `STRING` | _n/a_ | -| `VARCHAR(M)]` | `STRING` | _n/a_ | -| `BINARY(M)]` | `BYTES` or `STRING` | _n/a_Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | -| `VARBINARY(M)]` | `BYTES` or `STRING` | _n/a_Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | -| `TINYBLOB` | `BYTES` or `STRING` | _n/a_Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | -| `TINYTEXT` | `STRING` | _n/a_ | -| `BLOB` | `BYTES` or `STRING` | _n/a_Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | -| `TEXT` | `STRING` | _n/a_ | -| `MEDIUMBLOB` | `BYTES` or `STRING` | _n/a_Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | -| `MEDIUMTEXT` | `STRING` | _n/a_ | -| `LONGBLOB` | `BYTES` or `STRING` | _n/a_Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | -| `LONGTEXT` | `STRING` | _n/a_ | -| `JSON` | `STRING` | `io.debezium.data.Json`Contains the string representation of a `JSON` document, array, or scalar. | -| `ENUM` | `STRING` | `io.debezium.data.Enum`The `allowed` schema parameter contains the comma-separated list of allowed values. | -| `SET` | `STRING` | `io.debezium.data.EnumSet`The `allowed` schema parameter contains the comma-separated list of allowed values. | -| \`YEAR\[(2 | 4)]\` | `INT32` | -| `TIMESTAMP[(M)]` | `STRING` | `io.debezium.time.ZonedTimestamp`In [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format with microsecond precision. MySQL allows `M` to be in the range of `0-6`. | - -### 表对应的topic +| MySQL type | Literal type | Semantic type | +| :------------------ | :------------------ | :----------------------------------------------------------- | +| `BOOLEAN, BOOL` | `BOOLEAN` | *n/a* | +| `BIT(1)` | `BOOLEAN` | *n/a* | +| `BIT(>1)` | `BYTES` | `io.debezium.data.Bits`The `length` schema parameter contains an integer that represents the number of bits. The `byte[]` contains the bits in *little-endian* form and is sized to contain the specified number of bits.example (where n is bits)`numBytes = n/8 + (n%8== 0 ? 0 : 1)` | +| `TINYINT` | `INT16` | *n/a* | +| `SMALLINT[(M)]` | `INT16` | *n/a* | +| `MEDIUMINT[(M)]` | `INT32` | *n/a* | +| `INT, INTEGER[(M)]` | `INT32` | *n/a* | +| `BIGINT[(M)]` | `INT64` | *n/a* | +| `REAL[(M,D)]` | `FLOAT32` | *n/a* | +| `FLOAT[(M,D)]` | `FLOAT64` | *n/a* | +| `DOUBLE[(M,D)]` | `FLOAT64` | *n/a* | +| `CHAR(M)]` | `STRING` | *n/a* | +| `VARCHAR(M)]` | `STRING` | *n/a* | +| `BINARY(M)]` | `BYTES` or `STRING` | *n/a*Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | +| `VARBINARY(M)]` | `BYTES` or `STRING` | *n/a*Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | +| `TINYBLOB` | `BYTES` or `STRING` | *n/a*Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | +| `TINYTEXT` | `STRING` | *n/a* | +| `BLOB` | `BYTES` or `STRING` | *n/a*Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | +| `TEXT` | `STRING` | *n/a* | +| `MEDIUMBLOB` | `BYTES` or `STRING` | *n/a*Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | +| `MEDIUMTEXT` | `STRING` | *n/a* | +| `LONGBLOB` | `BYTES` or `STRING` | *n/a*Either the raw bytes (the default), a base64-encoded String, or a hex-encoded String, based on the [binary handling mode](https://debezium.io/documentation/reference/1.3/connectors/mysql.html#mysql-property-binary-handling-mode) setting | +| `LONGTEXT` | `STRING` | *n/a* | +| `JSON` | `STRING` | `io.debezium.data.Json`Contains the string representation of a `JSON` document, array, or scalar. | +| `ENUM` | `STRING` | `io.debezium.data.Enum`The `allowed` schema parameter contains the comma-separated list of allowed values. | +| `SET` | `STRING` | `io.debezium.data.EnumSet`The `allowed` schema parameter contains the comma-separated list of allowed values. | +| `YEAR[(2|4)]` | `INT32` | `io.debezium.time.Year` | +| `TIMESTAMP[(M)]` | `STRING` | `io.debezium.time.ZonedTimestamp`In [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format with microsecond precision. MySQL allows `M` to be in the range of `0-6`. | + +## 表对应的topic * 一个表对应一个topic,格式为`serverName.databaseName.tableName` -### 支持的MySQL拓扑 +## 支持的MySQL拓扑 -#### _Standalone_ +### *Standalone* * 当使用一个MySQL服务器时,该服务器必须启用binlog(可选启用GTIDs),以便Debezium MySQL连接器可以监视服务器。这通常是可以接受的,因为二进制日志还可以用作增量备份。在这种情况下,MySQL连接器总是连接并遵循这个独立的MySQL服务器实例。 -#### _Primary and replica_ +### *Primary and replica* * Debezium MySQL连接器可以跟随一个主服务器或一个副本(如果该副本启用了binlog),但是连接器只能看到集群中对该服务器可见的更改。通常,除了多主拓扑之外,这不是问题。 * 连接器记录其在服务器binlog中的位置,这在集群中的每个服务器上都是不同的。因此,连接器将只需要遵循一个MySQL服务器实例。如果该服务器发生故障,必须重新启动或恢复该服务器,连接器才能继续运行。 -#### _High available clusters_ +### *High available clusters* * MySQL有各种各样的高可用性解决方案,它们使其更容易容忍,几乎可以立即从问题和故障中恢复。大多数HA MySQL集群使用GTIDs,以便副本能够跟踪任何主服务器上的所有更改。 -#### _Multi-primary_ +### *Multi-primary* -#### _Hosted_ +### *Hosted* -### 配置MYSQL服务端 +## 配置MYSQL服务端 -#### 为Debezium创建一个MySQL用户 +### 为Debezium创建一个MySQL用户 ```sql -- 创建用户 @@ -785,7 +783,7 @@ GRANT SELECT, RELOAD, SHOW DATABASES, REPLICATION SLAVE, REPLICATION CLIENT ON * FLUSH PRIVILEGES; ``` -#### 开启binlog +### 开启binlog ```sql -- 检查log-bin是否开启 @@ -802,7 +800,7 @@ binlog_row_image = FULL expire_logs_days = 10 ``` -#### 开启全局事务标识 +### 开启全局事务标识 * 全局事务标识符(GTIDs)唯一地标识集群内服务器上发生的事务。虽然Debezium MySQL连接器不需要GTIDs,但使用GTIDs可以简化复制,并允许您更容易地确认主服务器和副本服务器是否一致。 @@ -813,33 +811,33 @@ enforce_gtid_consistency=ON show global variables like '%GTID%'; ``` -#### 设置会话超时时间 +### 设置会话超时时间 ```properties interactive_timeout= wait_timeout= ``` -#### 开启查询log event +### 开启查询log event ```properties binlog_rows_query_log_events=ON ``` -### 部署Mysql connector +## 部署Mysql connector -#### 安装Mysql Connector +### 安装Mysql Connector -**必需环境** +#### 必需环境 * zk、kafka、kafka-connect、Mysql Server -**提供** +#### 提供 * 将[mysql connector插件](https://repo1.maven.org/maven2/io/debezium/debezium-connector-mysql/1.3.0.Final/debezium-connector-mysql-1.3.0.Final-plugin.tar.gz)放入到kafkaConnect环境中 * 添加kafka connect配置`plugin.path=/kafka/connect` -#### 配置Mysql connector +### 配置Mysql connector * 监听`inventory`库 @@ -863,7 +861,7 @@ binlog_rows_query_log_events=ON ``` * 将配置添加至kafka connect - * 使用[kafka connect restful api](https://kafka.apache.org/documentation/#connect\_rest) + * 使用[kafka connect restful api](https://kafka.apache.org/documentation/#connect_rest) ``` 1. Method:POST,URL:http://ip:port/connectors 提交connector @@ -915,3 +913,4 @@ binlog_rows_query_log_events=ON } } ``` + diff --git a/bigdata/collect/debezium/README.md b/bigdata/collect/debezium/README.md deleted file mode 100644 index 61c6625b..00000000 --- a/bigdata/collect/debezium/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# debezium - diff --git a/bigdata/collect/flume/README.md b/bigdata/collect/flume/README.md deleted file mode 100644 index 369006e1..00000000 --- a/bigdata/collect/flume/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# flume - diff --git a/bigdata/collect/sqoop/README.md b/bigdata/collect/sqoop/README.md deleted file mode 100644 index b5a9a1f7..00000000 --- a/bigdata/collect/sqoop/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# sqoop - diff --git a/bigdata/datalake/README.md b/bigdata/datalake/README.md deleted file mode 100644 index 404667cc..00000000 --- a/bigdata/datalake/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# datalake - diff --git a/bigdata/datalake/hudi/README.md b/bigdata/datalake/hudi/README.md deleted file mode 100644 index a03ba4af..00000000 --- a/bigdata/datalake/hudi/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# hudi - diff --git a/bigdata/datalake/iceberg/README.md b/bigdata/datalake/iceberg/README.md deleted file mode 100644 index 416d0e07..00000000 --- a/bigdata/datalake/iceberg/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# iceberg - diff --git a/bigdata/engine/README.md b/bigdata/engine/README.md deleted file mode 100644 index 438322d6..00000000 --- a/bigdata/engine/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# engine - diff --git a/bigdata/engine/flink/README.md b/bigdata/engine/flink/README.md deleted file mode 100644 index 7130ff21..00000000 --- a/bigdata/engine/flink/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# flink - diff --git a/bigdata/engine/flink/books/README.md b/bigdata/engine/flink/books/README.md deleted file mode 100644 index cb3639ed..00000000 --- a/bigdata/engine/flink/books/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# books - diff --git a/bigdata/engine/flink/books/flink-nei-he-yuan-li-yu-shi-xian/README.md b/bigdata/engine/flink/books/flink-nei-he-yuan-li-yu-shi-xian/README.md deleted file mode 100644 index b3932f59..00000000 --- a/bigdata/engine/flink/books/flink-nei-he-yuan-li-yu-shi-xian/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Flink内核原理与实现 - diff --git a/bigdata/engine/flink/connector/README.md b/bigdata/engine/flink/connector/README.md deleted file mode 100644 index c3866303..00000000 --- a/bigdata/engine/flink/connector/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# connector - diff --git "a/bigdata/engine/flink/core/Checkpoint\346\234\272\345\210\266.md" "b/bigdata/engine/flink/core/Checkpoint\346\234\272\345\210\266.md" index 48ffef68..bdc8795e 100644 --- "a/bigdata/engine/flink/core/Checkpoint\346\234\272\345\210\266.md" +++ "b/bigdata/engine/flink/core/Checkpoint\346\234\272\345\210\266.md" @@ -1,5 +1,4 @@ # Checkpoint机制剖析 - ## 容错与状态 ### Checkpoint @@ -18,14 +17,20 @@ #### 检查点算法 * 一种简单的想法 - * 暂停应用,保存状态到检查点,再重新恢复应用。 + + * 暂停应用,保存状态到检查点,再重新恢复应用。 + * Flink实现方式 - * 基于Chandy-Lamport算法的分布式快照 - * 将检查点的保存和数据处理分离开,不暂停整个应用,对Source进行`checkpoint barrier`控制 + + * 基于Chandy-Lamport算法的分布式快照 + * 将检查点的保存和数据处理分离开,不暂停整个应用,对Source进行`checkpoint barrier`控制 + * 检查点屏障(Checkpoint Barrier) - * Flink的检查点算法用到一种称为屏障(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查点分开。 - * Barrier之前到来的数据导致的状态更改,都会被包含在当前分界线所属的检查点中;基于barrier之后的数据导致的所有更改,就会被包含在之后的检查点中。 -* **检查点barrier流程** + + * Flink的检查点算法用到一种称为屏障(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查点分开。 + * Barrier之前到来的数据导致的状态更改,都会被包含在当前分界线所属的检查点中;基于barrier之后的数据导致的所有更改,就会被包含在之后的检查点中。 + +* **检查点barrier流程** * 有两个输入流的应用程序,并行的两个Source任务来读取,JobManager会向每个Source任务发送一个带有新checkpoint ID的消息,通过这种方式来启动checkpoint。 * 数据源将它们的状态写入checkpoint,并发出一个`checkpointbarrier`,状态后端在状态存入checkpoint之后,会返回通知给source任务,source任务就会向JobManager确认checkpoint完成。 @@ -33,15 +38,16 @@ * 当收到所有输入分区的barrier时,任务就将其状态保存到`状态后端的checkpoint中`,然后将barrier继续向下游转发,下游继续正常处理数据。 * Sink任务向JobManager确认状态保存到checkpoint完毕,当所有任务都确认已成功将状态保存到checkpoint时,checkpoint完毕。 - checkpoint1 + ![checkpoint1](../img/checkpoint1.jpg) - checkpoint1 + ![checkpoint1](../img/checkpoint2.jpg) - checkpoint1 + ![checkpoint1](../img/checkpoint3.jpg) #### checkpoint配置 * checkpoint默认情况下`仅用于恢复失败的作业,并不保留,当程序取消时checkpoint就会被删除`。可以通过配置来保留checkpoint,保留的checkpoint在作业失败或取消时不会被清除。 + * **`ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION`**:当作业取消时,保留作业的 checkpoint。注意,这种情况下,需要手动清除该作业保留的 checkpoint。 * **`ExternalizedCheckpointCleanup.DELETE_ON_CANCELLATION`**:当作业取消时,删除作业的 checkpoint。仅当作业失败时,作业的 checkpoint 才会被保留。 @@ -73,12 +79,12 @@ env.getCheckpointConfig().setPreferCheckpointForRecovery(true); ``` * **设置重启策略** - * noRestart - * fallBackRestart:回滚 - * fixedDelayRestart: 固定延迟时间重启策略,在固定时间间隔内重启 - * failureRateRestart: 失败率重启 + * noRestart + * fallBackRestart:回滚 + * fixedDelayRestart: 固定延迟时间重启策略,在固定时间间隔内重启 + * failureRateRestart: 失败率重启 -**checkpoint存储目录** +##### checkpoint存储目录 * checkpoint由元数据文件、数据文件组成。通过`state.checkpoints.dir`配置元数据文件和数据文件存储路径,也可以在代码中设置。 @@ -95,13 +101,14 @@ env.getCheckpointConfig().setPreferCheckpointForRecovery(true); ``` * 其中 **SHARED** 目录保存了可能被多个 checkpoint 引用的文件,**TASKOWNED** 保存了不会被 JobManager 删除的文件,**EXCLUSIVE** 则保存那些仅被单个 checkpoint 引用的文件。 + * 从保留的checkpoint中恢复状态 ```shell $ bin/flink run -s :checkpointMetaDataPath [:runArgs] ``` -**flink-conf容错配置** +##### flink-conf容错配置 ```yaml state.backend: rocksdb @@ -153,25 +160,25 @@ datasource.uid("network-source").map(new WordCountMapFunction()) * 使用YARN触发Savepoint,`flink savepoint :jobId [:targetDirctory] -yid :yarnAppId` * 使用savepoint取消作业,`flink cancel -s [:targetDirectory] :jobId` * 从savepoint恢复,`flink run -s :savepointPath [:runArgs]` - * \--allowNoRestoredState 跳过无法映射到新程序的状态 + * --allowNoRestoredState 跳过无法映射到新程序的状态 * 删除savepoint,`flink savepoint -d :savepointPath` ### 状态快照 #### 概念 -* _快照_ – 是 Flink 作业状态全局一致镜像的通用术语。快照包括指向每个数据源的指针(例如,到文件或 Kafka 分区的偏移量)以及每个作业的有状态运算符的状态副本,该状态副本是处理了 sources 偏移位置之前所有的事件后而生成的状态。 -* _Checkpoint_ – 一种由 Flink 自动执行的快照,其目的是能够从故障中恢复。Checkpoints 可以是增量的,并为快速恢复进行了优化。 -* _外部化的 Checkpoint_ – 通常 checkpoints 不会被用户操纵。Flink 只保留作业运行时的最近的 _n_ 个 checkpoints(_n_ 可配置),并在作业取消时删除它们。但你可以将它们配置为保留,在这种情况下,你可以手动从中恢复。 -* _Savepoint_ – 用户出于某种操作目的(例如有状态的重新部署/升级/缩放操作)手动(或 API 调用)触发的快照。Savepoints 始终是完整的,并且已针对操作灵活性进行了优化 +- *快照* – 是 Flink 作业状态全局一致镜像的通用术语。快照包括指向每个数据源的指针(例如,到文件或 Kafka 分区的偏移量)以及每个作业的有状态运算符的状态副本,该状态副本是处理了 sources 偏移位置之前所有的事件后而生成的状态。 +- *Checkpoint* – 一种由 Flink 自动执行的快照,其目的是能够从故障中恢复。Checkpoints 可以是增量的,并为快速恢复进行了优化。 +- *外部化的 Checkpoint* – 通常 checkpoints 不会被用户操纵。Flink 只保留作业运行时的最近的 *n* 个 checkpoints(*n* 可配置),并在作业取消时删除它们。但你可以将它们配置为保留,在这种情况下,你可以手动从中恢复。 +- *Savepoint* – 用户出于某种操作目的(例如有状态的重新部署/升级/缩放操作)手动(或 API 调用)触发的快照。Savepoints 始终是完整的,并且已针对操作灵活性进行了优化 #### 原理 -* 基于异步barrier快照(asynchronous barrier snapshotting),当 checkpoint coordinator(job manager 的一部分)指示 task manager 开始 checkpoint 时,它会让所有 sources 记录它们的偏移量,并将编号的 _checkpoint barriers_ 插入到它们的流中。这些 barriers 流经 job graph,标注每个 checkpoint 前后的流部分。 +* 基于异步barrier快照(asynchronous barrier snapshotting),当 checkpoint coordinator(job manager 的一部分)指示 task manager 开始 checkpoint 时,它会让所有 sources 记录它们的偏移量,并将编号的 *checkpoint barriers* 插入到它们的流中。这些 barriers 流经 job graph,标注每个 checkpoint 前后的流部分。 -![Checkpoint barriers are inserted into the streams](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/stream\_barriers.svg) +![Checkpoint barriers are inserted into the streams](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/stream_barriers.svg) -![Barrier alignment](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/stream\_aligning.svg) +![Barrier alignment](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/stream_aligning.svg) * Flink 的 state backends 利用写时复制(copy-on-write)机制允许当异步生成旧版本的状态快照时,能够不受影响地继续流处理。只有当快照被持久保存后,这些旧版本的状态才会被当做垃圾回收。 @@ -196,19 +203,19 @@ checkpoint_start_delay = end_to_end_duration - synchronous_duration - asynchrono StreamExecutionEnvironment.getCheckpointConfig().setMinPauseBetweenCheckpoints(milliseconds) ``` -![Illustration how the minimum-time-between-checkpoints parameter affects checkpointing behavior.](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/checkpoint\_tuning.svg) +![Illustration how the minimum-time-between-checkpoints parameter affects checkpointing behavior.](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/checkpoint_tuning.svg) #### 优化RocksDB -**增量checkpoint** +##### 增量checkpoint * 开启rocksDB增量checkpoint可以减少checkpoint的时间。 -**定时器存储在RocksDB或JVM堆** +##### 定时器存储在RocksDB或JVM堆 * 默认情况下timers存储在rocksDB中,这是更健壮和可扩展的选择。当性能调优只有少量计时器(没有窗口,在ProcessFunction中不使用计时器)的任务时,将这些计时器放在堆中可以提高性能。要小心使用此特性,因为基于堆的计时器可能会增加检查点时间,而且自然不能扩展到内存之外。 -**优化RocksDB内存** +##### 优化RocksDB内存 * 默认情况下RocksDB状态后端使用Flink管理的RocksDBs缓冲区和缓存的内存预算`state.backend.rocksdb.memory.managed: true` * 修改`state.backend.rocksdb.memory.write-buffer-ratio`比率 @@ -223,7 +230,7 @@ StreamExecutionEnvironment.getCheckpointConfig().setMinPauseBetweenCheckpoints(m ### 一致性分类 * AT-MOST-ONCE 最多一次 - * 当任务故障时,最简单的做法是什么都不敢,即不恢复丢失的状态,也不重播丢失的数据。 + * 当任务故障时,最简单的做法是什么都不敢,即不恢复丢失的状态,也不重播丢失的数据。 * AT-LEAST-ONCE 至少一次 * EXACTLY-ONCE 精准一次 @@ -252,21 +259,21 @@ StreamExecutionEnvironment.getCheckpointConfig().setMinPauseBetweenCheckpoints(m * 从故障恢复时,数据不会重复写入外部系统 * 幂等写入 * 事务写入 - * Write-Ahead-Log WAL - * 把结果数据先当成状态保存,然后在收到checkpoint完成的通知时,一次性写入sink系统 - * 简单已于实现,由于数据提前在状态后端中做了缓存,所以无论什么sink系统,都能一批搞定 - * DataStream API提供一个模版类:`GenericWriteAheadSink`来实现。 - * 存在的问题,延迟性大,如果存在批量写入失败时需要考虑回滚重放。 - * 2PAC(Two-Phase-Commit) - * 对于每个checkpoint,sink任务会启动一个事务,并将接下来所有接受的数据添加到事务里。 - * 然后将这些数据写入外部sink系统,但不提交它们--这时只是"预提交" - * 当它收到checkpoint完成的通知时,它才正式提交事务,实现结果真正写入(参考checkpoint barrier sink端写入完成后的ack checkpoint通知) - * Flink提供`TwoPhaseCommitSinkFunction`接口,参考`FlinkKafkaProducer` - * 对外部sink系统的要求 - * 外部sink系统提供事务支持,或者sink任务必须能够模拟外部系统上的事务 - * 在checkpoint的间隔期间,必须能够开启一个事务并接收数据写入 - * 在收到checkpoint完成的通知之前,事务必须时“等待提交”的状态。在故障恢复情况下,可能需要一些时间。如果这个时候sink系统关闭了事务,那么未提交的数据就丢失了。 - * sink任务必须能在进程失败后恢复事务,提交事务时幂等操作。 + * Write-Ahead-Log WAL + * 把结果数据先当成状态保存,然后在收到checkpoint完成的通知时,一次性写入sink系统 + * 简单已于实现,由于数据提前在状态后端中做了缓存,所以无论什么sink系统,都能一批搞定 + * DataStream API提供一个模版类:`GenericWriteAheadSink`来实现。 + * 存在的问题,延迟性大,如果存在批量写入失败时需要考虑回滚重放。 + * 2PAC(Two-Phase-Commit) + * 对于每个checkpoint,sink任务会启动一个事务,并将接下来所有接受的数据添加到事务里。 + * 然后将这些数据写入外部sink系统,但不提交它们--这时只是"预提交" + * 当它收到checkpoint完成的通知时,它才正式提交事务,实现结果真正写入(参考checkpoint barrier sink端写入完成后的ack checkpoint通知) + * Flink提供`TwoPhaseCommitSinkFunction`接口,参考`FlinkKafkaProducer` + * 对外部sink系统的要求 + * 外部sink系统提供事务支持,或者sink任务必须能够模拟外部系统上的事务 + * 在checkpoint的间隔期间,必须能够开启一个事务并接收数据写入 + * 在收到checkpoint完成的通知之前,事务必须时“等待提交”的状态。在故障恢复情况下,可能需要一些时间。如果这个时候sink系统关闭了事务,那么未提交的数据就丢失了。 + * sink任务必须能在进程失败后恢复事务,提交事务时幂等操作。 ### Flink+Kafka端到端一致性保证 @@ -286,4 +293,4 @@ StreamExecutionEnvironment.getCheckpointConfig().setMinPauseBetweenCheckpoints(m * 每个内部的transform任务遇到barrier时,都会把状态存到checkpoint里,sink任务首先把数据写入到外部kafka,这些数据都属于预提交的事务;遇到barrier时,把状态保存到状态后端,并开启新的预提交事务。 * 当所有算子任务的快照完成,也就是checkpoint完成时,jobmanager会向所有任务发送通知,确认这次checkpoint完成,sink任务收到确认通知,正式提交事务,kafka中未确认数据改为"已确认" -![kafka](../img/kafka俩阶段提交.jpg) +![kafka](../img/kafka俩阶段提交.jpg) \ No newline at end of file diff --git a/bigdata/engine/flink/core/README.md b/bigdata/engine/flink/core/README.md deleted file mode 100644 index 35343a39..00000000 --- a/bigdata/engine/flink/core/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# core - diff --git a/bigdata/engine/flink/core/TableSQLOverview.md b/bigdata/engine/flink/core/TableSQLOverview.md index 70736c13..1156b3a4 100644 --- a/bigdata/engine/flink/core/TableSQLOverview.md +++ b/bigdata/engine/flink/core/TableSQLOverview.md @@ -1,8 +1,6 @@ -# TableSQLOverview +# 概览 -## 概览 - -### 基本程序结构 +## 基本程序结构 ![](../img/blinkcalctie.jpg) @@ -21,7 +19,7 @@ Table table=tableEnvironment.from("tableName").select($("id")); table.executeInsert() ``` -### 创建TableEnvironment +## 创建TableEnvironment * 创建表执行环境 @@ -42,13 +40,15 @@ table.executeInsert() } ``` + + * 基于TableEnvironment做操作 * 注册Catalog * 在Catalog中注册表 * 执行SQL查询 * 注册UDF -### 表(Table) +## 表(Table) * TableEnvironment可以注册Catalog,并可以基于Catalog注册表 * 表是由一个标识符来指定,由catalog名、数据库名和对象名组成。 @@ -56,14 +56,14 @@ table.executeInsert() * 常规表一般可以用来描述外部数据,比如文件、数据库或消息队列数据,也可以从DataStream转换而来 * 视图可以从现有的表中创建,通常是table API或SQL查询的一个结果集。 -### 动态表(Dynamic Tables) +## 动态表(Dynamic Tables) ![Dynamic tables](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/table-streaming/stream-query-stream.png) * 动态表是Flink对流数据的Table API和SQL支持的核心概念。 * 与表示批处理数据的静态表不同,动态表随时间变化的。 -#### 持续查询(Continuous Query) +### 持续查询(Continuous Query) * 动态表可以像静态的批处理表一样进行查询,查询一个动态表产生持续查询(Continuous Query) * 连续查询永远不会终止,并会生成另一个动态表。 @@ -71,64 +71,64 @@ table.executeInsert() ![Continuous Non-Windowed Query](https://ci.apache.org/projects/flink/flink-docs-release-1.12/fig/table-streaming/query-groupBy-cnt.png) -#### 流式表查询的处理过程 +### 流式表查询的处理过程 * 将流转换为动态表。 * 在动态表上计算一个连续查询,生成一个新的动态表。 * 生成的动态表被转换回流。 -#### 表到流的转换 +### 表到流的转换 动态表可以像普通数据库表一样通过 `INSERT`、`UPDATE` 和 `DELETE` 来不断修改。它可能是一个只有一行、不断更新的表,也可能是一个 insert-only 的表,没有 `UPDATE` 和 `DELETE` 修改,或者介于两者之间的其他表。 在将动态表转换为流或将其写入外部系统时,需要对这些更改进行编码。Flink的 Table API 和 SQL 支持三种方式来编码一个动态表的变化: -* **Append-only 流:** 仅通过 `INSERT` 操作修改的动态表可以通过输出插入的行转换为流。 -* **Retract 流:** retract 流包含两种类型的 message: _add messages_ 和 _retract messages_ 。通过将`INSERT` 操作编码为 add message、将 `DELETE` 操作编码为 retract message、将 `UPDATE` 操作编码为更新(先前)行的 retract message 和更新(新)行的 add message,将动态表转换为 retract 流。下图显示了将动态表转换为 retract 流的过程。 +- **Append-only 流:** 仅通过 `INSERT` 操作修改的动态表可以通过输出插入的行转换为流。 +- **Retract 流:** retract 流包含两种类型的 message: *add messages* 和 *retract messages* 。通过将`INSERT` 操作编码为 add message、将 `DELETE` 操作编码为 retract message、将 `UPDATE` 操作编码为更新(先前)行的 retract message 和更新(新)行的 add message,将动态表转换为 retract 流。下图显示了将动态表转换为 retract 流的过程。 ![Dynamic tables](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/table-streaming/undo-redo-mode.png) -* **Upsert 流:** upsert 流包含两种类型的 message: _upsert messages_ 和_delete messages_。转换为 upsert 流的动态表需要(可能是组合的)唯一键。通过将 `INSERT` 和 `UPDATE` 操作编码为 upsert message,将 `DELETE` 操作编码为 delete message ,将具有唯一键的动态表转换为流。消费流的算子需要知道唯一键的属性,以便正确地应用 message。与 retract 流的主要区别在于 `UPDATE` 操作是用单个 message 编码的,因此效率更高。下图显示了将动态表转换为 upsert 流的过程。 +- **Upsert 流:** upsert 流包含两种类型的 message: *upsert messages* 和*delete messages*。转换为 upsert 流的动态表需要(可能是组合的)唯一键。通过将 `INSERT` 和 `UPDATE` 操作编码为 upsert message,将 `DELETE` 操作编码为 delete message ,将具有唯一键的动态表转换为流。消费流的算子需要知道唯一键的属性,以便正确地应用 message。与 retract 流的主要区别在于 `UPDATE` 操作是用单个 message 编码的,因此效率更高。下图显示了将动态表转换为 upsert 流的过程。 ![Dynamic tables](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/table-streaming/redo-mode.png) -## Flink SQL架构 +# Flink SQL架构 -### Old Planner架构 +## Old Planner架构 ![](../img/Oldplannerr架构.jpg) * 存在的问题:虽然面向用户的 Table API & SQL 是统一的,但是流式和批式任务在翻译层分别对应了 DataStreamAPI 和 DataSetAPI,在 Runtime 层面也要根据不同的 API 获取执行计划,两层的设计使得整个架构能够复用的模块有限,不易扩展。 -### Blink Planner架构 +## Blink Planner架构 * Blink Planner将批 SQL 处理作为流 SQL 处理的特例,尽量对通用的处理和优化逻辑进行抽象和复用,通过 Flink 内部的 Stream Transformation API 实现流 & 批的统一处理,替代原 Flink Planner 将流 & 批区分处理的方式。 * 从SQL语句到Operation,再转换为Transformation,最后转换为Execution执行。 -![](../img/BlinkPlanner%E6%9E%B6%E6%9E%84.jpg) +![](../img/BlinkPlanner架构.jpg) -#### 从SQL到Operation +### 从SQL到Operation * Blink ParserImpl#parse将SQL语句生成为Operation树,生成新的Table对象。 * 1) 解析SQL字符串转换为QueryOperation。2) SQL字符串解析为SqlNode。3)校验SqlNode。4)调用Calcite SQLToRelConverter将SqlNode转换为RelNode逻辑树。5)RelNode转换为Operation。 -#### Operation到Transformation +### Operation到Transformation * DQL、DML转换,ModifyOperation→RelNode→FlinkPhysicalRel→ExecNode→Transformation。 -#### Blink优化 +### Blink优化 -**优化器** +#### 优化器 * Blink没有使用Calcite的优化器,而是通过规则组合和Calcite优化器的组合,分为流和批实现了自定义的优化器。 ![](../books/Flink内核原理与实现/img/Blink优化器.jpg) -**代价计算** +#### 代价计算 * 代价计算通过数据量、CPU资源使用、内存资源使用、IO资源使用和网络资源使用来进行计算。 -### Flink SQL工作流 +## Flink SQL工作流 ![](../img/FlinkSQL工作流.jpeg) @@ -137,19 +137,19 @@ table.executeInsert() * Logical Plan通过优化器优化为物理执行计划(Physical Plan) * 通过代码生成技术生成Transformations后进一步编译为可执行的JobGraph提交运行 -#### Logical Planning +### Logical Planning * Flink SQL 引擎使用 Apache Calcite SQL Parser 将 SQL 文本解析为词法树,SQL Validator 获取 Catalog 中元数据的信息进行语法分析和验证,转化为关系代数表达式(RelNode),再由 Optimizer 将关系代数表达式转换为初始状态的逻辑执行计划。 ![](../img/LogicalPlan.jpg) -**Flink SQL优化器优化方式** +#### Flink SQL优化器优化方式 -**Expression Reduce** +##### Expression Reduce * 表达式(Expression) 是 SQL 中最常见的语法。比如 t1.id 是一个表达式, 1 + 2 + t1.value 也是一个表达式。优化器在优化过程中会递归遍历树上节点,尽可能预计算出每个表达式的值,这个过程就称为表达式折叠。这种转换在逻辑上等价,通过优化后,真正执行时不再需要为每一条记录都计算一遍 1 + 2。 -**PushDown Optimization** +##### PushDown Optimization * 下推优化是指在保持关系代数语义不变的前提下将 SQL 语句中的变换操作尽可能下推到靠近数据源的位置以获得更优的性能,常见的下推优化有谓词下推(Predicate Pushdown),投影下推(Projection Pushdown,有时也译作列裁剪)等。 * Predicate Pushdown(谓词下推) @@ -158,68 +158,68 @@ table.executeInsert() * Projection Pushdown(列裁剪) * 列裁剪是 Projection Pushdown 更直观的描述方式,指在优化过程中去掉没有使用的列来降低 I / O 开销,提升性能。但与谓词下推只移动节点位置不同,投影下推可能会增加节点个数。比如最后计算出的投影组合应该放在 TableScan 操作之上,而 TableScan 节点之上没有 Projection 节点,优化器就会显式地新增 Projection 节点来完成优化。另外如果输入表是基于列式存储的(如 Parquet 或 ORC 等),优化还会继续下推到 Scan 操作中进行。 -#### Physical Planning on Batch +### Physical Planning on Batch * 逻辑执行计划描述了执行步骤和每一步需要完成的操作,但没有描述操作的具体实现方式。而物理执行计划会考虑物理实现的特性,生成每一个操作的具体实现方式。比如 Join 是使用 SortMergeJoin、HashJoin 或 BroadcastHashJoin 等。优化器在生成逻辑执行计划时会计算整棵树上每一个节点的 Cost,对于有多种实现方式的节点(比如 Join 节点),优化器会展开所有可能的 Join 方式分别计算。最终整条路径上 Cost 最小的实现方式就被选中成为 Final Physical Plan。 -#### Translation\&Code Generation +### Translation&Code Generation * 从 Physical Plan 到生成 Transformation Tree 过程中就使用了 Code Generation,将各种优化后的物理执行计划动态生成Transformations,最终在转换为StreamGraph-》VerxtGraph-》JobGraph -#### Physical Planning on Stream +### Physical Planning on Stream -**Retraction Mechanism(撤回流)** +#### **Retraction Mechanism**(撤回流) * Retraction 是流式数据处理中撤回过早下发(Early Firing)数据的一种机制,类似于传统数据库的 Update 操作。级联的聚合等复杂 SQL 中如果没有 Retraction 机制,就会导致最终的计算结果与批处理不同,这也是目前业界很多流计算引擎的缺陷。 -* Flink SQL 在流计算领域中的一个重大贡献就是首次提出了这个机制的具体实现方案。Retraction 机制又名 Changelog 机制,因为某种程度上 Flink 将输入的流数据看作是数据库的 Changelog,每条输入数据都可以看作是对数据库的一次变更操作,比如 Insert,Delete 或者 Update。以 MySQL 数据库为例,其Binlog 信息以二进制形式存储,其中 Update\_rows\_log\_event 会对应 2 条标记 Before Image (BI) 和 After Image (AI),分别表示某一行在更新前后的信息。 -* 在 Flink SQL 优化器生成流作业的 Physical Plan 时会判断当前节点是否是更新操作,如果是则会同时发出 2 条消息 update\_before 和 update\_after 到下游节点,update\_before 表示之前“错误”下发的数据,需要被撤回,update\_after 表示当前下发的“正确”数据。下游收到后,会在结果上先减去 update\_before,再加上 update\_after。 -* update\_before 是一条非常关键的信息,相当于标记出了导致当前结果不正确的那个“元凶”。不过额外操作会带来额外的开销,有些情况下不需要发送 update\_before 也可以获得正确的结果,比如下游节点接的是 UpsertSink(MySQL 或者 HBase的情况下,数据库可以按主键用 update\_after 消息覆盖结果)。是否发送 update\_before 由优化器决定,用户不需要关心。 +* Flink SQL 在流计算领域中的一个重大贡献就是首次提出了这个机制的具体实现方案。Retraction 机制又名 Changelog 机制,因为某种程度上 Flink 将输入的流数据看作是数据库的 Changelog,每条输入数据都可以看作是对数据库的一次变更操作,比如 Insert,Delete 或者 Update。以 MySQL 数据库为例,其Binlog 信息以二进制形式存储,其中 Update_rows_log_event 会对应 2 条标记 Before Image (BI) 和 After Image (AI),分别表示某一行在更新前后的信息。 +* 在 Flink SQL 优化器生成流作业的 Physical Plan 时会判断当前节点是否是更新操作,如果是则会同时发出 2 条消息 update_before 和 update_after 到下游节点,update_before 表示之前“错误”下发的数据,需要被撤回,update_after 表示当前下发的“正确”数据。下游收到后,会在结果上先减去 update_before,再加上 update_after。 +* update_before 是一条非常关键的信息,相当于标记出了导致当前结果不正确的那个“元凶”。不过额外操作会带来额外的开销,有些情况下不需要发送 update_before 也可以获得正确的结果,比如下游节点接的是 UpsertSink(MySQL 或者 HBase的情况下,数据库可以按主键用 update_after 消息覆盖结果)。是否发送 update_before 由优化器决定,用户不需要关心。 -**Update\_before Decision** +#### **Update_before Decision** 1. 确定每个节点对应的changelog变更类型 - * 数据库中最常见的三种操作类型分别是 Insert (记为 \[I]),Delete(记为 \[D]),Update(记为 \[U])。优化器首先会自底向上检查每个节点,判断它属于哪(几)种类型,分别打上对应标记。 + * 数据库中最常见的三种操作类型分别是 Insert (记为 [I]),Delete(记为 [D]),Update(记为 [U])。优化器首先会自底向上检查每个节点,判断它属于哪(几)种类型,分别打上对应标记。 2. **确定每个节点发送的消息类型** - * 在 Flink 中 Update 由两条 update\_before(简称 UB)和 update\_after (简称 UA)来表示,其中 UB 消息在某些情况下可以不发送,从而提高性能。 + * 在 Flink 中 Update 由两条 update_before(简称 UB)和 update_after (简称 UA)来表示,其中 UB 消息在某些情况下可以不发送,从而提高性能。 * 在1 中优化器自底向上推导出了每个节点对应的 Changelog 变更操作,这一步里会先自顶向下推断当前节点需要父节点提供的消息类型,直到遇到第一个不需要父节点提供任何消息类型的节点,再往上回推每个节点最终的实现方式和需要的消息类型。 -### Flink SQL内部优化 +## Flink SQL内部优化 -#### 查询优化器 +### 查询优化器 * 查询优化器分为两类:基于规则的优化器(Rule-Base Optimizer,RBO)和基于代价的优化器(Cost-Based Optimizer,CBO); -**RBO** +#### RBO * RBO根据事先设定好的优化规则对SQL计划树进行转换,降低计算成本。只要SQL语句相同,应用完规则就会得到相同的SQL物理执行计划,也就是说RBO并不考虑数据的规模、数据倾斜等问题,对数据不敏感,导致优化后的执行计划往往并不是最优的。这就要求SQL的使用者了解更多的RBO的规则,使用门槛更高。 -**CBO** +#### CBO * CBO优化器根据事先设定好的优化规则对SQL计划树反复应用规则,SQL语句生成一组可能被使用的执行计划,然后CBO会根据统计信息和代价模型(Cost Model)计算每个执行计划的代价,从中挑选代价最小的执行计划。由上可知,CBO中有两个依赖:统计信息和代价模型。统计信息的准确与否、代价模型的合理与否都会影响CBO选择最优计划。 * 一般情况下,CBO是优于RBO的,原因是RBO是一种只认规则,只针对数据不敏感的过时的优化器。在实际场景中,数据往往是有变化的,通过RBO生成的执行计划很有可能不是最优的。 -#### BinaryRow +### BinaryRow -* Flink1.9.x前,Flink Runtime层各算子间传递的数据结构是Row,其内部实现是 Object\[],因此需要维护额外的Object Metadata,计算过程中还涉及到大量序列化/反序列化(特别是只需要处理某几个字段时需要反序列化整个 Row),primitive 类型的拆 / 装箱等,都会带来大量额外的性能开销。 +* Flink1.9.x前,Flink Runtime层各算子间传递的数据结构是Row,其内部实现是 Object[],因此需要维护额外的Object Metadata,计算过程中还涉及到大量序列化/反序列化(特别是只需要处理某几个字段时需要反序列化整个 Row),primitive 类型的拆 / 装箱等,都会带来大量额外的性能开销。 * Blink Planner使用二进制数据结构的BinaryRow来表示Record。BinaryRow 作用于默认大小为 32K 的 Memory Segment,直接映射到内存。BinaryRow 内部分为 Header,定长区和变长区。Header 用于存储 Retraction 消息的标识,定长区使用 8 个 bytes 来记录字段的 Nullable 信息及所有 primitive 和可以在 8 个 bytes 内表示的类型。其它类型会按照基于起始位置的 offset 存放在变长区。 * 首先存储上更为紧凑,去掉了额外开销;其次在序列化和反序列化上带来的显著性能提升,可根据 offset 只反序列化需要的字段,在开启 Object Reuse 后,序列化可以直接通过内存拷贝完成。 ![](../img/BinaryRow.jpg) -#### Mini-batch Processing +### Mini-batch Processing * 在内存中 buffer 一定量的数据,预先做一次聚合后再更新 State,则不但会降低操作 State 的开销,还会有效减少发送到下游的数据量,提升吞吐量,降低两层聚合时由 Retraction 引起的数据抖动, 这就是 Mini-batch 攒批优化的核心思想。 * 把所有的数据先聚合一次,类似一个微批处理,然后再把这个数据写到 State 里面,或者在从 State 里面查出来,这样可以大大的减轻对 State 查询的压力。 -![img](https://mmbiz.qpic.cn/mmbiz\_gif/8AsYBicEePu5KNuaibsibpYmAJ1cm1apjUicDic2QTTU5ueDVEBezeervrG84lNVyREu8JpRGUTT2ygZDhKWErnajrQ/640?wx\_fmt=gif\&tp=webp\&wxfrom=5\&wx\_lazy=1) +![img](https://mmbiz.qpic.cn/mmbiz_gif/8AsYBicEePu5KNuaibsibpYmAJ1cm1apjUicDic2QTTU5ueDVEBezeervrG84lNVyREu8JpRGUTT2ygZDhKWErnajrQ/640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=1) -#### 数据倾斜处理 +### 数据倾斜处理 * 对于数据倾斜的优化,主要分为是否带 DISTINCT 去重语义的两种方式。对于普通聚合的数据倾斜,Flink 引入了 Local-Global 两阶段优化,类似于 MapReduce 增加 Local Combiner 的处理模式。而对于带有去重的聚合,Flink 则会将用户的 SQL 按原有聚合的 key 组合再加上 DISTINCT key 做 Hash 取模后改写为两层聚合来进行打散。 -![img](https://mmbiz.qpic.cn/mmbiz\_gif/8AsYBicEePu5KNuaibsibpYmAJ1cm1apjUicrwcM3ZauvkAicGiaWicXzJIEPOPlO8aw8oh7kLhF10ZmhIevtwy083gFQ/640?wx\_fmt=gif\&tp=webp\&wxfrom=5\&wx\_lazy=1) +![img](https://mmbiz.qpic.cn/mmbiz_gif/8AsYBicEePu5KNuaibsibpYmAJ1cm1apjUicrwcM3ZauvkAicGiaWicXzJIEPOPlO8aw8oh7kLhF10ZmhIevtwy083gFQ/640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=1) -#### Top-N重写 +### Top-N重写 ```sql SELECT* @@ -230,12 +230,12 @@ FROM( WHERE rowNum <= 3 ``` -* 在生成 Plan 方面,ROW\_NUMBER 语义对应 OverAggregate 窗口节点和一个过滤行数的 Calc 节点,而这个窗口节点在实现层面需要为每一个到达的数据重新将 State 中的历史数据拿出来排序,这显然不是最优解。 -* 我们知道流式场景求解极大 / 小值的最优操作是通过维护一个 size 为 N 的 minHeap / maxHeap。由实现反推出我们需要在优化器上新增一条规则,在遇到 ROW\_NUMBER 生成的逻辑节点后,将其优化为一个特殊的 Rank 节点,对应上述的最优实现方式(当然这只是特殊 Rank 对应的其中一种实现)。这便是 Top-N Rewrite 的核心思想。 +* 在生成 Plan 方面,ROW_NUMBER 语义对应 OverAggregate 窗口节点和一个过滤行数的 Calc 节点,而这个窗口节点在实现层面需要为每一个到达的数据重新将 State 中的历史数据拿出来排序,这显然不是最优解。 +* 我们知道流式场景求解极大 / 小值的最优操作是通过维护一个 size 为 N 的 minHeap / maxHeap。由实现反推出我们需要在优化器上新增一条规则,在遇到 ROW_NUMBER 生成的逻辑节点后,将其优化为一个特殊的 Rank 节点,对应上述的最优实现方式(当然这只是特殊 Rank 对应的其中一种实现)。这便是 Top-N Rewrite 的核心思想。 -## Table\&SQL API +# Table&SQL API -### Table API类型转换 +## Table API类型转换 * WindowTable在1.13后发生改变,对window语法也进行了修改。 @@ -248,38 +248,38 @@ WHERE rowNum <= 3 * AggregatedTable:对分组之后的Table(如GroupedTable和WindowedGroupTable)执行AggregationFunction聚合函数的结果 * FlatAggregateTable:对分组之后的Table(如GroupedTable和WindowedGroupTable)执行TableAggregationFunction(表聚合函数)的结果 -### Flink SQL +## Flink SQL * DQL:查询语句 * DML:INSERT语句,不包含UPDATE、DELETE语句,后面这两类语句的运算实际上在Flink SQL中也有体现,通过Retract召回实现了流上的UPDATE和DELETE。 * DDL:Create、Drop、Alter语句 -## Catalogs +# Catalogs * Catalog 提供了元数据信息,例如数据库、表、分区、视图以及数据库或其他外部系统中存储的函数和信息。 -### Catalog类型 +## Catalog类型 -#### GenericInMemoryCatalog +### GenericInMemoryCatalog * `GenericInMemoryCatalog` 是基于内存实现的 Catalog,所有元数据只在 session 的生命周期内可用。 -#### JdbcCatalog +### JdbcCatalog * `JdbcCatalog` 使得用户可以将 Flink 通过 JDBC 协议连接到关系数据库。`PostgresCatalog` 是当前实现的唯一一种 JDBC Catalog。 -#### HiveCatalog +### HiveCatalog * `HiveCatalog` 有两个用途:作为原生 Flink 元数据的持久化存储,以及作为读写现有 Hive 元数据的接口。 -#### 用户自定义 Catalog +### 用户自定义 Catalog * Catalog 是可扩展的,用户可以通过实现 `Catalog` 接口来开发自定义 Catalog。 想要在 SQL CLI 中使用自定义 Catalog,用户除了需要实现自定义的 Catalog 之外,还需要为这个 Catalog 实现对应的 `CatalogFactory` 接口。 * `CatalogFactory` 定义了一组属性,用于 SQL CLI 启动时配置 Catalog。 这组属性集将传递给发现服务,在该服务中,服务会尝试将属性关联到 `CatalogFactory` 并初始化相应的 Catalog 实例。 -### 创建Flink表注册到Catalog中 +## 创建Flink表注册到Catalog中 -#### 使用SQL DDL +### 使用SQL DDL ```java // Create a HiveCatalog @@ -297,7 +297,7 @@ tableEnv.executeSql("CREATE TABLE mytable (name STRING, age INT) WITH (...)"); tableEnv.listTables(); ``` -#### 使用Java +### 使用Java ```java // Create a catalog database @@ -326,9 +326,9 @@ catalog.createTable( List tables = catalog.listTables("mydb"); ``` -### Catalog API +## Catalog API -#### 数据库操作 +### 数据库操作 ```java / create database @@ -350,7 +350,7 @@ catalog.databaseExists("mydb"); catalog.listDatabases("mycatalog"); ``` -#### 表操作 +### 表操作 ```java // create table @@ -375,7 +375,7 @@ catalog.tableExists("mytable"); catalog.listTables("mydb"); ``` -#### 视图操作 +### 视图操作 ```java // create view @@ -400,7 +400,7 @@ catalog.tableExists("mytable"); catalog.listViews("mydb"); ``` -#### 分区操作 +### 分区操作 ```java // create view @@ -436,7 +436,7 @@ catalog.listPartitions(new ObjectPath("mydb", "mytable"), new CatalogPartitionSp catalog.listPartitions(new ObjectPath("mydb", "mytable"), Arrays.asList(epr1, ...)); ``` -#### 函数操作 +### 函数操作 ```java // create function @@ -458,7 +458,7 @@ catalog.functionExists("myfunc"); catalog.listFunctions("mydb"); ``` -## 流式聚合 +# 流式聚合 * 存在的问题 @@ -466,14 +466,14 @@ catalog.listFunctions("mydb"); 默认情况下,无界聚合算子是逐条处理输入的记录,即:(1)从状态中读取累加器,(2)累加/撤回记录至累加器,(3)将累加器写回状态,(4)下一条记录将再次从(1)开始处理。这种处理模式可能会增加 StateBackend 开销(尤其是对于 RocksDB StateBackend )。此外,生产中非常常见的数据倾斜会使这个问题恶化,并且容易导致 job 发生反压。 ``` -### MiniBatch 聚合 +## MiniBatch 聚合 * `将一组输入的数据缓存在聚合算子内部的缓冲区中`,当输入的数据被触发处理时,每个key只需一个操作即可访问状态,这样可以大大减少状态开销并获得更好的吞吐量。 * 但是,这可能会增加一些延迟,因为它会缓冲一些记录而不是立即处理它们。这是吞吐量和延迟之间的权衡。 -![img](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/table-streaming/minibatch\_agg.png) +![img](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/table-streaming/minibatch_agg.png) -#### 参数配置开启 +### 参数配置开启 ```java // access flink configuration @@ -484,14 +484,14 @@ configuration.setString("table.exec.mini-batch.allow-latency", "5 s"); // use 5 configuration.setString("table.exec.mini-batch.size", "5000"); // the maximum number of records can be buffered by each aggregate operator task ``` -### Local-Global 聚合 +## Local-Global 聚合 * Local-global聚合是为解决数据倾斜问题,通过将一组聚合氛围两个阶段,首先在上游进行本地聚合,然后在下游进行全局聚合,类似于MR中的Combine+Reduce模式。 * 每次本地聚合累积的输入数据量基于 mini-batch 间隔。 -![img](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/table-streaming/local\_agg.png) +![img](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/table-streaming/local_agg.png) -#### 开启配置 +### 开启配置 ```java // access flink configuration @@ -503,7 +503,7 @@ configuration.setString("table.exec.mini-batch.size", "5000"); configuration.setString("table.optimizer.agg-phase-strategy", "TWO_PHASE"); // enable two-phase, i.e. local-global aggregation ``` -### 在 distinct 聚合上使用 FILTER 修饰符 +## 在 distinct 聚合上使用 FILTER 修饰符 ```sql SELECT @@ -515,9 +515,9 @@ FROM T GROUP BY day ``` -## 执行配置 +# 执行配置 -### 获取配置 +## 获取配置 ```java public static void main(String[] args) { @@ -559,7 +559,7 @@ GROUP BY day } ``` -### 打印执行计划 +## 打印执行计划 ```java final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); @@ -569,9 +569,9 @@ final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); System.out.println(env.getExecutionPlan()); ``` -### Task故障恢复 +## Task故障恢复 -#### Restart Stragtegies +### Restart Stragtegies * none,off,disable:不设置重启策略 * fixeddelay,fixed-delay:固定延迟重启策略 @@ -608,3 +608,4 @@ env.setRestartStrategy(RestartStrategies.failureRateRestart( Time.of(10, TimeUnit.SECONDS) // 延时 )); ``` + diff --git a/bigdata/engine/flink/feature/README.md b/bigdata/engine/flink/feature/README.md deleted file mode 100644 index 785d41fd..00000000 --- a/bigdata/engine/flink/feature/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# feature - diff --git a/bigdata/engine/flink/monitor/README.md b/bigdata/engine/flink/monitor/README.md deleted file mode 100644 index 90cda299..00000000 --- a/bigdata/engine/flink/monitor/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# monitor - diff --git a/bigdata/engine/flink/practice/README.md b/bigdata/engine/flink/practice/README.md deleted file mode 100644 index 313a83d5..00000000 --- a/bigdata/engine/flink/practice/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# practice - diff --git "a/bigdata/engine/flink/practice/\350\256\260\345\275\225\344\270\200\346\254\241Flink\345\217\215\345\216\213\351\227\256\351\242\230.md" "b/bigdata/engine/flink/practice/\350\256\260\345\275\225\344\270\200\346\254\241Flink\345\217\215\345\216\213\351\227\256\351\242\230.md" index 4abece3c..b3551dae 100644 --- "a/bigdata/engine/flink/practice/\350\256\260\345\275\225\344\270\200\346\254\241Flink\345\217\215\345\216\213\351\227\256\351\242\230.md" +++ "b/bigdata/engine/flink/practice/\350\256\260\345\275\225\344\270\200\346\254\241Flink\345\217\215\345\216\213\351\227\256\351\242\230.md" @@ -1,69 +1,83 @@ -# 记录一次Flink反压问题 - -## 问题出现 - -![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351686-7c2bc225-de81-484a-9f4c-ff38f358a632.png#align=left\&display=inline\&height=372\&margin=%5Bobject%20Object%5D\&originHeight=372\&originWidth=1460\&size=0\&status=done\&style=none\&width=1460) ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351663-02121784-a3cc-42eb-ba4a-714912269fbd.png#align=left\&display=inline\&height=166\&margin=%5Bobject%20Object%5D\&originHeight=166\&originWidth=732\&size=0\&status=done\&style=none\&width=732) 根据subtask的watermark发现延迟了10几分钟,然后查看是否有异常或者BackPressure的情况最终发现,source->watermarks->filter端三个subtask反压都显示High ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351603-48eae46b-8bea-480c-8240-9d47926f34e8.png#align=left\&display=inline\&height=556\&margin=%5Bobject%20Object%5D\&originHeight=556\&originWidth=1542\&size=0\&status=done\&style=none\&width=1542) - -* 重启多次,问题依然存在。 - -## 反压的定位 - +# 问题出现 +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351686-7c2bc225-de81-484a-9f4c-ff38f358a632.png#align=left&display=inline&height=372&margin=%5Bobject%20Object%5D&originHeight=372&originWidth=1460&size=0&status=done&style=none&width=1460) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351663-02121784-a3cc-42eb-ba4a-714912269fbd.png#align=left&display=inline&height=166&margin=%5Bobject%20Object%5D&originHeight=166&originWidth=732&size=0&status=done&style=none&width=732) +根据subtask的watermark发现延迟了10几分钟,然后查看是否有异常或者BackPressure的情况最终发现,source->watermarks->filter端三个subtask反压都显示High +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351603-48eae46b-8bea-480c-8240-9d47926f34e8.png#align=left&display=inline&height=556&margin=%5Bobject%20Object%5D&originHeight=556&originWidth=1542&size=0&status=done&style=none&width=1542) +  + +- 重启多次,问题依然存在。 +# 反压的定位 [https://ververica.cn/developers/how-to-analyze-and-deal-with-flink-back-pressure/](https://ververica.cn/developers/how-to-analyze-and-deal-with-flink-back-pressure/) -* 正常任务checkpoint时间端发现非常短 +- 正常任务checkpoint时间端发现非常短 -![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351712-897811f8-6b9c-4850-980a-5499f4263054.png#align=left\&display=inline\&height=730\&margin=%5Bobject%20Object%5D\&originHeight=651\&originWidth=1920\&size=0\&status=done\&style=none\&width=2152) ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351598-90968eeb-f12b-427e-9e8c-a1f32798e3cc.png#align=left\&display=inline\&height=872\&margin=%5Bobject%20Object%5D\&originHeight=737\&originWidth=1920\&size=0\&status=done\&style=none\&width=2272) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351712-897811f8-6b9c-4850-980a-5499f4263054.png#align=left&display=inline&height=730&margin=%5Bobject%20Object%5D&originHeight=651&originWidth=1920&size=0&status=done&style=none&width=2152) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351598-90968eeb-f12b-427e-9e8c-a1f32798e3cc.png#align=left&display=inline&height=872&margin=%5Bobject%20Object%5D&originHeight=737&originWidth=1920&size=0&status=done&style=none&width=2272) -* 反压任务 +- 反压任务 -![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351632-946dfa8f-128a-45df-8828-31b19ab4c640.png#align=left\&display=inline\&height=670\&margin=%5Bobject%20Object%5D\&originHeight=670\&originWidth=2370\&size=0\&status=done\&style=none\&width=2370) ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034352170-e68b986e-a029-493c-98a1-0f4f646f4cf5.png#align=left\&display=inline\&height=464\&margin=%5Bobject%20Object%5D\&originHeight=374\&originWidth=1920\&size=0\&status=done\&style=none\&width=2382) ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351686-22e6a56f-8571-4b0f-9e24-91bcf9cf860f.png#align=left\&display=inline\&height=1266\&margin=%5Bobject%20Object%5D\&originHeight=920\&originWidth=1920\&size=0\&status=done\&style=none\&width=2642) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351632-946dfa8f-128a-45df-8828-31b19ab4c640.png#align=left&display=inline&height=670&margin=%5Bobject%20Object%5D&originHeight=670&originWidth=2370&size=0&status=done&style=none&width=2370) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034352170-e68b986e-a029-493c-98a1-0f4f646f4cf5.png#align=left&display=inline&height=464&margin=%5Bobject%20Object%5D&originHeight=374&originWidth=1920&size=0&status=done&style=none&width=2382) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351686-22e6a56f-8571-4b0f-9e24-91bcf9cf860f.png#align=left&display=inline&height=1266&margin=%5Bobject%20Object%5D&originHeight=920&originWidth=1920&size=0&status=done&style=none&width=2642) +  -* 大约可以看出来checkpoint做的时间过程,并且内部基本上是下游的subtask任务耗时比较长,因此初步怀疑是因为下游sink消费原因。 +- 大约可以看出来checkpoint做的时间过程,并且内部基本上是下游的subtask任务耗时比较长,因此初步怀疑是因为下游sink消费原因。 -**分析上游的subtask的Metrics** ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351683-588c48ed-6337-4fab-bdac-3f4765a9857d.png#align=left\&display=inline\&height=538\&margin=%5Bobject%20Object%5D\&originHeight=538\&originWidth=1462\&size=0\&status=done\&style=none\&width=1462) ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351642-49fda873-f991-4dd0-8146-e8bf7a640033.png#align=left\&display=inline\&height=720\&margin=%5Bobject%20Object%5D\&originHeight=720\&originWidth=1574\&size=0\&status=done\&style=none\&width=1574) **分析下游subtask的metrics** ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351595-219b38bf-159d-4ede-ab25-a37130ec5ab3.png#align=left\&display=inline\&height=516\&margin=%5Bobject%20Object%5D\&originHeight=516\&originWidth=1416\&size=0\&status=done\&style=none\&width=1416) +**分析上游的subtask的Metrics** +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351683-588c48ed-6337-4fab-bdac-3f4765a9857d.png#align=left&display=inline&height=538&margin=%5Bobject%20Object%5D&originHeight=538&originWidth=1462&size=0&status=done&style=none&width=1462) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351642-49fda873-f991-4dd0-8146-e8bf7a640033.png#align=left&display=inline&height=720&margin=%5Bobject%20Object%5D&originHeight=720&originWidth=1574&size=0&status=done&style=none&width=1574) +**分析下游subtask的metrics** +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351595-219b38bf-159d-4ede-ab25-a37130ec5ab3.png#align=left&display=inline&height=516&margin=%5Bobject%20Object%5D&originHeight=516&originWidth=1416&size=0&status=done&style=none&width=1416) -* 如果一个 Subtask 的发送端 Buffer 占用率很高,则表明它被下游反压限速了;如果一个 Subtask 的接受端 Buffer 占用很高,则表明它将反压传导至上游。 -* outPoolUsage 和 inPoolUsage 同为低或同为高分别表明当前 Subtask 正常或处于被下游反压,这应该没有太多疑问。而比较有趣的是当 outPoolUsage 和 inPoolUsage 表现不同时,这可能是出于反压传导的中间状态或者表明该 Subtask 就是反压的根源。 -* 如果一个 Subtask 的 outPoolUsage 是高,通常是被下游 Task 所影响,所以可以排查它本身是反压根源的可能性。如果一个 Subtask 的 outPoolUsage 是低,但其 inPoolUsage 是高,则表明它有可能是反压的根源。因为通常反压会传导至其上游,导致上游某些 Subtask 的 outPoolUsage 为高,我们可以根据这点来进一步判断。值得注意的是,反压有时是短暂的且影响不大,比如来自某个 Channel 的短暂网络延迟或者 TaskManager 的正常 GC,这种情况下我们可以不用处理。 -* 可以分析出来上游分下游限速里。 +- 如果一个 Subtask 的发送端 Buffer 占用率很高,则表明它被下游反压限速了;如果一个 Subtask 的接受端 Buffer 占用很高,则表明它将反压传导至上游。 +- outPoolUsage 和 inPoolUsage 同为低或同为高分别表明当前 Subtask 正常或处于被下游反压,这应该没有太多疑问。而比较有趣的是当 outPoolUsage 和 inPoolUsage 表现不同时,这可能是出于反压传导的中间状态或者表明该 Subtask 就是反压的根源。 +- 如果一个 Subtask 的 outPoolUsage 是高,通常是被下游 Task 所影响,所以可以排查它本身是反压根源的可能性。如果一个 Subtask 的 outPoolUsage 是低,但其 inPoolUsage 是高,则表明它有可能是反压的根源。因为通常反压会传导至其上游,导致上游某些 Subtask 的 outPoolUsage 为高,我们可以根据这点来进一步判断。值得注意的是,反压有时是短暂的且影响不大,比如来自某个 Channel 的短暂网络延迟或者 TaskManager 的正常 GC,这种情况下我们可以不用处理。 +- 可以分析出来上游分下游限速里。 -![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034352017-4fc649c2-6ae7-4b8f-9c98-3d919a184f9f.png#align=left\&display=inline\&height=584\&margin=%5Bobject%20Object%5D\&originHeight=584\&originWidth=1506\&size=0\&status=done\&style=none\&width=1506) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034352017-4fc649c2-6ae7-4b8f-9c98-3d919a184f9f.png#align=left&display=inline&height=584&margin=%5Bobject%20Object%5D&originHeight=584&originWidth=1506&size=0&status=done&style=none&width=1506) -* 通常来说,floatingBuffersUsage 为高则表明反压正在传导至上游,而 exclusiveBuffersUsage 则表明了反压是否存在倾斜(floatingBuffersUsage 高、exclusiveBuffersUsage 低为有倾斜,因为少数 channel 占用了大部分的 Floating Buffer)。 +- 通常来说,floatingBuffersUsage 为高则表明反压正在传导至上游,而 exclusiveBuffersUsage 则表明了反压是否存在倾斜(floatingBuffersUsage 高、exclusiveBuffersUsage 低为有倾斜,因为少数 channel 占用了大部分的 Floating Buffer)。 - +  +# 分析原因 +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351637-51c7a177-19e0-46af-adb1-f6e1ebd83e5e.png#align=left&display=inline&height=462&margin=%5Bobject%20Object%5D&originHeight=462&originWidth=1722&size=0&status=done&style=none&width=1722) -## 分析原因 +- 可以看出来subtask的数据并不是特别的倾斜 -![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034351637-51c7a177-19e0-46af-adb1-f6e1ebd83e5e.png#align=left\&display=inline\&height=462\&margin=%5Bobject%20Object%5D\&originHeight=462\&originWidth=1722\&size=0\&status=done\&style=none\&width=1722) -* 可以看出来subtask的数据并不是特别的倾斜 -> **此外,最常见的问题可能是用户代码的执行效率问题(频繁被阻塞或者性能问题)**。最有用的办法就是对 TaskManager 进行 CPU profile,从中我们可以分析到 Task Thread 是否跑满一个 CPU 核:如果是的话要分析 CPU 主要花费在哪些函数里面,比如我们生产环境中就偶尔遇到卡在 Regex 的用户函数(ReDoS);如果不是的话要看 Task Thread 阻塞在哪里,可能是用户函数本身有些同步的调用,可能是 checkpoint 或者 GC 等系统活动导致的暂时系统暂停。 当然,性能分析的结果也可能是正常的,只是作业申请的资源不足而导致了反压,这就通常要求拓展并行度。值得一提的,在未来的版本 Flink 将会直接在 WebUI 提供 JVM 的 CPU 火焰图\[5],这将大大简化性能瓶颈的分析。 **另外 TaskManager 的内存以及 GC 问题也可能会导致反压**,包括 TaskManager JVM 各区内存不合理导致的频繁 Full GC 甚至失联。推荐可以通过给 TaskManager 启用 G1 垃圾回收器来优化 GC,并加上 -XX:+PrintGCDetails 来打印 GC 日志的方式来观察 GC 的问题。 +> **此外,最常见的问题可能是用户代码的执行效率问题(频繁被阻塞或者性能问题)**。最有用的办法就是对 TaskManager 进行 CPU profile,从中我们可以分析到 Task Thread 是否跑满一个 CPU 核:如果是的话要分析 CPU 主要花费在哪些函数里面,比如我们生产环境中就偶尔遇到卡在 Regex 的用户函数(ReDoS);如果不是的话要看 Task Thread 阻塞在哪里,可能是用户函数本身有些同步的调用,可能是 checkpoint 或者 GC 等系统活动导致的暂时系统暂停。 +> 当然,性能分析的结果也可能是正常的,只是作业申请的资源不足而导致了反压,这就通常要求拓展并行度。值得一提的,在未来的版本 Flink 将会直接在 WebUI 提供 JVM 的 CPU 火焰图[5],这将大大简化性能瓶颈的分析。 +> **另外 TaskManager 的内存以及 GC 问题也可能会导致反压**,包括 TaskManager JVM 各区内存不合理导致的频繁 Full GC 甚至失联。推荐可以通过给 TaskManager 启用 G1 垃圾回收器来优化 GC,并加上 -XX:+PrintGCDetails 来打印 GC 日志的方式来观察 GC 的问题。 -## 测试解决 +# 测试解决 -* 调整sink to kafka为print打印控制台 +- 调整sink to kafka为print打印控制台 发现仍然存在反压问题,排除sink写入kafka速度过慢问题,因原来写es存在延迟因此改为kafka,因此这一次先排除这个问题。 -* 降低cep时间时间窗口大小,由3分钟-》1分钟-》20s +- 降低cep时间时间窗口大小,由3分钟-》1分钟-》20s 反压出现的时间越来越靠后,大体问题定位在cep算子相关,并且此时每次checkpoint的时间在增大,尽管state的大小相同但是时间成倍增大,故修改checkpoint相关配置继续测试发现问题仍然存在 -* 分析线程taskmanager线程占比,发现cep算子占用cpu百分之94,故增大算子并发度3为6线程cpu占用降低如下健康状态 +- 分析线程taskmanager线程占比,发现cep算子占用cpu百分之94,故增大算子并发度3为6线程cpu占用降低如下健康状态 + +  +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034352181-0867f21e-a060-44fc-932d-970afc6634d8.png#align=left&display=inline&height=1530&margin=%5Bobject%20Object%5D&originHeight=1235&originWidth=1920&size=0&status=done&style=none&width=2378) + +- 反压经历1个时间也没有再出现,后续会持续跟进,并且会尝试调大cep的时间窗口,尝试配置出最佳实践 +- 增大分区后发现数据倾斜严重,因为kafka分区为3,但是并行度为6因此cep算子的6个subtask数据倾斜严重,因此在添加source端执行reblance方法,强制轮询方式分配数据 -![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587034352181-0867f21e-a060-44fc-932d-970afc6634d8.png#align=left\&display=inline\&height=1530\&margin=%5Bobject%20Object%5D\&originHeight=1235\&originWidth=1920\&size=0\&status=done\&style=none\&width=2378) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587092048094-5efc6856-0b8f-48e4-beed-76dc58c70a0c.png#align=left&display=inline&height=1011&margin=%5Bobject%20Object%5D&originHeight=1011&originWidth=1920&size=0&status=done&style=none&width=2618) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587092048384-b2fb5b8d-2019-4632-8d5e-0b73dc27adea.png#align=left&display=inline&height=698&margin=%5Bobject%20Object%5D&originHeight=698&originWidth=1838&size=0&status=done&style=none&width=1838) +![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587092048119-cd6fa5ae-0d94-41b7-8487-40a99cfa1513.png#align=left&display=inline&height=748&margin=%5Bobject%20Object%5D&originHeight=748&originWidth=1884&size=0&status=done&style=none&width=1884) -* 反压经历1个时间也没有再出现,后续会持续跟进,并且会尝试调大cep的时间窗口,尝试配置出最佳实践 -* 增大分区后发现数据倾斜严重,因为kafka分区为3,但是并行度为6因此cep算子的6个subtask数据倾斜严重,因此在添加source端执行reblance方法,强制轮询方式分配数据 +- 可以看出来这里数据相比以前均匀很多 -![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587092048094-5efc6856-0b8f-48e4-beed-76dc58c70a0c.png#align=left\&display=inline\&height=1011\&margin=%5Bobject%20Object%5D\&originHeight=1011\&originWidth=1920\&size=0\&status=done\&style=none\&width=2618) ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587092048384-b2fb5b8d-2019-4632-8d5e-0b73dc27adea.png#align=left\&display=inline\&height=698\&margin=%5Bobject%20Object%5D\&originHeight=698\&originWidth=1838\&size=0\&status=done\&style=none\&width=1838) ![](https://cdn.nlark.com/yuque/0/2020/png/361846/1587092048119-cd6fa5ae-0d94-41b7-8487-40a99cfa1513.png#align=left\&display=inline\&height=748\&margin=%5Bobject%20Object%5D\&originHeight=748\&originWidth=1884\&size=0\&status=done\&style=none\&width=1884) -* 可以看出来这里数据相比以前均匀很多 -## Cep配置 +# Cep配置 -* 并行度,kafka分区的double -* cep时间窗口:30s -* sink:2个sink到kafka +- 并行度,kafka分区的double +- cep时间窗口:30s +- sink:2个sink到kafka diff --git a/bigdata/engine/flink/sourcecode/README.md b/bigdata/engine/flink/sourcecode/README.md deleted file mode 100644 index 4f028f3d..00000000 --- a/bigdata/engine/flink/sourcecode/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# sourcecode - diff --git "a/bigdata/engine/flink/sourcecode/TaskExecutor\345\206\205\345\255\230\346\250\241\345\236\213\345\216\237\347\220\206\346\267\261\345\205\245.md" "b/bigdata/engine/flink/sourcecode/TaskExecutor\345\206\205\345\255\230\346\250\241\345\236\213\345\216\237\347\220\206\346\267\261\345\205\245.md" index eb8295fd..4ae19a00 100644 --- "a/bigdata/engine/flink/sourcecode/TaskExecutor\345\206\205\345\255\230\346\250\241\345\236\213\345\216\237\347\220\206\346\267\261\345\205\245.md" +++ "b/bigdata/engine/flink/sourcecode/TaskExecutor\345\206\205\345\255\230\346\250\241\345\236\213\345\216\237\347\220\206\346\267\261\345\205\245.md" @@ -1,53 +1,51 @@ -# TaskExecutor内存模型原理深入 +# TaskManager内存配置 -## TaskManager内存配置 +## 内存配置 -### 内存配置 - -#### 配置概览 +### 配置概览 ![](../img/TaskExecutor内存配置.jpg) -#### 严格和非严格配置 +### 严格和非严格配置 ![](../img/Flink内存配置.jpg) * 不建议`total process`、`total flink`、`task Heap&Managed`同时配置两项以上,因为子项和总内存都配置的话会因为严格匹配的原因导致冲突,所以其中至少有一项配置。 -#### 配置参数描述 +### 配置参数描述 -| **组成部分** | **配置参数** | **描述** | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [框架堆内存(Framework Heap Memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_tm.html#framework-memory) | [`taskmanager.memory.framework.heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-framework-heap-size) | 用于 Flink 框架的 JVM 堆内存(进阶配置)。 | -| [任务堆内存(Task Heap Memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_tm.html#task-operator-heap-memory) | [`taskmanager.memory.task.heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-task-heap-size) | 用于 Flink 应用的算子及用户代码的 JVM 堆内存。 | -| [托管内存(Managed memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_tm.html#managed-memory) | [`taskmanager.memory.managed.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-managed-size) [`taskmanager.memory.managed.fraction`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-managed-fraction) | 由 Flink 管理的用于排序、哈希表、缓存中间结果及 RocksDB State Backend 的本地内存。 | -| [框架堆外内存(Framework Off-heap Memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_tm.html#framework-memory) | [`taskmanager.memory.framework.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-framework-off-heap-size) | 用于 Flink 框架的[堆外内存(直接内存或本地内存)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_tm.html#configure-off-heap-memory-direct-or-native)(进阶配置)。 | -| [任务堆外内存(Task Off-heap Memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_tm.html#configure-off-heap-memory-direct-or-native) | [`taskmanager.memory.task.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-task-off-heap-size) | 用于 Flink 应用的算子及用户代码的[堆外内存(直接内存或本地内存)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_tm.html#configure-off-heap-memory-direct-or-native)。 | -| 网络内存(Network Memory) | [`taskmanager.memory.network.min`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-network-min) [`taskmanager.memory.network.max`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-network-max) [`taskmanager.memory.network.fraction`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-network-fraction) | 用于任务之间数据传输的直接内存(例如网络传输缓冲)。该内存部分为基于 [Flink 总内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup.html#configure-total-memory)的[受限的等比内存部分](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup.html#capped-fractionated-components)。 | -| [JVM Metaspace](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup.html#jvm-parameters) | [`taskmanager.memory.jvm-metaspace.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-jvm-metaspace-size) | Flink JVM 进程的 Metaspace。 | -| JVM 开销 | [`taskmanager.memory.jvm-overhead.min`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-jvm-overhead-min) [`taskmanager.memory.jvm-overhead.max`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-jvm-overhead-max) [`taskmanager.memory.jvm-overhead.fraction`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-jvm-overhead-fraction) | 用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内存部分为基于[进程总内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup.html#configure-total-memory)的[受限的等比内存部分](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup.html#capped-fractionated-components)。 | +| **组成部分** | **配置参数** | **描述** | +| :----------------------------------------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| [框架堆内存(Framework Heap Memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_tm.html#framework-memory) | [`taskmanager.memory.framework.heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-framework-heap-size) | 用于 Flink 框架的 JVM 堆内存(进阶配置)。 | +| [任务堆内存(Task Heap Memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_tm.html#task-operator-heap-memory) | [`taskmanager.memory.task.heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-task-heap-size) | 用于 Flink 应用的算子及用户代码的 JVM 堆内存。 | +| [托管内存(Managed memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_tm.html#managed-memory) | [`taskmanager.memory.managed.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-managed-size) [`taskmanager.memory.managed.fraction`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-managed-fraction) | 由 Flink 管理的用于排序、哈希表、缓存中间结果及 RocksDB State Backend 的本地内存。 | +| [框架堆外内存(Framework Off-heap Memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_tm.html#framework-memory) | [`taskmanager.memory.framework.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-framework-off-heap-size) | 用于 Flink 框架的[堆外内存(直接内存或本地内存)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_tm.html#configure-off-heap-memory-direct-or-native)(进阶配置)。 | +| [任务堆外内存(Task Off-heap Memory)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_tm.html#configure-off-heap-memory-direct-or-native) | [`taskmanager.memory.task.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-task-off-heap-size) | 用于 Flink 应用的算子及用户代码的[堆外内存(直接内存或本地内存)](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_tm.html#configure-off-heap-memory-direct-or-native)。 | +| 网络内存(Network Memory) | [`taskmanager.memory.network.min`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-network-min) [`taskmanager.memory.network.max`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-network-max) [`taskmanager.memory.network.fraction`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-network-fraction) | 用于任务之间数据传输的直接内存(例如网络传输缓冲)。该内存部分为基于 [Flink 总内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup.html#configure-total-memory)的[受限的等比内存部分](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup.html#capped-fractionated-components)。 | +| [JVM Metaspace](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup.html#jvm-parameters) | [`taskmanager.memory.jvm-metaspace.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-jvm-metaspace-size) | Flink JVM 进程的 Metaspace。 | +| JVM 开销 | [`taskmanager.memory.jvm-overhead.min`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-jvm-overhead-min) [`taskmanager.memory.jvm-overhead.max`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-jvm-overhead-max) [`taskmanager.memory.jvm-overhead.fraction`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#taskmanager-memory-jvm-overhead-fraction) | 用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内存部分为基于[进程总内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup.html#configure-total-memory)的[受限的等比内存部分](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup.html#capped-fractionated-components)。 | -### 内存模型 +## 内存模型 -* Flink JVM 进程的\*进程总内存(Total Process Memory)\*包含了由 Flink 应用使用的内存(_Flink 总内存_)以及由运行 Flink 的 JVM 使用的内存。 _Flink 总内存(Total Flink Memory)包括 JVM 堆内存(Heap Memory)和堆外内存(Off-Heap Memory)。 其中堆外内存包括直接内存(Direct Memory)和本地内存(Native Memory)_。 +* Flink JVM 进程的*进程总内存(Total Process Memory)*包含了由 Flink 应用使用的内存(*Flink 总内存*)以及由运行 Flink 的 JVM 使用的内存。 *Flink 总内存(Total Flink Memory)*包括 *JVM 堆内存(Heap Memory)*和*堆外内存(Off-Heap Memory)*。 其中堆外内存包括*直接内存(Direct Memory)*和*本地内存(Native Memory)*。 -#### 内存分块图 +### 内存分块图 ![Simple memory model](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/detailed-mem-model.svg) ![](../img/TaskExecutor内存模型.jpg) -**Frameworks和Task 的区别** +#### Frameworks和Task 的区别 ![](../img/TaskExecutorFlink内存分布.jpg) * 区别:是否计入Slot资源 * 总用量限制 - * \-Xmx=FrameworkHeap+Task Heap - * \-Xx:MaxDiectMemorySize=...+Framework Off-heap+Task Off-Heap + * -Xmx=FrameworkHeap+Task Heap + * -Xx:MaxDiectMemorySize=...+Framework Off-heap+Task Off-Heap * Slot和Framwork的内存无隔离 -**Heap和Off-Heap Memory** +#### Heap和Off-Heap Memory * Heap * 大多数Java对象 @@ -59,7 +57,7 @@ * Native * JNI,C/C++,Python,etc -**NetWork Memory** +#### NetWork Memory * Direct Memory * 用于数据传输缓冲 @@ -77,7 +75,7 @@ gates 上游task数量 subpartitions 下游subtask数量 ``` -**Managed Memory** +#### Managed Memory * Native Memory * 用于:RocksDBStateBackend、Batch Operator、HeapStateBackend/无状态不需要Managed Meory可以设置为0 @@ -89,7 +87,7 @@ subpartitions 下游subtask数量 * 设置RocksDB使用内存大小为Managed Memory大小 * 目的:防止容器内存超用 -**JVM Metaspace& Overhead** +#### JVM Metaspace& Overhead * Metaspace * 存放JVM加载的类的元数据 @@ -104,36 +102,36 @@ subpartitions 下游subtask数量 * Code Cache * Thread Stack -### 内存特性 +## 内存特性 -\###Java内存类型 +###Java内存类型 ![](../img/Jvm内存分类.jpg) * Heap和OffHeap * 底层又分为Young和Old区域 -#### Heap Memory特性 +### Heap Memory特性 -* 包括:`Framework Heap Memory`和`Task heap Memory` +* 包括:`Framework Heap Memory`和`Task heap Memory` * 用量上限受于JVM控制 - * \-Xmx:Framework Heap+Task heap + * -Xmx:Framework Heap+Task heap * 达到上限后触发GC * GC后仍然空间不足,触发OOM异常并退出 -#### Direct Memory\&Metaspace特性 +### Direct Memory&Metaspace特性 -**Direct Meory** +#### Direct Meory * 包括Framework和Task的Off-Heap以及Netowrk memory * 用量受-XX:MaxDirectMemorySize限制 * 达到上限时触发GC,GC后仍然空间不足触发OOM异常并退出 -**Metaspace** +#### Metaspace * 受限制与-XX:MetaspaceSize -#### Native Memory特性 +### Native Memory特性 * Framework Off-Heap Memory(部分) * Task Off-Heap Memory(部分) @@ -141,46 +139,50 @@ subpartitions 下游subtask数量 * JVM Overhead * 用量上限不受JVM控制 -## JobManager内存 +# JobManager内存 -### 参数配置 +## 参数配置 -#### 配置总内存 +### 配置总内存 * jobmanager.memory.flink.size * jobmanager.memory.process.size -#### 详细配置 +### 详细配置 -![Flink's process memory model](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/process\_mem\_model.svg) +![Flink's process memory model](https://ci.apache.org/projects/flink/flink-docs-release-1.11/fig/process_mem_model.svg) -| **组成部分** | **配置参数** | **描述** | -| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [JVM 堆内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_jobmanager.html#configure-jvm-heap) | [`jobmanager.memory.heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-heap-size) | JobManager 的 _JVM 堆内存_。 | -| [堆外内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup\_jobmanager.html#configure-off-heap-memory) | [`jobmanager.memory.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-off-heap-size) | JobManager 的_堆外内存(直接内存或本地内存)_。 | -| [JVM Metaspace](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup.html#jvm-parameters) | [`jobmanager.memory.jvm-metaspace.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-jvm-metaspace-size) | Flink JVM 进程的 Metaspace。 | -| JVM 开销 | [`jobmanager.memory.jvm-overhead.min`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-jvm-overhead-min) [`jobmanager.memory.jvm-overhead.max`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-jvm-overhead-max) [`jobmanager.memory.jvm-overhead.fraction`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-jvm-overhead-fraction) | 用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内存部分为基于[进程总内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup.html#configure-total-memory)的[受限的等比内存部分](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem\_setup.html#capped-fractionated-components)。 | +| **组成部分** | **配置参数** | **描述** | +| :----------------------------------------------------------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| [JVM 堆内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_jobmanager.html#configure-jvm-heap) | [`jobmanager.memory.heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-heap-size) | JobManager 的 *JVM 堆内存*。 | +| [堆外内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup_jobmanager.html#configure-off-heap-memory) | [`jobmanager.memory.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-off-heap-size) | JobManager 的*堆外内存(直接内存或本地内存)*。 | +| [JVM Metaspace](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup.html#jvm-parameters) | [`jobmanager.memory.jvm-metaspace.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-jvm-metaspace-size) | Flink JVM 进程的 Metaspace。 | +| JVM 开销 | [`jobmanager.memory.jvm-overhead.min`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-jvm-overhead-min) [`jobmanager.memory.jvm-overhead.max`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-jvm-overhead-max) [`jobmanager.memory.jvm-overhead.fraction`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-jvm-overhead-fraction) | 用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内存部分为基于[进程总内存](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup.html#configure-total-memory)的[受限的等比内存部分](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/memory/mem_setup.html#capped-fractionated-components)。 | -### 内存特性 +## 内存特性 -#### 堆内内存 +### 堆内内存 * 以下会用到堆内内存 * 用于Flink框架 * 在作业提交时(例如一些特殊的批处理 Source)及 Checkpoint 完成的回调函数中执行的用户代码 * 需要多少堆内内存取决于运行的作业数量、作业的结构及上述用户代码需求。 -#### 堆外内存 +### 堆外内存 -* _堆外内存_包括 _JVM 直接内存_ 和 _本地内存_。 可以通过配置参数 [`jobmanager.memory.enable-jvm-direct-memory-limit`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-enable-jvm-direct-memory-limit) 设置是否启用 _JVM 直接内存限制_。 如果该配置项设置为 `true`,Flink 会根据配置的_堆外内存_大小设置 JVM 参数 _-XX:MaxDirectMemorySize_。 +* *堆外内存*包括 *JVM 直接内存* 和 *本地内存*。 可以通过配置参数 [`jobmanager.memory.enable-jvm-direct-memory-limit`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-enable-jvm-direct-memory-limit) 设置是否启用 *JVM 直接内存限制*。 如果该配置项设置为 `true`,Flink 会根据配置的*堆外内存*大小设置 JVM 参数 *-XX:MaxDirectMemorySize*。 * 可以通过配置参数 [`jobmanager.memory.off-heap.size`](https://ci.apache.org/projects/flink/flink-docs-release-1.11/zh/ops/config.html#jobmanager-memory-off-heap-size) 设置堆外内存的大小。 如果遇到 JobManager 进程抛出 “OutOfMemoryError: Direct buffer memory” 的异常,可以尝试调大这项配置。 * 以下情况可能用到堆外内存: - * Flink 框架依赖(例如 Akka 的网络通信) - * 在作业提交时(例如一些特殊的批处理 Source)及 Checkpoint 完成的回调函数中执行的用户代码 + - Flink 框架依赖(例如 Akka 的网络通信) + - 在作业提交时(例如一些特殊的批处理 Source)及 Checkpoint 完成的回调函数中执行的用户代码 -## 工具 +# 工具 -### bash-java-utils.jar +## bash-java-utils.jar * Flink目录bin下的bash-java-utils.jar + * https://github.com/KarmaGYZ/flink-memory-calculator + + + diff --git a/bigdata/engine/spark/README.md b/bigdata/engine/spark/README.md deleted file mode 100644 index 110f2048..00000000 --- a/bigdata/engine/spark/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# spark - diff --git a/bigdata/engine/spark/practice/README.md b/bigdata/engine/spark/practice/README.md deleted file mode 100644 index 313a83d5..00000000 --- a/bigdata/engine/spark/practice/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# practice - diff --git a/bigdata/engine/spark/spark sql/SparkSQL API.md b/bigdata/engine/spark/spark sql/SparkSQL API.md index c0be6fbc..6c1fba74 100644 --- a/bigdata/engine/spark/spark sql/SparkSQL API.md +++ b/bigdata/engine/spark/spark sql/SparkSQL API.md @@ -1,16 +1,16 @@ -# SparkSQL API +# Structured API基本使用 -## Structured API基本使用 + -[一、创建DataFrame和Dataset]()\ -[二、Columns列操作]()\ -[三、使用Structured API进行基本查询]()\ -[四、使用Spark SQL进行基本查询]()\ +## 一、创建DataFrame和Dataset -### 一、创建DataFrame和Dataset - -#### 1.1 创建DataFrame +### 1.1 创建DataFrame Spark 中所有功能的入口点是 `SparkSession`,可以使用 `SparkSession.builder()` 创建。创建后应用程序就可以从现有 RDD,Hive 表或 Spark 数据源创建 DataFrame。示例如下: @@ -25,14 +25,15 @@ import spark.implicits._ 可以使用 `spark-shell` 进行测试,需要注意的是 `spark-shell` 启动后会自动创建一个名为 `spark` 的 `SparkSession`,在命令行中可以直接引用即可: -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/spark-sql-shell.png)\ +
+
-#### 1.2 创建Dataset +### 1.2 创建Dataset Spark 支持由内部数据集和外部数据集来创建 DataSet,其创建方式分别如下: -**1. 由外部数据集创建** +#### 1. 由外部数据集创建 ```scala // 1.需要导入隐式转换 @@ -47,7 +48,7 @@ val ds = spark.read.json("/usr/file/emp.json").as[Emp] ds.show() ``` -**2. 由内部数据集创建** +#### 2. 由内部数据集创建 ```scala // 1.需要导入隐式转换 @@ -64,11 +65,11 @@ val caseClassDS = Seq(Emp("ALLEN", 300.0, 30, 7499, "1981-02-20 00:00:00", "SALE caseClassDS.show() ``` -#### 1.3 由RDD创建DataFrame +### 1.3 由RDD创建DataFrame Spark 支持两种方式把 RDD 转换为 DataFrame,分别是使用反射推断和指定 Schema 转换: -**1. 使用反射推断** +#### 1. 使用反射推断 ```scala // 1.导入隐式转换 @@ -85,7 +86,7 @@ val rddToDS = spark.sparkContext .toDS() // 如果调用 toDF() 则转换为 dataFrame ``` -**2. 以编程方式指定Schema** +#### 2. 以编程方式指定Schema ```scala import org.apache.spark.sql.Row @@ -110,7 +111,7 @@ val deptDF = spark.createDataFrame(rowRDD, schema) deptDF.show() ``` -#### 1.4 DataFrames与Datasets互相转换 +### 1.4 DataFrames与Datasets互相转换 Spark 提供了非常简单的转换方法用于 DataFrame 与 Dataset 间的互相转换,示例如下: @@ -124,11 +125,11 @@ scala> ds.toDF() res2: org.apache.spark.sql.DataFrame = [COMM: double, DEPTNO: bigint ... 6 more fields] ``` -### 二、Columns列操作 +## 二、Columns列操作 -#### 2.1 引用列 +### 2.1 引用列 -Spark 支持多种方法来构造和引用列,最简单的是使用 `col()` 或 `column()` 函数。 +Spark 支持多种方法来构造和引用列,最简单的是使用 `col() ` 或 `column() ` 函数。 ```scala col("colName") @@ -139,7 +140,7 @@ df.select($"ename", $"job").show() df.select('ename, 'job).show() ``` -#### 2.2 新增列 +### 2.2 新增列 ```scala // 基于已有列值新增列 @@ -148,14 +149,14 @@ df.withColumn("upSal",$"sal"+1000) df.withColumn("intCol",lit(1000)) ``` -#### 2.3 删除列 +### 2.3 删除列 ```scala // 支持删除多个列 df.drop("comm","job").show() ``` -#### 2.4 重命名列 +### 2.4 重命名列 ```scala df.withColumnRenamed("comm", "common").show() @@ -163,10 +164,9 @@ df.withColumnRenamed("comm", "common").show() 需要说明的是新增,删除,重命名列都会产生新的 DataFrame,原来的 DataFrame 不会被改变。 -\ - +
-### 三、使用Structured API进行基本查询 +## 三、使用Structured API进行基本查询 ```scala // 1.查询员工姓名及工作 @@ -188,12 +188,11 @@ df.select("deptno").distinct().show() df.groupBy("deptno").count().show() ``` -\ +
+## 四、使用Spark SQL进行基本查询 -### 四、使用Spark SQL进行基本查询 - -#### 4.1 Spark SQL基本使用 +### 4.1 Spark SQL基本使用 ```scala // 1.首先需要将 DataFrame 注册为临时视图 @@ -218,7 +217,7 @@ spark.sql("SELECT DISTINCT(deptno) FROM emp").show() spark.sql("SELECT deptno,count(ename) FROM emp group by deptno").show() ``` -#### 4.2 全局临时视图 +### 4.2 全局临时视图 上面使用 `createOrReplaceTempView` 创建的是会话临时视图,它的生命周期仅限于会话范围,会随会话的结束而结束。 @@ -232,32 +231,32 @@ df.createGlobalTempView("gemp") spark.sql("SELECT ename,job FROM global_temp.gemp").show() ``` -## 聚合函数 +# 聚合函数 -### 简单聚合 +## 简单聚合 -#### count +### count ```scala df.select(count($"id")).show() ``` -#### countDistinct +### countDistinct ```scala // 计算姓名不重复的员工人数 empDF.select(countDistinct("deptno")).show() ``` -#### approx\_count\_distinct +### approx_count_distinct -* 通常在使用大型数据集时,你可能关注的只是近似值而不是准确值,这时可以使用 approx\_count\_distinct 函数,并可以使用第二个参数指定最大允许误差。 +* 通常在使用大型数据集时,你可能关注的只是近似值而不是准确值,这时可以使用 approx_count_distinct 函数,并可以使用第二个参数指定最大允许误差。 ```scala empDF.select(approx_count_distinct("ename",0.1)).show() ``` -#### first & last +### first & last * 获取 DataFrame 中指定列的第一个值或者最后一个值。 @@ -265,7 +264,7 @@ empDF.select(approx_count_distinct("ename",0.1)).show() empDF.select(first("ename"),last("job")).show() ``` -#### min & max +### min & max * 获取 DataFrame 中指定列的最小值或者最大值。 @@ -273,7 +272,7 @@ empDF.select(first("ename"),last("job")).show() empDF.select(min("sal"),max("sal")).show() ``` -#### sum & sumDistinct +### sum & sumDistinct * 求和以及求指定列所有不相同的值的和。 @@ -282,7 +281,7 @@ empDF.select(sum("sal")).show() empDF.select(sumDistinct("sal")).show() ``` -#### avg +### avg * 内置的求平均数的函数。 @@ -290,25 +289,26 @@ empDF.select(sumDistinct("sal")).show() empDF.select(avg("sal")).show() ``` -#### 聚合数据到集合 +### 聚合数据到集合 ```scala empDF.agg(collect_set("job"), collect_list("ename")).show() ``` -## Join +# Join Spark 中支持多种连接类型: -* **Inner Join** : 内连接; -* **Full Outer Join** : 全外连接; -* **Left Outer Join** : 左外连接; -* **Right Outer Join** : 右外连接; -* **Left Semi Join** : 左半连接; -* **Left Anti Join** : 左反连接; -* **Natural Join** : 自然连接; -* **Cross (or Cartesian) Join** : 交叉 (或笛卡尔) 连接。 ++ **Inner Join** : 内连接; ++ **Full Outer Join** : 全外连接; ++ **Left Outer Join** : 左外连接; ++ **Right Outer Join** : 右外连接; ++ **Left Semi Join** : 左半连接; ++ **Left Anti Join** : 左反连接; ++ **Natural Join** : 自然连接; ++ **Cross (or Cartesian) Join** : 交叉 (或笛卡尔) 连接。 其中内,外连接,笛卡尔积均与普通关系型数据库中的相同,如下图所示: -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/sql-join.jpg) +
+ diff --git a/bigdata/engine/spark/spark-sql/sparksql-you-hua-fen-xi.md "b/bigdata/engine/spark/spark sql/SparkSQL\344\274\230\345\214\226\345\210\206\346\236\220.md" similarity index 100% rename from bigdata/engine/spark/spark-sql/sparksql-you-hua-fen-xi.md rename to "bigdata/engine/spark/spark sql/SparkSQL\344\274\230\345\214\226\345\210\206\346\236\220.md" diff --git "a/bigdata/engine/spark/spark streaming/SparkStreaming\346\225\264\345\220\210Flume.md" "b/bigdata/engine/spark/spark streaming/SparkStreaming\346\225\264\345\220\210Flume.md" index d5c96dec..9f63865d 100644 --- "a/bigdata/engine/spark/spark streaming/SparkStreaming\346\225\264\345\220\210Flume.md" +++ "b/bigdata/engine/spark/spark streaming/SparkStreaming\346\225\264\345\220\210Flume.md" @@ -1,14 +1,12 @@ -# SparkStreaming整合Flume - -### 一、简介 +## 一、简介 Apache Flume 是一个分布式,高可用的数据收集系统,可以从不同的数据源收集数据,经过聚合后发送到分布式计算框架或者存储系统中。Spark Straming 提供了以下两种方式用于 Flume 的整合。 -### 二、推送式方法 +## 二、推送式方法 在推送式方法 (Flume-style Push-based Approach) 中,Spark Streaming 程序需要对某台服务器的某个端口进行监听,Flume 通过 `avro Sink` 将数据源源不断推送到该端口。这里以监听日志文件为例,具体整合方式如下: -#### 2.1 配置日志收集Flume +### 2.1 配置日志收集Flume 新建配置 `netcat-memory-avro.properties`,使用 `tail` 命令监听文件内容变化,然后将新的文件内容通过 `avro sink` 发送到 hadoop001 这台服务器的 8888 端口: @@ -37,7 +35,7 @@ a1.channels.c1.capacity = 1000 a1.channels.c1.transactionCapacity = 100 ``` -#### 2.2 项目依赖 +### 2.2 项目依赖 项目采用 Maven 工程进行构建,主要依赖为 `spark-streaming` 和 `spark-streaming-flume`。 @@ -61,9 +59,10 @@ a1.channels.c1.transactionCapacity = 100 2.4.3 + ``` -#### 2.3 Spark Streaming接收日志数据 +### 2.3 Spark Streaming接收日志数据 调用 FlumeUtils 工具类的 `createStream` 方法,对 hadoop001 的 8888 端口进行监听,获取到流数据并进行打印: @@ -88,12 +87,13 @@ object PushBasedWordCount { } ``` -#### 2.4 项目打包 +### 2.4 项目打包 因为 Spark 安装目录下是不含有 `spark-streaming-flume` 依赖包的,所以在提交到集群运行时候必须提供该依赖包,你可以在提交命令中使用 `--jar` 指定上传到服务器的该依赖包,或者使用 `--packages org.apache.spark:spark-streaming-flume_2.12:2.4.3` 指定依赖包的完整名称,这样程序在启动时会先去中央仓库进行下载。 这里我采用的是第三种方式:使用 `maven-shade-plugin` 插件进行 `ALL IN ONE` 打包,把所有依赖的 Jar 一并打入最终包中。需要注意的是 `spark-streaming` 包在 Spark 安装目录的 `jars` 目录中已经提供,所以不需要打入。插件配置如下: + ```xml @@ -182,14 +182,13 @@ object PushBasedWordCount { ``` +使用 `mvn clean package` 命令打包后会生产以下两个 Jar 包,提交 ` 非 original` 开头的 Jar 即可。 -使用 `mvn clean package` 命令打包后会生产以下两个 Jar 包,提交 `非 original` 开头的 Jar 即可。 - -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/spark-streaming-flume-jar.png) +
-#### 2.5 启动服务和提交作业 +### 2.5 启动服务和提交作业 -启动 Flume 服务: + 启动 Flume 服务: ```shell flume-ng agent \ @@ -207,36 +206,35 @@ spark-submit \ /usr/appjar/spark-streaming-flume-1.0.jar ``` -#### 2.6 测试 +### 2.6 测试 这里使用 `echo` 命令模拟日志产生的场景,往日志文件中追加数据,然后查看程序的输出: -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/spark-flume-input.png) +
Spark Streaming 程序成功接收到数据并打印输出: -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/spark-flume-console.png) +
-#### 2.7 注意事项 +### 2.7 注意事项 -**1. 启动顺序** +#### 1. 启动顺序 这里需要注意的,不论你先启动 Spark 程序还是 Flume 程序,由于两者的启动都需要一定的时间,此时先启动的程序会短暂地抛出端口拒绝连接的异常,此时不需要进行任何操作,等待两个程序都启动完成即可。 -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/flume-retry.png) +
-**2. 版本一致** +#### 2. 版本一致 最好保证用于本地开发和编译的 Scala 版本和 Spark 的 Scala 版本一致,至少保证大版本一致,如都是 `2.11`。 -\ +
- -### 三、拉取式方法 +## 三、拉取式方法 拉取式方法 (Pull-based Approach using a Custom Sink) 是将数据推送到 `SparkSink` 接收器中,此时数据会保持缓冲状态,Spark Streaming 定时从接收器中拉取数据。这种方式是基于事务的,即只有在 Spark Streaming 接收和复制数据完成后,才会删除缓存的数据。与第一种方式相比,具有更强的可靠性和容错保证。整合步骤如下: -#### 3.1 配置日志收集Flume +### 3.1 配置日志收集Flume 新建 Flume 配置文件 `netcat-memory-sparkSink.properties`,配置和上面基本一致,只是把 `a1.sinks.k1.type` 的属性修改为 `org.apache.spark.streaming.flume.sink.SparkSink`,即采用 Spark 接收器。 @@ -265,7 +263,7 @@ a1.channels.c1.capacity = 1000 a1.channels.c1.transactionCapacity = 100 ``` -#### 2.2 新增依赖 +### 2.2 新增依赖 使用拉取式方法需要额外添加以下两个依赖: @@ -284,7 +282,7 @@ a1.channels.c1.transactionCapacity = 100 注意:添加这两个依赖只是为了本地测试,Spark 的安装目录下已经提供了这两个依赖,所以在最终打包时需要进行排除。 -#### 2.3 Spark Streaming接收日志数据 +### 2.3 Spark Streaming接收日志数据 这里和上面推送式方法的代码基本相同,只是将调用方法改为 `createPollingStream`。 @@ -309,7 +307,7 @@ object PullBasedWordCount { } ``` -#### 2.4 启动测试 +### 2.4 启动测试 启动和提交作业流程与上面相同,这里给出执行脚本,过程不再赘述。 @@ -324,9 +322,9 @@ flume-ng agent \ 提交 Spark Streaming 作业: -``` +```shel spark-submit \ --class com.heibaiying.flume.PullBasedWordCount \ --master local[4] \ /usr/appjar/spark-streaming-flume-1.0.jar -``` +``` \ No newline at end of file diff --git a/bigdata/engine/spark/spark-sql/README.md b/bigdata/engine/spark/spark-sql/README.md deleted file mode 100644 index f1e44798..00000000 --- a/bigdata/engine/spark/spark-sql/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# spark sql - diff --git a/bigdata/engine/spark/spark-streaming/README.md b/bigdata/engine/spark/spark-streaming/README.md deleted file mode 100644 index 7429c3db..00000000 --- a/bigdata/engine/spark/spark-streaming/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# spark streaming - diff --git a/bigdata/engine/spark/yuan-ma-fen-xi/README.md b/bigdata/engine/spark/yuan-ma-fen-xi/README.md deleted file mode 100644 index a34a56b9..00000000 --- a/bigdata/engine/spark/yuan-ma-fen-xi/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 源码分析 - diff --git a/bigdata/engine/spark/yuan-ma-fen-xi/spark-he-xin-dui-xiang.md b/bigdata/engine/spark/yuan-ma-fen-xi/spark-he-xin-dui-xiang.md deleted file mode 100644 index c194e0a8..00000000 --- a/bigdata/engine/spark/yuan-ma-fen-xi/spark-he-xin-dui-xiang.md +++ /dev/null @@ -1,87 +0,0 @@ -# Spark核心对象 - -## BlockManager数据存储与管理机制 - -* BlockManager是整个Spark底层负责数据存储与管理的一个组件,Driver和Executor的所有数据都由对应的BlockManager进行管理。 -* Driver上有BlockManagerMaster,负责对各个节点上BlockManager内部管理的数据的元数据进行维护,比如block的增删改查等操作,都会在这里维护好元数据的变更。 -* 每个节点都有一个BlockManager,每个BlockManager创建之后,第一件事情是向BlockManagerMaster进行注册,此时BlockManagerMaster会为其创建对应的BlockManagerInfo - -![BlockManager原理](../源码分析/img/BlockManager原理.jpg) - -* **BlockManagerMaster与BlockManager类似于NameNode和DataNode的关系:BlockManagerMaster中保存中BlockManager内部管理数据的元数据,进行维护,当BlockManager进行Block增删改等操作,都会在BlockManagerMaster中进行元数据的变更。** -* 每个节点上都有一个BlockManager,BlockManager中有3个核心组件: - * DiskStore:负责对磁盘数据进行读写 - * MemoryStore:负责对内存数据进行读写 - * BlockTransferService:负责建立BlockManager到远程其他节点的BlockManager连接,负责对远程其他节点的BlockManager的数据进行读写。 -* **每个BlockManager创建之后,会向BlockManagerMaster进行注册,此时BlockManagerMaster会为其创建对应的BlockManagerInfo。使用BlockManager进行写操作时,如果使用persist会优先将数据写入内存中,如果内存大小不够,会使用自己的算法,将内存中的部分写入磁盘(根据StoageLevel);如果persist指定了副本会使用BlockTransferService**向其他节点写入一份副本。使用BlockManager进行读操作时,对于ShuffleRead操作,如果能从本地节点读取,就利用DiskStore或者MemoryStore从本地读取,如果没有的话就会利用**BlockTransferService**从其他节点的BlockManager建立连接来读取数据。 -* 如果使用BlockManager进行增删改操作,就会将Block的BlockStatus上报到BlockManagerMaster,在BlockamangerMaster上会对指定BlockManager的BlockManagerInfo内部的BlockStatus进行增删改查操作,从而来维护元数据。 - -## Spark共享变量底层实现 - -* 默认情况下,一个算子的函数中如果使用到外部变量,那么这个**变量就会被拷贝到每个task中**,此时每个task只能操作自己的那份副本,类似于copyOnWrite的概念,无法做到多个task共享一份变量。 -* Spark提供了**Broadcast Variable和Accumulator累加变量**,Broadcast Variable将用到的变量,仅仅**为每个节点拷贝一份**,即每个**Executor**拷贝一份,这样可以优化性能,减少资源和网络传输的消耗。Accumulator可以让**多个task共同操作一份变量**,主要可以进行变量累加操作。 - -### 闭包源码 - -* 下类可以看下闭包 - -``` -类ClosureCleaner -``` - -### Scala序列化 - -* 重写writeReplace方法,scala序列化会利用java的方式,java在序列化时会判断是否存在该方法,存在就调用,spark累加器利用这一特性实现对数据的判断 - - ```scala - // Called by Java when serializing an object - final protected def writeReplace(): Any = { - if (atDriverSide) { - if (!isRegistered) { - throw new UnsupportedOperationException( - "Accumulator must be registered before send to executor") - } - val copyAcc = copyAndReset() - assert(copyAcc.isZero, "copyAndReset must return a zero value copy") - val isInternalAcc = name.isDefined && name.get.startsWith(InternalAccumulator.METRICS_PREFIX) - if (isInternalAcc) { - // Do not serialize the name of internal accumulator and send it to executor. - copyAcc.metadata = metadata.copy(name = None) - } else { - // For non-internal accumulators, we still need to send the name because users may need to - // access the accumulator name at executor side, or they may keep the accumulators sent from - // executors and access the name when the registered accumulator is already garbage - // collected(e.g. SQLMetrics). - copyAcc.metadata = metadata - } - copyAcc - } else { - this - } - } - - --- ObjectOutputStream - if (!desc.hasWriteReplaceMethod() || - (obj = desc.invokeWriteReplace(obj)) == null || - (repCl = obj.getClass()) == cl) - { - break; - } - cl = repCl; - ``` - - ### 广播变量 - - * **广播变量是在每个Executor上保留外部数据的只读变量,而不是给每个任务发送一个副本**,每个task都会保存一份它所使用的外部变量时,对于Executor内存的消耗是非常大的,因此可以将大型外部变量封装成广播变量,此时一个Executor保存一个变量副本,此Executor上所有task共用此变量,**不再是一个task单独保存一个副本,这在一定程度降低了Spark任务的内存占用。** - * Spark尝试使用高效的广播算法分发广播变量,以降低通信成本,Spark提供的Broadcast Variable是只读的,并且在每个Executor上只会有一个副本,而不会为每个task都拷贝一份副本,因此,它的最大作用,**就是减少变量到各个节点的网络传输消耗。** - -![task使用外部变量](../源码分析/img/task使用的外部变量.jpg) - -![广播变量](../源码分析/img/广播变量.jpg) - -### 累加器 - -* Accumulator是仅仅被相关操作累加的变量,因此可以并行中被有效支持,可以实现计数器或总和计数。 -* \*\*Accumulator存储在Driver端,集群上运行的task进行Accumulator的累加,随后把值发送给Driver端,在Driver端汇总,由于Accumulator存在Driver端,从节点读取不到Accumulator的数值。\*\*Spark中的主要用于多节点对一个变量进行共享性的操作,task只能对其进行累加,不能对其进行读取。 - -![Accumulator原理](../源码分析/img/Accumulator累加器.jpg) diff --git "a/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\345\206\205\345\255\230\347\256\241\347\220\206.md" "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\345\206\205\345\255\230\347\256\241\347\220\206.md" index 0215f1ad..64a766a9 100644 --- "a/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\345\206\205\345\255\230\347\256\241\347\220\206.md" +++ "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\345\206\205\345\255\230\347\256\241\347\220\206.md" @@ -1,50 +1,50 @@ -# Spark内存管理 - [Spark内存管理详解](https://www.ibm.com/developerworks/cn/analytics/library/ba-cn-apache-spark-memory-management/index.html) -## Driver和Executor +# Driver和Executor * Driver为主控进程,负责创建Spark上下文,提交Spark作业(Job),并将作业转化为计算任务(Task),在各个Executor进程间协调任务的调度。 * Executor负责计算逻辑,上报自身状态给ApplicationMaster,计算结果返回给Driver,为持久化RDD提供功能。 -## 堆内和堆外内存规划 +# 堆内和堆外内存规划 * 作为JVM进程,**Executor的内存管理建立在JVM的内存管理上,Spark对JVM的堆内(On-heap)空间进行了更为详细的分配,以充分利用内存。** Spark也引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,进一步优化了内存的使用。 + * **堆内内存受到JVM统一管理,堆外内存是直接向操作系统进行内存的申请和释放。** -![堆内和堆外](img/Executor堆外与堆内内存.jpg) +![堆内和堆外](./img/Executor堆外与堆内内存.jpg) -### 堆内内存 +## 堆内内存 -* 堆内内存大小,由Spark应用程序启动时通过指定\*\*--executor-memory**或**spark.executor.memory\*\*参数配置。Executor内运行的并发任务共享JVM堆内内存,**这些任务在缓存RDD数据和广播(Broadcast)数据时占用的内存被规划为存储(Storage)内存**,\*\*这些任务在执行Shuffle时占用的内存被规划为执行(Execution)内存,剩余部分不做特殊规划,那些Spark内部的对象实例,或者用户定义的Spark应用程序中的对象实例,均占用剩余空间。\*\*不同的内存管理方式,三个部分占用的内存空间是不同的。 +* 堆内内存大小,由Spark应用程序启动时通过指定**--executor-memory**或**spark.executor.memory**参数配置。Executor内运行的并发任务共享JVM堆内内存,**这些任务在缓存RDD数据和广播(Broadcast)数据时占用的内存被规划为存储(Storage)内存**,**这些任务在执行Shuffle时占用的内存被规划为执行(Execution)内存,剩余部分不做特殊规划,那些Spark内部的对象实例,或者用户定义的Spark应用程序中的对象实例,均占用剩余空间。**不同的内存管理方式,三个部分占用的内存空间是不同的。 -#### 规划式管理 +### 规划式管理 * Spark会在申请后和释放前记录这些内存 -#### 申请内存流程 +### 申请内存流程 1. Spark在代码new一个对象实例 2. 对象实例会去堆内存申请空间,并且创建对象,然后将引用返回到栈上。 3. Spark保存该对象的引用,记录该对象占用的内存。 -#### 释放内存 +### 释放内存 1. Spark记录该对象释放的内存,删除对象引用。 2. 此时会等待JVM的GC回收该对象在堆上占用的内存。 -#### OOM存在的原因 +### OOM存在的原因 -> JVM 的对象可以以序列化的方式存储,序列化的过程是将对象转换为二进制字节流,本质上可以理解为将非连续空间的链式存储转化为连续空间或块存储,在访问时则需要进行序列化的逆过程——反序列化,将字节流转化为对象,序列化的方式可以节省存储空间,但增加了存储和读取时候的计算开销。 Spark 中序列化的对象,由于是字节流的形式,其占用的内存大小可直接计算,而对于非序列化的对象,其占用的内存是通过周期性地采样近似估算而得,即并不是每次新增的数据项都会计算一次占用的内存大小,这种方法降低了时间开销但是有可能误差较大,导致某一时刻的实际内存有可能远远超出预期。此外,在被 Spark 标记为释放的对象实例,很有可能在实际上并没有被 JVM 回收,导致实际可用的内存小于 Spark 记录的可用内存。所以 Spark 并不能准确记录实际可用的堆内内存,从而也就无法完全避免内存溢出(OOM, Out of Memory)的异常。 +> JVM 的对象可以以序列化的方式存储,序列化的过程是将对象转换为二进制字节流,本质上可以理解为将非连续空间的链式存储转化为连续空间或块存储,在访问时则需要进行序列化的逆过程——反序列化,将字节流转化为对象,序列化的方式可以节省存储空间,但增加了存储和读取时候的计算开销。 +> Spark 中序列化的对象,由于是字节流的形式,其占用的内存大小可直接计算,而对于非序列化的对象,其占用的内存是通过周期性地采样近似估算而得,即并不是每次新增的数据项都会计算一次占用的内存大小,这种方法降低了时间开销但是有可能误差较大,导致某一时刻的实际内存有可能远远超出预期。此外,在被 Spark 标记为释放的对象实例,很有可能在实际上并没有被 JVM 回收,导致实际可用的内存小于 Spark 记录的可用内存。所以 Spark 并不能准确记录实际可用的堆内内存,从而也就无法完全避免内存溢出(OOM, Out of Memory)的异常。 > -> 虽然不能精准控制堆内内存的申请和释放,但 Spark 通过对存储内存和执行内存各自独立的规划管理,可以决定是否要在存储内存里缓存新的 RDD,以及是否为新的任务分配执行内存,在一定程度上可以提升内存的利用率,减少异常的出现。 +> 虽然不能精准控制堆内内存的申请和释放,但 Spark 通过对存储内存和执行内存各自独立的规划管理,可以决定是否要在存储内存里缓存新的 RDD,以及是否为新的任务分配执行内存,在一定程度上可以提升内存的利用率,减少异常的出现。 -### 堆外内存 +## 堆外内存 * 为了进一步优化shuffle时排序的效率,引入堆外内存可以直接在工作节点的系统内存中开辟空间,存储经过序列化的二进制数据。将内存对象分配在Jvm的堆以外的内存,这些内存直接受操作系统管理,从而减少JVM GC堆应用的影响。 * 默认情况下不启用堆外内存,通过配置**spark.memory.offHeap.enabled**参数启用,由**spark.memory.offHeap.size**参数设定堆外空间的大小。除了没有other空间,堆外内存与堆内内存划分方式相同,所有运行中的并发任务共享存储。 -### 内存管理接口 +## 内存管理接口 ```scala /** @@ -60,7 +60,7 @@ private[spark] abstract class MemoryManager( onHeapExecutionMemory: Long) //堆内 execution memory ``` -#### 主要方法 +### 主要方法 ```scala //申请存储内存 @@ -79,13 +79,13 @@ def releaseUnrollMemory(numBytes: Long, memoryMode: MemoryMode): Unit * 这里的MemoryMode就是使用cache需要指定的内存模式,根据这个参数决定是堆内操作还是堆外操作。 -## 内存空间分配 +# 内存空间分配 -### 静态内存管理 +## 静态内存管理 * 早期采用的方式,**存储内存、执行内存和其他内存的大小是固定的,用户可以指定相关配置** -![静态内存管理](img/静态内存划分.jpg) +![静态内存管理](./img/静态内存划分.jpg) ``` 可用的存储内存 = systemMaxMemory * spark.storage.memoryFraction * spark.storage.safetyFraction @@ -94,44 +94,44 @@ def releaseUnrollMemory(numBytes: Long, memoryMode: MemoryMode): Unit 其中 systemMaxMemory 取决于当前 JVM 堆内内存的大小,最后可用的执行内存或者存储内存要在此基础上与各自的 memoryFraction 参数和 safetyFraction 参数相乘得出。上述计算公式中的两个 safetyFraction 参数,其意义在于在逻辑上预留出 1-safetyFraction 这么一块保险区域,降低因实际内存超出当前预设范围而导致 OOM 的风险(对于非序列化对象的内存采样估算会产生误差)。值得注意的是,这个预留的保险区域仅仅是一种逻辑上的规划,在具体使用时 Spark 并没有区别对待,和"其它内存"一样交给了 JVM 去管理。 ``` -### 堆外内存管理 +## 堆外内存管理 * 堆外的空间分配较为简单,只有存储内存和执行内存。用的执行内存和存储内存占用的空间大小直接由参数 **spark.memory.storageFraction** 决定,由于堆外内存占用的空间可以被精确计算,所以无需再设定保险区域。 -![堆外内存](img/静态内存堆外.jpg) +![堆外内存](./img/静态内存堆外.jpg) * 静态内存管理机制实现起来较为简单,但如果用户不熟悉 Spark 的存储机制,或没有根据具体的数据规模和计算任务或做相应的配置,很容易造成"一半海水,一半火焰"的局面,即存储内存和执行内存中的一方剩余大量的空间,而另一方却早早被占满,不得不淘汰或移出旧的内容以存储新的内容。由于新的内存管理机制的出现,这种方式目前已经很少有开发者使用,出于兼容旧版本的应用程序的目的,Spark 仍然保留了它的实现。 -### 统一内存管理 +## 统一内存管理 -![img](img/统一内存管理.jpg) +![img](./img/统一内存管理.jpg) -#### 统一内存堆外管理 +### 统一内存堆外管理 -![堆外内存](img/统一内存堆外管理.jpg) +![堆外内存](./img/统一内存堆外管理.jpg) -#### 动态内存管理 +### 动态内存管理 其中最重要的优化在于**动态占用机制**,其规则如下: -* 设定基本的存储内存和执行内存区域(**spark.storage.storageFraction** 参数),该设定确定了双方各自拥有的空间的范围 -* 双方的空间都不足时,则存储到硬盘;若己方空间不足而对方空余时,可借用对方的空间;(存储空间不足是指不足以放下一个完整的 Block) -* 执行内存的空间被对方占用后,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间 -* 存储内存的空间被对方占用后,无法让对方"归还",因为需要考虑 Shuffle 过程中的很多因素,实现起来较为复杂 +- 设定基本的存储内存和执行内存区域(**spark.storage.storageFraction** 参数),该设定确定了双方各自拥有的空间的范围 +- 双方的空间都不足时,则存储到硬盘;若己方空间不足而对方空余时,可借用对方的空间;(存储空间不足是指不足以放下一个完整的 Block) +- 执行内存的空间被对方占用后,可让对方将占用的部分转存到硬盘,然后"归还"借用的空间 +- 存储内存的空间被对方占用后,无法让对方"归还",因为需要考虑 Shuffle 过程中的很多因素,实现起来较为复杂 -![动态内存管理](img/动态内存调用机制.jpg) +![动态内存管理](./img/动态内存调用机制.jpg) -## Storage内存管理 +# Storage内存管理 -### RDD的持久化机制 +## RDD的持久化机制 -* RDD是Spark最基本的数据抽象,是只读的分区记录(Partition)集合,**只能基于在稳定存储中的数据集上创建,或者其他RDD上转换产生新的RDD。转换后的RDD和旧的RDD存在依赖关系,构成了"血统(Lineage)"。凭借血统,Spark 保证了每一个 RDD 都可以被重新恢复。但 RDD 的所有转换都是惰性的,即只有当一个返回结果给 Driver 的行动(Action)发生时,Spark 才会创建任务读取 RDD,然后真正触发转换的执行**。 -* Task 在启动之初读取一个分区时,会先判断这个分区是否已经被持久化,如果没有则需要检查 Checkpoint 或按照血统重新计算。所以如果一个 RDD 上要执行多次Action,可以在第一次行动中使用 persist 或 cache 方法,在内存或磁盘中持久化或缓存这个 RDD,从而在后面的行动时提升计算速度。事实上,cache 方法是使用默认的 MEMORY\_ONLY 的存储级别将 RDD 持久化到内存,故缓存是一种特殊的持久化。 **堆内和堆外存储内存的设计,便可以对缓存** **RDD** **时使用的内存做统一的规划和管理** -* RDD 的持久化由 **Spark 的 Storage 模块** \[7] 负责,实现了 **RDD 与物理存储的解耦合**。Storage 模块**负责管理 Spark 在计算过程中产生的数据,将那些在内存或磁盘、在本地或远程存取数据的功能封装了起来**。在具体实现时 **Driver 端和 Executor 端的 Storage 模块构成了主从式的架构**,即 **Driver 端的 BlockManager 为 Master,Executor 端的 BlockManager 为 Slave**。Storage 模块在逻辑上以 **Block 为基本存储单位,RDD 的每个 Partition 经过处理后唯一对应一个 Block**(BlockId 的格式为 rdd\_RDD-ID\_PARTITION-ID )。**Master 负责整个 Spark 应用程序的 Block 的元数据信息的管理和维护,而 Slave 需要将 Block 的更新等状态上报到 Master,同时接收 Master 的命令,例如新增或删除一个 RDD**。 +* RDD是Spark最基本的数据抽象,是只读的分区记录(Partition)集合,**只能基于在稳定存储中的数据集上创建,或者其他RDD上转换产生新的RDD。**转换后的RDD和旧的RDD存在依赖关系,构成了"血统(Lineage)"。凭借血统,Spark 保证了每一个 RDD 都可以被重新恢复。但 RDD 的**所有转换都是惰性的,即只有当一个返回结果给 Driver 的行动(Action)发生时,Spark 才会创建任务读取 RDD,然后真正触发转换的执行**。 +* Task 在启动之初读取一个分区时,会先判断这个分区是否已经被持久化,如果没有则需要检查 Checkpoint 或按照血统重新计算。所以如果一个 RDD 上要执行多次Action,可以在第一次行动中使用 persist 或 cache 方法,在内存或磁盘中持久化或缓存这个 RDD,从而在后面的行动时提升计算速度。事实上,cache 方法是使用默认的 MEMORY_ONLY 的存储级别将 RDD 持久化到内存,故缓存是一种特殊的持久化。 **堆内和堆外存储内存的设计,便可以对缓存** **RDD** **时使用的内存做统一的规划和管理** +* RDD 的持久化由 **Spark 的 Storage 模块** [7] 负责,实现了 **RDD 与物理存储的解耦合**。Storage 模块**负责管理 Spark 在计算过程中产生的数据,将那些在内存或磁盘、在本地或远程存取数据的功能封装了起来**。在具体实现时 **Driver 端和 Executor 端的 Storage 模块构成了主从式的架构**,即 **Driver 端的 BlockManager 为 Master,Executor 端的 BlockManager 为 Slave**。Storage 模块在逻辑上以 **Block 为基本存储单位,RDD 的每个 Partition 经过处理后唯一对应一个 Block**(BlockId 的格式为 rdd_RDD-ID_PARTITION-ID )。**Master 负责整个 Spark 应用程序的 Block 的元数据信息的管理和维护,而 Slave 需要将 Block 的更新等状态上报到 Master,同时接收 Master 的命令,例如新增或删除一个 RDD**。 -!\[Storage]\(./img/Spark Storage模板.jpg) +![Storage](./img/Spark Storage模板.jpg) -#### 存储级别 +### 存储级别 ```scala case "NONE" => NONE @@ -150,48 +150,48 @@ def releaseUnrollMemory(numBytes: Long, memoryMode: MemoryMode): Unit * 存储级别从disk、memory、offheap三个维度定义了RDD的Partition(Block)的存储方式: * 存储位置:磁盘/堆内内存/堆外内存。 - * 存储形式:Block 缓存到存储内存后,是否为非序列化的形式。如 MEMORY\_ONLY 是非序列化方式存储,OFF\_HEAP 是序列化方式存储。 - * 副本数量:大于 1 时需要远程冗余备份到其他节点。如 DISK\_ONLY\_2 需要远程备份 1 个副本。 + * 存储形式:Block 缓存到存储内存后,是否为非序列化的形式。如 MEMORY_ONLY 是非序列化方式存储,OFF_HEAP 是序列化方式存储。 + * 副本数量:大于 1 时需要远程冗余备份到其他节点。如 DISK_ONLY_2 需要远程备份 1 个副本。 -### RDD缓存的过程 +## RDD缓存的过程 -* RDD **在缓存到存储内存之前**,**Partition 中的数据一般以迭代器(**[**Iterator**](http://www.scala-lang.org/docu/files/collections-api/collections\_43.html)**)的数据结构来访问**,这是 Scala 语言中一种遍历数据集合的方法。通过 Iterator 可以**获取分区中每一条序列化或者非序列化的数据项(Record)**,这些 Record 的对象实例在逻辑上占用了 **JVM 堆内内存的 other 部分的空间**,**同一 Partition 的不同 Record 的空间并不连续。** -* 读取Other区Iterator的Record后,**Partition 被转换成 Block,Record 在堆内或堆外存储内存中占用一块连续的空间。将Partition由不连续的存储空间转换为连续存储空间的过程,Spark称之为"展开"(Unroll)**。**Block 有序列化和非序列化**两种存储格式,具体以哪种方式取决于**该 RDD 的存储级别**。非**序列化的 Block 以一种 DeserializedMemoryEntry 的数据结构定义,用一个数组存储所有的对象实例,序列化的 Block 则以 SerializedMemoryEntry的数据结构定义,用字节缓冲区(ByteBuffer)来存储二进制数据**。每个 Executor 的 **Storage 模块用一个链式 Map 结构(LinkedHashMap)来管理堆内和堆外存储内存中所有的 Block 对象的实例**,对这个 LinkedHashMap 新增和删除间接记录了内存的申请和释放,同时也是为了LinkedHashMap自带的LRU特性。 -* 因为\*\*不能保证存储空间可以一次容纳 Iterator 中的所有数据,当前的计算任务在 Unroll 时要向 MemoryManager 申请足够的 Unroll 空间来临时占位,空间不足则 Unroll 失败,空间足够时可以继续进行。**对于**序列化的 Partition,其所需的 Unroll 空间可以直接累加计算,一次申请。**而**非序列化的 Partition 则要在遍历 Record 的过程中依次申请,即每读取一条 Record,采样估算其所需的 Unroll 空间并进行申请,空间不足时可以中断,释放已占用的 Unroll 空间。\*\*如果最终 Unroll 成功,**当前 Partition 所占用的 Unroll 空间被转换为正常的缓存 RDD 的存储空间。** +* RDD **在缓存到存储内存之前**,**Partition 中的数据一般以迭代器([Iterator](http://www.scala-lang.org/docu/files/collections-api/collections_43.html))的数据结构来访问**,这是 Scala 语言中一种遍历数据集合的方法。通过 Iterator 可以**获取分区中每一条序列化或者非序列化的数据项(Record)**,这些 Record 的对象实例在逻辑上占用了 **JVM 堆内内存的 other 部分的空间**,**同一 Partition 的不同 Record 的空间并不连续。** +* 读取Other区Iterator的Record后,**Partition 被转换成 Block,Record 在堆内或堆外存储内存中占用一块连续的空间。**将**Partition由不连续的存储空间转换为连续存储空间的过程,Spark称之为"展开"(Unroll)**。**Block 有序列化和非序列化**两种存储格式,具体以哪种方式取决于**该 RDD 的存储级别**。非**序列化的 Block 以一种 DeserializedMemoryEntry 的数据结构定义,用一个数组存储所有的对象实例,序列化的 Block 则以 SerializedMemoryEntry的数据结构定义,用字节缓冲区(ByteBuffer)来存储二进制数据**。每个 Executor 的 **Storage 模块用一个链式 Map 结构(LinkedHashMap)来管理堆内和堆外存储内存中所有的 Block 对象的实例**,对这个 LinkedHashMap 新增和删除间接记录了内存的申请和释放,同时也是为了LinkedHashMap自带的LRU特性。 +* 因为**不能保证存储空间可以一次容纳 Iterator 中的所有数据,当前的计算任务在 Unroll 时要向 MemoryManager 申请足够的 Unroll 空间来临时占位,空间不足则 Unroll 失败,空间足够时可以继续进行。**对于**序列化的 Partition,其所需的 Unroll 空间可以直接累加计算,一次申请。**而**非序列化的 Partition 则要在遍历 Record 的过程中依次申请,即每读取一条 Record,采样估算其所需的 Unroll 空间并进行申请,空间不足时可以中断,释放已占用的 Unroll 空间。**如果最终 Unroll 成功,**当前 Partition 所占用的 Unroll 空间被转换为正常的缓存 RDD 的存储空间。** **Spark Unroll** -!\[Spark Unroll]\(./img/Spark Unroll示意图.jpg) +![Spark Unroll](./img/Spark Unroll示意图.jpg) -### 淘汰和落盘 +## 淘汰和落盘 -* 由于同一个 Executor 的所有的计算任务共享有限的存储内存空间,当有新的 Block 需要缓存但是剩余空间不足且无法动态占用时,**就要对 LinkedHashMap 中的旧 Block 进行淘汰(Eviction LRU特性)**,而被淘汰的 Block 如果其存储级别中同时包含存储到磁盘的要求,则要对其进行落盘(Drop),否则直接删除该 Block。 +* 由于同一个 Executor 的所有的计算任务共享有限的存储内存空间,当有新的 Block 需要缓存但是剩余空间不足且无法动态占用时,**就要对 LinkedHashMap 中的旧 Block 进行淘汰(Eviction LRU特性)**,而被淘汰的 Block 如果其存储级别中同时包含存储到磁盘的要求,则要对其进行落盘(Drop),否则直接删除该 Block。 **存储内存的淘汰规则为:** -* 被淘汰的旧 Block 要与新 Block 的 MemoryMode 相同,即同属于堆外或堆内内存 -* 新旧 Block 不能属于同一个 RDD,避免循环淘汰 -* 旧 Block 所属 RDD 不能处于被读状态,避免引发一致性问题 -* 遍历 LinkedHashMap 中 Block,按照最近最少使用(LRU)的顺序淘汰,直到满足新 Block 所需的空间。其中 LRU 是 LinkedHashMap 的特性。 +- 被淘汰的旧 Block 要与新 Block 的 MemoryMode 相同,即同属于堆外或堆内内存 +- 新旧 Block 不能属于同一个 RDD,避免循环淘汰 +- 旧 Block 所属 RDD 不能处于被读状态,避免引发一致性问题 +- 遍历 LinkedHashMap 中 Block,按照最近最少使用(LRU)的顺序淘汰,直到满足新 Block 所需的空间。其中 LRU 是 LinkedHashMap 的特性。 -落盘的流程则比较简单,如果其存储级别符合**\_useDisk** 为 true 的条件,再根据其**\_deserialized** 判断是否是非序列化的形式,若是则对其进行序列化,最后将数据存储到磁盘,在 Storage 模块中更新其信息。 +落盘的流程则比较简单,如果其存储级别符合**_useDisk** 为 true 的条件,再根据其**_deserialized** 判断是否是非序列化的形式,若是则对其进行序列化,最后将数据存储到磁盘,在 Storage 模块中更新其信息。 -## Execution内存管理 +# Execution内存管理 -### 多任务间内存分配 +## 多任务间内存分配 -Executor 内运行的任务同样**共享执行内存**,Spark 用一个 HashMap 结构保存了任务到内存耗费的映射。**每个任务可占用的执行内存大小的范围为 1/2N \~ 1/N,其中 N 为当前 Executor 内正在运行的任务的个数。每个任务在启动之时,要向 MemoryManager 请求申请最少为 1/2N 的执行内存,如果不能被满足要求则该任务被阻塞,直到有其他任务释放了足够的执行内存,该任务才可以被唤醒。** +Executor 内运行的任务同样**共享执行内存**,Spark 用一个 HashMap 结构保存了任务到内存耗费的映射。**每个任务可占用的执行内存大小的范围为 1/2N ~ 1/N,其中 N 为当前 Executor 内正在运行的任务的个数。每个任务在启动之时,要向 MemoryManager 请求申请最少为 1/2N 的执行内存,如果不能被满足要求则该任务被阻塞,直到有其他任务释放了足够的执行内存,该任务才可以被唤醒。** -### Shuffle的内存占用 +## Shuffle的内存占用 执行内存主要用来`存储任务在执行 Shuffle 时占用的内存`,Shuffle 是`按照一定规则对 RDD 数据重新分区的过程`,主要来看 Shuffle 的`Write 和 Read`两阶段对执行内存的使用: -#### Shuffle Write +### Shuffle Write 1. 若在map端选择`普通的排序方式`,会采用`ExternalSorter进行外排`,在内存中存储数据时主要占用`堆内`执行空间。 2. 若在map端选择 `Tungsten的排序方式`,则采用`ShuffleExternalSorter`直接对`以序列化形式存储的数据排序`,在内存中存储数据时可以占用`堆外或堆内`执行空间,取决于用户`是否开启了堆外内存以及堆外执行内存是否足够`。 -#### Shuffle Read +### Shuffle Read 1. 在对reduce端的数据进行`聚合`时,要将数据交给`Aggregator`处理,在内存中存储数据时占用`堆内`执行空间。 2. 如果需要进行`最终结果排序`,则要将再次将数据交给`ExternalSorter处理,占用堆内执行空间`。 @@ -202,13 +202,16 @@ Executor 内运行的任务同样**共享执行内存**,Spark 用一个 HashMa > > **Tungsten排序** > -> Tungsten 采用的`页式内存管理机制`建立在 MemoryManager 之上,即 Tungsten 对执行内存的使用进行了一步的抽象,这样在 Shuffle 过程中无需关心数据具体存储在堆内还是堆外。每个`内存页用一个 MemoryBlock 来定义`,并用 `Object obj 和 long offset` 这两个变量统一标识一个内存页在系统内存中的地址。堆内的 MemoryBlock 是以 long 型数组的形式分配的内存,其 `obj 的值为是这个数组的对象引用,offset 是 long 型数组的在 JVM 中的初始偏移地址`,两者配合使用可以定位这个数组在`堆内的绝对地址`;堆外的 MemoryBlock 是直接申请到的`内存块`,其 obj 为 null,offset 是这个内存块在系统内存中的 `64 位绝对地址`。Spark 用 MemoryBlock 巧妙地将堆内和堆外内存页统一抽象封装,并用页表(pageTable)管理每个 Task 申请到的内存页。 +> Tungsten 采用的`页式内存管理机制`建立在 MemoryManager 之上,即 Tungsten 对执行内存的使用进行了一步的抽象,这样在 Shuffle 过程中无需关心数据具体存储在堆内还是堆外。每个`内存页用一个 MemoryBlock 来定义`,并用 `Object obj 和 long offset `这两个变量统一标识一个内存页在系统内存中的地址。堆内的 MemoryBlock 是以 long 型数组的形式分配的内存,其` obj 的值为是这个数组的对象引用,offset 是 long 型数组的在 JVM 中的初始偏移地址`,两者配合使用可以定位这个数组在`堆内的绝对地址`;堆外的 MemoryBlock 是直接申请到的`内存块`,其 obj 为 null,offset 是这个内存块在系统内存中的 `64 位绝对地址`。Spark 用 MemoryBlock 巧妙地将堆内和堆外内存页统一抽象封装,并用页表(pageTable)管理每个 Task 申请到的内存页。 > > Tungsten 页式管理下的所有内存用 64 位的逻辑地址表示,由页号和页内偏移量组成: > -> * 页号:占 13 位,唯一标识一个内存页,Spark 在申请内存页之前要先申请空闲页号。 -> * 页内偏移量:占 51 位,是在使用内存页存储数据时,数据在页内的偏移地址。 +> - 页号:占 13 位,唯一标识一个内存页,Spark 在申请内存页之前要先申请空闲页号。 +> - 页内偏移量:占 51 位,是在使用内存页存储数据时,数据在页内的偏移地址。 > > 有了统一的寻址方式,Spark 可以用 64 位逻辑地址的指针定位到`堆内或堆外的内存`,整个 Shuffle Write 排序的过程只需要对`指针进行排序`,并且无需反序列化,整个过程非常高效,对于内存访问效率和 CPU 使用效率带来了明显的提升. > > Spark 的存储内存和执行内存有着截然不同的管理方式:对于存储内存来说,Spark 用一个 LinkedHashMap 来集中管理所有的 Block,Block 由需要缓存的 RDD 的 Partition 转化而成;而对于执行内存,Spark 用 AppendOnlyMap 来存储 Shuffle 过程中的数据,在 Tungsten 排序中甚至抽象成为页式内存管理,开辟了全新的 JVM 内存管理机制。 + + + diff --git "a/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\346\240\270\345\277\203\345\257\271\350\261\241.md" "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\346\240\270\345\277\203\345\257\271\350\261\241.md" new file mode 100644 index 00000000..263d041c --- /dev/null +++ "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\346\240\270\345\277\203\345\257\271\350\261\241.md" @@ -0,0 +1,86 @@ +# BlockManager数据存储与管理机制 + +* BlockManager是整个Spark底层负责数据存储与管理的一个组件,Driver和Executor的所有数据都由对应的BlockManager进行管理。 +* Driver上有BlockManagerMaster,负责对各个节点上BlockManager内部管理的数据的元数据进行维护,比如block的增删改查等操作,都会在这里维护好元数据的变更。 +* 每个节点都有一个BlockManager,每个BlockManager创建之后,第一件事情是向BlockManagerMaster进行注册,此时BlockManagerMaster会为其创建对应的BlockManagerInfo + +![BlockManager原理](./img/BlockManager原理.jpg) + +* **BlockManagerMaster与BlockManager类似于NameNode和DataNode的关系:BlockManagerMaster中保存中BlockManager内部管理数据的元数据,进行维护,当BlockManager进行Block增删改等操作,都会在BlockManagerMaster中进行元数据的变更。** +* 每个节点上都有一个BlockManager,BlockManager中有3个核心组件: + * DiskStore:负责对磁盘数据进行读写 + * MemoryStore:负责对内存数据进行读写 + * BlockTransferService:负责建立BlockManager到远程其他节点的BlockManager连接,负责对远程其他节点的BlockManager的数据进行读写。 +* **每个BlockManager创建之后,会向BlockManagerMaster进行注册,此时BlockManagerMaster会为其创建对应的BlockManagerInfo。**使用BlockManager进行写操作时,如果使用persist会优先将数据写入内存中,如果内存大小不够,会使用自己的算法,将内存中的部分写入磁盘(根据StoageLevel);如果persist指定了副本会使用**BlockTransferService**向其他节点写入一份副本。使用BlockManager进行读操作时,对于ShuffleRead操作,如果能从本地节点读取,就利用DiskStore或者MemoryStore从本地读取,如果没有的话就会利用**BlockTransferService**从其他节点的BlockManager建立连接来读取数据。 +* 如果使用BlockManager进行增删改操作,就会将Block的BlockStatus上报到BlockManagerMaster,在BlockamangerMaster上会对指定BlockManager的BlockManagerInfo内部的BlockStatus进行增删改查操作,从而来维护元数据。 + +# Spark共享变量底层实现 + +* 默认情况下,一个算子的函数中如果使用到外部变量,那么这个**变量就会被拷贝到每个task中**,此时每个task只能操作自己的那份副本,类似于copyOnWrite的概念,无法做到多个task共享一份变量。 +* Spark提供了**Broadcast Variable和Accumulator累加变量**,Broadcast Variable将用到的变量,仅仅**为每个节点拷贝一份**,即每个**Executor**拷贝一份,这样可以优化性能,减少资源和网络传输的消耗。Accumulator可以让**多个task共同操作一份变量**,主要可以进行变量累加操作。 + +## 闭包源码 + +* 下类可以看下闭包 + +``` +类ClosureCleaner +``` + +## Scala序列化 + +* 重写writeReplace方法,scala序列化会利用java的方式,java在序列化时会判断是否存在该方法,存在就调用,spark累加器利用这一特性实现对数据的判断 + + ```scala + // Called by Java when serializing an object + final protected def writeReplace(): Any = { + if (atDriverSide) { + if (!isRegistered) { + throw new UnsupportedOperationException( + "Accumulator must be registered before send to executor") + } + val copyAcc = copyAndReset() + assert(copyAcc.isZero, "copyAndReset must return a zero value copy") + val isInternalAcc = name.isDefined && name.get.startsWith(InternalAccumulator.METRICS_PREFIX) + if (isInternalAcc) { + // Do not serialize the name of internal accumulator and send it to executor. + copyAcc.metadata = metadata.copy(name = None) + } else { + // For non-internal accumulators, we still need to send the name because users may need to + // access the accumulator name at executor side, or they may keep the accumulators sent from + // executors and access the name when the registered accumulator is already garbage + // collected(e.g. SQLMetrics). + copyAcc.metadata = metadata + } + copyAcc + } else { + this + } + } + + --- ObjectOutputStream + if (!desc.hasWriteReplaceMethod() || + (obj = desc.invokeWriteReplace(obj)) == null || + (repCl = obj.getClass()) == cl) + { + break; + } + cl = repCl; + ``` + + ## 广播变量 + + * **广播变量是在每个Executor上保留外部数据的只读变量,而不是给每个任务发送一个副本**,每个task都会保存一份它所使用的外部变量时,对于Executor内存的消耗是非常大的,因此可以将大型外部变量封装成广播变量,此时一个Executor保存一个变量副本,此Executor上所有task共用此变量,**不再是一个task单独保存一个副本,这在一定程度降低了Spark任务的内存占用。** + * Spark尝试使用高效的广播算法分发广播变量,以降低通信成本,Spark提供的Broadcast Variable是只读的,并且在每个Executor上只会有一个副本,而不会为每个task都拷贝一份副本,因此,它的最大作用,**就是减少变量到各个节点的网络传输消耗。** + +![task使用外部变量](./img/task使用的外部变量.jpg) + +![广播变量](./img/广播变量.jpg) + +## 累加器 + +* Accumulator是仅仅被相关操作累加的变量,因此可以并行中被有效支持,可以实现计数器或总和计数。 +* **Accumulator存储在Driver端,集群上运行的task进行Accumulator的累加,随后把值发送给Driver端,在Driver端汇总,由于Accumulator存在Driver端,从节点读取不到Accumulator的数值。**Spark中的主要用于多节点对一个变量进行共享性的操作,task只能对其进行累加,不能对其进行读取。 + +![Accumulator原理](./img/Accumulator累加器.jpg) + diff --git a/bigdata/engine/spark/yuan-ma-fen-xi/spark-zu-jian-tong-xin.md "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\347\273\204\344\273\266\351\200\232\344\277\241.md" similarity index 74% rename from bigdata/engine/spark/yuan-ma-fen-xi/spark-zu-jian-tong-xin.md rename to "bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\347\273\204\344\273\266\351\200\232\344\277\241.md" index 4dfc632f..f458dd54 100644 --- a/bigdata/engine/spark/yuan-ma-fen-xi/spark-zu-jian-tong-xin.md +++ "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\347\273\204\344\273\266\351\200\232\344\277\241.md" @@ -4,12 +4,13 @@ * Spark2.x使用Netty作为内部通信组件。spark基于netty新的rpc框架借鉴了Akka的中的设计, 它是基于Actor模型。 -![组件通信](../源码分析/img/组件通信.jpg) +![组件通信](./img/组件通信.jpg) ## Spark Actor模型 -!\[image-20200705174334769]\(./img/Spark Actor.jpg) +![image-20200705174334769](./img/Spark Actor.jpg) * Endpoint(Client/Master/Worker)有一个InBox和N个OutBox(N>=1,N取决于当前Endpoint于多少其他的Endpoint进行通信,一个与其通讯的其他Endpoint对应一个OutBox),Endpoint接收到的消息被写入InBox,发送出去的消息写入OutBox并被发送到其他Endpoint的InBox中。 -![Spark通信机制原理](../源码分析/img/Spark通信机制原理.png) +![Spark通信机制原理](./img/Spark通信机制原理.png) + diff --git a/bigdata/engine/spark/yuan-ma-fen-xi/spark-tiao-du-he-shuffle-jie-xi.md "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\350\260\203\345\272\246\345\222\214Shuffle\350\247\243\346\236\220.md" similarity index 63% rename from bigdata/engine/spark/yuan-ma-fen-xi/spark-tiao-du-he-shuffle-jie-xi.md rename to "bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\350\260\203\345\272\246\345\222\214Shuffle\350\247\243\346\236\220.md" index e1375607..52e7f816 100644 --- a/bigdata/engine/spark/yuan-ma-fen-xi/spark-tiao-du-he-shuffle-jie-xi.md +++ "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/Spark\350\260\203\345\272\246\345\222\214Shuffle\350\247\243\346\236\220.md" @@ -1,45 +1,43 @@ -# Spark调度和Shuffle解析 +# Spark任务的调度 -## Spark任务的调度 - -### Spark任务调度概述 +## Spark任务调度概述 `当Driver起来后,Driver则会根据用户程序逻辑准备任务,并根据Executor资源情况逐步分发任务。` -#### 基础概念 +### 基础概念 * Job是以Action算子为界,遇到一个Action则触发一个Job * Stage是Job的子集,以RDD宽依赖为界,遇到Shuffle做一次划分。 * Task是Stage的子集,以并行度(分区数)来衡量,分区数是多少,则有多少个task。 -![任务调度](../源码分析/img/任务调度过程.jpg) +![任务调度](./img/任务调度过程.jpg) -### Spark Stage级别调度 +## Spark Stage级别调度 -#### DAGScheduler负责Stage级的调度。 +### DAGScheduler负责Stage级的调度。 * 一个Stage是否被提交需要看他的parent的stage是否被提交,如果被提交则提交该stage,否则提交parent stage。DAGScheduler提交Stage会将stage中的task打包成一个TaskSet集,并且序列化传递给TaskScheduler。 -### Spark Task级别调度 +## Spark Task级别调度 * `TaskScheduler负责Task级的调度,将DAGScheduler给过来的TaskSet按照指定的调度策略分发到Executor上执行,调度过程中SchedulerBackend负责提供可用资源。` - * TaskScheduler会将TaskSet封装为TaskSetManager加入到调度队列中。 + * TaskScheduler会将TaskSet封装为TaskSetManager加入到调度队列中。 -![TaskSetManager结构](../源码分析/img/TaskManager结构.jpg) +![TaskSetManager结构](./img/TaskManager结构.jpg) -* ​ TaskSetManager负责监控管理同一个Stage中的Tasks,TaskScheduler就是以TaskSetManager为单元来调度任务。 +* ​ TaskSetManager负责监控管理同一个Stage中的Tasks,TaskScheduler就是以TaskSetManager为单元来调度任务。 -![Task调度流程](../源码分析/img/Task调度流程.jpg) +![Task调度流程](./img/Task调度流程.jpg) * TaskSetManager加入rootPool调度池之后,调用SchedulerBackend的riviveOffsets方法给driverEndpoint发送ReviveOffsets消息;DriverEndpoint收到ReviveOffset消息后调用makeOffset方法,过滤出活跃状态的Executor,然后将Executor封装成WorkerOffer对象;准备好计算资源后,taskScheduler基于这些资源调用resourceOffer在Executor上分配task。 -### 调度策略 +## 调度策略 -### FIFO策略 +## FIFO策略 * FIFO策略就是将TaskSetManager按照先进先出的方式入队出队。 -```scala +``` scala # 任务加入队列 override def addTaskSetManager(manager: Schedulable, properties: Properties) { rootPool.addSchedulable(manager) @@ -54,44 +52,44 @@ override def addSchedulable(schedulable: Schedulable) { val schedulableQueue = new ConcurrentLinkedQueue[Schedulable] ``` -#### 公平调度 +### 公平调度 * Fair模式有一个rootPool和多个子Pool,各个子Pool存储着所有待分配的TaskSetManager。 * 在Fair模式中,需要先对子Pool进行排序,再对Pool里面TaskSetManager进行排序,因为Pool和TaskSetManager都继承了Schedulable特质,使用相同的排序算法。 * 排序过程基于Fair-shar比较,需要考虑对象的runningTasks值、minShare值、weight值,来决定谁先执行。 -![公平调度策略](../源码分析/img/公平调度策略.jpg) +![公平调度策略](./img/公平调度策略.jpg) -### 本地化调度 +## 本地化调度 * DAGScheduler切割Job,划分Stage,通过调用submitStage来提交一个Stage对应的taskset,subStage会调用submitMissingTasks,submitMissingTasks确定每个需要计算的task的preferredLocations(数据本地化调度),通过调用getPreferrdeLocations()得到partition的优先位置,**partition对应一个task,此partition的优先位置就是task的优先位置**,对于要提交到TaskScheduler的TaskSet中的每一个task,该task优先位置与其parittion对应的优先位置一致。 -### 失败重试与黑名单机制 +## 失败重试与黑名单机制 -#### 失败重试机制 +### 失败重试机制 * Executor会将执行状态上报给SchedulerBackend,SchedulerBackend则告诉TaskScheduler,TaskScheduler找到该Task对应的失败与成功状态,**对于失败的Task,会记录它失败的次数,如果失败次数没超过最大重试次数,那么就把它放回待调度的TaskPool中,否则整个Application失败** -#### 黑名单机制 +### 黑名单机制 * 如果多次失败后仍然失败,就将其task放入黑名单,下次再进行执行时直接跳过该task。 -## Shuffle解析 +# Shuffle解析 -### ShuffleMapStage与ResultStage +## ShuffleMapStage与ResultStage -![spark任务调度](../源码分析/img/spark任务调度.jpg) +![spark任务调度](./img/spark任务调度.jpg) * **ShuffleMapStage**的结束伴随着**shuffle文件的写磁盘**。 * **ResultStage**基本上对应代码中的action算子,即将一个函数应用在RDD的各个partition的数据集上,意味着一个Job的运行结束。 -### Shuffle中的任务个数 +## Shuffle中的任务个数 * SparkShuffle**分为map和reduce阶段**,或者称为**ShuffleRead阶段和ShuffleWrite阶段**,map过程和reduce都会由若干个task来执行。 * 例如,spark任务从HDFS中读取数据,初始**RDD分区个数由该文件split个数决定**,也就是一个split对应生成的RDD的一个partition,假设为N。初始RDD经过一系列算子计算后,假设分区个数布标,当执行到Shuffle操作时,**map端的task个数和partition个数一致**,即map task为N个。 * reduce端的stage默认取`spark.default.parallelism`这个配置项作为分区数,如果没有配置,**则以map端的最后一个RDD的分区数作为其分区数(也就是N),那么分区数就决定reduce端的task的个数**。 -#### 源码分析 +### 源码分析 ``` ExecutorBackend @@ -102,93 +100,97 @@ ExecutorBackend -- TaskRunner.run() ``` -#### reduce端数据读取 +### reduce端数据读取 * **map端task和reduce端task不在同一个stage中**,map task位于ShuffleMapStage,reduce task位于ResultStage,map task会先执行,后执行的reduce端需要去读取map端刷新到磁盘的数据。 -* reduce端的数据拉取过程: - - 1. map task执行完毕后会将计算状态及磁盘小文件位置等信息封装到MapStatus对象中,然后由本进程中的MapOutPutTrackerWorker对象将mapStatus对象发送给Driver进程的MapOutPutTrackerMaster对象; - 2. **在reduce task开始执行之前会让本进程中的MapOutPutTrackerWorker向Driver进程中的MapOutPutTrackerMaster发请求,请求磁盘小文件信息;** - 3. **当所有的Map task执行完毕后,Driver进程中的MapOutPutTrackerMaster就拿到了所有map端输出的磁盘小文件信息**,此时就会将消息发送给请求过来的MapOutPutTrackerWorker。 - 4. 完成之前的操作之后,由BlockTransforService去Executor0所在的节点拉数据,默认启动5个子线程。每次拉取的数据量不能超过48M(reduce task每次最多拉取48M数据,将拉取来的数据存储到Executor内存的20%内存中)。 - - ``` - --BlockStoreShuffleReader - -- read方法,reduce端数据拉取源码 - ``` - - ### HashShuffle - - ```scala - private[spark] trait ShuffleManager { - - /** - * Register a shuffle with the manager and obtain a handle for it to pass to tasks. - */ - def registerShuffle[K, V, C]( - shuffleId: Int, - numMaps: Int, - dependency: ShuffleDependency[K, V, C]): ShuffleHandle - - /** Get a writer for a given partition. Called on executors by map tasks. */ - def getWriter[K, V](handle: ShuffleHandle, mapId: Int, context: TaskContext): ShuffleWriter[K, V] - - /** - * Get a reader for a range of reduce partitions (startPartition to endPartition-1, inclusive). - * Called on executors by reduce tasks. - */ - def getReader[K, C]( - handle: ShuffleHandle, - startPartition: Int, - endPartition: Int, - context: TaskContext): ShuffleReader[K, C] - - /** - * Remove a shuffle's metadata from the ShuffleManager. - * @return true if the metadata removed successfully, otherwise false. - */ - def unregisterShuffle(shuffleId: Int): Boolean - - /** - * Return a resolver capable of retrieving shuffle block data based on block coordinates. - */ - def shuffleBlockResolver: ShuffleBlockResolver - - /** Shut down this ShuffleManager. */ - def stop(): Unit - } - ``` - #### HashShuffleManager +* reduce端的数据拉取过程: + + 1. map task执行完毕后会将计算状态及磁盘小文件位置等信息封装到MapStatus对象中,然后由本进程中的MapOutPutTrackerWorker对象将mapStatus对象发送给Driver进程的MapOutPutTrackerMaster对象; + 2. **在reduce task开始执行之前会让本进程中的MapOutPutTrackerWorker向Driver进程中的MapOutPutTrackerMaster发请求,请求磁盘小文件信息;** + 3. **当所有的Map task执行完毕后,Driver进程中的MapOutPutTrackerMaster就拿到了所有map端输出的磁盘小文件信息**,此时就会将消息发送给请求过来的MapOutPutTrackerWorker。 + 4. 完成之前的操作之后,由BlockTransforService去Executor0所在的节点拉数据,默认启动5个子线程。每次拉取的数据量不能超过48M(reduce task每次最多拉取48M数据,将拉取来的数据存储到Executor内存的20%内存中)。 + + ``` + --BlockStoreShuffleReader + -- read方法,reduce端数据拉取源码 + ``` + + ## HashShuffle + + ```scala + private[spark] trait ShuffleManager { + + /** + * Register a shuffle with the manager and obtain a handle for it to pass to tasks. + */ + def registerShuffle[K, V, C]( + shuffleId: Int, + numMaps: Int, + dependency: ShuffleDependency[K, V, C]): ShuffleHandle + + /** Get a writer for a given partition. Called on executors by map tasks. */ + def getWriter[K, V](handle: ShuffleHandle, mapId: Int, context: TaskContext): ShuffleWriter[K, V] + + /** + * Get a reader for a range of reduce partitions (startPartition to endPartition-1, inclusive). + * Called on executors by reduce tasks. + */ + def getReader[K, C]( + handle: ShuffleHandle, + startPartition: Int, + endPartition: Int, + context: TaskContext): ShuffleReader[K, C] + + /** + * Remove a shuffle's metadata from the ShuffleManager. + * @return true if the metadata removed successfully, otherwise false. + */ + def unregisterShuffle(shuffleId: Int): Boolean + + /** + * Return a resolver capable of retrieving shuffle block data based on block coordinates. + */ + def shuffleBlockResolver: ShuffleBlockResolver + + /** Shut down this ShuffleManager. */ + def stop(): Unit + } + ``` + + + + ### HashShuffleManager - #### 未优化 + ### 未优化 - * 早期HashShuffleManager,未经优化前。 + * 早期HashShuffleManager,未经优化前。 - HashShuffleManager + ![HashShuffleManager](./img/HashShuffleManager.jpg) - * 根据hash算法去读各自的文件,但是每个任务都会根据reduce的个数参数对应的文件,这样就会导致太多小文件,效率就会低下。 + * 根据hash算法去读各自的文件,但是每个任务都会根据reduce的个数参数对应的文件,这样就会导致太多小文件,效率就会低下。 - #### 优化方案 + ### 优化方案 + + ![HashShuffleManager](./img/优化HashShuffleManager.jpg) - HashShuffleManager * 核数如果过多就会导致文件过多,这样也会导致文件过多的问题。 -### SortShuffleManager +## SortShuffleManager -#### 原理流程 +### 原理流程 -![原理流程](../源码分析/img/SortShuffle原理.jpg) +![原理流程](./img/SortShuffle原理.jpg) -#### bypass运行机制 +### bypass运行机制 * bypass运行机制的触发条件: * **shuffle map task**数量小于**spark.shuffle.sort.bypassMergeThreshold**不需要进行排序,直接使用hash即可。 * 不是聚合类的shuffle算子。 -![bypass机制](../源码分析/img/bypass机制.jpg) +![bypass机制](./img/bypass机制.jpg) -#### bypass机制源码剖析 +### bypass机制源码剖析 ```scala --SortShuffleManager @@ -233,3 +235,4 @@ ExecutorBackend -- SortShuffleManager.canUseSerializedShuffle(dependency) ``` + diff --git a/bigdata/engine/spark/yuan-ma-fen-xi/yarn-de-bu-shu-liu-cheng.md "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/yarn\347\232\204\351\203\250\347\275\262\346\265\201\347\250\213.md" similarity index 85% rename from bigdata/engine/spark/yuan-ma-fen-xi/yarn-de-bu-shu-liu-cheng.md rename to "bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/yarn\347\232\204\351\203\250\347\275\262\346\265\201\347\250\213.md" index c931938d..867c60ed 100644 --- a/bigdata/engine/spark/yuan-ma-fen-xi/yarn-de-bu-shu-liu-cheng.md +++ "b/bigdata/engine/spark/\346\272\220\347\240\201\345\210\206\346\236\220/yarn\347\232\204\351\203\250\347\275\262\346\265\201\347\250\213.md" @@ -1,6 +1,4 @@ -# yarn的部署流程 - -## SparkSubmit +# SparkSubmit ``` -- main 启动进程 @@ -20,7 +18,7 @@ -- mainMethod.invoke(null, args) ``` -## Client +# Client ``` --main 主方法 @@ -40,7 +38,7 @@ -- yarnClient.submitApplication(appContext) //提交指令 ``` -## ApplicationMaster +# ApplicationMaster ``` --main @@ -70,10 +68,10 @@ --runExecutorLauncher ``` -## CoarseGrainedExecutorBackend +# CoarseGrainedExecutorBackend * 生命周期 - * constructor -> onStart -> receive\* -> onStop + * constructor -> onStart -> receive* -> onStop ``` -- main @@ -86,34 +84,44 @@ --new Executor ``` -## Yarn部署流程图 +# Yarn部署流程图 * 根据上面源码流程跟进 -![yarn部署流程图](../源码分析/img/sparkSubmit部署流程.jpg) +![yarn部署流程图](./img/sparkSubmit部署流程.jpg) -* 通用流程 +* 通用流程 - 通用流程 + ![通用流程](./img/SparkSubmit通用流程.png) - ## Yarn模式运行机制 + # Yarn模式运行机制 - ### Yarn Client模式 + ## Yarn Client模式 - !\[yarn Client]\(./img/yarn Client模式.jpg) + ![yarn Client](./img/yarn Client模式.jpg) - ### Yarn Cluster模式 + ## Yarn Cluster模式 - !\[yarn Cluster]\(./img/yarn Cluster模式.jpg) + ![yarn Cluster](./img/yarn Cluster模式.jpg) -### Client和Cluster模式的区别 +## Client和Cluster模式的区别 * Driver存储地址 + * Client:本地机器 * Cluster:Yarn的Nodemanager的ApplicationMaster上 + * 流量激增问题 + * Client:因为Driver放在本地机器负责Spark调度,所以流量会激增 * Cluster放在Yarn集群没有这类问题 + * 网络问题 + * Client:Driver放在本地,通常来说本地机器和Yarn机器不在一个机房,所以存在网络问题 + * Cluster:Driver和Yarn集群运行在同一个机房内 + + + + \ No newline at end of file diff --git a/bigdata/graph/README.md b/bigdata/graph/README.md deleted file mode 100644 index 19a5b522..00000000 --- a/bigdata/graph/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# graph - diff --git a/bigdata/graph/nebula-graph/2.-kuai-su-ru-men.md "b/bigdata/graph/nebula graph/2.\345\277\253\351\200\237\345\205\245\351\227\250.md" similarity index 70% rename from bigdata/graph/nebula-graph/2.-kuai-su-ru-men.md rename to "bigdata/graph/nebula graph/2.\345\277\253\351\200\237\345\205\245\351\227\250.md" index 0984bb43..01325c2e 100644 --- a/bigdata/graph/nebula-graph/2.-kuai-su-ru-men.md +++ "b/bigdata/graph/nebula graph/2.\345\277\253\351\200\237\345\205\245\351\227\250.md" @@ -1,8 +1,6 @@ -# 2.快速入门 +# Docker Compose搭建Nebula -## Docker Compose搭建Nebula - -### 部署和链接Nebula +## 部署和链接Nebula * 下载docker-compose配置 @@ -25,7 +23,7 @@ docker run --rm -ti --network nebula-docker-compose_nebula-net --entrypoint=/bin nebula-console -u user -p password --address=graphd --port=9669 ``` -## 管理Nebula Graph服务 +# 管理Nebula Graph服务 * Nebula Graph使用脚本`nebula.service`管理服务,包括启动、停止、重启、中止和查看。 * `nebula.service`的默认路径是`/usr/local/nebula/scripts`,如果修改过安装路径,请使用实际路径。 @@ -37,14 +35,14 @@ sudo /usr/local/nebula/scripts/nebula.service ``` -* \-v:显示详细调试信息 -* \-c:指定配置文件路径,默认路径为/user/lical/nebula/etc/ +* -v:显示详细调试信息 +* -c:指定配置文件路径,默认路径为/user/lical/nebula/etc/ * metad:管理meta服务 * graph:管理graph服务 * storaged:管理storage服务 * all:管理所有服务 -### 启动Nebula garph服务 +## 启动Nebula garph服务 ```shell # 非容器部署 @@ -53,7 +51,7 @@ sudo /usr/local/nebula/scripts/nebula.service start all docker-compose up -d ``` -### 停止Nebula Graph服务 +## 停止Nebula Graph服务 * 禁止使用kill -9方式停止服务,存在数据丢失概率。 @@ -64,25 +62,25 @@ sudo /usr/local/nebula/scripts/nebula.service stop all docker-compose down ``` -### 查看Nebula Graph服务 +## 查看Nebula Graph服务 ``` # 非容器 sudo /usr/local/nebula/scripts/nebula.service status all ``` -## 连接Nebula Graph +# 连接Nebula Graph -### Nebula Console +## Nebula Console ```shell nebula-console.exe -addr -port -u -p [-t 120] [-e "nGQL_statement" | -f filename.nGQL] ``` -!\[]\(./img/nebula console.jpg) +![](./img/nebula console.jpg) -### Nebula Console导出模式 +## Nebula Console导出模式 * 导出模式开启时,Nebula Console会导出所有请求的结果到CSV格式文件中。关闭导出模式会停止导出。使用语法如下: * 开启导出模式 @@ -97,13 +95,13 @@ nebula-console.exe -addr -port -u -p :UNSET CSV ``` -## 基本操作语法 +# 基本操作语法 -### 图空间和Schema +## 图空间和Schema * 一个Nebula Graph实例由一个或多个图空间组成。每个图空间都是物理隔离的,用户可以在同一个实例中使用不同的图空间存储不同的数据集。 -![](<../nebula graph/img/图空间.jpg>) +![](./img/图空间.jpg) * Nebula Graph在图空间中定义了Schema * 点(vertex):表示现实世界中的实体。一个点可以有一个或多个标签。 @@ -113,7 +111,7 @@ nebula-console.exe -addr -port -u -p ![The demo dataset](https://docs-cdn.nebula-graph.com.cn/docs-2.0/2.quick-start/dataset-for-crud.png) -### 异步实现创建和修改 +## 异步实现创建和修改 **Nebula Graph中执行如下创建和修改操作,是异步实现的,需要在下一个心跳周期才同步数据。** @@ -128,10 +126,12 @@ nebula-console.exe -addr -port -u -p **默认心跳周期是10秒。修改心跳周期参数`heartbeat_interval_secs`** * 确保数据同步后续操作能顺利进行,需要以下方法之一: + * 执行`SHOW`或`DESCRIBE`命令检查相应对象的状态,确保创建或修改已完成。如果没有完成,请等待几秒重试。 - * 等待2个心跳周期(20秒)。 -### 创建和选择图空间 + - 等待2个心跳周期(20秒)。 + +## 创建和选择图空间 * 创建图空间 @@ -145,11 +145,11 @@ CREATE SPACE [IF NOT EXISTS] CREATE SPACE IF NOT EXISTS test_graph_space(PARTITION_NUM=5,REPLICA_FACTOR=1,VID_TYPE=INT64); ``` -| 参数 | 说明 | -| --------------- | --------------------------------------------------------------------------------------------------------------------------------- | -| partition\_num | 指定图空间的分片数量。建议设置为5倍的集群硬盘数量。例如集群中有3个硬盘,建议设置15个分片。 | -| replica\_factor | 指定每个分片的副本数量。建议在生产环境中设置为3,在测试环境中设置为1。由于需要进行基于quorum的选举,副本数量必须是**奇数**。 | -| vid\_type | 指定点ID的数据类型。可选值为`FIXED_STRING()`和`INT64`。`FIXED_STRING()`表示数据类型为字符串,最大长度为`N`,超出长度会报错;`INT64`表示数据类型为整数。默认值为`FIXED_STRING(8)`。 | +| 参数 | 说明 | +| -------------- | ------------------------------------------------------------ | +| partition_num | 指定图空间的分片数量。建议设置为5倍的集群硬盘数量。例如集群中有3个硬盘,建议设置15个分片。 | +| replica_factor | 指定每个分片的副本数量。建议在生产环境中设置为3,在测试环境中设置为1。由于需要进行基于quorum的选举,副本数量必须是**奇数**。 | +| vid_type | 指定点ID的数据类型。可选值为`FIXED_STRING()`和`INT64`。`FIXED_STRING()`表示数据类型为字符串,最大长度为`N`,超出长度会报错;`INT64`表示数据类型为整数。默认值为`FIXED_STRING(8)`。 | * 查看全部图空间 @@ -163,7 +163,7 @@ show spaces; use space_name ``` -### 创建标签和边类型 +## 创建标签和边类型 ```shell CREATE {TAG | EDGE} { | }( @@ -182,11 +182,11 @@ nebula> CREATE EDGE follow(degree int); nebula> CREATE EDGE serve(start_year int, end_year int); ``` -### 插入点和边 +## 插入点和边 * 使用INSERT基于存在的tag插入点或者edgeType插入边 -#### 插入点 +### 插入点 ```shell INSERT VERTEX ([, ...]) @@ -205,7 +205,7 @@ INSERT VERTEX team(name) VALUES 24:("laker"); * `VID`是Vertex ID的缩写,`VID`在一个图空间中是唯一的。 -#### 插入边 +### 插入边 ```shell INSERT EDGE ([, ...]) @@ -221,7 +221,7 @@ INSERT EDGE follow(degree) VALUES 100->101@1:(95); INSERT EDGE serve(start_year,end_year) VALUES 100 ->24:(1996,2010),101->23:(2003,2008); ``` -### 查询数据 +## 查询数据 * [GO](https://docs.nebula-graph.com.cn/2.0.1/3.ngql-guide/7.general-query-statements/3.go/)语句可以根据指定的条件遍历数据库。`GO`语句从一个或多个点开始,沿着一条或多条边遍历,返回`YIELD`子句中指定的信息。 @@ -232,16 +232,16 @@ OVER [REVERSELY] [BIDIRECT] YIELD [DISTINCT] ; ``` -* [FETCH](https://docs.nebula-graph.com.cn/2.0.1/3.ngql-guide/7.general-query-statements/4.fetch/)语句可以获得点或边的属性。 +- [FETCH](https://docs.nebula-graph.com.cn/2.0.1/3.ngql-guide/7.general-query-statements/4.fetch/)语句可以获得点或边的属性。 - * 查询标签属性 + - 查询标签属性 - ```shell - FETCH PROP ON { | | *} - [YIELD [DISTINCT] ]; - ``` + ```shell + FETCH PROP ON { | | *} + [YIELD [DISTINCT] ]; + ``` - * 查询边属性 + - 查询边属性 ``` FETCH PROP ON -> [@] @@ -249,7 +249,7 @@ FETCH PROP ON -> [@] [YIELD [DISTINCT] ]; ``` -* [LOOKUP](https://docs.nebula-graph.com.cn/2.0.1/3.ngql-guide/7.general-query-statements/5.lookup/)语句是基于[索引](https://docs.nebula-graph.com.cn/2.0.1/2.quick-start/4.nebula-graph-crud/#\_14)的,和`WHERE`子句一起使用,查找符合特定条件的数据。 +- [LOOKUP](https://docs.nebula-graph.com.cn/2.0.1/3.ngql-guide/7.general-query-statements/5.lookup/)语句是基于[索引](https://docs.nebula-graph.com.cn/2.0.1/2.quick-start/4.nebula-graph-crud/#_14)的,和`WHERE`子句一起使用,查找符合特定条件的数据。 ``` LOOKUP ON { | } @@ -257,18 +257,19 @@ WHERE [AND expression ...])] [YIELD ]; ``` -* [MATCH](https://docs.nebula-graph.com.cn/2.0.1/3.ngql-guide/7.general-query-statements/2.match/)语句是查询图数据最常用的,但是它依赖[索引](https://docs.nebula-graph.com.cn/2.0.1/2.quick-start/4.nebula-graph-crud/#\_14)去匹配Nebula Graph中的数据模型。 +- [MATCH](https://docs.nebula-graph.com.cn/2.0.1/3.ngql-guide/7.general-query-statements/2.match/)语句是查询图数据最常用的,但是它依赖[索引](https://docs.nebula-graph.com.cn/2.0.1/2.quick-start/4.nebula-graph-crud/#_14)去匹配Nebula Graph中的数据模型。 ``` MATCH [] RETURN ; ``` -#### Go查询 +### Go查询 * YIELD:指定该查询需要返回的值或结果。 -* +* $$:表示边的终点 * $^:表示边的起点 -* \|:组合多个查询的管道符,将前一个查询的结果集用于后一个查询。 +* |:组合多个查询的管道符,将前一个查询的结果集用于后一个查询。 * $-:表示管道符前面的查询输出的结果集。 -### 修改点和边 +## 修改点和边 + diff --git a/bigdata/graph/nebula-graph/README.md b/bigdata/graph/nebula-graph/README.md deleted file mode 100644 index b024a08e..00000000 --- a/bigdata/graph/nebula-graph/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# nebula graph - diff --git a/bigdata/hadoop/hdfs/hdfs-ji-qun-guan-li.md "b/bigdata/hadoop/HDFS/HDFS\351\233\206\347\276\244\347\256\241\347\220\206.md" similarity index 100% rename from bigdata/hadoop/hdfs/hdfs-ji-qun-guan-li.md rename to "bigdata/hadoop/HDFS/HDFS\351\233\206\347\276\244\347\256\241\347\220\206.md" diff --git a/bigdata/hadoop/yarn/hadoop-xiang-guan-zu-jian-sheng-chan-ji-bie-pei-zhi.md "b/bigdata/hadoop/Hadoop\347\233\270\345\205\263\347\273\204\344\273\266\347\224\237\344\272\247\347\272\247\345\210\253\351\205\215\347\275\256.md" similarity index 100% rename from bigdata/hadoop/yarn/hadoop-xiang-guan-zu-jian-sheng-chan-ji-bie-pei-zhi.md rename to "bigdata/hadoop/Hadoop\347\233\270\345\205\263\347\273\204\344\273\266\347\224\237\344\272\247\347\272\247\345\210\253\351\205\215\347\275\256.md" diff --git a/bigdata/hadoop/mapreduce/mapreduce-de-gong-zuo-yuan-li-pou-xi.md "b/bigdata/hadoop/MapReduce/MapReduce\347\232\204\345\267\245\344\275\234\345\216\237\347\220\206\345\211\226\346\236\220.md" similarity index 100% rename from bigdata/hadoop/mapreduce/mapreduce-de-gong-zuo-yuan-li-pou-xi.md rename to "bigdata/hadoop/MapReduce/MapReduce\347\232\204\345\267\245\344\275\234\345\216\237\347\220\206\345\211\226\346\236\220.md" diff --git a/bigdata/hadoop/mapreduce/mapreduce-shu-ru-shu-chu-pou-xi.md "b/bigdata/hadoop/MapReduce/MapReduce\350\276\223\345\205\245\350\276\223\345\207\272\345\211\226\346\236\220.md" similarity index 100% rename from bigdata/hadoop/mapreduce/mapreduce-shu-ru-shu-chu-pou-xi.md rename to "bigdata/hadoop/MapReduce/MapReduce\350\276\223\345\205\245\350\276\223\345\207\272\345\211\226\346\236\220.md" diff --git a/bigdata/hadoop/mapreduce/fen-bu-shi-chu-li-kuang-jia-mapreduce.md "b/bigdata/hadoop/MapReduce/\345\210\206\345\270\203\345\274\217\345\244\204\347\220\206\346\241\206\346\236\266MapReduce.md" similarity index 100% rename from bigdata/hadoop/mapreduce/fen-bu-shi-chu-li-kuang-jia-mapreduce.md rename to "bigdata/hadoop/MapReduce/\345\210\206\345\270\203\345\274\217\345\244\204\347\220\206\346\241\206\346\236\266MapReduce.md" diff --git a/bigdata/hadoop/README.md b/bigdata/hadoop/README.md deleted file mode 100644 index b9df0de9..00000000 --- a/bigdata/hadoop/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# hadoop - diff --git a/bigdata/hadoop/hdfs/README.md b/bigdata/hadoop/hdfs/README.md deleted file mode 100644 index aaaa517a..00000000 --- a/bigdata/hadoop/hdfs/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# HDFS - diff --git a/bigdata/hadoop/mapreduce/README.md b/bigdata/hadoop/mapreduce/README.md deleted file mode 100644 index e06c1001..00000000 --- a/bigdata/hadoop/mapreduce/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# MapReduce - diff --git a/bigdata/hadoop/yarn/README.md b/bigdata/hadoop/yarn/README.md deleted file mode 100644 index fa05e1e2..00000000 --- a/bigdata/hadoop/yarn/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Yarn - diff --git a/bigdata/kvstore/README.md b/bigdata/kvstore/README.md deleted file mode 100644 index 9745e167..00000000 --- a/bigdata/kvstore/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# kvstore - diff --git "a/bigdata/kvstore/hbase/Hbase\350\277\207\346\273\244\345\231\250.md" "b/bigdata/kvstore/hbase/Hbase\350\277\207\346\273\244\345\231\250.md" index a10c9a59..d64b7d94 100644 --- "a/bigdata/kvstore/hbase/Hbase\350\277\207\346\273\244\345\231\250.md" +++ "b/bigdata/kvstore/hbase/Hbase\350\277\207\346\273\244\345\231\250.md" @@ -1,37 +1,42 @@ # Hbase 过滤器详解 -[一、HBase过滤器简介](Hbase过滤器.md#一HBase过滤器简介)\ -[二、过滤器基础](Hbase过滤器.md#二过滤器基础)\ - [2.1 Filter接口和FilterBase抽象类](Hbase过滤器.md#21--Filter接口和FilterBase抽象类)\ - [2.2 过滤器分类](Hbase过滤器.md#22-过滤器分类)\ -[三、比较过滤器](Hbase过滤器.md#三比较过滤器)\ - [3.1 比较运算符](Hbase过滤器.md#31-比较运算符)\ - [3.2 比较器](Hbase过滤器.md#32-比较器)\ - [3.3 比较过滤器种类](Hbase过滤器.md#33-比较过滤器种类)\ - [3.4 DependentColumnFilter](Hbase过滤器.md#34-DependentColumnFilter)\ -[四、专用过滤器](Hbase过滤器.md#四专用过滤器)\ - [4.1 单列列值过滤器 (SingleColumnValueFilter)](Hbase过滤器.md#41-单列列值过滤器-SingleColumnValueFilter)\ - [4.2 单列列值排除器 (SingleColumnValueExcludeFilter)](Hbase过滤器.md#42-单列列值排除器-SingleColumnValueExcludeFilter)\ - [4.3 行键前缀过滤器 (PrefixFilter)](Hbase过滤器.md#43-行键前缀过滤器-PrefixFilter)\ - [4.4 列名前缀过滤器 (ColumnPrefixFilter)](Hbase过滤器.md#44-列名前缀过滤器-ColumnPrefixFilter)\ - [4.5 分页过滤器 (PageFilter)](Hbase过滤器.md#45-分页过滤器-PageFilter)\ - [4.6 时间戳过滤器 (TimestampsFilter)](Hbase过滤器.md#46-时间戳过滤器-TimestampsFilter)\ - [4.7 首次行键过滤器 (FirstKeyOnlyFilter)](Hbase过滤器.md#47-首次行键过滤器-FirstKeyOnlyFilter)\ -[五、包装过滤器](Hbase过滤器.md#五包装过滤器)\ - [5.1 SkipFilter过滤器](Hbase过滤器.md#51-SkipFilter过滤器)\ - [5.2 WhileMatchFilter过滤器](Hbase过滤器.md#52-WhileMatchFilter过滤器)\ -[六、FilterList](Hbase过滤器.md#六FilterList)\ + + ## 一、HBase过滤器简介 Hbase 提供了种类丰富的过滤器(filter)来提高数据处理的效率,用户可以通过内置或自定义的过滤器来对数据进行过滤,所有的过滤器都在服务端生效,即谓词下推(predicate push down)。这样可以保证过滤掉的数据不会被传送到客户端,从而减轻网络传输和客户端处理的压力。 -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/hbase-fliter.png) +
+ + ## 二、过滤器基础 -### 2.1 Filter接口和FilterBase抽象类 +### 2.1 Filter接口和FilterBase抽象类 Filter 接口中定义了过滤器的基本方法,FilterBase 抽象类实现了 Filter 接口。所有内置的过滤器则直接或者间接继承自 FilterBase 抽象类。用户只需要将定义好的过滤器通过 `setFilter` 方法传递给 `Scan` 或 `put` 的实例即可。 @@ -57,16 +62,18 @@ setFilter(Filter filter) } ``` -FilterBase 的所有子类过滤器如下: - -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/hbase-filterbase-subclass.png) +FilterBase 的所有子类过滤器如下:
> 说明:上图基于当前时间点(2019.4)最新的 Hbase-2.1.4 ,下文所有说明均基于此版本。 + + ### 2.2 过滤器分类 HBase 内置过滤器可以分为三类:分别是比较过滤器,专用过滤器和包装过滤器。分别在下面的三个小节中做详细的介绍。 + + ## 三、比较过滤器 所有比较过滤器均继承自 `CompareFilter`。创建一个比较过滤器需要两个参数,分别是**比较运算符**和**比较器实例**。 @@ -80,13 +87,13 @@ HBase 内置过滤器可以分为三类:分别是比较过滤器,专用过 ### 3.1 比较运算符 -* LESS (<) -* LESS\_OR\_EQUAL (<=) -* EQUAL (=) -* NOT\_EQUAL (!=) -* GREATER\_OR\_EQUAL (>=) -* GREATER (>) -* NO\_OP (排除所有符合条件的值) +- LESS (<) +- LESS_OR_EQUAL (<=) +- EQUAL (=) +- NOT_EQUAL (!=) +- GREATER_OR_EQUAL (>=) +- GREATER (>) +- NO_OP (排除所有符合条件的值) 比较运算符均定义在枚举类 `CompareOperator` 中 @@ -104,38 +111,39 @@ public enum CompareOperator { ``` > 注意:在 1.x 版本的 HBase 中,比较运算符定义在 `CompareFilter.CompareOp` 枚举类中,但在 2.0 之后这个类就被标识为 @deprecated ,并会在 3.0 移除。所以 2.0 之后版本的 HBase 需要使用 `CompareOperator` 这个枚举类。 +> ### 3.2 比较器 所有比较器均继承自 `ByteArrayComparable` 抽象类,常用的有以下几种: -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/hbase-bytearraycomparable.png) +
-* **BinaryComparator** : 使用 `Bytes.compareTo(byte [],byte [])` 按字典序比较指定的字节数组。 -* **BinaryPrefixComparator** : 按字典序与指定的字节数组进行比较,但只比较到这个字节数组的长度。 -* **RegexStringComparator** : 使用给定的正则表达式与指定的字节数组进行比较。仅支持 `EQUAL` 和 `NOT_EQUAL` 操作。 -* **SubStringComparator** : 测试给定的子字符串是否出现在指定的字节数组中,比较不区分大小写。仅支持 `EQUAL` 和 `NOT_EQUAL` 操作。 -* **NullComparator** :判断给定的值是否为空。 -* **BitComparator** :按位进行比较。 +- **BinaryComparator** : 使用 `Bytes.compareTo(byte [],byte [])` 按字典序比较指定的字节数组。 +- **BinaryPrefixComparator** : 按字典序与指定的字节数组进行比较,但只比较到这个字节数组的长度。 +- **RegexStringComparator** : 使用给定的正则表达式与指定的字节数组进行比较。仅支持 `EQUAL` 和 `NOT_EQUAL` 操作。 +- **SubStringComparator** : 测试给定的子字符串是否出现在指定的字节数组中,比较不区分大小写。仅支持 `EQUAL` 和 `NOT_EQUAL` 操作。 +- **NullComparator** :判断给定的值是否为空。 +- **BitComparator** :按位进行比较。 `BinaryPrefixComparator` 和 `BinaryComparator` 的区别不是很好理解,这里举例说明一下: 在进行 `EQUAL` 的比较时,如果比较器传入的是 `abcd` 的字节数组,但是待比较数据是 `abcdefgh`: -* 如果使用的是 `BinaryPrefixComparator` 比较器,则比较以 `abcd` 字节数组的长度为准,即 `efgh` 不会参与比较,这时候认为 `abcd` 与 `abcdefgh` 是满足 `EQUAL` 条件的; -* 如果使用的是 `BinaryComparator` 比较器,则认为其是不相等的。 ++ 如果使用的是 `BinaryPrefixComparator` 比较器,则比较以 `abcd` 字节数组的长度为准,即 `efgh` 不会参与比较,这时候认为 `abcd` 与 `abcdefgh` 是满足 `EQUAL` 条件的; ++ 如果使用的是 `BinaryComparator` 比较器,则认为其是不相等的。 ### 3.3 比较过滤器种类 比较过滤器共有五个(Hbase 1.x 版本和 2.x 版本相同),见下图: -![](https://gitee.com/heibaiying/BigData-Notes/raw/master/pictures/hbase-compareFilter.png) +
-* **RowFilter** :基于行键来过滤数据; -* **FamilyFilterr** :基于列族来过滤数据; -* **QualifierFilterr** :基于列限定符(列名)来过滤数据; -* **ValueFilterr** :基于单元格 (cell) 的值来过滤数据; -* **DependentColumnFilter** :指定一个参考列来过滤其他列的过滤器,过滤的原则是基于参考列的时间戳来进行筛选 。 ++ **RowFilter** :基于行键来过滤数据; ++ **FamilyFilterr** :基于列族来过滤数据; ++ **QualifierFilterr** :基于列限定符(列名)来过滤数据; ++ **ValueFilterr** :基于单元格 (cell) 的值来过滤数据; ++ **DependentColumnFilter** :指定一个参考列来过滤其他列的过滤器,过滤的原则是基于参考列的时间戳来进行筛选 。 前四种过滤器的使用方法相同,均只要传递比较运算符和运算器实例即可构建,然后通过 `setFilter` 方法传递给 `scan`: @@ -157,11 +165,11 @@ DependentColumnFilter(final byte [] family, final byte[] qualifier, final ByteArrayComparable valueComparator) ``` -* **family** :列族 -* **qualifier** :列限定符(列名) -* **dropDependentColumn** :决定参考列是否被包含在返回结果内,为 true 时表示参考列被返回,为 false 时表示被丢弃 -* **op** :比较运算符 -* **valueComparator** :比较器 ++ **family** :列族 ++ **qualifier** :列限定符(列名) ++ **dropDependentColumn** :决定参考列是否被包含在返回结果内,为 true 时表示参考列被返回,为 false 时表示被丢弃 ++ **op** :比较运算符 ++ **valueComparator** :比较器 这里举例进行说明: @@ -174,9 +182,13 @@ DependentColumnFilter dependentColumnFilter = new DependentColumnFilter( new BinaryPrefixComparator(Bytes.toBytes("xiaolan"))); ``` -* 首先会去查找 `student:name` 中值以 `xiaolan` 开头的所有数据获得 `参考数据集`,这一步等同于 valueFilter 过滤器; -* 其次再用参考数据集中所有数据的时间戳去检索其他列,获得时间戳相同的其他列的数据作为 `结果数据集`,这一步等同于时间戳过滤器; -* 最后如果 `dropDependentColumn` 为 true,则返回 `参考数据集`+`结果数据集`,若为 false,则抛弃参考数据集,只返回 `结果数据集`。 ++ 首先会去查找 `student:name` 中值以 `xiaolan` 开头的所有数据获得 ` 参考数据集 `,这一步等同于 valueFilter 过滤器; + ++ 其次再用参考数据集中所有数据的时间戳去检索其他列,获得时间戳相同的其他列的数据作为 ` 结果数据集 `,这一步等同于时间戳过滤器; + ++ 最后如果 `dropDependentColumn` 为 true,则返回 ` 参考数据集 `+` 结果数据集 `,若为 false,则抛弃参考数据集,只返回 ` 结果数据集 `。 + + ## 四、专用过滤器 @@ -186,8 +198,8 @@ DependentColumnFilter dependentColumnFilter = new DependentColumnFilter( 基于某列(参考列)的值决定某行数据是否被过滤。其实例有以下方法: -* **setFilterIfMissing(boolean filterIfMissing)** :默认值为 false,即如果该行数据不包含参考列,其依然被包含在最后的结果中;设置为 true 时,则不包含; -* **setLatestVersionOnly(boolean latestVersionOnly)** :默认为 true,即只检索参考列的最新版本数据;设置为 false,则检索所有版本数据。 ++ **setFilterIfMissing(boolean filterIfMissing)** :默认值为 false,即如果该行数据不包含参考列,其依然被包含在最后的结果中;设置为 true 时,则不包含; ++ **setLatestVersionOnly(boolean latestVersionOnly)** :默认为 true,即只检索参考列的最新版本数据;设置为 false,则检索所有版本数据。 ```shell SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter( @@ -236,7 +248,7 @@ public PageFilter(final long pageSize) { 客户端进行分页查询,需要传递 `startRow`(起始 RowKey),知道起始 `startRow` 后,就可以返回对应的 pageSize 行数据。这里唯一的问题就是,对于第一次查询,显然 `startRow` 就是表格的第一行数据,但是之后第二次、第三次查询我们并不知道 `startRow`,只能知道上一次查询的最后一条数据的 RowKey(简单称之为 `lastRow`)。 -我们不能将 `lastRow` 作为新一次查询的 `startRow` 传入,因为 scan 的查询区间是\[startRow,endRow) ,即前开后闭区间,这样 `startRow` 在新的查询也会被返回,这条数据就重复了。 +我们不能将 `lastRow` 作为新一次查询的 `startRow` 传入,因为 scan 的查询区间是[startRow,endRow) ,即前开后闭区间,这样 `startRow` 在新的查询也会被返回,这条数据就重复了。 同时在不使用第三方数据库存储 RowKey 的情况下,我们是无法通过知道 `lastRow` 的下一个 RowKey 的,因为 RowKey 的设计可能是连续的也有可能是不连续的。 @@ -281,7 +293,9 @@ while (true) { System.out.println("total rows: " + totalRows); ``` -> 需要注意的是在多台 Regin Services 上执行分页过滤的时候,由于并行执行的过滤器不能共享它们的状态和边界,所以有可能每个过滤器都会在完成扫描前获取了 PageCount 行的结果,这种情况下会返回比分页条数更多的数据,分页过滤器就有失效的可能。 +>需要注意的是在多台 Regin Services 上执行分页过滤的时候,由于并行执行的过滤器不能共享它们的状态和边界,所以有可能每个过滤器都会在完成扫描前获取了 PageCount 行的结果,这种情况下会返回比分页条数更多的数据,分页过滤器就有失效的可能。 + + ### 4.6 时间戳过滤器 (TimestampsFilter) @@ -317,6 +331,8 @@ Filter filter1 = new ValueFilter(CompareOperator.NOT_EQUAL, Filter filter2 = new SkipFilter(filter1); ``` + + ### 5.2 WhileMatchFilter过滤器 `WhileMatchFilter` 包装一个过滤器,当被包装的过滤器遇到一个需要过滤的 KeyValue 实例时,`WhileMatchFilter` 则结束本次扫描,返回已经扫描到的结果。下面是其使用示例: @@ -386,8 +402,8 @@ public FilterList(final Filter... filters) 多个过滤器组合的结果由 `operator` 参数定义 ,其可选参数定义在 `Operator` 枚举类中。只有 `MUST_PASS_ALL` 和 `MUST_PASS_ONE` 两个可选的值: -* **MUST\_PASS\_ALL** :相当于 AND,必须所有的过滤器都通过才认为通过; -* **MUST\_PASS\_ONE** :相当于 OR,只有要一个过滤器通过则认为通过。 ++ **MUST_PASS_ALL** :相当于 AND,必须所有的过滤器都通过才认为通过; ++ **MUST_PASS_ONE** :相当于 OR,只有要一个过滤器通过则认为通过。 ```java @InterfaceAudience.Public diff --git a/bigdata/kvstore/hbase/README.md b/bigdata/kvstore/hbase/README.md deleted file mode 100644 index 942ef4a6..00000000 --- a/bigdata/kvstore/hbase/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# hbase - diff --git a/bigdata/kvstore/rocksdb/README.md b/bigdata/kvstore/rocksdb/README.md deleted file mode 100644 index a630ab25..00000000 --- a/bigdata/kvstore/rocksdb/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# rocksdb - diff --git "a/bigdata/kvstore/rocksdb/Rocksdb\347\273\204\344\273\266\346\217\217\350\277\260.md" "b/bigdata/kvstore/rocksdb/Rocksdb\347\273\204\344\273\266\346\217\217\350\277\260.md" index cf73c331..294815ad 100644 --- "a/bigdata/kvstore/rocksdb/Rocksdb\347\273\204\344\273\266\346\217\217\350\277\260.md" +++ "b/bigdata/kvstore/rocksdb/Rocksdb\347\273\204\344\273\266\346\217\217\350\277\260.md" @@ -1,14 +1,12 @@ -# Rocksdb组件描述 - -## Options +# Options * Options包含Rocksdb基础的写入配置,以及如何初始化RocksDB。 -### Writer Buffer Size +## Writer Buffer Size * 这可以为每个数据库和/或每个列族设置。 -#### Column Family Write Buffer Size +### Column Family Write Buffer Size * 设置列族使用的最大write buffer,它表示在转换为已排序的磁盘文件之前要在内存中建立的数据量(由磁盘上未排序的日志支持)。 默认值为 64 MB。 @@ -19,7 +17,7 @@ columnFamilyOptions.setWriteBufferSize(64<<20); ``` -#### Database Write Buffer Size +### Database Write Buffer Size * 这是数据库中所有列族的所有写缓冲区的最大大小。它表示在写入磁盘之前在所有列族的memtable中构建的数据量。默认是关闭的,也是就0. @@ -29,13 +27,14 @@ dbOptions.setDbWriteBufferSize(64<<30); ``` -### Block Cache Size +## Block Cache Size * 您可以创建您选择的大小的块缓存来缓存未压缩的数据。推荐Block Cache Size设置为总内存的3分之1, -### Compression +## Compression * 控制前`n-1`层的压缩格式配置,推荐使用`kLZ4Compression`或`kSnappyCompression`格式 + * 控制第`n`层的压缩格式配置,推荐使用`kZSTD`或`kZlibCompression`格式 ```java @@ -57,11 +56,11 @@ } ``` -### Bloom Filters +## Bloom Filters * 如果你有很多点查找操作(如Get()),那么Bloom Filter可以帮助加速这些操作,相反,如果你的大部分操作是范围扫描(如Iterator()),那么Bloom Filter将没有帮助。 -### RateLimiter +## RateLimiter * DbOptions配置,设置db的每秒处理request速率。 @@ -73,7 +72,7 @@ } ``` -### Sst FileManager +## Sst FileManager * sst文件最终存储的地方 @@ -90,11 +89,11 @@ } ``` -## MemTable +# MemTable * MemTable 是一种内存数据结构,在将数据刷新到 SST 文件之前保存数据。 它同时服务于读和写——新的写入总是将数据插入到 memtable 中,而读取必须在从 SST 文件读取之前查询 memtable,因为 memtable 中的数据较新。 一旦一个 memtable 满了,它就会变得不可变并被一个新的 memtable 取代。 后台线程会将内存表的内容刷新到 SST 文件中,然后可以销毁内存表。 -### 影响memtable的重要配置 +## 影响memtable的重要配置 * `AdvancedColumnFamilyOptions::memtable_factory:memtable` 的工厂对象。 通过指定工厂对象,用户可以更改 memtable 的底层实现,并提供特定于实现的选项(默认:SkipListFactory)。 * `ColumnFamilyOptions::write_buffer_size`:单个memtable的大小,默认64MB @@ -117,24 +116,24 @@ } ``` -### Skiplist memtable +## Skiplist memtable * 基于skiplist的memtable在读写、随机访问和顺序扫描方面都具有良好的性能。此外,它还提供了一些其他memtable实现目前不支持的其他有用特性,如并发插入(Concurrent Insert)和使用提示插入(Insert with Hint)。 -### HashSkiplist MemTable +## HashSkiplist MemTable * HashSkipList将哈希表中的数据组织为跳跃列表,而HashLinkList将哈希表中的数据组织为排序后的单个链表。 * 当执行查找或插入键时,使用Options检索目标键的前缀。前缀提取器,用于查找散列桶。在哈希桶中,所有的比较都是使用整个(内部)键完成的,就像基于memtable的SkipList一样。 * 基于散列的memtable的最大限制是跨多个前缀进行扫描需要复制和排序,这非常慢,而且内存成本很高。 -### Flush +## Flush * 触发memtable flush的场景有三种: - * Memtable size超过ColumnFamilyOptions::write\_buffer\_size。 - * 所有列族的总memtable大小超过DBOptions::db\_write\_buffer\_size,或者DBOptions::write\_buffer\_manager表示flush。在这种情况下,将刷新最大的memtable。 - * 总WAL文件大小超过DBOptions::max\_total\_wal\_size。在这种情况下,带有最旧数据的memtable将被刷新,以允许清除带有该memtable数据的WAL文件。 + * Memtable size超过ColumnFamilyOptions::write_buffer_size。 + * 所有列族的总memtable大小超过DBOptions::db_write_buffer_size,或者DBOptions::write_buffer_manager表示flush。在这种情况下,将刷新最大的memtable。 + * 总WAL文件大小超过DBOptions::max_total_wal_size。在这种情况下,带有最旧数据的memtable将被刷新,以允许清除带有该memtable数据的WAL文件。 -### 并发插入 +## 并发插入 * 在不支持memtable并发插入的情况下,多线程并发写入RocksDB将会依次应用于memtable。并发memtable insert在默认情况下是启用的,可以通过`DBOptions::allow_concurrent_memtable_write`选项关闭,尽管只有基于skiplist的memtable支持该特性。 @@ -142,32 +141,32 @@ options.setAllowConcurrentMemtableWrite(true); ``` -### Insert with Hint +## Insert with Hint -* 就地更新可以通过切换`inplace_update_support`标志来启用。但是,这个标志默认设置为false,因为这个线程安全的就地更新支持与并发memtable写不兼容。注意,默认情况下,allow\_concurrent\_memtable\_write的bool值设置为true。 +* 就地更新可以通过切换`inplace_update_support`标志来启用。但是,这个标志默认设置为false,因为这个线程安全的就地更新支持与并发memtable写不兼容。注意,默认情况下,allow_concurrent_memtable_write的bool值设置为true。 -### 不同Memtable对比 +## 不同Memtable对比 -| Mem Table Type | SkipList | HashSkipList | HashLinkList | Vector | -| ------------------ | -------------------------------------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------------- | ---------------------------------------------- | -| Optimized Use Case | General | 在特定键前缀内的范围查询 | 在特定的键前缀范围查询,每个前缀只有少量的行 | 随机写工作负载大 | -| Index type | binary search | hash + binary search | hash + linear search | linear search | -| 支持完全有序的全数据库扫描? | 天然支持 | 开销非常大(复制并排序以创建临时的总排序视图) | 开销非常大(复制并排序以创建临时的总排序视图) | 开销非常大(复制并排序以创建临时的总排序视图) | -| 内存开销 | 平均(每个条目约1.33个指针) | High (Hash Buckets + Skip List Metadata for non-empty buckets + multiple pointers per entry) | Lower (Hash buckets + pointer per entry) | Low (pre-allocated space at the end of vector) | -| MemTable Flush | 速度快,内存持续增加 | 速度慢,临时内存占用率高 | 速度慢,临时内存占用率高 | 速度慢,内存持续增加 | -| Concurrent Insert | Supported | Not supported | Not supported | Not supported | -| Insert with Hint | Supported (in case there are no concurrent insert) | Not supported | Not supported | Not supported | +| Mem Table Type | SkipList | HashSkipList | HashLinkList | Vector | +| --------------------------- | -------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------- | ---------------------------------------------- | +| Optimized Use Case | General | 在特定键前缀内的范围查询 | 在特定的键前缀范围查询,每个前缀只有少量的行 | 随机写工作负载大 | +| Index type | binary search | hash + binary search | hash + linear search | linear search | +| 支持完全有序的全数据库扫描? | 天然支持 | 开销非常大(复制并排序以创建临时的总排序视图) | 开销非常大(复制并排序以创建临时的总排序视图) | 开销非常大(复制并排序以创建临时的总排序视图) | +| 内存开销 | 平均(每个条目约1.33个指针) | High (Hash Buckets + Skip List Metadata for non-empty buckets + multiple pointers per entry) | Lower (Hash buckets + pointer per entry) | Low (pre-allocated space at the end of vector) | +| MemTable Flush | 速度快,内存持续增加 | 速度慢,临时内存占用率高 | 速度慢,临时内存占用率高 | 速度慢,内存持续增加 | +| Concurrent Insert | Supported | Not supported | Not supported | Not supported | +| Insert with Hint | Supported (in case there are no concurrent insert) | Not supported | Not supported | Not supported | * 总的来说查询多、随机读取使用SkipList,写负载大使用Vector。 -## Write Ahead Log +# Write Ahead Log -### 概述 +## 概述 * RocksDB 的每次更新都会写入两个位置:1) 一个名为 memtable 的内存数据结构(稍后刷新到 SST 文件)和 2) 在磁盘上提前写入日志 (WAL)。 如果发生故障,可以使用预写日志完全恢复memtable中的数据,这是将数据库恢复到原始状态所必需的。 在默认配置中,RocksDB 通过在每次用户写入后刷新 WAL 来保证进程崩溃一致性。 * 预写日志(Write ahead log, WAL)将memtable操作序列化到日志文件中。在发生故障时,可以使用WAL文件将数据库恢复到一致状态,通过从日志中重建memtable。当memtable被安全刷新到persistent medium时,相应的WAL日志就会被废弃并被归档。最终,归档的日志在一段时间后从磁盘清除。 -### Wal生命周期 +## Wal生命周期 * 一旦db被打开,一个新的WAL将被创建在磁盘上,以持久化所有的写操作(WAL是在所有列族之间共享的)。 * 当进行db#put操作后,WAL应该已经记录了所有写操作。WAL将保持打开状态,并继续记录未来的写操作,直到它的大小达到`DBOptions::max_total_wal_size`。 @@ -175,33 +174,33 @@ * 此时会有两个 WAL,`旧的 WAL 包含 key1 到 key4,新的 WAL 包含 key5 和 key6`。 因为旧的 WAL 仍然包含`至少一个列族(“默认”)的实时数据`,所以还不能删除它。 只有当用户`最终决定刷新“默认”列族时,旧的 WAL 才能自动从磁盘归档和清除`。 * 当 1) 打开一个新数据库,2) 刷新一个列族时,就会创建一个 WAL。 当所有列族刷新超过 WAL 中包含的最大序列号时,WAL 将被删除(或归档,如果启用了归档),或者换句话说,WAL 中的所有数据都已持久化到 SST 文件。 存档的 WAL 将被移动到一个单独的位置,并在稍后从磁盘中清除。 由于复制目的,实际删除可能会延迟 -### WAL配置 +## WAL配置 * `DBOptions.wal_dir`:设置RocksDB存放预写日志文件的目录,允许wal与实际数据分开存放。 * `DBOptions::WAL_ttl_seconds, DBOptions::WAL_size_limit_MB`:这两个字段会影响归档wal被删除的速度。非零值表示触发归档WAL删除的时间和磁盘空间阈值 * `DBOptions::max_total_wal_size`:为了限制wal的大小,RocksDB使用`DBOptions::max_total_wal_size`作为列族刷新的触发器。一旦wal超过这个大小,RocksDB将开始强制刷新列族,以允许删除一些最古老的wal。当以非均匀频率更新列族时,这个配置可能很有用。如果没有大小限制,当不经常更新的列族有一段时间没有刷新时,用户可能需要保留非常旧的wall。 * `DBOptions::avoid_flush_during_recovery` * `DBOptions::manual_wal_flush`:确定 WAL 刷新是在每次写入后自动还是纯手动(用户必须调用 FlushWAL 来触发 WAL 刷新)。 -* `DBOptions::wal_filter`:通过DBOptions::wal\_filter,用户可以提供一个过滤器对象,以便在恢复过程中处理wal时调用。注:ROCKSDB\_LITE模式不支持 +* `DBOptions::wal_filter`:通过DBOptions::wal_filter,用户可以提供一个过滤器对象,以便在恢复过程中处理wal时调用。注:ROCKSDB_LITE模式不支持 * `WriteOptions::disableWAL`:不关心数据丢失可以关闭WAL -### WAL filter +## WAL filter -#### 事务日志迭代器 +### 事务日志迭代器 * 事务日志迭代器提供了在RocksDB实例之间复制数据的方法。一旦一个WAL由于列族刷新而被归档,WAL将被归档而不是立即删除。目标是允许事务日志迭代器继续读取WAL,并将其发送给关注者进行重放。 -### WAL File Format +## WAL File Format -#### WAL管理器 +### WAL管理器 * 在WAL目录下生成的文件序号越高越好。为了重建数据库的状态,这些文件是按照序列号顺序读取的。WAL管理器提供了将WAL文件作为单个单元读取的抽象。在内部,它使用Reader或Writer抽象来打开和读取文件。 -#### Reader/Writer +### Reader/Writer * Writer提供了将日志记录追加到日志文件的抽象。媒体特定的内部细节由WriteableFile接口处理。类似地,Reader提供了从日志文件中顺序读取日志记录的抽象。SequentialFile接口处理内部媒体特定的细节。 -#### Log File Format +### Log File Format * 日志文件由一系列长度可变的记录组成,记录由`kBlockSize`(32k)组成。如果某条记录不能填入剩余空间,则用空数据填充剩余空间。写入器以kBlockSize的块为单位写入,读取器以kBlockSize的块为单位读取。 @@ -215,9 +214,9 @@ P = Padding ``` -#### Records Format +### Records Format -**Legacy Record Format** +#### Legacy Record Format ``` +---------+-----------+-----------+--- ... ---+ @@ -235,7 +234,7 @@ Payload = Byte stream as long as specified by the payload size * 日志文件的内容是一个32KB的块序列。唯一的例外是文件的尾部可能包含部分块。每块由一下结构组成: -``` +```c++ block := record* trailer? record := checksum: uint32 // crc32c of type and data[] @@ -246,7 +245,7 @@ record := * FIRST、MIDDLE、LAST是用于被分割成多个片段的用户记录的类型(通常是因为块边界)。FIRST是用户记录的第一个片段的类型,LAST是用户记录的最后一个片段的类型,MID是用户记录所有内部片段的类型。FULL记录包含整个用户记录的内容。 -**Recyclabe Record Format** +#### Recyclabe Record Format ``` +---------+-----------+-----------+----------------+--- ... ---+ @@ -258,61 +257,61 @@ records written by the most recent log writer vs a previous one. 日志文件号,以便我们区分由最近的日志记录器所写的记录与以前的记录。 ``` -### Wal恢复模式 +## Wal恢复模式 * 每个应用程序都是独一无二的,RocksDB需要一定的一致性保证。RocksDB中的每一条提交的记录都会被持久化。未提交的记录记录在write-ahead-log (write-ahead-log)中。当RocksDB干净地关闭时,所有未提交的数据都会在关闭前提交,因此一致性总是得到保证。当RocksDB被杀死或机器重新启动时,RocksDB需要将自己恢复到一致的状态。其中一个重要的恢复操作是在WAL中重放未提交的记录。不同的WAL恢复模式定义了WAL重放的行为。 -#### kTolerateCorruptedTailRecords +### kTolerateCorruptedTailRecords * 允许数据丢失,WAL重放会忽略在日志末尾发现的任何错误。其理由是,在不完全关闭时,日志的尾部可能会有不完整的写操作。这是一种启发式模式,系统无法区分日志尾部的损坏和未完成的写入。任何其他的IO错误,将被认为是数据损坏。 -#### kAbsoluteConsistency +### kAbsoluteConsistency * WAL重放过程中出现的任何IO错误都被认为是数据损坏。对于那些连一条记录都不能丢失的应用程序和/或有其他方法恢复未提交的数据的应用程序,这种模式是理想的。 -#### kSkipAnyCorruptedRecords +### kSkipAnyCorruptedRecords * 在这种模式下,读取日志时的任何IO错误都被忽略。系统试图恢复尽可能多的数据。这对于灾难恢复是理想的。 -### WAL性能 +## WAL性能 -#### Non-Sync模式 +### Non-Sync模式 * 当`WriteOptions.sync = false`(默认),WAL写不同步到磁盘。除非操作系统认为它必须刷新数据(例如,太多脏页),否则用户不需要等待任何I/O写入。 * 用户如果想减少写操作系统页面缓存所带来的CPU延迟,可以选择`Options.manual_wal_flush = true`。使用这个选项,WAL写操作甚至`不会刷新`到文件系统页面缓存,而是保留在RocksDB中。用户需要调用`DB::FlushWAL()`使缓冲条目进入文件系统。 * 用户可以通过调用`DB::SyncWAL()`强制WAL文件fsync。该函数不会阻塞正在其他线程中执行的写操作。 * 在这种模式下,WAL写不是崩溃安全的。 -#### Sync Mode +### Sync Mode * `WriteOptions.sync = true`(默认) -#### Group Commit +### Group Commit * 和其他大多数依赖日志的系统一样,RocksDB支持团队承诺提高WAL的写吞吐量,以及写放大。RocksDB的组提交以一种自然的方式实现:当不同线程同时写入同一个DB时,所有符合合并条件的未完成的写入将被合并到一起,并写入WAL一次,使用一个fsync。通过这种方式,相同数量的I/ o可以完成更多的写操作。 * 具有不同写选项的写操作可能不符合组合的要求。最大组大小为1MB。RocksDB不会试图通过主动延迟写入来增加批处理大小。 -#### Number of I/Os per write +### Number of I/Os per write * 如果`Options.recycle_log_file_num=false(默认)`,RocksDB总是为新的WAL段创建新文件。每次WAL写都会改变数据和文件大小,所以每次fsync至少会产生两个I/ o,一个用于数据,一个用于元数据。注意,RocksDB调用fallocate()来为文件预留足够的空间,但它并不会阻止fsync中的元数据I/O。 * `Options.recycle_log_file_num = true`将保留一个WAL文件池并尝试重用它们。当写入现有日志文件时,从大小为0开始使用随机写入。在写入到达文件末尾之前,文件大小不会改变,因此可以避免元数据的I/O(也取决于文件系统挂载选项)。假设大多数WAL文件都有类似的大小,元数据所需的I/O将是最小的。 -#### 写放大 +### 写放大 -* 注意,对于某些用例,同步WAL可能会引入一些重要的写扩展。当写操作很小的时候,因为整个块/页可能需要更新,所以即使写操作很小,我们也可能需要两次4KB的写操作(一次用于数据,一次用于元数据)。如果写仅为40字节,则更新8KB,则写放大为8KB /40字节\~= 200。它甚至很容易比lsm树的写放大值还要大。 +* 注意,对于某些用例,同步WAL可能会引入一些重要的写扩展。当写操作很小的时候,因为整个块/页可能需要更新,所以即使写操作很小,我们也可能需要两次4KB的写操作(一次用于数据,一次用于元数据)。如果写仅为40字节,则更新8KB,则写放大为8KB /40字节~= 200。它甚至很容易比lsm树的写放大值还要大。 -## MANIFEST +# MANIFEST * RocksDB是文件系统和存储介质无关的。文件系统操作不是原子操作,在系统故障时很容易出现不一致。即使打开了日志记录,文件系统也不能保证不干净重启时的一致性。POSIX文件系统也不支持原子批处理操作。因此,不可能依靠嵌入在RocksDB数据存储文件中的元数据来重新启动时重建RocksDB的最后一致状态。 * RocksDB有一个内置的机制来克服POSIX文件系统的这些限制,通过使用Manifest日志文件中的`Version Edit Records保存RocksDB状态更改的事务日志`。MANIFEST用于在重启时将RocksDB恢复到最新的已知一致状态。 -### 术语 +## 术语 -* _**MANIFEST**_是指在事务日志中跟踪RocksDB状态变化的系统 -* _**Manifest log**_ 是指包含 RocksDB 状态快照/编辑的单个日志文件 -* _**CURRENT**_ 是指当前最新的mainfest log +* ***MANIFEST***是指在事务日志中跟踪RocksDB状态变化的系统 +* ***Manifest log*** 是指包含 RocksDB 状态快照/编辑的单个日志文件 +* ***CURRENT*** 是指当前最新的mainfest log -### 工作原理 +## 工作原理 * MANIFEST是RocksDB状态变化的事务日志。MANIFEST由- MANIFEST日志文件和指向最新MANIFEST文件(CURRENT)的指针组成。Manifest日志是正在滚动名为Manifest -(seq号)的日志文件。序列号总是在增加。CURRENT是一个特殊的文件,它指向最新的清单日志文件。 * 在系统(重新)启动时,最新的manifest日志包含RocksDB的一致状态。RocksDB状态的任何后续更改都记录到manifest日志文件中。当manifest日志文件超过一定大小时,将使用RocksDB状态的快照创建一个新的manifest日志文件。更新最新的清单文件指针并同步文件系统。成功更新CURRENT文件后,将清除冗余清单日志。 @@ -323,7 +322,7 @@ CURRENT = File pointer to the latest manifest log MANIFEST- = Contains snapshot of RocksDB state and subsequent modifications ``` -### Version Edit +## Version Edit * RocksDB在任何给定时间的某个状态被称为版本(又名快照)。对版本的任何修改都被认为是版本编辑。Version(或RocksDB状态快照)是通过连接一系列版本编辑来构造的。本质上,清单日志文件是一系列版本编辑。 @@ -334,11 +333,11 @@ manifest-log-file = { version, version-edit* } = { version-edit* } ``` -### Version Edit Layout +## Version Edit Layout * Manifest log是一个Version Edit记录的序列。这个Version Edit记录由编辑标识号标识。 -#### 数据类型 +### 数据类型 * 简单数据类型 @@ -357,31 +356,31 @@ String - Length prefixed string data |<- Var32 ->|<-- n -->| ``` -## Block Cache +# Block Cache * 块缓存是RocksDB在内存中缓存数据用于读取的地方。用户可以将缓存对象以所需的容量(size)传递给RocksDB实例。一个Cache对象可以在同一个进程中被多个RocksDB实例共享,从而允许用户控制整个缓存容量。块缓存存储未压缩的块。用户可以选择设置第二个块缓存存储压缩块。读取将首先从未压缩的块缓存中获取数据块,然后从压缩的块缓存中获取数据块。如果使用Direct-IO,压缩块缓存可以替代操作系统页面缓存。 * RocksDB 中有两种缓存实现,分别是 LRUCache 和 ClockCache。 两种类型的缓存都被分片以减轻锁争用。 容量平均分配给每个分片,分片不共享容量。 默认情况下,每个缓存将被分片为最多 64 个分片,每个分片的容量不低于 512k 字节。 -## Write Buffer Manager +# Write Buffer Manager * writer buffer manager帮助用户控制跨多个列族和/或数据库实例的内存表使用的总内存。 用户可以通过两种方式启用此控件: * 将跨多个列族和数据库的内存表总使用量限制在阈值以下。 * 花费memtable内存使用来阻塞缓存,这样RocksDB的内存就可以被单个限制所限制。 -### memtable的总内存限制 +## memtable的总内存限制 * 在创建写缓冲区管理器对象时给出内存限制。RocksDB将尝试将总内存限制在这个限制之下。 * 在5.6或更高版本中,您插入的DB的一个列族会触发flush, * 如果可变memtable大小超过了限制的90% * 如果总内存超过限制,只有当可变memtable大小也超过了限制的50%时,才会触发更激进的刷新。 -## Compaction +# Compaction -### 压缩算法概览 +## 压缩算法概览 * Rocksdb提供以下压缩算法:Classic Leveled, Tiered, Tiered+Leveled(Level Compaction), Leveled-N, FIFO -#### Classic Leveled +### Classic Leveled ``` 什么是扇入和扇出? @@ -397,60 +396,61 @@ String - Length prefixed string data * 以读写放大为代价最小化空间放大,LSM树是一系列的层级,每个level都是将多个文件排序后运行。每一level都比前一level大很多,相邻能级的尺寸比有时被称为扇出,当所有能级之间使用同一个扇出时,写放大就会最小化。压缩到N级(Ln)将Ln-1中的数据合并到Ln中。压缩到Ln会重写之前合并到Ln的数据。在最坏的情况下,每个级别的写放大等于扇出,但在实践中往往比扇出少,这在Hyeontaek Lim等人的论文中解释。在最初的LSM论文中,压缩是全对全的——所有来自Ln-1的数据都与来自Ln的所有数据合并。对于LevelDB和RocksDB是一些对一些的——Ln-1中的一些数据与Ln中的一些(重叠的)数据合并。 -#### Leveled-N +### Leveled-N * Leveled-N 压缩类似于leveled压缩,但写入更少,读取放大更多。 它允许每个级别有多个排序运行。 压缩将所有从 Ln-1 排序的运行合并到一个来自 Ln 的排序运行中,这是一个级别。 然后在名称中添加“-N”以表示每个级别可以有 n 次排序运行。 Dostoevsky 论文定义了一种名为 Fluid LSM 的压缩算法,其中最大级别有 1 次排序运行,但非最大级别可以有超过 1 次排序运行。 水平压缩完成到最大级别。 -#### Tiered +### Tiered -* Tiered Compaction以读和空间放大为代价最小化写放大。每个级别有 N 个排序运行。 Ln 中的每个排序运行比 Ln-1 中的排序运行大 \~N 倍。 压缩合并一个级别中的所有排序运行,以在下一个级别创建一个新的排序运行。 在这种情况下,N 类似于用于水平压缩的扇出。 合并到 Ln 时,压缩不会读取/重写 Ln 中的排序运行。 每级写入放大为 1,这远低于扇出的水平。 +* Tiered Compaction以读和空间放大为代价最小化写放大。每个级别有 N 个排序运行。 Ln 中的每个排序运行比 Ln-1 中的排序运行大 ~N 倍。 压缩合并一个级别中的所有排序运行,以在下一个级别创建一个新的排序运行。 在这种情况下,N 类似于用于水平压缩的扇出。 合并到 Ln 时,压缩不会读取/重写 Ln 中的排序运行。 每级写入放大为 1,这远低于扇出的水平。 -#### Tiered+Leveled +### Tiered+Leveled * Tiered+Leveled 的写入放大比 leveled 小,空间放大比 tiered 小。它对较小的级别使用 tiered,对较大的级别使用 leveled。 LSM 树从分层切换到分层的级别是灵活的,Leveled compaction在Rocksdb中就是Tiered+Leveled。 -#### FIFO +### FIFO * FIFOStyle压缩删除过时的文件,并可用于类似缓存的数据。 -### Leveled Compaction +## Leveled Compaction -#### 存储文件的结构 +### 存储文件的结构 -![](img/level\_structure.png) +![](./img/level_structure.png) * 在每个级别(除了级别0)内,数据范围被划分为多个SST文件 -![](img/level\_files.png) +![](./img/level_files.png) * level是有序的运行因为每个keys在SST都是有序的。为了确定一个键的位置,我们首先对所有文件的开始/结束键进行二分搜索以确定哪个文件可能包含该键,然后在文件内部进行二分搜索以定位确切位置。 总之,这是对level中所有键的完整二分搜索。 * 所有非 0 级别都有目标大小。 Compaction 的目标是将这些级别的数据大小限制在目标以下。 规模目标通常呈指数增长: -![](img/level\_targets.png) +![](./img/level_targets.png) -#### Compactions +### Compactions * 当 L0 文件数量达到 `level0_file_num_compaction_trigger` 时触发 Compaction,L0 的文件将合并到 L1。后续L1超过在压缩到L2依次进行压缩 -![](img/pre\_l0\_compaction.png) +![](./img/pre_l0_compaction.png) * 除了L0到L1,后续level压缩可以并行执行压缩。 -![](img/pre\_l1\_compaction.png) +![](./img/pre_l1_compaction.png) -![](img/pre\_l2\_compaction.png) +![](./img/pre_l2_compaction.png) * 允许的最大压缩数由 `max_background_compactions` 控制。但是,默认情况下,L0 到 L1 的压缩不是并行化的。 在某些情况下,它可能成为限制总压缩速度的瓶颈。 RocksDB 仅支持 L0 到 L1 的基于子压缩的并行化。 要启用它,用户可以将 `max_subcompactions` 设置为大于 1。 然后,我们将尝试对范围进行分区并使用多个线程来执行它: -![](img/subcompaction.png) +![](./img/subcompaction.png) -#### Compaction Picking +### Compaction Picking * 当多个level触发了compaction条件,RocksDB需要选择一个level首先去压缩,为每个level生成一个分数: * 对于非0的level,这个分数是level的总大小除以目标大小。 如果已经选择了要压缩到下一级的文件,则这些文件的大小不包括在总大小中,因为它们很快就会消失。 - * 对于 0 级,分数是文件总数除以 `level0_file_num_compaction_trigger` 或超过 `max_bytes_for_level_base` 的总大小,以较大者为准。 (如果文件大小小于 `level0_file_num_compaction_trigger`,无论分数有多大,都不会从 level 0 触发压缩。) + * 对于 0 级,分数是文件总数除以` level0_file_num_compaction_trigger` 或超过 `max_bytes_for_level_base` 的总大小,以较大者为准。 (如果文件大小小于` level0_file_num_compaction_trigger`,无论分数有多大,都不会从 level 0 触发压缩。) * 分数越高的level最先压缩。 -#### 周期压缩 +### 周期压缩 + +* 如果存在压缩过滤器,RocksDB 会确保数据在一定时间后通过压缩过滤器。 这是通过 `options.periodic_compaction_seconds` 实现的。 将其设置为 0 将禁用此功能。 保留默认值,即 UINT64_MAX - 1,表示 RocksDB 控制该功能。 目前,RocksDB 会将值更改为 30 天。 每当 RocksDB 尝试选择压缩时,超过 30 天的文件将有资格进行压缩并被压缩到相同的级别。 -* 如果存在压缩过滤器,RocksDB 会确保数据在一定时间后通过压缩过滤器。 这是通过 `options.periodic_compaction_seconds` 实现的。 将其设置为 0 将禁用此功能。 保留默认值,即 UINT64\_MAX - 1,表示 RocksDB 控制该功能。 目前,RocksDB 会将值更改为 30 天。 每当 RocksDB 尝试选择压缩时,超过 30 天的文件将有资格进行压缩并被压缩到相同的级别。 diff --git a/bigdata/mq/README.md b/bigdata/mq/README.md deleted file mode 100644 index cd3433fe..00000000 --- a/bigdata/mq/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# mq - diff --git a/bigdata/mq/kafka/README.md b/bigdata/mq/kafka/README.md deleted file mode 100644 index 3c234ce1..00000000 --- a/bigdata/mq/kafka/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# kafka - diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/README.md b/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/README.md deleted file mode 100644 index 832e5516..00000000 --- a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# kafka权威指南 - diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/2.-an-zhuang-kafka.md "b/bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/2.\345\256\211\350\243\205Kafka.md" similarity index 100% rename from bigdata/mq/kafka/kafka-quan-wei-zhi-nan/2.-an-zhuang-kafka.md rename to "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/2.\345\256\211\350\243\205Kafka.md" diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/3.kafka-sheng-chan-zhe.md "b/bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/3.Kafka\347\224\237\344\272\247\350\200\205.md" similarity index 100% rename from bigdata/mq/kafka/kafka-quan-wei-zhi-nan/3.kafka-sheng-chan-zhe.md rename to "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/3.Kafka\347\224\237\344\272\247\350\200\205.md" diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/4.kafka-xiao-fei-zhe.md "b/bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/4.Kafka\346\266\210\350\264\271\350\200\205.md" similarity index 100% rename from bigdata/mq/kafka/kafka-quan-wei-zhi-nan/4.kafka-xiao-fei-zhe.md rename to "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/4.Kafka\346\266\210\350\264\271\350\200\205.md" diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/5.-shen-ru-kafka.md "b/bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/5.\346\267\261\345\205\245Kafka.md" similarity index 100% rename from bigdata/mq/kafka/kafka-quan-wei-zhi-nan/5.-shen-ru-kafka.md rename to "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/5.\346\267\261\345\205\245Kafka.md" diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/6.-ke-kao-de-xiao-xi-chuan-shu.md "b/bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/6.\345\217\257\351\235\240\347\232\204\346\266\210\346\201\257\344\274\240\350\276\223.md" similarity index 100% rename from bigdata/mq/kafka/kafka-quan-wei-zhi-nan/6.-ke-kao-de-xiao-xi-chuan-shu.md rename to "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/6.\345\217\257\351\235\240\347\232\204\346\266\210\346\201\257\344\274\240\350\276\223.md" diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/7.-gou-jian-shu-ju-guan-dao.md "b/bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/7.\346\236\204\345\273\272\346\225\260\346\215\256\347\256\241\351\201\223.md" similarity index 100% rename from bigdata/mq/kafka/kafka-quan-wei-zhi-nan/7.-gou-jian-shu-ju-guan-dao.md rename to "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/7.\346\236\204\345\273\272\346\225\260\346\215\256\347\256\241\351\201\223.md" diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/8.-kua-ji-qun-shu-ju-jing-xiang.md "b/bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/8.\350\267\250\351\233\206\347\276\244\346\225\260\346\215\256\351\225\234\345\203\217.md" similarity index 100% rename from bigdata/mq/kafka/kafka-quan-wei-zhi-nan/8.-kua-ji-qun-shu-ju-jing-xiang.md rename to "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/8.\350\267\250\351\233\206\347\276\244\346\225\260\346\215\256\351\225\234\345\203\217.md" diff --git a/bigdata/mq/kafka/kafka-quan-wei-zhi-nan/9.-guan-li-kafka.md "b/bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/9.\347\256\241\347\220\206Kafka.md" similarity index 100% rename from bigdata/mq/kafka/kafka-quan-wei-zhi-nan/9.-guan-li-kafka.md rename to "bigdata/mq/kafka/kafka\346\235\203\345\250\201\346\214\207\345\215\227/9.\347\256\241\347\220\206Kafka.md" diff --git a/bigdata/mq/kafka/shen-ru-li-jie-kafka/README.md b/bigdata/mq/kafka/shen-ru-li-jie-kafka/README.md deleted file mode 100644 index 2f10b9b7..00000000 --- a/bigdata/mq/kafka/shen-ru-li-jie-kafka/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 深入理解Kafka - diff --git "a/bigdata/mq/pulsar/1.\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/bigdata/mq/pulsar/1.\345\277\253\351\200\237\345\205\245\351\227\250.md" index e8c4c5b5..4e76cbcb 100644 --- "a/bigdata/mq/pulsar/1.\345\277\253\351\200\237\345\205\245\351\227\250.md" +++ "b/bigdata/mq/pulsar/1.\345\277\253\351\200\237\345\205\245\351\227\250.md" @@ -1,20 +1,15 @@ -# 1.快速入门 - -## 快速使用 - -### 本地安装Pulsar - +# 快速使用 +## 本地安装Pulsar * 单机模式pulsar需要pulsar broker、必要的zookeeper和BookKeeper组件 -#### 下载二进制包 - +### 下载二进制包 ```shell wget https://archive.apache.org/dist/pulsar/pulsar-2.8.0/apache-pulsar-2.8.0-bin.tar.gz tar xvfz apache-pulsar-2.8.0-bin.tar.gz cd apache-pulsar-2.8.0 ``` -**安装分层存储携带程序** +#### 安装分层存储携带程序 ```shell wget https://archive.apache.org/dist/pulsar/pulsar-2.8.0/apache-pulsar-offloaders-2.8.0-bin.tar.gz @@ -22,82 +17,71 @@ wget https://archive.apache.org/dist/pulsar/pulsar-2.8.0/apache-pulsar-offloader mv apache-pulsar-offloaders-2.8.0/offloaders offloaders ``` -#### 启动单机模式Pulsar - +### 启动单机模式Pulsar ```shell bin/pulsar standalone ``` -#### 使用单机模式Pulsar - -**Consume消息** +### 使用单机模式Pulsar +#### Consume消息 ```shell bin/pulsar-client consume my-topic -s "first-subscription" ``` -**Produce消息** - +#### Produce消息 ```shell bin/pulsar-client produce my-topic --messages "hello-pulsar" ``` -#### 终止单机模式Pulsar - +### 终止单机模式Pulsar * Ctrl+C终止单机模式Pulsar的运行 -### Docker里配置单机Pulsar - -#### docker中启动Pulsar - +## Docker里配置单机Pulsar +### docker中启动Pulsar ```shell docker run -it -p 6650:6650 -p 8080:8080 --mount source=pulsardata,target=/pulsar/data --mount source=pulsarconf,target=/pulsar/conf apachepulsar/pulsar:2.8.0 bin/pulsar standalone ``` - -#### 在Docker中运行Pulsar +### 在Docker中运行Pulsar * pulsar://localhost:6650 * http://localhost:8080 -#### 获取topic数据 - +### 获取topic数据 ```shell curl http://localhost:8080/admin/v2/persistent/public/default/my-topic/stats | python -m json.tool ``` -## 概述 - -## 概述 - -### Pulsar特性 +# 概述 +# 概述 +## Pulsar特性 * 单实例原声支持多个集群,可跨机房在集群间无缝地完成消息复制。 * 极低的发布延迟和端到端延迟。 * 可无缝扩展到超过1百万个topic。 * 支持多种订阅模式(独占订阅、共享订阅、故障转移订阅) * 通过Apache BookKeeper提供的持久化消息存储机制保证消息传递 - * 由轻量级的 serverless 计算框架 Pulsar Functions 实现流原生的数据处理。 - * 基于 Pulsar Functions 的 serverless connector 框架 Pulsar IO 使得数据更易移入、移出 Apache Pulsar。 - * 分层式存储可在数据陈旧时,将数据从热存储卸载到冷/长期存储(如S3、GCS)中。 - -### 消息 + * 由轻量级的 serverless 计算框架 Pulsar Functions 实现流原生的数据处理。 + * 基于 Pulsar Functions 的 serverless connector 框架 Pulsar IO 使得数据更易移入、移出 Apache Pulsar。 + * 分层式存储可在数据陈旧时,将数据从热存储卸载到冷/长期存储(如S3、GCS)中。 +## 消息 * Pulsar采用发布-订阅的设计模式(pub-sub),producers发布消息到topic,consumers订阅这些topic处理消费消息并且当处理完毕时发送ack到broker。 -#### 消息格式 - -| 组件 | 说明 | -| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Value / data payload | 消息所承载的数据。 尽管消息数据也可以符合数据 [schemas](https://pulsar.apache.org/docs/zh-CN/next/schema-get-started),但所有 Pulsar 消息都包含原始字节。 | -| Key | 消息可以选择用键进行标记,这在 [topic 压缩](https://pulsar.apache.org/docs/zh-CN/next/concepts-topic-compaction) 等操作很有用。 | -| Properties | 用户自定义属性的键值对(可选)。 | -| Producer 名称 | 生成消息的 producer 的名称。 如果不指定,则使用默认名称 | -| Sequence ID | 每个 Pulsar 消息都存储在其主题上的有序序列中。消息的序列 ID 是其在该序列中的顺序。 | -| Publish time | 消息发布的时间戳,由 producer 自动添加。 | -| Event time | 应用程序可以附加到消息的时间戳(可选), 例如处理消息的时间。 如果没有明确设置,则消息的事件时间为 `0`。 | +### 消息格式 + +| 组件 | 说明 | +| :------------------- | :----------------------------------------------------------- | +| Value / data payload | 消息所承载的数据。 尽管消息数据也可以符合数据 [schemas](https://pulsar.apache.org/docs/zh-CN/next/schema-get-started),但所有 Pulsar 消息都包含原始字节。 | +| Key | 消息可以选择用键进行标记,这在 [topic 压缩](https://pulsar.apache.org/docs/zh-CN/next/concepts-topic-compaction) 等操作很有用。 | +| Properties | 用户自定义属性的键值对(可选)。 | +| Producer 名称 | 生成消息的 producer 的名称。 如果不指定,则使用默认名称 | +| Sequence ID | 每个 Pulsar 消息都存储在其主题上的有序序列中。消息的序列 ID 是其在该序列中的顺序。 | +| Publish time | 消息发布的时间戳,由 producer 自动添加。 | +| Event time | 应用程序可以附加到消息的时间戳(可选), 例如处理消息的时间。 如果没有明确设置,则消息的事件时间为 `0`。 | | TypedMessageBuilder | 用于构造消息。 您可以使用 `TypedMessageBuilder` 设置消息的键值对属性。 在设置 `TypedMessageBuilder` 时,最佳的选择是将 key 设置为字符串。 如果将 key 设置为其他类型(例如,AVRO 对象),则 key 会以字节形式发送,这时 consumer 就很难使用了。 | -#### 消息配置 +### 消息配置 * Message 默认最大可携带 5 MB 数据。 @@ -108,86 +92,88 @@ maxMessageSize=5242880 nettyMaxFrameSizeBytes=5253120 ``` -#### Producer +### Producer * producer是连接topic的程序将消息发布到一个Pulsar broker上。 -**发送模式** +#### 发送模式 -| 发送模式 | 说明 | -| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Sync send | Producer 将在发送每条消息后等待 broker 的确认。 如果未收到确认,则 producer 将认为发送失败。 | -| 异步发送 | Producer 将把消息放于阻塞队列中,并立即返回 然后,客户端将在后台将消息发送给 broker。 如果队列已满([最大大小可配置](https://pulsar.apache.org/docs/zh-CN/next/reference-configuration#broker)),则调用 API 时,producer 可能会立即被阻止或失败,具体取决于传递给 producer 的参数。 | +| 发送模式 | 说明 | +| :-------- | ------------------------------------------------------------ | +| Sync send | Producer 将在发送每条消息后等待 broker 的确认。 如果未收到确认,则 producer 将认为发送失败。 | +| 异步发送 | Producer 将把消息放于阻塞队列中,并立即返回 然后,客户端将在后台将消息发送给 broker。 如果队列已满([最大大小可配置](https://pulsar.apache.org/docs/zh-CN/next/reference-configuration#broker)),则调用 API 时,producer 可能会立即被阻止或失败,具体取决于传递给 producer 的参数。 | -**访问模式** +#### 访问模式 * 对于producer来说topic上可以有不同的访问模式 * Exclusive:默认的配置是仅有一个producer可以在topic上发布消息 * WaitForExclusive:如果已经有一个生产者连接了主题,生产者创建过程被挂起(而不是超时) 直到这个生产者获得了 `Exclusive` 访问权限。 -**压缩策略** +#### 压缩策略 * LZ4 * ZLIB * ZSTD * SNAPPY -**批量处理** +#### 批量处理 * 当批量处理启用时,producer 会在`单个请求中积累并发送一批消息`。 批量处理的量大小由`最大消息数`和`最大发布延迟`定义。 因此,积压数量是分批处理的总数,而不是信息总数。 * Pulsar将批次被跟踪并存储为单个单元,而不是单个消息。 Consumer 将批量处理的消息拆分成单个消息。 但即使启用了批量处理,也始终将计划中的消息(通过 `deliverAt` 或者 `deliverAfter` 进行配置) 作为单个消息发送。当consumer确认了一个批的所有消息才算确认。Broker 维护批量索引的确认状态并跟踪每批索引的确认状态,以避免向 consumer 发送已确认的消息(端到端一致性)。默认情况下,禁用批处理索引确认(acknowledgmentAtBatchIndexLevelEnabled=false)。 您可以通过在代理端将knowledgeAtBatchIndexLevelEnabled 参数设置为true 来启用批量索引确认。启用批量索引确认将会导致更多内存开销。 -**分块** +#### 分块 * batch消息和分块不能同时开启,分块仅支持持久化的topic,分块仅支持独占和故障转移订阅模式。 * 当启用分块(chunking) 时(`chunkingEnabled=true`) ,如果消息大小大于允许的最大发布有效载荷大小,则 producer 将原始消息分割成分块的消息,并将它们与块状的元数据一起单独和按顺序发布到 broker。 在 broker 中,分块的消息将和普通的消息以相同的方式存储在 Managed Ledger 上。 唯一的区别是,consumer 需要缓冲分块消息,并在收集完所有分块消息后将其合并成真正的消息。 Managed Ledger 上的分块消息可以和普通消息交织在一起。 如果 producer 未能发布消息的所有分块,则当 consumer 未能在过期时间(expire time) 内接收所有分块时,consumer 可以过期未完成的分块。 默认情况下,过期时间设置为1小时。 * Consumer 会缓存收到的块状消息,直到收到消息的所有分块为止。 然后 consumer 将分块的消息拼接在一起,并将它们放入接收器队列中。 客户端从接收器队列中消费消息。 一旦 consumer 使用整个大消息并确认,consumer 就会在内部发送与该大消息关联的所有分块消息的确认。设置`maxPendingChunkedMessage`参数当达到阈值时,consumer通过静默确认未分块的消息或通过将其标记为未确认,要求broker稍后重新发送这些消息。 -**处理一个producer和一个订阅consumer 的分块消息** +##### 处理一个producer和一个订阅consumer 的分块消息 -* 如下图所示,当生产者向主题发送一批大的分块消息和普通的非分块消息时。 假设生产者发送的消息为 M1,M1 有三个分块 M1-C1,M1-C2 和 M1-C3。 这个 broker 在其管理的ledger里面保存所有的三个块消息,然后以相同的顺序分发给消费者(独占/灾备模式)。 消费者将在内存缓存所有的块消息,直到收到所有的消息块。将这些消息合并成为原始的消息M1,发送给处理进程。 ![](img/producer到consumer分块消息.png) +* 如下图所示,当生产者向主题发送一批大的分块消息和普通的非分块消息时。 假设生产者发送的消息为 M1,M1 有三个分块 M1-C1,M1-C2 和 M1-C3。 这个 broker 在其管理的ledger里面保存所有的三个块消息,然后以相同的顺序分发给消费者(独占/灾备模式)。 消费者将在内存缓存所有的块消息,直到收到所有的消息块。将这些消息合并成为原始的消息M1,发送给处理进程。 +![](./img/producer到consumer分块消息.png) -**多个producer到一个consumer的分块消息** +##### 多个producer到一个consumer的分块消息 * 当多个生产者发布块消息到单个主题,这个 Broker 在同一个 Ledger 里面保存来自不同生产者的所有块消息。 如下所示,生产者1发布的消息 M1,M1 由 M1-C1, M1-C2 和 M1-C3 三个块组成。 生产者2发布的消息 M2,M2 由 M2-C1, M2-C2 和 M2-C3 三个块组成。 这些特定消息的所有分块是顺序排列的,但是其在 ledger 里面可能不是连续的。 这种方式会给消费者带来一定的内存负担。因为消费者会为每个大消息在内存开辟一块缓冲区,以便将所有的块消息合并为原始的大消息。 -![](img/多个producer到consumer分块消息.png) +![](./img/多个producer到consumer分块消息.png) -#### Consumer +### Consumer * Consumer 向 broker 发送消息流获取申请([flow permit request](https://pulsar.apache.org/docs/zh-CN/next/develop-binary-protocol#flow-control))以获取消息。 在 Consumer 端有一个队列,用于接收从 broker 推送来的消息。 你能够通过[`receiverQueueSize`](https://pulsar.apache.org/docs/zh-CN/next/client-libraries-java#configure-consumer)参数配置队列的长度 (队列的默认长度是`1000`) 每当 `consumer.receive()` 被调用一次,就从缓冲区(buffer)获取一条消息。 -**接收模式** +#### 接收模式 * consumer支持sync和async的方式从broker中接收数据 -| 发送模式 | 说明 | -| ---- | ---------------------------------------------------------------------------------------------------------------------- | -| 同步接收 | 同步模式,在收到消息之前都是被阻塞的。 | +| 发送模式 | 说明 | +| :------- | :----------------------------------------------------------- | +| 同步接收 | 同步模式,在收到消息之前都是被阻塞的。 | | 异步接收 | 异步接收模式会立即返回一个 future 值(如 Java 中的 [`CompletableFuture`](http://www.baeldung.com/java-completablefuture)),一旦收到新的消息就立刻完成。 | -**监听** +#### 监听 * pulsar java client提供一个`MessageListener`接口,可以通过实现接口一旦接收到消息,`received`方法将被调用,这里类似于RocketMQ -**确认** +#### 确认 * consumer发送一个ack请求到broker后这条消息被消费成功,被消费的消息将会被永久的存储,只有在所有订阅用户确认后才能删除。 -* 可以通过以下两种方式之一确认消息: - * 单独的ack。通过单独的确认,消费者确认每条消息并向broker发送确认请求。共享订阅模式,消息都是单条确认模式。 +* 可以通过以下两种方式之一确认消息: + + * 单独的ack。通过单独的确认,消费者确认每条消息并向broker发送确认请求。共享订阅模式,消息都是单条确认模式。 - ```java - consumer.acknowledge(msg); - ``` + ```java + consumer.acknowledge(msg); + ``` - * 累计的ack。使用累积ack,使用者只确认它接收到的最后一条消息。流中直到(包括)所提供消息的所有消息都不会被重新传递给该消费者。 + * 累计的ack。使用累积ack,使用者只确认它接收到的最后一条消息。流中直到(包括)所提供消息的所有消息都不会被重新传递给该消费者。 - ```java - consumer.acknowledgeCumulative(msg); - ``` + ```java + consumer.acknowledgeCumulative(msg); + ``` -**取消确认** +#### 取消确认 * 在独占消费模式和灾备订阅模式中,消费者仅仅只能对收到的最后一条消息进行取消确认。 @@ -195,9 +181,9 @@ nettyMaxFrameSizeBytes=5253120 consumer.negativeAcknowledge(msg); ``` -**死信topic** +#### 死信topic -* 当一些消息不能成功消费,在此机制中,无法使用的消息存储在单独的主题中,称为死信主题。您可以决定如何处理死信主题中的消息。默认死信topic名称,--DLQ +* 当一些消息不能成功消费,在此机制中,无法使用的消息存储在单独的主题中,称为死信主题。您可以决定如何处理死信主题中的消息。默认死信topic名称,--DLQ ```java Consumer consumer = pulsarClient.newConsumer(Schema.BYTES) @@ -213,7 +199,7 @@ Consumer consumer = pulsarClient.newConsumer(Schema.BYTES) * 死信消息依赖于消息的重放,消息重放是由于ack超时和取消ack导致。 -**延时重试topic** +#### 延时重试topic * 很多在线的业务系统,由于业务逻辑处理出现异常,消息一般需要被重新消费。 若需要允许延时重新消费失败的消息,你可以配置生产者同时发送消息到业务主题和重试主题,并允许消费者自动重试消费。 配置了允许消费者自动重试。如果消息没有被消费成功,它将被保存到重试主题当中。并在指定延时时间后,自动重新消费重试主题里面的消费失败消息。 @@ -234,7 +220,7 @@ Consumer consumer = pulsarClient.newConsumer(Schema.BYTES) consumer.reconsumeLater(msg,3,TimeUnit.SECONDS); ``` -#### Topic +### Topic * 一个topic由一下组成 @@ -242,47 +228,47 @@ consumer.reconsumeLater(msg,3,TimeUnit.SECONDS); {persistent|non-persistent}://tenant/namespace/topic ``` -| Topic名称组成 | 说明 | -| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Topic名称组成 | 说明 | +| :-------------------- | :----------------------------------------------------------- | | `持久化` / `非持久化` | 用来标识 topic 的类型。 Pulsar 支持两种主题类型:[持久化](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview#persistent-storage)和[非持久化](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#non-persistent-topics)。 主题默认是持久化类型,如果不特殊指定主题类型,那主题就是持久化的。 对于持久化的主题,所有的消息都会被持久化的保存到磁盘当中(如果 broker 不是单机模式,消息会被持久化到多块磁盘),而非持久化的主题的数据不会被保存到磁盘里面。 | -| `租户` | 实例中的主题租户。租户是 Pulsar 多租户的基本要素,租户是可以跨越多个集群的。 | -| `命名空间` | 将相关联的 topic 作为一个组来管理,是管理 Topic 的基本单元。 大多数对 topic 的管理都是对[命名空间](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#namespaces)的一项配置。 每个租户里面可以有一个或者多个命名空间。 | -| `topic` | 主题名称是主题命名结构的最后一个部分,主题名字在 Pulsar 实例当中没有特殊意义。 | +| `租户` | 实例中的主题租户。租户是 Pulsar 多租户的基本要素,租户是可以跨越多个集群的。 | +| `命名空间` | 将相关联的 topic 作为一个组来管理,是管理 Topic 的基本单元。 大多数对 topic 的管理都是对[命名空间](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#namespaces)的一项配置。 每个租户里面可以有一个或者多个命名空间。 | +| `topic` | 主题名称是主题命名结构的最后一个部分,主题名字在 Pulsar 实例当中没有特殊意义。 | * 如果客户端指定了不存在的topic消费消息,pulsar会自动使用该主题命在该命名空间下创建个同名主题,例如:`my-topic`的名称为`persistent://my-tenant/my-namespace/my-topic` -#### 命名空间 +### 命名空间 * 命名空间是租户内部逻辑上的命名术语。 可以通过[admin API](https://pulsar.apache.org/docs/zh-CN/next/admin-api-namespaces#create)在租户下创建多个命名空间。 例如,包含多个应用程序的租户可以为每个应用程序创建单独的命名空间。 Namespace使得程序可以以层级的方式创建和管理topic Topic`my-tenant/app1` ,它的namespace是`app1`这个应用,对应的租户是 `my-tenant`。 你可以在namespace下创建任意数量的[topic](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#topics)。 -#### 订阅 +### 订阅 -* 订阅是命名好的配置规则,指导消息如何投递给消费者。 Pulsar 中有四种订阅模式: [独占](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#exclusive),[共享](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#shared),[灾备](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#failover)和[key共享](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#key\_shared) 下图展示了这三种模式: +* 订阅是命名好的配置规则,指导消息如何投递给消费者。 Pulsar 中有四种订阅模式: [独占](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#exclusive),[共享](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#shared),[灾备](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#failover)和[key共享](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging/#key_shared) 下图展示了这三种模式: -![](img/pulsar订阅模式.png) +![](./img/pulsar订阅模式.png) -**独享模式** +#### 独享模式 * 独享模式,只有一个消费者被运行链接subscription,如果多个消费者订阅一个topic使用相同的subscription就会报错。 -![](img/exclusive模式.png) +![](./img/exclusive模式.png) -**Failover模式** +#### Failover模式 -* _Failover_模式中,多个consumer可以绑定到同一个subscription。 主消费者会消费非分区主题或者分区主题中的每个分区的消息。当主消费者丢失链接,所有(未确认的和后续的)消息都将传递给下一个消费者。 +* *Failover*模式中,多个consumer可以绑定到同一个subscription。 主消费者会消费非分区主题或者分区主题中的每个分区的消息。当主消费者丢失链接,所有(未确认的和后续的)消息都将传递给下一个消费者。 * 对于分区topic来说,broker将按照消费者的优先级和消费者名称的词汇表顺序对消费者进行排序,然后试图将topic均匀的分配给优先级最高的消费者。 -**Shard(共享)** +#### Shard(共享) -* _shared_或者_round robin_模式中,多个消费者可以绑定到同一个订阅上。 消息通过round robin轮询机制分发给不同的消费者,并且每个消息仅会被分发给一个消费者。 当消费者断开连接,所有被发送给他,但没有被确认的消息将被重新安排,分发给其它存活的消费者。 +* *shared*或者*round robin*模式中,多个消费者可以绑定到同一个订阅上。 消息通过round robin轮询机制分发给不同的消费者,并且每个消息仅会被分发给一个消费者。 当消费者断开连接,所有被发送给他,但没有被确认的消息将被重新安排,分发给其它存活的消费者。 -![](img/shard订阅模式.png) +![](./img/shard订阅模式.png) -**Key\_Shard** +#### Key_Shard * 消息以分布的形式跨消费者交付,具有相同键或相同顺序键的消息只交付给一个消费者。无论消息被重新传递多少次,它都被传递给相同的消费者。当使用者连接或断开连接时,将导致所服务的使用者更改消息的某些键。 -![](img/keyshard订阅模式.png) +![](./img/keyshard订阅模式.png) ```java Producer producer = client.newProducer() @@ -291,7 +277,7 @@ Producer producer = client.newProducer() .create(); ``` -#### 多主题订阅 +### 多主题订阅 * 通过正则方式订阅,例如`persistent://public/default/finance-.*` * 明确指定的topic列表 @@ -312,38 +298,38 @@ Consumer allTopicsConsumer = pulsarClient.newConsumer() .subscribe(); ``` -#### 分区topic +### 分区topic * 普调topic限制在一个broker上,限制了topic的最大吞吐量,分区topic将数据按照分区存储在多个broker,吞吐量也会提升。分区topic实际是通过在底层拥有N个内部topic来实现的,这个N的数量就是等于分区的梳理。当向分区的topic发送消息,没条消息都被路由到其中一个broker。Pulsar自动处理跨broker的分区分布。 -![](img/分区topic.png) +![](./img/分区topic.png) * topic1有5个分区,因为分区多于broker数量,其中有两个broker要处理两个分区。第三个broker则只处理一个。(再次强调,分区的分布是Pulsar自动处理的)。 -**路由模式** +#### 路由模式 * producer发送消息到分区topic需要指定路由模式,决定每条消息被发布到的分区 -| 发送模式 | 说明 | -| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `RoundRobinPartition` | 如果消息没有指定 key,为了达到最大吞吐量,生产者会以 round-robin 方式将消息发布到所有分区。 请注意round-robin并不是作用于每条单独的消息,而是作用于延迟处理的批次边界,以确保批处理有效。 如果消息指定了key,分区生产者会根据key的hash值将该消息分配到对应的分区。 这是默认的模式。 | -| `SinglePartition` | 如果消息没有指定 key,生产者将会随机选择一个分区,并发布所有消息到这个分区。 如果消息指定了key,分区生产者会根据key的hash值将该消息分配到对应的分区。 | +| 发送模式 | 说明 | +| :-------------------- | :----------------------------------------------------------- | +| `RoundRobinPartition` | 如果消息没有指定 key,为了达到最大吞吐量,生产者会以 round-robin 方式将消息发布到所有分区。 请注意round-robin并不是作用于每条单独的消息,而是作用于延迟处理的批次边界,以确保批处理有效。 如果消息指定了key,分区生产者会根据key的hash值将该消息分配到对应的分区。 这是默认的模式。 | +| `SinglePartition` | 如果消息没有指定 key,生产者将会随机选择一个分区,并发布所有消息到这个分区。 如果消息指定了key,分区生产者会根据key的hash值将该消息分配到对应的分区。 | | `CustomPartition` | 使用自定义消息路由器实现来决定特定消息的分区。 用户可以创建自定义路由模式:使用 [Java client](https://pulsar.apache.org/docs/zh-CN/next/client-libraries-java) 并实现[MessageRouter](https://pulsar.incubator.apache.org/api/client/2.8.0-SNAPSHOT/org/apache/pulsar/client/api/MessageRouter) 接口。 | -**顺序保证** +#### 顺序保证 * 消息的排序与MessageRoutingMode和Message Key有关。通常,用户希望按键分区的顺序进行保证。当使用`SinglePartition`和`RoundRobinPartition`模式时,如果消息有key,消息将被路由到匹配的分区。 -| 顺序保证 | 说明 | 路由策略与消息Key | -| ----- | ------------------------------------------ | ------------------------------------------------------------ | -| 按键分区 | 所有具有相同 key 的消息将按顺序排列并放置在相同的分区(Partition)中。 | 使用 `SinglePartition` 或 `RoundRobinPartition` 模式,每条消息都需要有key。 | -| 生产者排序 | 来自同一生产者的所有消息都是有序的 | 路由策略为`SinglePartition`, 且每条消息都没有key。 | +| 顺序保证 | 说明 | 路由策略与消息Key | +| :--------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| 按键分区 | 所有具有相同 key 的消息将按顺序排列并放置在相同的分区(Partition)中。 | 使用 `SinglePartition` 或 `RoundRobinPartition` 模式,每条消息都需要有key。 | +| 生产者排序 | 来自同一生产者的所有消息都是有序的 | 路由策略为`SinglePartition`, 且每条消息都没有key。 | -**HashingScheme** +#### HashingScheme * [HashingScheme](https://pulsar.incubator.apache.org/api/client/2.8.0-SNAPSHOT/org/apache/pulsar/client/api/HashingScheme) 是代表一组标准散列函数的枚举。为一个指定消息选择分区时使用。有两种可用的散列函数: `JavaStringHash` 和`Murmur3_32Hash`. The default hashing function for producer is `JavaStringHash`. 请注意,当producer可能来自于不同语言客户端时,`JavaStringHash`是不起作用的。建议使用`Murmur3_32Hash`。有两种可用的散列函数: `JavaStringHash` 和`Murmur3_32Hash`. The default hashing function for producer is `JavaStringHash`. 请注意,当producer可能来自于不同语言客户端时,`JavaStringHash`是不起作用的。建议使用`Murmur3_32Hash`。 -#### 非持久topic +### 非持久topic * 默认的,pulsar保存所有没有去人的消息到多个Bookeeper的bookies中(存储节点)。持久化topic的消息数据可以在broker重启或订阅者出问题的情况下存活下来。隐藏,持久化topic上的消息数据可以在broker重启和consumer故障转移的时候继续存在。 * 非持久topic不持久存储到磁盘,只存在内存中。pulsar也提供了非持久topic。非持久topic的消息不会被保存在硬盘上,只存活于内存中。当使用非持久化topic分发时,杀pulsar的broker或关闭consumer,此topic上所有的瞬时消息都会丢失。 @@ -352,10 +338,10 @@ Consumer allTopicsConsumer = pulsarClient.newConsumer() non-persistent://tenant/namespace/topic ``` -* 非持久topic中,broker会立即发布消息给所有连接的订阅者,而不会在[BookKeeper](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview#persistent-storage)中_存储_。 如果有一个订阅者断开连接,broker将无法重发这些瞬时消息,订阅者将永远也不能收到这些消息了。 去掉持久化存储的步骤,在某些情况下,使得非持久topic的消息比持久topic稍微变快。 +* 非持久topic中,broker会立即发布消息给所有连接的订阅者,而不会在[BookKeeper](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview#persistent-storage)中*存储*。 如果有一个订阅者断开连接,broker将无法重发这些瞬时消息,订阅者将永远也不能收到这些消息了。 去掉持久化存储的步骤,在某些情况下,使得非持久topic的消息比持久topic稍微变快。 * 非持久topic的ack会在消息发送后立刻返回给producer -**客户端API** +#### 客户端API ```java PulsarClient client = PulsarClient.builder() @@ -374,7 +360,7 @@ Producer producer = client.newProducer() .create(); ``` -#### 消息保留和过期 +### 消息保留和过期 * broker默认配置 * 立即删除所有已经被consumer确认过的消息 @@ -383,29 +369,29 @@ Producer producer = client.newProducer() * 消息**存留**让你可以保存consumer确认过的消息 * 消息**过期**让你可以给未被确认的消息设置存活时长(TTL) -![](img/消息保存和过期.png) +![](./img/消息保存和过期.png) * 没有被留存规则覆盖的消息将会被删除,没有留存规则的话所有`被确认`的消息都会被删除。 * 有些消息即使还没有被确认,也被删除掉了。因为根据设置在namespace上的TTL,他们已经过期了。(例如,TTL为5分钟,过了十分钟消息还没被确认) -#### 消息去重 +### 消息去重 -**生产者幂等** +#### 生产者幂等 -* 消息去重的另外一种方法是确保每条消息_仅生产一次_。 这种方法通常被叫做**生产者幂等**。 这种方式的缺点是,把消息去重的工作推给了应用去做。 在 Pulsar 中,消息去重是在 [broker](https://pulsar.apache.org/docs/zh-CN/next/reference-terminology#broker)上处理的,用户不需要去修改客户端的代码。 相反,你只需要通过修改配置就可以实现。 +* 消息去重的另外一种方法是确保每条消息*仅生产一次*。 这种方法通常被叫做**生产者幂等**。 这种方式的缺点是,把消息去重的工作推给了应用去做。 在 Pulsar 中,消息去重是在 [broker](https://pulsar.apache.org/docs/zh-CN/next/reference-terminology#broker)上处理的,用户不需要去修改客户端的代码。 相反,你只需要通过修改配置就可以实现。 -**去重和exctly-onece** +#### 去重和exctly-onece * 消息去重,使 Pulsar 成为了流处理引擎(SPE)或者其他寻求 "仅仅一次" 语义的连接系统所需的理想消息系统。 如果消息系统没有提供自动去重能力,那么 SPE (流处理引擎) 或者其他连接系统就必须自己实现去重语义,这意味着需要应用去承担这部分的去重工作。 使用Pulsar,严格的顺序保证不会带来任何应用层面的代价。 -#### 消息延迟传递 +### 消息延迟传递 * 延时消息功能允许你能够过一段时间才能消费到这条消息,而不是消息发布后,就马上可以消费到。 * Broker 保存消息是不经过任何检查的。 当消费者消费一条消息时,如果这条消息是延时消息,那么这条消息会被加入到`DelayedDeliveryTracker`当中。 订阅检查机制会从`DelayedDeliveryTracker`获取到超时的消息,并交付给消费者。 -![](img/message\_delay.png) +![](./img/message_delay.png) -**broker** +#### broker * 延迟的默认时间和是否开启可以通过修改broker配置 @@ -420,16 +406,16 @@ delayedDeliveryEnabled=true delayedDeliveryTickTimeMillis=1000 ``` -**producer** +#### producer ```java // message to be delivered at the configured delay interval producer.newMessage().deliverAfter(3L, TimeUnit.Minute).value("Hello Pulsar!").send(); ``` -## 架构 +# 架构 -### 架构概述 +## 架构概述 * 单个pulsar实例由一个或多个pulsra集群组成,实例中的集群之前可以互相复制数据。 * 单个pulsar集群由以下三个部分组成: @@ -437,37 +423,37 @@ producer.newMessage().deliverAfter(3L, TimeUnit.Minute).value("Hello Pulsar!").s * 包含一个或多个 bookie 的 BookKeeper 集群负责消息的[持久化存储](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview/#persistent-storage)。 * 一个Zookeeper集群,用来处理多个Pulsar集群之间的协调任务。 -![](img/pulsar架构.png) +![](./img/pulsar架构.png) -### Brokers +## Brokers * pulsar的broker是一个无状态组件,主要负责运行另外俩个组件 * 一个HTTP服务器,它为生产者和消费者提供管理任务和主题查找的REST API。生产者连接到代理以发布消息,而消费者连接到代理以消费消息。 * 一个调度分发器,它是异步的TCP服务器,通过自定义的二进制协议应用所有相关的数据传输。 -* 出于性能的考虑, 通常从 [managed ledger (ledger是Pulsar底层存储BookKeeper中的概念,相当于一种记录的集合)](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview/#managed-ledgers) 缓存中调度消息, _除非_ 积压的消息超过这个缓存的大小。 如果积压的消息对于缓存来说太大了, 则Broker将开始从BookKeeper那里读取Entries(Entry同样是BookKeeper中的概念,相当于一条记录)。 +* 出于性能的考虑, 通常从 [managed ledger (ledger是Pulsar底层存储BookKeeper中的概念,相当于一种记录的集合)](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview/#managed-ledgers) 缓存中调度消息, *除非* 积压的消息超过这个缓存的大小。 如果积压的消息对于缓存来说太大了, 则Broker将开始从BookKeeper那里读取Entries(Entry同样是BookKeeper中的概念,相当于一条记录)。 -### 集群 +## 集群 * 一个pulsar实例包含一个或多个pulsar集群。集群包括 * 一个或者多个Pulsar [brokers](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview/#brokers) * 一个ZooKeeper协调器,用于集群级别的配置和协调 * 一组BookKeeper的Bookies用于消息的 [持久化存储](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview/#persistent-storage) -### 元数据存储 +## 元数据存储 * Pulsar 元数据存储维护一个 Pulsar 集群的所有元数据,例如主题元数据、模式、代理负载数据等。 Pulsar 使用 Apache ZooKeeper 进行元数据存储、集群配置和协调。 Pulsar 元数据存储可以部署在单独的 ZooKeeper 集群上,也可以部署在现有的 ZooKeeper 集群上。 您可以将一个 ZooKeeper 集群同时用于 Pulsar 元数据存储和 BookKeeper 元数据存储。 如果要部署连接到现有 BookKeeper 集群的 Pulsar broker,则需要分别为 Pulsar 元数据存储和 BookKeeper 元数据存储部署单独的 ZooKeeper 集群。 * **配置与仲裁存储**: 存储租户,命名域和其他需要全局一致的配置项 * 每个集群有自己独立的ZooKeeper保存集群内部配置和协调信息,例如归属信息,broker负载报告,BookKeeper ledger信息(这个是BookKeeper本身所依赖的)等等。 -### 配置存储 +## 配置存储 * 配置存储维护一个 Pulsar 实例的所有配置,例如集群、租户、命名空间、分区主题相关配置等。 一个 Pulsar 实例可以有一个本地集群、多个本地集群或多个跨区域集群。 因此,配置存储可以在 Pulsar 实例下的多个集群之间共享配置。 配置存储可以部署在单独的 ZooKeeper 集群上,也可以部署在现有的 ZooKeeper 集群上。 -### 持久化存储 +## 持久化存储 * 未确认送大的消息需要持久化存储直到它们被确认送达,这种消息推送方式叫做持久化消息推送。在pulsar内部,所有消息都被保存并同步N份,例如2个服务器保存4份,每个服务器上都有镜像的RAID存储。 -#### Apache bookeeper +### Apache bookeeper * pulsar用bookeeper作为持久化存储,bookeeper是一个分布式的wal系统,有以下特性适合pulsar * 能让Pulsar创建多个独立的日志,这种独立的日志就是[ledgers](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview/#ledgers). 随着时间的推移,Pulsar会为Topic创建多个ledgers @@ -476,42 +462,42 @@ producer.newMessage().deliverAfter(3L, TimeUnit.Minute).value("Hello Pulsar!").s * 提供不同的Bookies之间均匀的IO分布的特性。 * 容量和吞吐量都能水平扩展。并且容量可以通过在集群内添加更多的Bookies立刻提升。 * Bookies被设计成可以承载数千的并发读写的ledgers。 使用多个磁盘设备,一个用于日志,另一个用于一般存储,这样Bookies可以将读操作的影响和对于写操作的延迟分隔开。 -* 除了消息数据,_cursors_也会被持久化入BookKeeper。 Cursors是消费端[订阅](https://pulsar.apache.org/docs/zh-CN/next/reference-terminology#subscription)消费的位置。 BookKeeper让Pulsar可以用一种可扩展的方式存储消费位置。 +* 除了消息数据,*cursors*也会被持久化入BookKeeper。 Cursors是消费端[订阅](https://pulsar.apache.org/docs/zh-CN/next/reference-terminology#subscription)消费的位置。 BookKeeper让Pulsar可以用一种可扩展的方式存储消费位置。 ``` # 消息存储 persistent://tenant/namespace/topic ``` -![](img/broker-bookie.png) +![](./img/broker-bookie.png) -#### Ledgers +### Ledgers * Ledger是一个只追加的数据结构,并且只有一个写入器,这个写入器负责多个Bookeeper存储节点(Bookies)的写入。Ledger的条目会被复制到多个bookies。 * Pulsar Broker可以创建ledeger,添加内容到ledger和关闭ledger。 * 当一个ledger被关闭后,除非明确的要写数据或者是因为写入器挂掉导致ledger关闭,这个ledger只会以只读模式打开。 * 最后,当ledger中的条目不再有用的时候,整个legder可以被删除(ledger分布是跨Bookies的) -**Ledgers的读一致性** +#### Ledgers的读一致性 * BookKeeper的主要优势在于他能在有系统故障时保证读的一致性。 由于Ledger只能被一个进程写入(之前提的写入器进程),这样这个进程在写入时不会有冲突,从而写入会非常高效。 在一次故障之后,ledger会启动一个恢复进程来确定ledger的最终状态并确认最后提交到日志的是哪一个条目。 在这之后,能保证所有的ledger读进程读取到相同的内容。 -**Managed ledgers** +#### Managed ledgers -* 由于BookKeeper Ledgers提供了单一的日志抽象,在ledger的基础上我们开发了一个叫_managed ledger_的库,用以表示单个topic的存储层。 managed ledger即消息流的抽象,有一个写入器进程不断在流结尾添加消息,并且有多个cursors 消费这个流,每个cursor有自己的消费位置。 +* 由于BookKeeper Ledgers提供了单一的日志抽象,在ledger的基础上我们开发了一个叫*managed ledger*的库,用以表示单个topic的存储层。 managed ledger即消息流的抽象,有一个写入器进程不断在流结尾添加消息,并且有多个cursors 消费这个流,每个cursor有自己的消费位置。 - 一个managed ledger在内部用多个BookKeeper ledgers保存数据,这么做有两个原因: + 一个managed ledger在内部用多个BookKeeper ledgers保存数据,这么做有两个原因: - 1. 在故障之后,原有的某个ledger不能再写了,需要创建一个新的。 - 2. ledger在所有cursors消费完它所保存的消息之后就可以被删除,这样可以实现ledgers的定期翻滚从头写。 + 1. 在故障之后,原有的某个ledger不能再写了,需要创建一个新的。 + 2. ledger在所有cursors消费完它所保存的消息之后就可以被删除,这样可以实现ledgers的定期翻滚从头写。 -#### 日志存储 +### 日志存储 -* BookKeeper的_日志_文件包含事务日志。 在更新到 [ledger](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview/#ledgers)之前,bookie需要确保描述这个更新的事务被写到持久(非易失)存储上面。 在bookie启动和旧的日志文件大小达到上限(由 [`journalMaxSizeMB`](https://pulsar.apache.org/docs/zh-CN/next/reference-configuration#bookkeeper-journalMaxSizeMB) 参数配置)的时候,新的日志文件会被创建。 +* BookKeeper的*日志*文件包含事务日志。 在更新到 [ledger](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview/#ledgers)之前,bookie需要确保描述这个更新的事务被写到持久(非易失)存储上面。 在bookie启动和旧的日志文件大小达到上限(由 [`journalMaxSizeMB`](https://pulsar.apache.org/docs/zh-CN/next/reference-configuration#bookkeeper-journalMaxSizeMB) 参数配置)的时候,新的日志文件会被创建。 -### Pulsar proxy +## Pulsar proxy -* 架构上来看,Pulsar Proxy从ZooKeeper上面读取他所需要的所有信息。 当启动代理时,你只需要提供用于集群独有和实例范围的配置存储的ZooKeeper连接串。 +* 架构上来看,Pulsar Proxy从ZooKeeper上面读取他所需要的所有信息。 当启动代理时,你只需要提供用于集群独有和实例范围的配置存储的ZooKeeper连接串。 ```shell bin/pulsar proxy \ @@ -519,6 +505,6 @@ bin/pulsar proxy \ --configuration-store-servers zk-0,zk-1,zk-2 ``` -### 服务发现 +## 服务发现 * 客户端需要能够使用单个URL与整个Pulsar实例进行同学。Pulsar内部提供了服务发现机制,可以通过[配置Pulsar实例指南](https://pulsar.apache.org/docs/zh-CN/next/deploy-bare-metal#service-discovery-setup)设置。 diff --git "a/bigdata/mq/pulsar/2.\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" "b/bigdata/mq/pulsar/2.\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" index 1f6e2110..1c5b2f3d 100644 --- "a/bigdata/mq/pulsar/2.\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" +++ "b/bigdata/mq/pulsar/2.\345\216\237\347\220\206\344\270\216\345\256\236\350\267\265.md" @@ -1,24 +1,20 @@ -# 2.原理与实践 - -## Pulsar客户端 - -### 客户端设置步骤 - +# Pulsar客户端 +## 客户端设置步骤 1. 客户端将尝试通过向服务器(Broker)发送 HTTP 查找请求,来确定主题(Topic)所在的服务器(Broker)。 客户端通过查询 ZooKeeper 中 (缓存) 的元数据,来确定这条消息的 topic 在哪个 broker 上,如果该 topic 不在任何一个 broker 上,则把这个 topic 分配在负载最少的 broker 上。 2. 当客户端获取了broker的地址之后,将会创建一个TCP连接 (或复用连接池中的连接) 并且进行鉴权。 客户端和broker通过该连接交换基于自定义协议的二进制命令。 同时,客户端会向broker发送一条命令用以在broker上创建生产者/消费者,该命令将会在验证授权策略后生效。 -### Reader接口 +## Reader接口 -* 默认创建一个新订阅消费会定位在topic的末尾处,如果消费者使用已经存在的订阅来链接topic时,将从订阅内最早的未确认消息开始读取。消费者接口是基于消息确认机制来自动管理订阅游标位置。Pulsar 的 **reader 接口**允许应用程序手动管理游标。 当您使用 reader 连接到 topic 而不是连接到消费者时,需要指定 reader 在连接到该 topic 时开始读取_哪条消息_。 当连接到一个 topic 时,reader 接口支持的开始位置包括: - * Topic 中**最早**的可用消息 - * Topic 中**最新**的可用消息 - * 在最早和最新之间的其他消息。 如果你选择此选项,则需要明确提供消息 ID。 你的应用程序将需要提前“知道”这个消息 ID,可能要从持久化存储或缓存中获取。 -* Reader 接口内部是作为一个使用独占、非持久化订阅的被随机命名的一个消费者来实现的。与订阅或消费者不同, readers 在性质上是非持久性的,不会阻止某一主题中的数据被删除。所以\***强烈\***建议配置[数据保留时间](https://pulsar.apache.org/docs/zh-CN/next/cookbooks-retention-expiry)这个选项。 如果主题没有配置足够长的消息保留时间,就会出现消息还没有被 Reader 读取就被删除的情况。 这将导致 reader 读取不到这条消息。 为主题配置数据保留时间,就可以保证 reader 可以在一定时间内可以获取到该消息。 +* 默认创建一个新订阅消费会定位在topic的末尾处,如果消费者使用已经存在的订阅来链接topic时,将从订阅内最早的未确认消息开始读取。消费者接口是基于消息确认机制来自动管理订阅游标位置。Pulsar 的 **reader 接口**允许应用程序手动管理游标。 当您使用 reader 连接到 topic 而不是连接到消费者时,需要指定 reader 在连接到该 topic 时开始读取*哪条消息*。 当连接到一个 topic 时,reader 接口支持的开始位置包括: + - Topic 中**最早**的可用消息 + - Topic 中**最新**的可用消息 + - 在最早和最新之间的其他消息。 如果你选择此选项,则需要明确提供消息 ID。 你的应用程序将需要提前“知道”这个消息 ID,可能要从持久化存储或缓存中获取。 +* Reader 接口内部是作为一个使用独占、非持久化订阅的被随机命名的一个消费者来实现的。与订阅或消费者不同, readers 在性质上是非持久性的,不会阻止某一主题中的数据被删除。所以***强烈\***建议配置[数据保留时间](https://pulsar.apache.org/docs/zh-CN/next/cookbooks-retention-expiry)这个选项。 如果主题没有配置足够长的消息保留时间,就会出现消息还没有被 Reader 读取就被删除的情况。 这将导致 reader 读取不到这条消息。 为主题配置数据保留时间,就可以保证 reader 可以在一定时间内可以获取到该消息。 * 请注意 reader 可能会有一个 "backlog",但是该指标只是为了让用户了解到 reader 背后的运行情况,但是在进行任何积压配额计算是都不会考虑该因素。 -![](img/消费接口.png) +![](./img/消费接口.png) -#### 从最早开始消费 +### 从最早开始消费 ```java import org.apache.pulsar.client.api.Message; @@ -38,7 +34,7 @@ while (true) { } ``` -#### 从可用消息处开始消费 +### 从可用消息处开始消费 ```java Reader reader = pulsarClient.newReader() @@ -47,7 +43,7 @@ Reader reader = pulsarClient.newReader() .create(); ``` -#### 从最早和最新消息直接读取 +### 从最早和最新消息直接读取 ```java byte[] msgIdBytes = // Some byte array @@ -58,94 +54,100 @@ Reader reader = pulsarClient.newReader() .create(); ``` -## 消息压缩 +# 消息压缩 -* 消息数据高度可扩展的[持久存储](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview#persistent-storage),是Pulsar构建的主要目标。 Pulsar的topic让你可以持久存储所有你所需要的未被确认消息,同时保留了消息的顺序。 主题上生产的_所有_未被确认/未被处理的消息,Pulsar会默认存储。 在很多Pulsar的使用案例中,在topic累积大量的未被确认的消息是有必要的。但对于Pulsar的consumer来说,在完整的消息log中回退,将变得非常耗时。 -* 某些情况下,consumer并不需要完整的topic日志。 他们可能只需要几个值来构造一个更 "浅" 的日志图像, 也许仅仅只是最新的值。 对于这种应用场景,Pulsar提供了 **topic压缩**. 当你在topic上执行压缩,Pulsar会遍历topic的backlog然后把遥远_模糊_已经有了更新的消息移除。例如,它遍历一个以key为基础的topic,只留下关联到key上最新的消息。 +* 消息数据高度可扩展的[持久存储](https://pulsar.apache.org/docs/zh-CN/next/concepts-architecture-overview#persistent-storage),是Pulsar构建的主要目标。 Pulsar的topic让你可以持久存储所有你所需要的未被确认消息,同时保留了消息的顺序。 主题上生产的*所有*未被确认/未被处理的消息,Pulsar会默认存储。 在很多Pulsar的使用案例中,在topic累积大量的未被确认的消息是有必要的。但对于Pulsar的consumer来说,在完整的消息log中回退,将变得非常耗时。 +* 某些情况下,consumer并不需要完整的topic日志。 他们可能只需要几个值来构造一个更 "浅" 的日志图像, 也许仅仅只是最新的值。 对于这种应用场景,Pulsar提供了 **topic压缩**. 当你在topic上执行压缩,Pulsar会遍历topic的backlog然后把遥远*模糊*已经有了更新的消息移除。例如,它遍历一个以key为基础的topic,只留下关联到key上最新的消息。 * pulsar的topic压缩特性 * 运行通过topic日志更快地后退 * 仅使用持久化topic * 当backlog达到一定大小时,可以被自动触发,或者通过命令行手动触发。 - * 在概念上和操作上与[ retention和expiry ](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging#message-retention-and-expiry)是不同的。 但是,在topic压缩中,还是_会_尊重retention。 如果retention已经从topic的backlog中移除了消息,那么此条消息在压缩的topic账簿上也是无法被读取的。 + * 在概念上和操作上与[ retention和expiry ](https://pulsar.apache.org/docs/zh-CN/next/concepts-messaging#message-retention-and-expiry)是不同的。 但是,在topic压缩中,还是*会*尊重retention。 如果retention已经从topic的backlog中移除了消息,那么此条消息在压缩的topic账簿上也是无法被读取的。 -### Topic压缩的工作原理 +## Topic压缩的工作原理 * 通过命令行触发topic压缩,pulsar将会从头到尾迭代整个topic,对于它碰到的每个key,压缩程序将会只保留这个key最近的事件。之后,broker将会创建一个新的BookKeeper ledger然后开始对topic消息进行二次迭代。对于每条消息,如果key匹配到它的最新事件,key的数据内容,消息ID,元数据将会被写入最新创建的ledger。 如果key并没有匹配到最新的消息,消息将被跳过。 如果给定的消息,value是空的,它将被跳过并且视为删除。 在本topic第二次迭代结束时,新创建的BookKeeper ledger将被关闭,并将两个内容写入元数据 :BookKeeper ledger的ID及最新被压缩的消息的ID(这被称为topic的**压缩层位**)。 写入元数据后,压缩就完成了。 * 启用读取压缩功能的客户端(consumer和reader),将会尝试从topic中读取消息,或者: - * 像从正常的主题那样读取(如果消息的ID大于等于压缩层位),或 - * 从压缩层位的开始读取(如果消息ID小于压缩层位) + - 像从正常的主题那样读取(如果消息的ID大于等于压缩层位),或 + - 从压缩层位的开始读取(如果消息ID小于压缩层位) -## Pulsar Schema +# Pulsar Schema -### Schema Registry +## Schema Registry * Pulsar使用内置Schema Registry,发消息需要指定schema通过client.newProducer(JSONSchema.of(User.class))指定。 * 使用schema创建producer时,不需要将消息转换成字节数组,pulsar scehma会在后台执行操作。 -### Schema演化 +## Schema演化 * 为了满足新的业务要求,需要不断更新 schemas,这种更新就叫做 **schema 演化**。Schema 的任何变化都会影响到下游 consumers。 Schema 演化确保了下游 consumers 能够无缝地处理以旧 schemas 和以新 schemas 编码的数据。 -#### Pulsar对schema演化提供的支持 +### Pulsar对schema演化提供的支持 -1. 当 producer/consumer/ Reader连接到 broker 时,broker 会部署由 `schemaRegistryCompatibilityCheckers` 配置的 schema 兼容性检查器来进行 schema 兼容性检查。 +1. 当 producer/consumer/ Reader连接到 broker 时,broker 会部署由 `schemaRegistryCompatibilityCheckers` 配置的 schema 兼容性检查器来进行 schema 兼容性检查。 - Schema 兼容性检查器是每个 schema 类型都有的实例。 + Schema 兼容性检查器是每个 schema 类型都有的实例。 + + 目前,Avro 和 JSON 有自己的兼容性检查器,所有其他 schema 类型共享默认兼容性检查器,而此默认检查器禁用 schema 演化。 - 目前,Avro 和 JSON 有自己的兼容性检查器,所有其他 schema 类型共享默认兼容性检查器,而此默认检查器禁用 schema 演化。 2. Producer/consumer/ reader将其客户端 `SchemaInfo` 发送到 broker。 + 3. Broker 知道 schema 类型,并找到此类型的 schema 兼容性检查器。 -4. Broker 使用检查器,以兼容性检查策略来检查 `SchemaInfo` 是否与 topic 的最新 schema 兼容。 - 目前,兼容性检查策略是在命名空间级别配置的,适用于命名空间中的所有 topics。 +4. Broker 使用检查器,以兼容性检查策略来检查 `SchemaInfo` 是否与 topic 的最新 schema 兼容。 -## 事务 + 目前,兼容性检查策略是在命名空间级别配置的,适用于命名空间中的所有 topics。 -### 幂等Producer的局限性 +# 事务 + +## 幂等Producer的局限性 * 使用Pulsar幂等生产者可以避免数据丢失或重复,但它不为跨多个分区的写入提供保证。 + * 在Pulsar中,最高级别的消息传递保证是使用 [幂等生产者](https://pulsar.apache.org/docs/en/next/concepts-messaging/#producer-idempotency) 在单个分区上使用恰好一次语义,即每条消息只持久化一次,不会丢失和重复。 然而,这种解决办法有一些限制: - * 由于`单调递增的序列ID`,该方案仅适用于单个分区和单个生产者会话内(即生产一条消息),`因此向一个或多个分区生产多条消息时没有原子性`。 - 在这种情况下,如果在产生和接收消息的过程中出现一些故障(例如,client/broker/bookie崩溃、网络故障等),消息会被重新处理和重新投递,这可能会导致数据丢失或数据重复: + - 由于`单调递增的序列ID`,该方案仅适用于单个分区和单个生产者会话内(即生产一条消息),`因此向一个或多个分区生产多条消息时没有原子性`。 + + 在这种情况下,如果在产生和接收消息的过程中出现一些故障(例如,client/broker/bookie崩溃、网络故障等),消息会被重新处理和重新投递,这可能会导致数据丢失或数据重复: + + - 对于生产者:如果生产者重试发送消息,有些消息会被多次持久化;如果生产者不重试发送消息,一些消息会被持久化一次,而其他消息会丢失。 + - 对于消费者:由于消费者不知道代理是否收到消息,因此消费者可能不会重试发送 ack,从而导致其收到重复的消息。 - * 对于生产者:如果生产者重试发送消息,有些消息会被多次持久化;如果生产者不重试发送消息,一些消息会被持久化一次,而其他消息会丢失。 - * 对于消费者:由于消费者不知道代理是否收到消息,因此消费者可能不会重试发送 ack,从而导致其收到重复的消息。 * 消费者需要依赖更多的机制来确认(ack)消息一次。 -### 运行原理 +## 运行原理 -#### 事务协调器 +### 事务协调器 * 事务协调器(TC)是运行在 Pulsar Broker 中的一个模块。 - * 它维护事务的整个生命周期,并防止事务进入错误状态。 - * 它处理事务超时,并确保事务在事务超时后中止。 + - 它维护事务的整个生命周期,并防止事务进入错误状态。 + - 它处理事务超时,并确保事务在事务超时后中止。 -#### 事务日志 +### 事务日志 -* 所有事务元数据都保存在事务日志中。 事务日志由 `Pulsar 主题`记录。 如果事务协调器崩溃,它可以从事务日志恢复事务元数据,事务日志存储事务状态而不是事务实际消息。 +* 所有事务元数据都保存在事务日志中。 事务日志由` Pulsar 主题`记录。 如果事务协调器崩溃,它可以从事务日志恢复事务元数据,事务日志存储事务状态而不是事务实际消息。 -#### 事务缓存 +### 事务缓存 * 向事务内的主题分区生成的消息存储在该主题分区的事务缓冲区(TB)中。 在提交事务之前,事务缓冲区中的消息对消费者不可见。 当事务中止时,事务缓冲区中的消息将被丢弃。 * 事务缓冲区将所有正在进行和中止的事务存储在内存中。 所有消息都发送到实际的分区 Pulsar 主题。 提交事务后,事务缓冲区中的消息对消费者具体化(可见)。 事务中止时,事务缓冲区中的消息将被丢弃。 -#### 事务ID +### 事务ID * 事务ID(TxnID)标识Pulsar中的唯一事务。 事务 ID 长度是 128-bit。 最高 16 位保留给事务协调器的 ID,其余位用于每个事务协调器中单调递增的数字。 使用 TxnID 很容易定位事务崩溃。 -#### 待确认状态 +### 待确认状态 * 挂起确认状态在事务完成之前维护事务中的消息确认。 如果消息处于挂起确认状态,则在该消息从挂起确认状态中移除之前,其他事务无法确认该消息。 * 挂起的确认状态被保留到挂起的确认日志中(cursor ledger)。 新启动的broker可以从挂起的确认日志中恢复状态,以确保状态确认不会丢失。 -#### 数据流 +### 数据流 * [事务数据流](https://pulsar.apache.org/docs/zh-CN/next/txn-how/) -### 使用方式 +## 使用方式 -#### 快速开始 +### 快速开始 * 默认情况下事务是禁止的,并且2.8.0或更高版本才支持 * 开启事务,修改broker.conf @@ -171,7 +173,7 @@ PulsarClient client = PulsarClient.builder() .build(); ``` -#### 消费者确认事务 +### 消费者确认事务 ```java Consumer sinkConsumer = pulsarClient @@ -183,3 +185,4 @@ Consumer sinkConsumer = pulsarClient .enableBatchIndexAcknowledgment(true) // enable batch index acknowledgement .subscribe(); ``` + diff --git a/bigdata/mq/pulsar/README.md b/bigdata/mq/pulsar/README.md deleted file mode 100644 index ef9607d6..00000000 --- a/bigdata/mq/pulsar/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# pulsar - diff --git a/bigdata/olap/README.md b/bigdata/olap/README.md deleted file mode 100644 index e413303e..00000000 --- a/bigdata/olap/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# olap - diff --git a/bigdata/olap/clickhouse/README.md b/bigdata/olap/clickhouse/README.md deleted file mode 100644 index 86736d92..00000000 --- a/bigdata/olap/clickhouse/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# clickhouse - diff --git a/bigdata/olap/druid/README.md b/bigdata/olap/druid/README.md deleted file mode 100644 index 7e7d8454..00000000 --- a/bigdata/olap/druid/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# druid - diff --git a/bigdata/olap/hive/README.md b/bigdata/olap/hive/README.md deleted file mode 100644 index 15d0b587..00000000 --- a/bigdata/olap/hive/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# hive - diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/README.md b/bigdata/olap/hive/hive-bian-cheng-zhi-nan/README.md deleted file mode 100644 index c483b904..00000000 --- a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# hive编程指南 - diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/10.-cun-chu-he-an-quan-yi-ji-suo.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/10.\345\255\230\345\202\250\345\222\214\345\256\211\345\205\250\344\273\245\345\217\212\351\224\201.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/10.-cun-chu-he-an-quan-yi-ji-suo.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/10.\345\255\230\345\202\250\345\222\214\345\256\211\345\205\250\344\273\245\345\217\212\351\224\201.md" diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/11.hcatalog.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/11.HCatalog.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/11.hcatalog.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/11.HCatalog.md" diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/2.-shu-ju-lei-xing-he-wen-jian-ge-shi.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/2.\346\225\260\346\215\256\347\261\273\345\236\213\345\222\214\346\226\207\344\273\266\346\240\274\345\274\217.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/2.-shu-ju-lei-xing-he-wen-jian-ge-shi.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/2.\346\225\260\346\215\256\347\261\273\345\236\213\345\222\214\346\226\207\344\273\266\346\240\274\345\274\217.md" diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/3.hiveql-xiang-guan.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/3.HiveQL\347\233\270\345\205\263.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/3.hiveql-xiang-guan.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/3.HiveQL\347\233\270\345\205\263.md" diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/4.-suo-yin.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/4.\347\264\242\345\274\225.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/4.-suo-yin.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/4.\347\264\242\345\274\225.md" diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/5.-mo-shi-she-ji.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/5.\346\250\241\345\274\217\350\256\276\350\256\241.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/5.-mo-shi-she-ji.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/5.\346\250\241\345\274\217\350\256\276\350\256\241.md" diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/7.-qi-ta-wen-jian-ge-shi-he-ya-suo-fang-fa.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/7.\345\205\266\344\273\226\346\226\207\344\273\266\346\240\274\345\274\217\345\222\214\345\216\213\347\274\251\346\226\271\346\263\225.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/7.-qi-ta-wen-jian-ge-shi-he-ya-suo-fang-fa.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/7.\345\205\266\344\273\226\346\226\207\344\273\266\346\240\274\345\274\217\345\222\214\345\216\213\347\274\251\346\226\271\346\263\225.md" diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/8.-han-shu-kai-fa.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/8.\345\207\275\346\225\260\345\274\200\345\217\221.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/8.-han-shu-kai-fa.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/8.\345\207\275\346\225\260\345\274\200\345\217\221.md" diff --git a/bigdata/olap/hive/hive-bian-cheng-zhi-nan/9.-wen-jian-he-ji-lu-ge-shi-yi-ji-thrift-fu-wu.md "b/bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/9.\346\226\207\344\273\266\345\222\214\350\256\260\345\275\225\346\240\274\345\274\217\344\273\245\345\217\212Thrift\346\234\215\345\212\241.md" similarity index 100% rename from bigdata/olap/hive/hive-bian-cheng-zhi-nan/9.-wen-jian-he-ji-lu-ge-shi-yi-ji-thrift-fu-wu.md rename to "bigdata/olap/hive/hive\347\274\226\347\250\213\346\214\207\345\215\227/9.\346\226\207\344\273\266\345\222\214\350\256\260\345\275\225\346\240\274\345\274\217\344\273\245\345\217\212Thrift\346\234\215\345\212\241.md" diff --git a/bigdata/olap/impala/README.md b/bigdata/olap/impala/README.md deleted file mode 100644 index 8430b863..00000000 --- a/bigdata/olap/impala/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# impala - diff --git "a/bigdata/olap/kudu/Kudu\345\216\237\347\220\206\345\210\206\346\236\220.md" "b/bigdata/olap/kudu/Kudu\345\216\237\347\220\206\345\210\206\346\236\220.md" index 7fec2b66..99052ede 100644 --- "a/bigdata/olap/kudu/Kudu\345\216\237\347\220\206\345\210\206\346\236\220.md" +++ "b/bigdata/olap/kudu/Kudu\345\216\237\347\220\206\345\210\206\346\236\220.md" @@ -1,10 +1,8 @@ -# Kudu原理分析 +# Kudu存储原理 -## Kudu存储原理 +![kudu底层数据模型](./img/Kudu底层数据模型.jpg) -![kudu底层数据模型](img/Kudu底层数据模型.jpg) - -* RowSet包含一个MemRowSet及若干个DiskRowSet,DiskRowSet中包含一个BloomFile、Ad\_hoc Index、BaseData、DeltaMem及若干个RedoFile和UndoFile。 +* RowSet包含一个MemRowSet及若干个DiskRowSet,DiskRowSet中包含一个BloomFile、Ad_hoc Index、BaseData、DeltaMem及若干个RedoFile和UndoFile。 * `MemRowSet`:用于`新数据insert及已在MemRowSet中的数据的更新`,一个MemRowSet写满后会将数据刷到磁盘形成若干个DiskRowSet。默认是1G或者120S溢写阈值。 * `DiskRowSet`:用于`老数据的变更`,后台定期对DiskRowSet做compaction,以删除没用的数据及合并历史数据,减少查询过程中的IO开销。 * `BloomFile`:根据一个DiskRowSet中的key生成一个bloom filter,用于`快速模糊定位某一个key是否在DiskRowSet`中,用于compaction和MemRowSet溢写DiskRowSet使用。 @@ -14,75 +12,75 @@ * `RedoFile`是基于BaseData之后时间的变更记录,通过在BaseData上apply RedoFile中的记录,可获得较新数据一般指已经提交的数据。 * `DeltaMem`用于`DiskRowSet`中数据的变更,先写到内存中,写满后flush到磁盘形成RedoFile,RedoFile主要是溢写到磁盘的数据变更记录。 -### Redo与Undo +## Redo与Undo * REDO与UNDO与关系型数据库中的REDO与UNDO类似(在关系型数据库中,REDO日志记录了更新后的数据,可以用来恢复尚未写入Data File的已成功事务更新的数据。而UNDO日志用来记录事务更新之前的数据,可以用来在事务失败时进行回滚) -### MemRowSets和DiskRowSets +## MemRowSets和DiskRowSets * `MemRowSets`可以对比为HBase的`MemStore`,`DiskRowSets`可以理解为HBase的`HFile`。 * `MemRowSets`中的数据被Flush到磁盘之后,形成`DiskRowSets`。`DiskRowSets`中的数据,按照32MB大小为单位,按序划分成一个个的`DiskRowSet`。DiskRowSet中的数据按照`Column`进行组织,与Parquet类似。 * 每一个`Column`的数据被存储在一个相邻的数据区域,这个区域进一步被细分为一个个的小的Page单元,与HBase File中的Block类似,对每个Column Page可采用一些Encoding算法,及Compression算法。 * DiskRowSet是不可修改的,DiskRowSet分为base data和delta stores。base data负责存储`基础数据`,delta stores负责存储`base data中的变更数据`,最终再flush到redofile中。 -#### DiskRowSet底层存储模型 +### DiskRowSet底层存储模型 -![DiskRowSet模型](img/DiskRowSet模型.jpg) +![DiskRowSet模型](./img/DiskRowSet模型.jpg) * 数据从MemRowSet刷到磁盘后形成了一份DiskRowSet(只包含base data),每份DiskRowSet在`内存中都会有一个对应的DeltaMemStore`,负责记录此DiskRowSet后续的数据变更(更新、删除)。 * DeltaMemStore内部维护了一个B树索引,映射到每个`row_offset`对应得数据变更。DeltaMemStore数据增长到一定程度后转化成二进制文件存储到磁盘,形成一个DeltaFile,随着base data对应数据的不断变更,DeltaFile逐渐增长。 -### tablet发现过程 +## tablet发现过程 * 当创建Kudu客户端时,会从master server上获取tablet位置信息,然后直接与服务于该tablet的tablet server进行连接,以此来操作对应的tablet。 * 为了优化读取与写入路径,`客户端将保留该信息的本地缓存(类似于Hbase中的Client,也会缓存RegionServer的地址)`,以防止他们在每个请求时都需要查询主机的tablet位置信息。客户端缓存会存在缓存过时的问题,并且当写入被发送到不再是tablet leader的tablet server时,则将被拒绝。然后客户端将通过`查询master server发现新leader的位置来更新其缓存`。 -![tablet发现过程](img/tablet发现过程.jpg) +![tablet发现过程](./img/tablet发现过程.jpg) -### 写数据流程 +## 写数据流程 * 当client请求写数据时,`先根据主键从Master Server中获取要访问的目标Tablets,然后到依次对应的Tablet获取数据。`Kudu表存在主键约束需要进行主键是否存在的判断,这里可以通过底层的`BloomFilter`和`adHocIndex`结构来判断。一个Tablet中存在很多个`RowSets`,为了提高性能`尽可能减少要减少扫描的RowSets数量`。 * 通过每个RowSet中记录的`主键的(最大最小)范围`,过滤掉一批不存在目标主键的RowSets,然后在根据RowSet中的`BloomFilter`,过滤掉确定不存在目标主键的RowSets,再通过RowSets中的B树索引`adhocIndex`,精确定位目标主键是否存储。 * 如果主键已经存在,则报错(键重复),否则就进行写数据(写MemRowSet) -![kudu](img/Kudu写过程.jpg) +![kudu](./img/Kudu写过程.jpg) -### 读数据流程 +## 读数据流程 * 先根据要扫描数据的主键范围,定位到目标的Tablets,然后读取Tablets中的RowSets。在读取每个RowSet时,先根据主键过滤要scan范围,然后加载范围内的`base data`,再找到对应的`delta stores`,apply所有变更,最后`union`上MemRowSet中的数据,返回数据给Client。 -![kudu读过程](img/kudu读过程.jpg) +![kudu读过程](./img/kudu读过程.jpg) -### 更新数据流出 +## 更新数据流出 * 数据更新的核心是定位到待更新的位置,定位到具体位置后,然后将变更写到对应的delta store中 -![kudu更新过程](img/Kudu更新过程.jpg) +![kudu更新过程](./img/Kudu更新过程.jpg) -## Kudu伸扩容 +# Kudu伸扩容 * 热副本:连续接收写操作的tablet副本。例如,在时间序列用例中,时间列上最近范围分区的平板副本将持续接收最新数据,并且将是热副本。 * 冷副本:冷副本即不经常接收写操作的副本,例如,每隔几分钟一次。可以读取一个冷副本。例如,在时间序列用例中,一个时间列上以前的范围分区的tablet副本根本不会接收写操作,或者只是偶尔接收到最新的更新或添加操作,但可能会不断读取。 * 磁盘上的数据:跨所有磁盘、复制后、压缩后和编码后存储在tablet server上的数据总量。 -### Memory +## Memory * 通过`memory_limit_hard_bytes`决定Kudu tablet server可能使用的最大内存数,这个内存大小将被一个tablet server用来伸缩数据大小,写入负载以及并发读取。 -| Type | Multiplier | Description | -| -------------------------------- | ---------------------------------- | ----------------------------------------------------------------- | -| 磁盘上每TB数据所需的内存 | 1.5GB / 1TB数据 | Tablet server的基本操作所需的磁盘上每单位数据的内存量。 | -| 热副本的 MemRowSets和DeltaMemStores大小 | 每个热副本至少128MB | 每次MemRowSet刷新要刷新的最小数据量。 在大多数情况下,与插入相比更新应该很少,因此DeltaMemStores应该很小。 | -| Scans | 对于读取繁重的表,每核每列256KB | 表的扫描器使用的内存量,以及经常被读取的表经常需要的内存量。 | -| Block Cache | `--block_cache_capacity_mb`默认512MB | 保留给块缓存使用的内存量。 | +| Type | Multiplier | Description | +| :-------------------------------------- | :----------------------------------- | :----------------------------------------------------------- | +| 磁盘上每TB数据所需的内存 | 1.5GB / 1TB数据 | Tablet server的基本操作所需的磁盘上每单位数据的内存量。 | +| 热副本的 MemRowSets和DeltaMemStores大小 | 每个热副本至少128MB | 每次MemRowSet刷新要刷新的最小数据量。 在大多数情况下,与插入相比更新应该很少,因此DeltaMemStores应该很小。 | +| Scans | 对于读取繁重的表,每核每列256KB | 表的扫描器使用的内存量,以及经常被读取的表经常需要的内存量。 | +| Block Cache | `--block_cache_capacity_mb`默认512MB | 保留给块缓存使用的内存量。 | -* 8TB的硬盘大小,需要8\*1.5GB的tablet server内存 -* 200个热副本需要,200\*128MB=25.6GB,MemRowSet需要的刷新数据量 -* 40个列,40_40_256KB,表的扫描器使用的内存量 +* 8TB的硬盘大小,需要8*1.5GB的tablet server内存 +* 200个热副本需要,200*128MB=25.6GB,MemRowSet需要的刷新数据量 +* 40个列,40*40*256KB,表的扫描器使用的内存量 * Block Cache 512MB * 预计内存大小38.5GB,推荐的hard limit为52GB,查看内存负载,应该在50到75%之间 -#### 验证内存限制是否足够 +### 验证内存限制是否足够 * 设置完`--memory_limit_hard_bytes`后,内存使用应该保持在硬限制的50-75%左右,偶尔会出现高于75%但低于100%的峰值。如果tablet服务器始终运行在75%以上,则应该增加内存限制。 * 另外,监视内存拒绝的日志也很有用,它类似于: @@ -91,7 +89,7 @@ Service unavailable: Soft memory limit exceeded (at 96.35% of capacity) ``` -#### 内存指标 +### 内存指标 * 查看内存拒绝指标 * `leader_memory_pressure_rejections` @@ -99,44 +97,45 @@ Service unavailable: Soft memory limit exceeded (at 96.35% of capacity) * `transaction_memory_pressure_rejections` * 偶尔由于内存压力而被拒绝是正常的,并作为对客户机的背压。客户端将透明地重试操作。但是,任何操作都不应该超时。 -### 文件描述符 + +## 文件描述符 * 进程被分配了最大数量的打开的文件描述符(也称为fds)。如果一个tablet server试图打开太多的fds,它通常会崩溃,并显示类似“打开的文件太多”之类的信息。 **下表总结了Kudu tablet服务器进程中文件描述符使用的来源** -| Type | Multiplier | Description | -| ------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | -| File cache | Fixed by `--block_manager_max_open_files` (default 40% of process maximum) | Maximum allowed open fds reserved for use by the file cache. | -| Hot replicas | 2 per WAL segment, 1 per WAL index | Number of fds used by hot replicas. See below for more explanation. | -| Cold replicas | 3 per cold replica | Number of fds used per cold replica: 2 for the single WAL segment and 1 for the single WAL index. | +| Type | Multiplier | Description | +| :------------ | :----------------------------------------------------------- | :----------------------------------------------------------- | +| File cache | Fixed by `--block_manager_max_open_files` (default 40% of process maximum) | Maximum allowed open fds reserved for use by the file cache. | +| Hot replicas | 2 per WAL segment, 1 per WAL index | Number of fds used by hot replicas. See below for more explanation. | +| Cold replicas | 3 per cold replica | Number of fds used per cold replica: 2 for the single WAL segment and 1 for the single WAL index. | * 每个副本至少一个WAL segment和一个WAL index,并且应该有相同数量的段和索引。但是,如果一个副本的一个对等副本落后,那么它的段和索引的数量可能会更多。在对WALs进行垃圾收集时,关闭WAL段和索引fds。 -| Type | Amount | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -| file cache | 40% \* 32000 fds = 12800 fds | -| 1600 cold replicas | 1600 cold replicas \* 3 fds / cold replica = 4800 fds | -| 200 hot replicas | (2 / segment \* 10 segments/hot replica \* 200 hot replicas) + (1 / index \* 10 indices / hot replica \* 200 hot replicas) = 6000 fds | -| Total | 23600 fds | +| Type | Amount | +| :----------------- | :----------------------------------------------------------- | +| file cache | 40% * 32000 fds = 12800 fds | +| 1600 cold replicas | 1600 cold replicas * 3 fds / cold replica = 4800 fds | +| 200 hot replicas | (2 / segment * 10 segments/hot replica * 200 hot replicas) + (1 / index * 10 indices / hot replica * 200 hot replicas) = 6000 fds | +| Total | 23600 fds | * 因此,对于本例,tablet服务器进程有大约32000 - 23600 = 8400个fds -### Threads +## Threads -* 如果Kudu tablet server的线程数超过操作系统限制,则它将崩溃,通常在日志中显示一条消息,例如“ pthread\_create失败:资源暂时不可用”。 如果超出系统线程数限制,则同一节点上的其他进程也可能崩溃。 +* 如果Kudu tablet server的线程数超过操作系统限制,则它将崩溃,通常在日志中显示一条消息,例如“ pthread_create失败:资源暂时不可用”。 如果超出系统线程数限制,则同一节点上的其他进程也可能崩溃。 * 整个Kudu都将线程和线程池用于各种目的,但几乎所有线程和线程池都不会随负载或数据/tablet大小而扩展; 而是,线程数可以是硬编码常量,由配置参数定义的常量,也可以是基于静态维度(例如CPU内核数)的常量。 * 唯一的例外是WAL append线程,它存在于每个“热”副本中。注意,所有的副本在启动时都被认为是热的,因此tablet服务器的线程使用通常会在启动时达到峰值,然后稳定下来。 -## Kudu事务 +# Kudu事务 * kudu最终被设计来满足ACID,但是多tablet的事务kudu不支持。 * 写操作是一组要在存储引擎中插入、更新或删除的行,这些行位于带有多个副本的单个tablet上。写操作没有独立的“读集”,即它们在执行写操作之前不会扫描现有数据。每次写操作只关心将要更改的行之前的状态。写不是由用户显式地“提交”的。相反,它们在完成后由系统自动提交。 * 扫描是读取操作,可以遍历多个tablet并读取具有不同级别一致性或正确性保证的信息。扫描可以执行时间旅行读取,即用户可以设置过去的扫描时间戳,并返回反映存储引擎在那个时间点的状态的结果。 -### 单tablet写操作 +## 单tablet写操作 -* Kudu采用多版本并发控制(MVCC)和Raft一致性算法\[4]。Kudu的每个写操作都必须通过tablet的领导者. +* Kudu采用多版本并发控制(MVCC)和Raft一致性算法[4]。Kudu的每个写操作都必须通过tablet的领导者. 1. leader获取它将要更改的行的所有锁。 2. leader在写入提交副本之前给写入分配一个时间戳。这个时间戳将是MVCC中的写标记。 3. 在大多数副本确认更改后,实际的行被更改。 @@ -145,14 +144,15 @@ Service unavailable: Soft memory limit exceeded (at 96.35% of capacity) * 这种锁获取和时间戳分配的严格顺序被强制在tablet的所有副本之间一致。因此,写操作完全是根据时钟分配的时间戳排序的,相对于同一块tablet上的其他写操作。换句话说,写入具有严格可序列化的语义,尽管在有限的上下文中。 * 虽然多行写操作在ACID意义上是孤立且持久的,但还不是完全原子的。`批处理操作中的单个写操作失败不会回滚该操作,但会产生每行错误`。 -### 读操作 +## 读操作 * 扫描是由客户机执行的读取操作,这些操作可能跨一个或多个tablet跨越一个或多个行。当服务器接收到扫描请求时,它将获取MVCC状态的快照,然后根据用户选择的读取模式,采用两种方式中的一种进行处理。 * 设置读取模式,Java Call `KuduScannerBuilder#readMode(…)` 1. `READ_LATEST`:读默认读取模式,服务器获取MVCC状态的快照,并立即进行读取。这种模式下的读取只能产生“READ COMMIT”隔离。 2. `READ_AT_SNAPSHOT`:在此读取模式下,扫描是一致且可重复的。 快照的时间戳由服务器选择,或者由用户通过KuduScanner :: SetSnapshotMicros()显式设置。 建议明确设置时间戳; 服务器将等待,直到此时间戳为“安全”为止(直到所有具有较低时间戳的写操作都已完成并且可见)。 这种延迟加上外部一致性方法,最终将使Kudu对读取和写入具有完全严格的可序列化语义。 -### 建议 +## 建议 + +* 如果需要重复读取快照,请使用READ_AT_SNAPSHOT,并将时间戳设置为稍早(在2-5秒之间)。 这将避免Writes中描述的异常。 即使解决了异常问题,但对时间戳进行回溯将始终使扫描速度更快,因为它们不太可能被阻止。 +* 如果需要外部一致性,并且您决定使用COMMIT_WAIT,则需要仔细调整时间同步协议。 每个事务在执行时将等待最大时钟错误的2倍,通常为100毫秒 到1秒 默认设置的范围,也许更多。 因此,事务将至少花费200毫秒到2秒 使用默认设置时可能会完成,甚至可能超时。 -* 如果需要重复读取快照,请使用READ\_AT\_SNAPSHOT,并将时间戳设置为稍早(在2-5秒之间)。 这将避免Writes中描述的异常。 即使解决了异常问题,但对时间戳进行回溯将始终使扫描速度更快,因为它们不太可能被阻止。 -* 如果需要外部一致性,并且您决定使用COMMIT\_WAIT,则需要仔细调整时间同步协议。 每个事务在执行时将等待最大时钟错误的2倍,通常为100毫秒 到1秒 默认设置的范围,也许更多。 因此,事务将至少花费200毫秒到2秒 使用默认设置时可能会完成,甚至可能超时。 diff --git a/bigdata/olap/kudu/README.md b/bigdata/olap/kudu/README.md deleted file mode 100644 index 8042c199..00000000 --- a/bigdata/olap/kudu/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# kudu - diff --git a/bigdata/olap/kudu/paper/README.md b/bigdata/olap/kudu/paper/README.md deleted file mode 100644 index 2eee37d0..00000000 --- a/bigdata/olap/kudu/paper/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# paper - diff --git a/bigdata/olap/kylin/README.md b/bigdata/olap/kylin/README.md deleted file mode 100644 index afed20a3..00000000 --- a/bigdata/olap/kylin/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# kylin - diff --git a/bigdata/olap/presto/README.md b/bigdata/olap/presto/README.md deleted file mode 100644 index 0ec97f50..00000000 --- a/bigdata/olap/presto/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# presto - diff --git a/bigdata/scheduler/README.md b/bigdata/scheduler/README.md deleted file mode 100644 index e5af2166..00000000 --- a/bigdata/scheduler/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# scheduler - diff --git a/bigdata/tools/README.md b/bigdata/tools/README.md deleted file mode 100644 index b5ddbbab..00000000 --- a/bigdata/tools/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# tools - diff --git a/bigdata/tools/sqltree/README.md b/bigdata/tools/sqltree/README.md deleted file mode 100644 index f4b490db..00000000 --- a/bigdata/tools/sqltree/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# sqltree - diff --git a/bigdata/tools/sqltree/calcite/README.md b/bigdata/tools/sqltree/calcite/README.md deleted file mode 100644 index c79ec38c..00000000 --- a/bigdata/tools/sqltree/calcite/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# calcite - diff --git a/bigdata/zookeeper/README.md b/bigdata/zookeeper/README.md deleted file mode 100644 index 4a34c45f..00000000 --- a/bigdata/zookeeper/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# zookeeper - diff --git "a/bigdata/zookeeper/Zookeeper\346\223\215\344\275\234\344\270\216\351\203\250\347\275\262.md" "b/bigdata/zookeeper/Zookeeper\346\223\215\344\275\234\344\270\216\351\203\250\347\275\262.md" index e04663fe..6e8d9107 100644 --- "a/bigdata/zookeeper/Zookeeper\346\223\215\344\275\234\344\270\216\351\203\250\347\275\262.md" +++ "b/bigdata/zookeeper/Zookeeper\346\223\215\344\275\234\344\270\216\351\203\250\347\275\262.md" @@ -1,12 +1,10 @@ -# Zookeeper操作与部署 +# 分布式部署 -## 分布式部署 - -### 集群规划 +## 集群规划 * hadoop1、hadoop2、hadoop3 -### 环境准备 +## 环境准备 * 安装JDK环境 * 下载Zookeeper安装包 @@ -21,9 +19,9 @@ server.2=localhost:2889:3889 server.3=localhost:2890:3890 ``` -### 配置 +## 配置 -#### Hadoop1 +### Hadoop1 ```properties admin.serverPort=10001 @@ -69,7 +67,7 @@ cnxTimeout=5000 electionAlg ``` -#### Hadoop2 +### Hadoop2 ```properties tickTime=2000 @@ -84,7 +82,7 @@ server.2=localhost:2889:3889 server.3=localhost:2890:3890 ``` -#### Hadoop3 +### Hadoop3 ```properties tickTime=2000 @@ -99,11 +97,11 @@ server.2=localhost:2889:3889 server.3=localhost:2890:3890 ``` -### 开启ZookeeperJMX +## 开启ZookeeperJMX * ZK默认开启了JMX功能,但是只支持本地连接,修改bin目录下的zkServer.sh -#### 默认JMX配置 +### 默认JMX配置 ```properties -Dcom.sum.management.jmxremote.port=5000 @@ -111,39 +109,33 @@ server.3=localhost:2890:3890 -Dcom.sum.management.jmxremote.authenticate=false ``` -## Shell操作 +# Shell操作 ```shell # 链接客户端 zkCli.sh -server ip:port ``` -### create +## create ```shell create [-s] [-e] [-c] [-t ttl] path [data] [acl] ``` -* 创建顺序节点 - +* 创建顺序节点 ```shell create -s /name huangsm ``` - -* 创建临时节点 - +* 创建临时节点 ``` create -e /name huangsm 当客户端断开连接后,心跳机制就会断开,然后临时节点就会自动删除 ``` - -* 创建持久节点 - +* 创建持久节点 ``` create /name huangsm ``` - -**通过ephemeralOwner来判断是否是临时节点,如果ephemeralOwner不是0x0则为临时节点** +**通过ephemeralOwner来判断是否是临时节点,如果ephemeralOwner不是0x0则为临时节点** * 创建多级目录 @@ -151,36 +143,33 @@ create /name huangsm create /name/hsm hhh ``` -### set +## set ``` set [-s] [-v version] path data ``` - * 修改节点的值 ``` set /name "wbd" ``` -### delete +## delete ```shell delete [-v version] path deleteall path ``` - -### watcher +## watcher ```shell get [-s] [-w] path -w设置watcher -s表示顺序节点 ``` -* 父节点 增删改操作触发 -* 子节点 增删改操作触发 -* 创建父节点触发:nodeCreated - +* 父节点 增删改操作触发 +* 子节点 增删改操作触发 +* 创建父节点触发:nodeCreated ```shell stat -w /name 设置NodeCreated事件 create /name 123 @@ -189,8 +178,7 @@ WatchedEvent state:SyncConnected type:NodeCreated path:/name Created /name ``` -* 修改父节点触发:NodeDataChanged - +* 修改父节点触发:NodeDataChanged ```shell 设置修改事件 [zk: localhost:2181(CONNECTED) 39] get -w /name @@ -211,8 +199,7 @@ dataLength = 3 numChildren = 0 ``` -* 删除父节点触发:NodeDelete - +* 删除父节点触发:NodeDelete ```shell 设置删除事件 [zk: localhost:2181(CONNECTED) 42] ls -w /name @@ -221,96 +208,83 @@ numChildren = 0 WATCHER:: WatchedEvent state:SyncConnected type:NodeDeleted path:/name ``` +## ACL -### ACL - -* getAcl:获取某个节点的acl权限信息 +* getAcl:获取某个节点的acl权限信息 ```shell getAcl [-s] path ``` - -* setAcl:设置某个节点的acl权限信息 - +* setAcl:设置某个节点的acl权限信息 ```shell setAcl [-s] [-v version] [-R] path acl ``` -* addauth:输入认证授权信息,注册时输入明文密码(登录),但是在zk的系统里,密码是以加密的形式存在的 - +* addauth:输入认证授权信息,注册时输入明文密码(登录),但是在zk的系统里,密码是以加密的形式存在的 ```shell addauth scheme auth ``` - -#### acl的构成 - -* zk的acl通过\[scheme:id​ : id:permissions]来构成权限列表 - * scheme:代表采用的某种权限机制 - * world:world下只有一个id,即只有一个用户,也就是anyone,那么组合的写法就是world:anyone:\[permissions] - * auth:代表认证登录,需要需要注册用户权限就可以,形式为auth:user:password:\[permissions] - * digest:需要对密码加密才能访问,组合形式为:degest:username:BASE64(SHA1(password)):\[permissions] - * ip:设置为ip指定的ip地址,此时限制ip进行访问,比如ip:127.0.0.1:\[permissions] - * super:代表超级管理员,拥有所有的权限 - * id:代表允许访问的用户 - * permissions:权限组合字符串 - * 权限字符串缩写crdwa - * CREATE:创建子节点 - * READ:获取节点/子节点 - * WRITE:设置节点数据 - * DELETE:删除子节点 - * ADMIN:设置权限 - -#### 设置权限 - -* 第一种的world - +### acl的构成 + +* zk的acl通过[scheme:id​ : id:permissions]来构成权限列表 + * scheme:代表采用的某种权限机制 + * world:world下只有一个id,即只有一个用户,也就是anyone,那么组合的写法就是world:anyone:[permissions] + * auth:代表认证登录,需要需要注册用户权限就可以,形式为auth:user:password:[permissions] + * digest:需要对密码加密才能访问,组合形式为:degest:username:BASE64(SHA1(password)):[permissions] + * ip:设置为ip指定的ip地址,此时限制ip进行访问,比如ip:127.0.0.1:[permissions] + * super:代表超级管理员,拥有所有的权限 + * id:代表允许访问的用户 + * permissions:权限组合字符串 + * 权限字符串缩写crdwa + * CREATE:创建子节点 + * READ:获取节点/子节点 + * WRITE:设置节点数据 + * DELETE:删除子节点 + * ADMIN:设置权限 +### 设置权限 + + +* 第一种的world ```shell setAcl /name/abc world:anyone:crwa 设置创建、读、写、设置权限 ``` -* 第二种auth - +* 第二种auth ```shell setAcl /name/abc auth:user:pwd:cdrwa user和pwd都代表第一个注册的用户和密码 ``` - -* 第三种digest - +* 第三种digest ```shell setAcl /name/abc digest:user:BASE64(SHA1(pwd)):cdrwa //登录用户 addauth digest user:pwd ``` -* 第四种ip - +* 第四种ip ```shell setAcl /name/abc ip:127.0.0.1:cdrwa ``` -* 第五种super - +* 第五种super ```shell Super 1 修改zkServer.sh增加super管理员 2 重启zkServer ``` +图片 -![图片](img/super权限.jpg) - -1. zk四字命令Four letter worlds +1. zk四字命令Four letter worlds ```properties #添加四字命令白名单 4lw.commands.whitelist=* ``` -* zk可以通过它自身提供的简写命令来和服务器进行交互 -* 需要使用到nc命令,安装:yum install nc -* echo\[command]|nc \[ip]\[port] - +* zk可以通过它自身提供的简写命令来和服务器进行交互 +* 需要使用到nc命令,安装:yum install nc +* echo[command]|nc [ip][port] ```properties [stat] 查看zk的状态信息,以及是否mode [ruok] 查看当前zkserver是否启动,返回imok @@ -322,10 +296,9 @@ Super [wchs] 展示watch的信息 [wchc]与[wchp] session与watch及path与watch信息 ``` +# 原生API使用 -## 原生API使用 - -### 创建连接 +## 创建连接 ```java public class ZookeeperConnection implements Watcher { @@ -366,9 +339,10 @@ public class ZookeeperConnection implements Watcher { } } + ``` -### 会话重连 +## 会话重连 ```java public class ZkConnectionSessionWatcher implements Watcher { @@ -398,7 +372,7 @@ public class ZkConnectionSessionWatcher implements Watcher { } ``` -### 节点增删改查 +## 节点增删改查 ```java public class ZkNodeOperator implements Watcher { @@ -514,9 +488,10 @@ public class ZkNodeOperator implements Watcher { } } + ``` -#### 节点查询和监听 +### 节点查询和监听 ```java public class QueryAndWatch{ @@ -581,7 +556,7 @@ public class QueryAndWatch{ ``` -#### 子节点查询和监听 +### 子节点查询和监听 ```java public class ZKGetChildrenList implements Watcher { @@ -670,11 +645,12 @@ public class ZKGetChildrenList implements Watcher { } } + ``` -## Curator +# Curator -### 创建会话 +## 创建会话 ```java public static void createClient() { @@ -686,7 +662,7 @@ public static void createClient() { } ``` -### 创建节点 +## 创建节点 ```java public static void createNode() throws Exception { @@ -699,7 +675,7 @@ public static void createNode() throws Exception { } ``` -### 删除节点 +## 删除节点 ```java public static void deleteNode() throws Exception { @@ -719,7 +695,7 @@ public static void createNode() throws Exception { } ``` -### 读取数据 +## 读取数据 ```java public static void readNode() throws Exception { @@ -735,7 +711,7 @@ public static void createNode() throws Exception { } ``` -### 修改数据 +## 修改数据 ```java public static void updateNode() throws Exception { @@ -747,9 +723,9 @@ public static void createNode() throws Exception { } ``` -### Watcher +## Watcher -#### 添加节点监听器 +### 添加节点监听器 ```java public static void addWatcher() throws Exception { @@ -779,7 +755,7 @@ public static void createNode() throws Exception { } ``` -#### 添加子节点监听器 +### 添加子节点监听器 ```java public static void createChildrenNode() throws Exception { @@ -823,7 +799,7 @@ public static void createChildrenNode() throws Exception { } ``` -### Master选举机制 +## Master选举机制 ```java public class MasterLeaderSelector { @@ -853,3 +829,4 @@ public class MasterLeaderSelector { } } ``` + diff --git a/datawarehouse/README.md b/datawarehouse/README.md deleted file mode 100644 index ed60ab6c..00000000 --- a/datawarehouse/README.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -description: 数据仓库 ---- - -# datawarehouse - diff --git a/datawarehouse/fang-an-shi-jian/README.md b/datawarehouse/fang-an-shi-jian/README.md deleted file mode 100644 index ffa0a357..00000000 --- a/datawarehouse/fang-an-shi-jian/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 方案实践 - diff --git a/datawarehouse/li-lun/README.md b/datawarehouse/li-lun/README.md deleted file mode 100644 index 6dede70e..00000000 --- a/datawarehouse/li-lun/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 理论 - diff --git a/datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/README.md b/datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/README.md deleted file mode 100644 index 9f06ab4b..00000000 --- a/datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# 数据中台模块设计 - diff --git a/datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/shu-ju-zhong-tai-she-ji.md "b/datawarehouse/\346\225\260\346\215\256\344\270\255\345\217\260\346\250\241\345\235\227\350\256\276\350\256\241/\346\225\260\346\215\256\344\270\255\345\217\260\350\256\276\350\256\241.md" similarity index 100% rename from datawarehouse/shu-ju-zhong-tai-mo-kuai-she-ji/shu-ju-zhong-tai-she-ji.md rename to "datawarehouse/\346\225\260\346\215\256\344\270\255\345\217\260\346\250\241\345\235\227\350\256\276\350\256\241/\346\225\260\346\215\256\344\270\255\345\217\260\350\256\276\350\256\241.md" diff --git a/datawarehouse/fang-an-shi-jian/ji-yu-flink-de-shi-shi-shu-cang-jian-she.md "b/datawarehouse/\346\226\271\346\241\210\345\256\236\350\267\265/\345\237\272\344\272\216Flink\347\232\204\345\256\236\346\227\266\346\225\260\344\273\223\345\273\272\350\256\276.md" similarity index 100% rename from datawarehouse/fang-an-shi-jian/ji-yu-flink-de-shi-shi-shu-cang-jian-she.md rename to "datawarehouse/\346\226\271\346\241\210\345\256\236\350\267\265/\345\237\272\344\272\216Flink\347\232\204\345\256\236\346\227\266\346\225\260\344\273\223\345\273\272\350\256\276.md" diff --git a/datawarehouse/li-lun/shu-ju-cang-ku-shi-zhan.md "b/datawarehouse/\347\220\206\350\256\272/\346\225\260\346\215\256\344\273\223\345\272\223\345\256\236\346\210\230.md" similarity index 100% rename from datawarehouse/li-lun/shu-ju-cang-ku-shi-zhan.md rename to "datawarehouse/\347\220\206\350\256\272/\346\225\260\346\215\256\344\273\223\345\272\223\345\256\236\346\210\230.md" diff --git a/devops/README.md b/devops/README.md deleted file mode 100644 index 5c2ea23e..00000000 --- a/devops/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# devops - diff --git a/devops/maven/README.md b/devops/maven/README.md deleted file mode 100644 index 2e652f47..00000000 --- a/devops/maven/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# maven - diff --git a/mac/README.md b/mac/README.md deleted file mode 100644 index 19ce65a2..00000000 --- a/mac/README.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -description: mac工具 ---- - -# mac os - diff --git a/mac/iterm2/README.md b/mac/iterm2/README.md deleted file mode 100644 index 9bb0a263..00000000 --- a/mac/iterm2/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# iterm2 - diff --git a/servicemonitor/README.md b/servicemonitor/README.md deleted file mode 100644 index 260a4ac2..00000000 --- a/servicemonitor/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# servicemonitor - diff --git a/servicemonitor/prometheus/README.md b/servicemonitor/prometheus/README.md deleted file mode 100644 index cd33313b..00000000 --- a/servicemonitor/prometheus/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Prometheus -