DataVisualization_USE_R_ECHARTS

📺 数据可视化基础(使用RECharts

本文会从 RECharts 两个方面来展开数据可视化技术的一些使用方法。

[TOC]

📊 1. 数据可视化概述

  • 数据可视化概念

    • 狭义概念:指的是数据用统计图表方式呈现
    • 广义概念:是数据可视化、信息可视化以及科学可视化等等多个领域的统称
  • 数据可视化分类

    • 数据可视化
    • 信息可视化
    • 科学可视化
  • ⭐*** 数据可视化作用***

    • 数据表达:通过计算机图形图形技术来更加友好地展示数据信息,以方便人们理解和分析数据。
    • 数据操作:以计算提供的界面、接口和协议等条件为基础完成人与数据的交互需求。
    • 数据分析:通过计算机获得多维、多源、异构和海量数据所隐含信息的核心手段,它是数据存储、数据转换、数据计算和数据可视化的综合应用。
  • 数据可视化的工具

    • REChartsD3
    • PythonpyechartsExcel
    • 第三方商业工具:FineBIFineReport
  • 学习数据可视化需要了解(具备)什么?

    • 对色彩的感知:了解原色、间色、复色等,并且了解色彩的视觉感受(心理感受),当然也就是了解即可,色彩这种东西天赋更重要一些,知道三原色是啥就行。

      • 原色:红、黄、蓝。
      • 间色:可存在多个间色,当原色之间 1:1 匹配时则为:橙、绿、紫。
      • 复色:可存在多个复色(一定包含三原色),两个间色或一种原色和其对应的间色(黄 + 紫、蓝 + 橙)相混合得到复色。
      • 色彩感受:轻重感、冷暖感、前后感、收缩感 …
    • 数据可视化的基本流程

      • 数据采集
      • 数据处理
      • 可视化映射
      • 用户感知
    • 数据可视化的一些设计技巧

      • 设计原则:注重用户的视觉体验

      • 设计技巧:

        ① (颜色)建立视觉层次,用醒目的颜色突出数据,淡化其他元素

        ② (内容)高亮显示重点内容

        ③ (跨度)提升不同区域的色阶跨度

        ④ (场景)借助场景来表现数据指标

        ⑤ (转换)将抽象的不易理解的数字转换为容易被人感知的图表

        ⑥ (简洁)尽量让图表简洁

    • 数据可视化的图表类型

      在统计图表中每一种类型的图表中都可包含不同的数据可视化图形,如:柱状图、饼图、气泡图、热力图、趋势图、直方图、雷达图、色块图、漏斗图、和弦图、仪表盘、面积图、折线图、密度图 以及 K线图等。

🎫 2. R - R语言快速入门

参考文献:数据科学中的 R 语言 (bookdown.org)

2.1 R 语言概述

R语言是为数学研究工作者设计的一种数学编程语言,主要用于统计分析、绘图、数据挖掘数据可视化。

目前主流的数据分析语言有 RPythonMatlab 等。

R Python Matlab
语言学习难易程度 入门难度低 入门难度一般 入门难度一般
使用场景 数据分析、数据挖掘、机器学习、数据可视化等。 数据分析、机器学习、矩阵运算、科学数据可视化、数字图像处理、Web应用、网络爬虫、系统运维等。 矩阵计算、数值分析、科学数据可视化、机器学习、符号计算、数字图像处理、数字信号处理、仿真模拟等。
第三方支持 拥有大量的 Packages,能够调用 CC++FortranJava等其他程序语言。 拥有大量的第三方库,能够简便地调用CC++FortranJava等其他程序语言。 拥有大量专业的工具箱,在新版本中加入了对CC++Java的支持。
流行领域 工业界 ≈ 学术界 工业界 > 学术界 工业界 ≤ 学术界
软件成本 开源免费 开源免费 商业收费

2.2 R 语言中包的概念和使用

  • 软件包和软件库

    • 软件包:R 中的包是 R 函数、数据、预编译代码以一种定义完善的格式组成的集合。

    • 软件库:软件库指的是一个包含了若干个包的目录。你可以拥有一个系统级别的软件库,也可以为每个用户单独设立一个软件库。

    • R 自带了一系列默认包(包括 basedatasetsutilsgrDevicesgraphicsstats以及methods),它们提供了种类繁多的默认函数和数据集。

      • base:包含基础的 R 函数
      • database:自带的数据包,里面的数据结构包括矩阵、向量、数据框等
      • utils:工具函数包
      • grDevices:基础绘图工具包,提供调整图形颜色和字体的功能,可用于配色
      • graphics:基础绘图工具包
      • stats:统计函数的扩展包
      • methodsR 对象和其他编程工具定义方法和类比的扩展包
  • 包的相关操作

    • 查看包的安装路径:.libPaths()
    1
    2
    > .libPaths()
    [1] "D:/R/R-4.3.3/library"
    • 查看已安装的包:library()
    • 查看编译环境下已载入的包的列表:search()
    1
    2
    3
    > search()
    [1] ".GlobalEnv" "tools:rstudio" "package:stats" "package:graphics" "package:grDevices"
    [6] "package:utils" "package:datasets" "package:methods" "Autoloads" "package:base"
    • 下载包:

      • 通过 CRAN (The Comprehensive R Archive Network) 下载。CRAN 是 R 语言的综合档案网络。用户可以从 CRAN 上下载并安装各种第三方包来扩展 R 语言的功能。

        ① 指定包名进行安装:install.packages("PackageName")

        ② 使用图形界面安装:install.packages()

      • 手动安装:https://cran.r-project.org/web/packages/XML/index.html

      • 使用 RStudio 安装

    • 包的卸载:remove.packages("PackageName")

    • 包的载入:library("PackageName")

    1
    2
    3
    4
    5
    > library("XML")
    > search() # 可以看到 "package:XML" 已经被加载进来了
    [1] ".GlobalEnv" "package:XML" "tools:rstudio" "package:stats" "package:graphics"
    [6] "package:grDevices" "package:utils" "package:datasets" "package:methods" "Autoloads"
    [11] "package:base"
    • 包的取消载入:detach("package:PackageName", unload = True)
    1
    2
    3
    4
    > detach("package:XML", unload = TRUE)
    > search()
    [1] ".GlobalEnv" "tools:rstudio" "package:stats" "package:graphics" "package:grDevices"
    [6] "package:utils" "package:datasets" "package:methods" "Autoloads" "package:base"

2.3 R 语言的工作空间的概念和使用

  • 概念:工作空间(Work Space) 就是当前 R 的工作环境,它储存着所有用户定义的对象(向量、矩阵、函数、数据框、列表)。在一个 R 会话结束时,用户可以将当前工作空间保存到一个镜像中,并在下次启动 R 时自动载入它。
  • 设置工作目录:setwd() 或使用 RStudio 可视化窗口进行设置(SessionSet Working Directory
  • 获取工作目录:getwd()

2.4 R 语言的六大原子数据类型

  • 数字型(Numeric)
  • 逻辑型(Logical)
  • 整型(Integer)
  • 字符型(Character)
  • 复合型(Complex)
  • 原子型(Raw)

对于不同原子数据类型可以按照如下方式定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
> # 数值型(Numeric)
> varNumeric = 12.5
> varNumeric
[1] 12.5

> # 逻辑型(Logical)
> varLogical = TRUE
> varLogical
[1] TRUE

> # 整型(Integer)
> varInteger = 25L
> varInteger
[1] 25

> # 字符型(Character)
> varCharacter = "Hello World"
> varCharacter
[1] "Hello World"

> # 复合型(Complex)
> varComplex = 2 + 3i
> varComplex
[1] 2+3i

> # 原子型(Raw)
> varRaw = charToRaw('Hello')
> varRaw
[1] 48 65 6c 6c 6f

2.5 R 语言变量使用

  • 定义变量并赋值:使用 <-=->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> var1 = 12.5		# 定义 var1, 并赋值为 12.5
> print(var1) # 输出 var1
[1] 12.5

> var2 <- 13 # 定义 var2, 并赋值为 13(由右向左)
> print(var2) # 输出 var2
[1] 13

> 14 -> var3 # 定义 var3, 并赋值为 14(由左向右)
> print(var3) # 输出 var3
[1] 14

> var2 -> var3
> print(var3)
[1] 13
  • 变量的打印:print()cat()

    print()cat() 都可以向控制台输出文字,区别在于 print() 有返回值,其会返回输出的内容,无法使用转义字符,当使用转义字符时,会直接输出转义字符;而 cat() 没有返回值,可以使用转义字符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> varX = "Hello World"
> varY = "Hello World\n"

> printX <- print(varX)
[1] "Hello World"
> catX <- cat(varX, "\n") # 如果不添加转义字符会无法换行
Hello World

> printY <- print(varY)
[1] "Hello World\n" # 使用 print() 直接将转义字符进行了输出
> catY <- cat(varY)
Hello World # 使用 cat() 没有将转义字符直接输出, 而是转化成了响应的格式, 如"换行"

> print(printX) # print() 有返回值
[1] "Hello World"
> print(catX) # cat() 没有返回值
NULL
  • 变量的查看:ls()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> ls()						# 输出所有变量
[1] "catX" "catY" "cyl.f" "printX" "printY" "score" "tempdens" "var1" "var2" "var3"
[11] "varX" "varY"

> ls(pattern = "var") # 模式匹配部分变量, 包含 "var" 的
[1] "var1" "var2" "var3" "varX" "varY"

> ls(pattern = "*") # 模式匹配部分变量, 使用通配符
[1] "catX" "catY" "cyl.f" "printX" "printY" "score" "tempdens" "var1" "var2" "var3"
[11] "varX" "varY"

> ls(pattern = "2") # 模式匹配部分变量, 包含 "2" 的
[1] "var2"

> ls(all.names = TRUE) #
[1] ".Random.seed" "catX" "catY" "cyl.f" "printX" "printY" "score"
[8] "tempdens" "var1" "var2" "var3" "varX" "varY"
  • 变量的删除:rm()
1
2
3
4
5
6
7
8
> rm("var1", "var2")				# 删除 "var1" 和 "var2"

> rm(list = ls(pattern = "var")) # 删除 匹配到 "var" 的变量, list 是一个可以指定的参数

> rmlist = ls(pattern = "X")
> rmlist
[1] "catX" "printX"
> rm(list = rmlist)
  • [⭐] 变量的数据类型判别和转换

    • 判别:is.XXX(),返回 True/False
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    > # 数值型(Numeric)
    > varNumeric = 12.5
    > varNumeric
    [1] 12.5
    > is.numeric(varNumeric)
    [1] TRUE
    > is.logical(varNumeric)
    [1] FALSE
    > is.integer(varNumeric)
    [1] FALSE
    > is.character(varNumeric)
    [1] FALSE
    > is.complex(varNumeric)
    [1] FALSE
    > is.raw(varNumeric)
    [1] FALSE

    > # 逻辑型(Logical)
    > varLogical = TRUE
    > varLogical
    [1] TRUE
    > is.numeric(varLogical)
    [1] FALSE
    > is.logical(varLogical)
    [1] TRUE
    > is.integer(varLogical)
    [1] FALSE
    > is.character(varLogical)
    [1] FALSE
    > is.complex(varLogical)
    [1] FALSE
    > is.raw(varLogical)
    [1] FALSE
    • 转换:as.XXX()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    > # 变量类型转换
    > varNumToChar <- as.character(varNumeric)
    > varNumToChar
    [1] "12.5"
    > is.numeric(varNumToChar)
    [1] FALSE
    > is.logical(varNumToChar)
    [1] FALSE
    > is.integer(varNumToChar)
    [1] FALSE
    > is.character(varNumToChar)
    [1] TRUE <-------------------- 可以看到已经转换成了字符型
    > is.complex(varNumToChar)
    [1] FALSE
    > is.raw(varNumToChar)
    [1] FALSE
  • [⭐] 变量的数据类型查看

    • mode():查看数据元素类型
    • typeof():查看数据元素类型,基本等同于 mode() ,比 mode() 更为详细
    • class():查看数据结构,vectormatrixarraydataframelist
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    > # 查看变量类型
    > mode(varNumeric)
    [1] "numeric"
    > typeof(varNumeric)
    [1] "double"
    > class(varNumeric)
    [1] "numeric"

    > mode(varLogical)
    [1] "logical"
    > typeof(varLogical)
    [1] "logical"
    > class(varLogical)
    [1] "logical"

    > mode(varInteger)
    [1] "numeric"
    > typeof(varInteger)
    [1] "integer"
    > class(varInteger)
    [1] "integer"

    > mode(varCharacter)
    [1] "character"
    > typeof(varCharacter)
    [1] "character"
    > class(varCharacter)
    [1] "character"

    > mode(varComplex)
    [1] "complex"
    > typeof(varComplex)
    [1] "complex"
    > class(varComplex)
    [1] "complex"

    > mode(varRaw)
    [1] "raw"
    > typeof(varRaw)
    [1] "raw"
    > class(varRaw)
    [1] "raw"
  • [⭐] 字符串的应用

    • R语言中的文本,或者原子数据类型中 Character。在 R 语言中的单引号' ' 或 双引号" " 中写入的任何值都被视为字符串。在字符串构造中应用的规则:
      • 在字符串的开头和结尾的引号应该是两个双引号或两个单引号。它们不能被混合。
      • 双引号可以插入到以单引号开头和结尾的字符串中
      • 单引号可以插入以双引号开头和结尾的字符串。
      • 双引号不能插入以双引号开头和结尾的字符串。
      • 单引号不能插入以单引号开头和结尾的字符串。

2.7 R 语言的运算符的概念和使用

  • 运算符的概念

  • 算数运算符

    • +加、-减、*乘、/
    • %% 整除取余
    • %/% 整除
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    > # 相除
    > 6 / 4
    [1] 1.5

    > # %% 整除取余
    > 6 %% 4
    [1] 2

    > # %/% 整除
    > 6 %/% 4
    [1] 1
  • 关系运算符

    • < 小于、> 大于、= 等于
    • <=小于等于、>= 大于等于
    • != 不等于
  • 逻辑运算符

    • &
    • |
    • &&
    • ||

    &&||为值逻辑,&|为位逻辑
    说人话就是,&&||是讲两个操作目的值做逻辑运算,无论操作对象是向量还是标量,返回值都是一个逻辑值,(NOW)&&|| 运算符只接受长度为 1 的逻辑值作为参数;而 &| 是讲两个对象按位比较,其返回值的长度与对象是标量还是向量有关。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    > # 逻辑运算符
    > v <- c(3, 1, TRUE, 2+3i)
    > t <- c(4, 1, FALSE, 2+3i)
    > print(v&t)
    [1] TRUE TRUE FALSE TRUE

    > v <- c(3, 0, TRUE, 2+2i)
    > t <- c(1, 3, TRUE, 2+3i)
    > print(v && t)
    Error in v && t : 'length = 4' in coercion to 'logical(1)'

    > TRUE && TRUE
    [1] TRUE
    > TRUE && FALSE
    [1] FALSE
    > FALSE && TRUE
    [1] FALSE
    > FALSE && FALSE
    [1] FALSE
  • 赋值运算符

    • <-
    • =
    • ->
    • <<-
    • ->>

    <−=<<− 运算符: 称为左赋值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    > # 赋值运算符
    > # 左赋值
    > v1 <- c(3,1,TRUE,2+3i)
    > v2 <<- c(3,1,TRUE,2+3i)
    > v3 = c(3,1,TRUE,2+3i)

    > print(v1)
    [1] 3+0i 1+0i 1+0i 2+3i

    > print(v2)
    [1] 3+0i 1+0i 1+0i 2+3i

    > print(v3)
    [1] 3+0i 1+0i 1+0i 2+3i

    ->->> 运算符:称为右赋值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    > # 右赋值
    > c(3,1,TRUE,2+3i) -> v1
    > c(3,1,TRUE,2+3i) ->> v2

    > print(v1)
    [1] 3+0i 1+0i 1+0i 2+3i

    > print(v2)
    [1] 3+0i 1+0i 1+0i 2+3i
  • 其他运算符

    • : 为向量创建数字序列。
    1
    2
    3
    > v <- 2:8
    > print(v)
    [1] 2 3 4 5 6 7 8
    • %in% 用于确定元素是否属于向量。
    1
    2
    3
    4
    5
    6
    7
    > v1 <- 8
    > v2 <- 12
    > t <- 1:10
    > print(v1 %in% t) # 8 是否再 1~10 中
    [1] TRUE
    > print(v2 %in% t) # 12 是否再 1~10 中
    [1] FALSE
    • %*% 用于求两个向量的内积,也称为点乘
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    > M = matrix( c(2,6,5,1,10,4), nrow = 2, ncol = 3, byrow = TRUE)

    # M
    2, 6, 5
    1, 10, 4

    # t(M)
    2, 1
    6, 10
    5, 4

    # M %*% t(M)
    2*2 + 6*6 + 5*5, 1*2 + 10* 6 + 4*5
    2*1 + 10*6 + 4*5, 1*1 + 10*10 + 4*4

    > t = M %*% t(M)
    > print(t)
    [,1] [,2]
    [1,] 65 82
    [2,] 82 117

    > #===================================================================#
    > M = matrix(c(2, 6, 5, 1, 10, 4), nrow = 2,ncol = 3, byrow = TRUE)
    > N = matrix(c(1, 2, 3, 4, 5, 6), nrow = 3, ncol = 2, byrow = TRUE)
    > t = M %*% N
    > print(t)
    [,1] [,2]
    [1,] 45 58
    [2,] 51 66

2.8 R 语言的六大数据对象

R 语言有六种基本的数据结构(数据对象):向量vector、列表list、矩阵matrix、数组array、数据框data.frame 和 因子factor 类型。

2.8.1 向量 vector
  • 概念:向量是最基本的 R 语言数据对象,向量的元素支持六种原子数据类型,即逻辑,整数,双精度,复合,字符和原型。

  • 特征:一个向量的所有元素都必须属于相同的类型。如果不是,R将强制执行类型转换。

  • 创建向量:c()seq()rep()

    • c() 这里的c就是 combineconcatenate 的意思,它要求元素之间用英文的逗号分隔,且元素的数据类型是统一的,比如都是数值。c() 函数把一组数据聚合到了一起,就构成了一个向量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    > # 创建向量
    > # 使用 c()
    > low <- c(1, 2, 3)
    > high <- c(4, 5, 6)
    > sequence <- c(low, high)
    > sequence
    [1] 1 2 3 4 5 6

    > # 给变量命名
    > x <- c('a' = 5, 'b' = 6, 'c' = 7, 'd' = 8)
    > x
    a b c d
    5 6 7 8

    > x <- c(5, 6, 7, 8)
    > names(x) <- c('a', 'b', 'c', 'd')
    > x
    a b c d
    5 6 7 8

    如果向量元素很多,用手工一个个去输入,那就成了体力活,不现实。在特定情况下,有几种偷懒方法:

    • seq() 函数可以生成等差数列,from 参数指定数列的起始值,to 参数指定数列的终止值,by 参数指定数值的间距。
    1
    2
    3
    4
    > # 使用 seq()
    > s1 <- seq(from = 0, to = 10, by = 0.5)
    > s1
    [1] 0.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0
    • rep()repeat(重复)的意思,可以用于产生重复出现的数字序列:x 用于重复的向量,times 参数可以指定要生成的个数,each 参数可以指定每个元素重复的次数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    > s2 <- rep(x = c(0, 1), times = 3)
    > s2
    [1] 0 1 0 1 0 1

    > s3 <- rep(x = c(0, 1), each = 3)
    > s3
    [1] 0 0 0 1 1 1

    > s4 <- rep(x = c(0, 1), time = 3, each = 3)
    > s4
    [1] 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1
  • 向量元素访问

    1
    2
    3
    4
    5
    > # 定义一个向量
    > t <- seq(from = 10, to = 20, by = 0.4)
    > t
    [1] 10.0 10.4 10.8 11.2 11.6 12.0 12.4 12.8 13.2 13.6 14.0 14.4 14.8 15.2 15.6 16.0 16.4 16.8 17.2 17.6 18.0 18.4 18.8
    [24] 19.2 19.6 20.0
    • t[index]:使用这种方式进行访问,index 默认从 1 开始。
    1
    2
    3
    4
    5
    > # 方式 1
    > t[1]
    [1] 10
    > t[2]
    [1] 10.4
    • t[Logic Index]TRUE 表示读取,FALSE 为不读取。
    1
    2
    3
    4
    5
    6
    7
    > # 方式 2
    > t[c(TRUE, TRUE, FALSE, TRUE)]
    [1] 10.0 10.4 11.2 11.6 12.0 12.8 13.2 13.6 14.4 14.8 15.2 16.0 16.4 16.8 17.6 18.0 18.4 19.2 19.6 20.0
    > t[c(1, 0, 2, 0, 3, 0, 1)]
    [1] 10.0 10.4 10.8 10.0
    > t[c(6, 0, 0, 0, 1, 0, 1)]
    [1] 12 10 10
    • t[name]:通过 name 进行访问。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    > # 方式 3
    > names(t) <- c("v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", "v10")
    > t
    v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
    10.0 10.4 10.8 11.2 11.6 12.0 12.4 12.8 13.2 13.6 14.0 14.4 14.8 15.2 15.6 16.0 16.4 16.8 17.2 17.6 18.0 18.4 18.8
    <NA> <NA> <NA>
    19.2 19.6 20.0
    > t["v1"]
    v1
    10
    • t[-index]:索引为负数,则会删除该位置的元素。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    > # 方式 4
    > t
    v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
    10.0 10.4 10.8 11.2 11.6 12.0 12.4 12.8 13.2 13.6 14.0 14.4 14.8 15.2 15.6 16.0 16.4 16.8 17.2 17.6 18.0 18.4 18.8
    <NA> <NA> <NA>
    19.2 19.6 20.0
    > t[-1]
    v2 v3 v4 v5 v6 v7 v8 v9 v10 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
    10.4 10.8 11.2 11.6 12.0 12.4 12.8 13.2 13.6 14.0 14.4 14.8 15.2 15.6 16.0 16.4 16.8 17.2 17.6 18.0 18.4 18.8 19.2
    <NA> <NA>
    19.6 20.0
    > t[c(-2: -4)]
    v1 v5 v6 v7 v8 v9 v10 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
    10.0 11.6 12.0 12.4 12.8 13.2 13.6 14.0 14.4 14.8 15.2 15.6 16.0 16.4 16.8 17.2 17.6 18.0 18.4 18.8 19.2 19.6 20.0
  • 向量元素运算

    • 长度相同的向量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    > # 长度不同的向量
    > v3 = c(10, 20)
    > v4 = c(10, 20, 10, 20, 10, 20)
    > add.result = v1 + v3
    > add.result = v1 + v4
    > # 向量的运算
    > # 长度相同的向量
    > v1 = c(1, 2, 3, 4, 5, 6)
    > v2 = c(6, 7, 8, 9, 10, 11)

    > add.result = v1 + v2
    > sub.result = v1 - v2

    > add.result
    [1] 7 9 11 13 15 17
    > sub.result
    [1] -5 -5 -5 -5 -5 -5
    • 长度不同的向量

      如果长度不同,较短的向量会循环补充到与较长的向量长度相同,在进行运算。但是前提是:长的向量的长度必须为短的向量的整数倍。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    > # 长度不同的向量
    > v3 = c(10, 20)
    > v4 = c(10, 20, 10, 20, 10, 20)
    > add.result = v1 + v3
    > add.result
    [1] 11 22 13 24 15 26
    > add.result = v1 + v4
    > add.result
    [1] 11 22 13 24 15 26
2.8.2 列表 list
  • 概念:列表是 R 语言的对象集合,可以用来保存不同类型的数据,可以是数字、字符串、向量、另一个列表等,当然还可以包含矩阵和函数。R语言创建列表使用 list() 函数。
  • 列表的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> # 列表的创建
> list_data = list("Hello", "World", c(1, 10), TRUE, 51.23, 119L)
> list_data
[[1]]
[1] "Hello"

[[2]]
[1] "World"

[[3]]
[1] 1 10

[[4]]
[1] TRUE

[[5]]
[1] 51.23

[[6]]
[1] 119
  • 列表元素的重命名(names 函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
> # 列表的重命名
> list_data <- list(c("Hello", "World"), matrix(c(1, 2, 3, 4, 5, 6), nrow=2), list("Happy", 1314L))
> list_data
[[1]]
[1] "Hello" "World"

[[2]]
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6

[[3]]
[[3]][[1]]
[1] "Happy"

[[3]][[2]]
[1] 1314

> names(list_data) <- c("HelloString", "MatrixElem", "ListElem")
> list_data
$HelloString
[1] "Hello" "World"

$MatrixElem
[,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6

$ListElem
$ListElem[[1]]
[1] "Happy"

$ListElem[[2]]
[1] 1314
  • 列表元素的访问——增删改查

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    > # (查)
    > # 根据索引访问
    > list_data[0]
    named list()
    > list_data[3]
    $ListElem
    $ListElem[[1]]
    [1] "Happy"

    $ListElem[[2]]
    [1] 1314

    > # 根据元素名称访问
    > list_data$HelloString
    [1] "Hello" "World"
    > list_data$ListElem
    [[1]]
    [1] "Happy"

    [[2]]
    [1] 1314

    > list_data[4]<-"Hello"
    > list_data
    $HelloString
    [1] "Hello" "World"

    $MatrixElem
    [,1] [,2] [,3]
    [1,] 1 3 5
    [2,] 2 4 6

    $ListElem
    $ListElem[[1]]
    [1] "Happy"

    $ListElem[[2]]
    [1] 1314

    [[4]]
    [1] "Hello"
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    > # (增)
    > list_data[4]<-"Hello"
    > list_data
    $HelloString
    [1] "Hello" "World"

    $MatrixElem
    [,1] [,2] [,3]
    [1,] 1 3 5
    [2,] 2 4 6

    $ListElem
    $ListElem[[1]]
    [1] "Happy"

    $ListElem[[2]]
    [1] 1314


    [[4]]
    [1] "Hello"
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    > # (改)
    > list_data[4] <- 12
    > list_data
    $HelloString
    [1] "Hello" "World"

    $MatrixElem
    [,1] [,2] [,3]
    [1,] 1 3 5
    [2,] 2 4 6

    $ListElem
    $ListElem[[1]]
    [1] "Happy"

    $ListElem[[2]]
    [1] 1314

    [[4]]
    [1] 12
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    > # (删)
    > list_data[4] <- NULL
    > list_data
    $HelloString
    [1] "Hello" "World"

    $MatrixElem
    [,1] [,2] [,3]
    [1,] 1 3 5
    [2,] 2 4 6

    $ListElem
    $ListElem[[1]]
    [1] "Happy"

    $ListElem[[2]]
    [1] 1314
  • 列表的合并:使用 c 可以将多个列表合并为一个列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
> # 合并列表
> list1 <- list(1, 2, 3)
> list2 <- list("A", "B", "C")
> class(list1)
[1] "list"
> class(list2)
[1] "list"
> merged.list <- c(list1, list2)
> merged.list
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[1] 3

[[4]]
[1] "A"

[[5]]
[1] "B"

[[6]]
[1] "C"
> class(merged.list)
[1] "list"
  • 列表和向量之间的转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
> # 列表转换为向量
> list1 <- list(1 : 5)
> list2 <- list(10: 14)
> list3 <- list("hello", matrix(c(1, 2, 3, 4), nrow=2), 3)
> list1
[[1]]
[1] 1 2 3 4 5

> list2
[[1]]
[1] 10 11 12 13 14

> list3
[[1]]
[1] "hello"

[[2]]
[,1] [,2]
[1,] 1 3
[2,] 2 4

[[3]]
[1] 3

> v1 <- unlist(list1)
> v2 <- unlist(list2)
> v3 <- unlist(list3)
> class(v1)
[1] "integer"
> class(v2)
[1] "integer"
> class(v3)
[1] "character"
> v3
[1] "hello" "1" "2" "3" "4" "3"
2.8.3 矩阵 matrix
  • 概念:R 语言为线性代数的研究提供了矩阵类型,这种数据结构很类似于其它语言中的二维数组。
  • 矩阵的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
> # 矩阵的创建
> m1 = matrix(data=c(3: 14))
> m1
[,1]
[1,] 3
[2,] 4
[3,] 5
[4,] 6
[5,] 7
[6,] 8
[7,] 9
[8,] 10
[9,] 11
[10,] 12
[11,] 13
[12,] 14
> m2 = matrix(data=c(3: 14), nrow=4)
> m2
[,1] [,2] [,3]
[1,] 3 7 11
[2,] 4 8 12
[3,] 5 9 13
[4,] 6 10 14
> m2 = matrix(data=c(3: 14), ncol=4)
> m2
[,1] [,2] [,3] [,4]
[1,] 3 6 9 12
[2,] 4 7 10 13
[3,] 5 8 11 14
> m3 <- matrix(data=c(3, 14), nrow=4, byrow=TRUE) # 默认为 TRUE
> m3
[,1]
[1,] 3
[2,] 14
[3,] 3
[4,] 14

> rownames <- c("row1", "row2", "row3", "row4")
> colnames <- c("col1", "col2", "col3")
> m4 <- matrix(data=c(3: 14), nrow=4, byrow=TRUE, dimnames=list(rownames, colnames))
> m4
col1 col2 col3
row1 3 4 5
row2 6 7 8
row3 9 10 11
row4 12 13 14
  • 矩阵转置
1
2
3
4
5
6
7
> # 矩阵转置 t()
> # R 语言矩阵提供了 t() 函数, 可以实现矩阵的行列转换
> t(m4)
row1 row2 row3 row4
col1 3 6 9 12
col2 4 7 10 13
col3 5 8 11 14
  • 矩阵元素访问
1
2
3
4
> m4[c("row1", "row4"), c("col1", "col3")]
col1 col3
row1 3 5
row4 12 14
  • 矩阵相关操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
> # 矩阵元素操作
> # 矩阵计算
> m1 <- matrix(data=c(3: 14), nrow=4)
> m1
[,1] [,2] [,3]
[1,] 3 7 11
[2,] 4 8 12
[3,] 5 9 13
[4,] 6 10 14
> m2 <- matrix(data=c(1: 12), nrow=4)
> m2
[,1] [,2] [,3]
[1,] 1 5 9
[2,] 2 6 10
[3,] 3 7 11
[4,] 4 8 12
> m1 + m2
[,1] [,2] [,3]
[1,] 4 12 20
[2,] 6 14 22
[3,] 8 16 24
[4,] 10 18 26

> # 矩阵转换
> # 矩阵 -> 向量
> is.matrix(m1)
[1] TRUE
> class(m1)
[1] "matrix" "array"
> v1 <- as.vector(m1)
> v1
[1] 3 4 5 6 7 8 9 10 11 12 13 14
> class(v1)
[1] "integer"

> # 矩阵组合
> # cbind() 把其自变量横向拼成一个大矩阵, 横向组合, 行数一致
> # rbind() 把其自变量纵向拼成一个大矩阵, 纵向组合, 列数一致
> v1 = c(1, 2, 3, 4, 5)
> v2 = c(6, 7, 8, 9,10)
> y1 = cbind(v1, v2)
> y2 = rbind(v1, v2)
> y1
v1 v2
[1,] 1 6
[2,] 2 7
[3,] 3 8
[4,] 4 9
[5,] 5 10
> y2
[,1] [,2] [,3] [,4] [,5]
v1 1 2 3 4 5
v2 6 7 8 9 10
>
2.8.4 数组 array
  • 概念:数组也是 R 语言的对象,R 语言可以创建一维或多维数组。
  • 数组的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
> # 创建数组
> # 一维数组
> v1 <- c(5, 9, 1)
> a1 <- array(v1)
> a1
[1] 5 9 1
> is.array(a1)
[1] TRUE
> a <- array(10: 15, dim=9)
> a
[1] 10 11 12 13 14 15 10 11 12
> a <- array(10: 15, dim=c(2))
> a
[1] 10 11
>
> # 二维数组
> a <- array(10: 15, dim=c(2, 3)) # 10, 11, 12, 13, 14, 15
> a
[,1] [,2] [,3]
[1,] 10 12 14
[2,] 11 13 15
> is.array(a)
[1] TRUE
>
> # 三维数组
> a <- array(10: 15, dim=c(2, 3, 2))
> a
, , 1
[,1] [,2] [,3]
[1,] 10 12 14
[2,] 11 13 15

, , 2
[,1] [,2] [,3]
[1,] 10 12 14
[2,] 11 13 15

> column.names <- c("COL1", "COL2", "COL3")
> row.names <- c("ROW1", "ROW2", "ROW3")
> matrix.names <- c("Matrix1", "Matrix2")
>
> vector1 <- c(5, 9, 3)
> vector2 <- c(10, 11, 12, 13, 14, 15)
> a <- array(c(vector1, vector2), dim=c(3, 3, 2), dimnames = list(row.names, column.names, matrix.names))
> a
, , Matrix1
COL1 COL2 COL3
ROW1 5 10 13
ROW2 9 11 14
ROW3 3 12 15

, , Matrix2
COL1 COL2 COL3
ROW1 5 10 13
ROW2 9 11 14
ROW3 3 12 15
  • 数组元素访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> # 数组访问
> a[1] # 一维数组的访问
[1] 5
> a[1: 6] # 一维数组的访问——向量访问
[1] 5 9 3 10 11 12
>
> dim2_a <- array(c(vector1, vector2), dim=c(3, 3))
> dim2_a
[,1] [,2] [,3]
[1,] 5 10 13
[2,] 9 11 14
[3,] 3 12 15
> dim2_a[1: 1] # 二维数组的访问, 访问的必须是二维数组
[1] 5
> dim2_a[1: 2, 1: 2] # 二维数组的访问——矩阵访问
[,1] [,2]
[1,] 5 10
[2,] 9 11
>
> a[3, 3, 1] # 三维数组
[1] 15
2.8.5 数据框 data.frame
  • 概念:数据框 DataFrame 可以理解成我们常说的表格,数据框是 R 语言的数据结构,是特殊的二维列表。数据框每一列都有一个唯一的列名,长度都是相等的,同一列的数据类型需要一致,不同列的数据类型可以不一样。数据框的特点:

    • 列名称应为非空。
    • 行名称应该是唯一的。
    • 存储在数据框中的数据可以是数字,字符型等。
    • 每个列应包含相同数量的数据项。
  • 数据框的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
> # 数据框
> # 创建数据框对象
> df1 <- data.frame(
+ # tag = value
+ 姓名 = c("张三", "李四", "王五"),
+ 工号 = c("001", "002", "003"),
+ 月薪 = c(1000, 2000, 3000)
+ )
> df1
姓名 工号 月薪
1 张三 001 1000
2 李四 002 2000
3 王五 003 3000
  • 查看数据框结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> # 查看数据框结构(Structure)
> str(df1)
'data.frame': 3 obs. of 3 variables:
$ 姓名: chr "张三" "李四" "王五"
$ 工号: chr "001" "002" "003"
$ 月薪: num 1000 2000 3000
> # 查看数据框描述性统计量
> summary(df1)
姓名 工号 月薪
Length:3 Length:3 Min. :1000
Class :character Class :character 1st Qu.:1500
Mode :character Mode :character Median :2000
Mean :2000
3rd Qu.:2500
Max. :3000
  • 数据框访问、新增列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
> # 提取数据框中的列
> df1$姓名
[1] "张三" "李四" "王五"
> df2 <- data.frame(df1$姓名, df1$月薪)
> df2
df1.姓名 df1.月薪
1 张三 1000
2 李四 2000
3 王五 3000
>
> # 提取数据框中的行
> # 提取前两行
> result <- df1[1:2, ]
> result
姓名 工号 月薪
1 张三 001 1000
2 李四 002 2000
>
> # 提取前两行, 前两列
> result <- df1[1:2, 1:3]
> result
姓名 工号 月薪
1 张三 001 1000
2 李四 002 2000
>
> # 提取第1、3行, 第1、2列的数据
> result <- df1[c(1, 3), c(1, 2)]
> result
姓名 工号
1 张三 001
3 王五 003
>
> # 新增列
> df1$部门 <- c("运营", "技术", "运营")
> df1
姓名 工号 月薪 部门
1 张三 001 1000 运营
2 李四 002 2000 技术
3 王五 003 3000 运营
  • 数据框合并
1
2
# 数据框合并
# cbind()/rbind()
  • 数据框筛选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
> # 数据框筛选
> student <- data.frame(
+ sno = c(1, 2, 3),
+ sname = c("zhangsan", "lisi", "wangwu"),
+ sage = c(10, 12, 14)
+ )
> student
sno sname sage
1 1 zhangsan 10
2 2 lisi 12
3 3 wangwu 14
>
> student[1] # 第一个字段
sno
1 1
2 2
3 3
> student[1, 2] # 第一行, 第二列
[1] "zhangsan"
> student$sage>12 # FALSE FALSE TRUE
[1] FALSE FALSE TRUE
>
> # [...] 里面一个参数取字段, 两个参数取行列
> student[student$sage>11] # 执行判断, 得到结果 c(FALSE, TRUE, TRUE)
sname sage
1 zhangsan 10
2 lisi 12
3 wangwu 14
> student[c(FALSE, TRUE, TRUE)] # 使用 c(FALSE, TRUE, TRUE) 进行运算
sname sage
1 zhangsan 10
2 lisi 12
3 wangwu 14
> student[student$sage>11, ] # 选择年龄大于 11 的, 并取所有字段
sno sname sage
2 2 lisi 12
3 3 wangwu 14
> student[c(FALSE, TRUE, TRUE), ] # 选择年龄大于 11 的行, 并取所有字段
sno sname sage
2 2 lisi 12
3 3 wangwu 14
> student[student$sage>11, 2] # 选择年龄大于 11 的, 并取第 2 个字段
[1] "lisi" "wangwu"
> student[student$sage>11, c(1, 2)] # 选择年龄大于 11 的, 并取第 1、2 个字段
sno sname
2 2 lisi
3 3 wangwu
>
> subset(student, select = c("sname", "sage")) # 可以理解为: SELECT sname, sage FROM student;
sname sage
1 zhangsan 10
2 lisi 12
3 wangwu 14
> subset(student, select = c("sname", "sage"), sage>11 & sno == 2) # 可以理解问: SELECT sname, sage FROM student WHERE sage>11 AND sno=2;
sname sage
2 lisi 12
2.8.6 因子 factor
  • 概念:因子用于存储不同类别的数据类型,例如:人的性别有 两个类别,年龄来分可以有 未成年人成年人
  • 因子创建
1
2
3
4
5
6
7
8
9
10
11
12
> # 因子创建
> # 例 1:男 2:女
> x <- c(1, 2, 1, 2, 2, 1, 3)
> factor(x)
[1] 1 2 1 2 2 1 3
Levels: 1 2 3
> factor(x, levels = c(1, 2))
[1] 1 2 1 2 2 1 <NA>
Levels: 1 2
> factor(x, levels = c(1, 2), labels = c("男", "女"))
[1] 男 女 男 女 女 男 <NA>
Levels: 男 女

2.9 R 语言判断、控制、循环

2.9.1 判断语句
  • if 语句
1
2
3
4
5
6
7
8
9
10
11
> # 控制语句
> x <- TRUE
> if (x) {
+ print("x 为真")
+ }
[1] "x 为真"
>
> if (is.logical(x)) {
+ print("x 是逻辑型数据")
+ }
[1] "x 是逻辑型数据"
  • if...else... 语句 和 if...else if... 语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> x <- 10L
> if (is.logical(x)) {
+ print("x 是逻辑型数据")
+ } else if(is.numeric(x)) {
+ print("x 是数值型数据")
+ } else {
+ print("x 不是逻辑型数据, 也不是数值型数据")
+ }
[1] "x 是数值型数据"
>
> y <- c("a", "b", "c", "e")
> if("e" %in% y) {
+ print("y 中包含 `e`")
+ } else if ("a" %in% y) {
+ print("y 中包含 `a`")
+ } else {
+ print("不存在")
+ }
[1] "y 中包含 `e`"
  • switch 语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
> # switch语句
> # x <- switch (object,
> # case = action
> # )
>
> # expression(object) 是一个常量表达式, 可以是整数或字符串
> # 如果是整数, 则返回对应 case 的位置值, 如果整数不在范围内则返回 NULL
> x <- switch (1,
+ "a",
+ "b",
+ "c",
+ "d"
+ )
> x
[1] "a"
>
> x <- switch (1,
+ "a" = "a1",
+ "b" = "b1",
+ "c" = "c1",
+ "d" = "d1"
+ )
> x
[1] "a1"
>
> # expression 如果是字符串, 则对应的是 case 中变量名对应的值, 没有匹配则没有返回值
> x <- switch ("c",
+ "a" = 1,
+ "b" = 2,
+ "c" = "cn"
+ )
>
> x
[1] "cn"
2.9.2 循环语句
  • repeat + if(...) { break }
1
2
3
4
5
6
7
8
9
10
11
12
13
> # 循环语句
> # repeat + if(...) { break }
> v <- c("Google", "Micrsoft", "OpenAI", "LinkIn")
> cnt <- 3
> repeat {
+ print(v[cnt])
+ cnt <- cnt + 1
+ if(cnt > 6) { break; }
+ }
[1] "OpenAI"
[1] "LinkIn"
[1] NA
[1] NA
  • while()
1
2
3
4
5
6
7
8
9
10
11
> 
> # while 循环
> v <- c("Google", "Micrsoft", "OpenAI", "LinkIn")
> cnt <- 3
> while(cnt <= 5) {
+ print(v[cnt])
+ cnt <- cnt + 1
+ }
[1] "OpenAI"
[1] "LinkIn"
[1] NA
  • for()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> # for 循环
> letters
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
> LETTERS
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
> LETTERS[1: 6]
[1] "A" "B" "C" "D" "E" "F"
> for (i in LETTERS[1: 6]) {
+ print(i)
+ }
[1] "A"
[1] "B"
[1] "C"
[1] "D"
[1] "E"
[1] "F"
2.9.3 控制语句
  • break
1
2
3
4
5
6
7
8
9
10
> # break语句
> for (i in LETTERS[1: 6]) {
+ if (i == "D") {
+ break;
+ }
+ print(i)
+ }
[1] "A"
[1] "B"
[1] "C"
  • next
1
2
3
4
5
6
7
8
9
10
11
12
> # next语句
> for (i in LETTERS[1: 6]) {
+ if (i == "D") {
+ next;
+ }
+ print(i)
+ }
[1] "A"
[1] "B"
[1] "C"
[1] "E"
[1] "F"

2.10 R 语言函数

概念:函数是一组一起执行一个任务的语句。

2.10.1 内置函数:
  • print()
  • cat()
  • list()
  • array()
2.10.2 自定义函数

R 语言函数通常由以下几个部分组成:

  • 函数名
  • 参数
  • 函数体
  • 返回值

具体格式如下:

1
2
3
4
5
function_name <- function(arg_1, arg_2, ...) {
# 函数体
# 执行的代码块
return(output) # 返回值
}

下面由一些例子来演示:

只演示几种情况,其他情况(如有参函数、有默认参数值的函数都和其他语言类似)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> # R 语言函数
> new.function <- function() {
+ # 函数体
+ for(i in 1: 6){
+ print(i^2)
+ }
+ }
>
> new.function()
[1] 1
[1] 4
[1] 9
[1] 16
[1] 25
[1] 36

2.11 R语言读写数据文件、数据库

2.11.1 读写数据文件
读写类型 读写方法 备注
从键盘输入数据 edit() 使用方法见下方代码
读写 .txt 格式文件 read.table
write.table
-
读写 .csv 格式文件 read.csv()
write.csv()
-
读写 .xlsx 格式文件 read.xlsx()
write.xlsx()
使用之前需要下载并加载 R语言的 xlsx
  1. 从键盘中输入数据
1
2
3
4
5
6
7
8
9
10
11
> # 从键盘读入数据, edit() 和 fix() 可以可视化的输入数据
> mydata <- data.frame(age=numeric(0), gender=character(0), weight=numeric(0))
> mydata
[1] age gender weight
<0> (0-长度的row.names)
> edit(mydata)
age gender weight
1 12 2 120
2 13 2 NA
>
> fix(mydata)
  1. 读写 .txt 格式文件
1
2
3
4
5
stutxt <- read.table("/path/to/file.txt", header=TRUE, sep=",")
stutxt
class(stutxt)

write.table(stutxt, "/path/to/write.txt", sep=",")
  1. 读写 .csv 格式文件
1
2
3
4
csvfile <- read.csv("/path/to/file.csv")
csvfile

write.csv(csvfile, "/path/to/write.csv")
  1. 读写 .xlsx 格式文件
1
2
3
4
5
6
7
8
9
10
11
12
install.packages("xlsx")
library()
search()
library("x1sx")

student <- read.xlsx("/path/to/file.xlsx", sheetIndex=1, encoding = "UTF-8")
student

city<-read.xlsx("/path/to/file.xlsx", sheetName = "city",encoding = "UTF-8")
city

write.xlsx(city, "/path/to/write.xlsx", sheetName = "city")
2.11.2 读写数据库

非重点,有需要可自行了解

2.12 R 语言基础绘图

2.12.1 R 语言绘制barplot() 条形图、堆叠条形图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
####### 1. 绘制条形图 barplot()
# 1.1 height: 高度, 通过这个参数可以指定要画多少个柱子, 以及每个柱子的高度, 其值有两种——向量、矩阵
# 1.1.1 向量 vector, 此时会根据向量的长度确定图中有多少个柱子, 向量中的每个值就是高度
barplot(height = 1: 5)

# 1.1.2 矩阵 matrix, 此时用于画堆积柱状图
data <- cbind(a = 1: 4, b = 1: 4)
data
class(data)
barplot(height = data)

# 1.2 width: 宽度, 控制每个柱子的宽度, 默认值为 1, 值得注意的是, 这个参数的值是可以循环使用的
barplot(height = 1: 5)
barplot(height = 1: 5, width=1)
barplot(height = 1: 5, width=c(1, 1, 1, 1, 1))
barplot(height = 1: 5, width=1: 5)

data <- cbind(a = 1: 4, b = 1:4)
barplot(height = data)
barplot(height = data, width=1)
barplot(height = data, width=1:2)

# 1.3 space: 间隔, 指定每个柱子左边的空白区域的宽度, 这个值为一个百分比, 默认值为 0.2
barplot(height=1: 5, space=1)
barplot(height=1: 5, space=5: 1)
barplot(height=data, space=1)
barplot(height=data, space=1: 2)

# 1.4 names.arg: 每个柱子下面的标记, 当 height 为 vector 时, 默认的标记为向量的 names 属性
barplot(height=1:3, names.arg=c("a","b","c"))
v <- 1:3
names(v) <- c("c" ,"b" ,"a")
v
barplot(height=v)

# 1.5 horiz: 逻辑值, 默认为 FALSE, 当值为 TRUE 时, 将 X 轴和 Y 轴转置
barplot(height=1:3, horiz = FALSE)
barplot(height=1:3, horiz = TRUE)
data

barplot(height=data, horiz= FALSE)
barplot(height=data, horiz = TRUE)

# 1.6 col: 柱子的填充色, 默认为灰色
# 1.7 border: 柱子边框的颜色, 默认为 black, 当 border为 NA时, 表示没有边框
# 1.8 main: 设置图形标题
c <- colors()
c
c[2: 5]

barplot(1: 4, col=c[2:5], border = NA, main="图形标题")

barplot(1: 4, col=rainbow(4))
barplot(1: 4, col=c("red", "green", "antiquewhite", "white"))
barplot(data, col=rainbow(4))

# 1.9 & 1.10 density 设置柱子用线条填充, density 控制线条的密度,angle 控制线条的角度
par(mfrow=c(1, 3))
par

barplot(rep(1, 4), density = 1, angle = 30, main = "density = 1")
barplot(rep(1, 4), density = 2, angle = 90, main = "density = 2")
barplot(rep(1, 4), density = 3, angle = 180, main = "density = 3")

# 1.11 axes: 逻辑值, 控制是否显示轴线(如 y 轴线)
barplot(data, axes = F)
barplot(data, axes = T)

# 1.12 axisnames: 控制是否显示柱子的标签
barplot(data, axisnames = F)
barplot(data, axisnames = T)


# 1.13 beside: 当输入参数为矩阵时, 默认是堆积柱状图, beside 默认值为 FALSE;
# beside = FALSE 时, 条形图的高度是矩阵的数值, 矩形条是水平堆叠的
# beside = TRUE 时, 条形图的高度是矩阵的数值, 矩形条是并列的
data
barplot(data, beside = F)
barplot(data, beside = T)

# 1.14 legend.text: 图例的文字说明
# 只有当 height 参数的值是 matrix 时, 才起作用
# 默认显示的是 matrix 的 rownames 属性
# 其值有两种指定形式:
# 第一种: 逻辑值, 是否需要显示图例, 默认为FALSE
par(mfrow=c(1, 1))
data
rownames(data) <- c("A1", "A2", "A3", "A4")
data
barplot(data, legend.text = TRUE)

# 第二种, 指定图例中的文字内容
# 相当于修改了 matrix 的 rownames 属性
barplot(data, legend.text = c("D", "E", "F", "G"))
2.12.2 R 语言绘制 饼图、3D饼图绘制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
####### 2. 绘制饼图
# 2.1 x: 数值向量, 表示每个扇形的面积
x <- c(21, 62, 10, 53)
pie(x)

# 2.2 labels: 字符型向量, 表示各扇形面积标签
x<-c(21, 62, 10, 53)
names(x) <- c("London", "New York", "singapore", "Mumbai")
x
pie(x)

x<-c(21, 62, 10, 53)
pie(x)
pie(x, labels = c("London", "New York", "singapore", "Mumbai"))

# 2.3 radius: 饼图的半径
x<-c(21, 62, 10, 53)
pie(x, labels = c("London", "New York", "singapore", "Mumbai"), radius = 1)

# 2.4 main: 饼图的标题
pie(x, labels = c("London", "New York", "singapore", "Mumbai"), radius =1, main="饼图")

# 2.5 clockwise: 一个逻辑值, 用来指定饼图各个切片是否按顺时针做出分割
pie(x, labels = c("London", "New York", "singapore", "Mumbai"), radius =1, main="饼图", clockwise = TRUE)

# 2.6 & 2.7
# angle: 设置底纹的斜率
# density: 底纹的密度, 默认值为NULL
pie(x, labels = c("London", "New York", "singapore", "Mumbai"), radius =1, main="饼图", clockwise = TRUE, density = 10, angle = 20)

# 2.8 col: 每个扇面的颜色, 相当于调色板
pie(x, labels = c("London", "New York", "singapore", "Mumbai"),
radius =1, main="饼图", clockwise = TRUE,
density = 10, angle = 20, col = rainbow(length(x)))

pie(x, labels = c("London", "New York", "singapore", "Mumbai"),
radius =1, main="饼图", clockwise = TRUE,
col = rainbow(length(x)))

# 2.9 设置百分数
piepercent <- round(x/sum(x) * 100, 1)
pie(x, piepercent, radius=0.5, main="饼图", clockwise= TRUE, col=rainbow(length(x)))

# 2.10 设置图例
legend("topright", c("London", "New York", "Singapore", "Mumbai"),
fill = rainbow(length(x)), cex = 0.8)

legend("topright", c("London", "New York", "Singapore", "Mumbai"),
fill = rainbow(length(x)), horiz = TRUE, cex = 0.5)

# 2.11 绘制 3D 饼图
install.packages("plotrix")
library("plotrix")
lbl <- c("London", "New York", "Singapore", "Mumbai")
pie3D(x, labels = lbl, explode = 0.1, main = "城市 3D 饼图")
2.12.3 R 语言散点图、折线图、散点矩阵图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
####### 3. 绘制散点图、折线图、散点矩阵图
# 3.1 绘制散点图 plot()
# x: 横坐标, x轴的数据集合
# y: 纵坐标, y轴的数据集合
# type: 绘图的类型, type="p" 为点(默认);
# type="l" 在图形中数据显示为线;
# type="b" 在图形中数据显示为点和连接线;
# type="o" 在图形中数据点覆盖在线上;
# type="h" 在图形中数据显示为从点到x轴的垂直线;
# type="s" 在图形中数据显示为阶梯图;
# type="n" 在图形中数据不显示
# main: 图表标题
# xlab/ylab: x轴/y轴的标签名称
# xlim/ylim: x轴/y轴的范围
# axes: 布尔值, 是否绘制两个坐标轴
# pch: 点的样式由 pch 的取值决定
# cex: 表示相对于默认大小缩放倍数的数值。默认大小为1, 1.5表示放大为默认值的1.5倍; 0.5表示做小为默认大小的0.5倍;

cars # 加载车辆时速-刹车距离数据集
cars$speed # 查看车辆时速
cars$dist # 查看车辆刹车距离

plot(cars$speed, cars$dist)
plot(cars[, 1], cars[, 2])
plot(cars)

plot(cars, main="车辆时速-刹车距离散点图", xlab="车速", ylab="刹车距离",
col="red", pch=4, cex=1)

with(classData, plot(height, weight))

# 3.2 绘制折线图 plot()
# type: 绘图的类型, type="p" 为点(默认);
# type="l" 在图形中数据显示为线;
# type="b" 在图形中数据显示为点和连接线;
# type="o" 在图形中数据点覆盖在线上;
# type="h" 在图形中数据显示为从点到 x 轴的垂直线(直线条形图);
# type="s" 在图形中数据显示为阶梯图;
# type="c" 在图形中数据虚线图;
# type="n" 在图形中数据不显示
plot(cars$speed, cars$dist, type="l")
plot(cars$speed, cars$dist, type="b")
plot(cars$speed, cars$dist, type="o")

# plot 可以配合lines() 函数绘制出多条折线图
# lines() 函数必须与 plot() 函数配合才能使用, 先用 plot() 函数画出一个图形
# round(x, digits = 0) 舍入函数, x 为数值向量, digits表示有效位数, 可以为负数, 负数表示舍入十位(-1)、百位(-2)...等
x <- 1: 10
beijing <- round(rnorm(10, mean = 20, sd = 2), 2)
beijing
shanghai <- round(rnorm(10, mean = 23, sd = 3), 2)
shanghai
guangzhou <- round(rnorm(10, mean = 18, sd = 1), 2)
guangzhou

plot(x, beijing, type = "l", ylim = c(16, 30), lwd=2, main = "北上广近十天气温变化趋势图")
lines(x, shanghai, type = "b", col="blue", lwd=2)
lines(x, guangzhou, type = "l", col="red", lwd=2)

# 用 plot() 会单独绘制一张新图
plot(x, shanghai, type = "l", ylim = c(16, 30), lwd=2, main = "北上广近十天气温变化趋势图")
plot(x, guangzhou, type = "l", ylim = c(16, 30), lwd=2, main = "北上广近十天气温变化趋势图")

# 3.3 绘制散点矩阵图
mtcars # 加载 mtcars 数据集, 数据来自1974年美国汽车趋势杂志, 包括32辆汽车(1973-74款) 的油耗和10个方面的汽车设计和性能
# mpg(Miles Per Gallon): 油耗(每加仑英里(美国)), 功能更强大, 更重的汽车往往消耗更多的燃油
# cyl(cylinders): 汽缸数, 功率更大的汽车通常具有更多的汽缸
# disp(displacement, in cubic inches): 排量(立方英寸), 发动机气缸的总容积
# hp(horsepower): 总马力:这是汽车产生的功率的量度
# drat:driveshaft ratio:后轴比率:这描述了驱动轴的转动与车轮的转动如何对应。较高的值会降低燃油效率。
# wt(weight): 重量(1000磅)
# qsec(1/4 mile time; a measure of acceleration): 1/4英里时间:汽车的速度和加速度
# vs(‘V’ or straight - engine shape): 发动机缸体, 表示车辆的发动机形状是"V" 形还是更常见的直形。
# am(transmission; auto or manual): 变速箱, 这表示汽车的变速箱是自动(0)还是手动(1)。
# gear(abbreviation of gears): 前进挡的数量, 跑车往往具有更多的挡位
# carb(abbreviation of carburetors): 化油器数量, 与更强大的发动机相关

pairs(mtcars[, c("wt", "mpg", "disp", "cyl")])
pairs(vs~wt + mpg + disp + cyl, data=mtcars)
2.12.4 R 语言绘制直方图
1

2.12.5 R 语言绘制核密度图
1

2.12.6 R 语言绘制箱线图
1

2.12.7 R 语言小提琴图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
####### 7. 小提琴图 
# vioplot 包中的 vioplot 函数用于绘制小提琴图, 小提琴图是核密度图与箱线图的结合
# install.packages("vioplot")
# install.packages("vioplot")
library("vioplot")

# `==` 意为比较等号两端是否相等
mpg4 <- mtcars$mpg[mtcars$cyl == 4]
mpg4
mpg6 <- mtcars$mpg[mtcars$cyl == 6]
mpg6
mpg8 <- mtcars$mpg[mtcars$cyl == 8]
mpg8

vioplot(mpg4, mpg6, mpg8, names=c("4-cyl", "6-cyl", "8-cyl"), col="gold")
title("四/六/八缸 车辆的油耗 (英里/加仑)", ylab="Miles Per Gallon", xlab="Num Of Cylinders")

# 图片的保存
pdf(file="fig-vioplot.pdf", height = 10, width = 10, family = "GB1")

vioplot(mpg~cyl, data=mtcars, xlab="气缸数", ylab="加仑每英里", main="里程数据", names=c("4-cyl", "6-cyl", "8-cyl"))
dev.off # 关闭图像设备, 我们经常使用图像设备来创建和保存图形输出
# 当我们完成图形绘制并且不再需要将图形输出到设备上时, 我们需要关闭图像设备以释放系统资源并确保图形正确保存
# 在 R 中, 我们可以使用 dev.off() 函数来关闭当前活动的图像设备

2.13 R 绘图常用拓展库 ggplot2 绘图

简介:ggplot2是一款强大的图形可视化R包,其作图方式易于理解,且生成的图形精美,定制化程度也很高,是 R 语言中很流行的可视化工具之一。

ggplot2 包是 R 的一个作图用的扩展包,它实现了 图形的语法,将一个作图任务分成若干子任务,只需要完成各个子任务就可以完成作图。在绘制常用图形时,只需要两个步骤:

  1. 将图形所展现的数据输入到 ggplot() 函数中

  2. 调用某个 geom_xxx() 函数,指定图形类型,如散点图、面积图、曲线图、盒型图等。geom_xxx() 提供了各种基本图形,如geom_blank() 不绘制图形、geom_point() 每个观测为一个散点、geom_hline(), geom_vline(), geom_abline() 绘制线等。

ggplot2 的作图的一般步骤为:

  1. 准备数据:一般为数据框,且一般为长表,即每个观测时间占一行,每个观测变量占一列
  2. 输入数据:将数据输入到 ggplot() 函数中,并指定参与作图的每个变量分别映射到哪些图形特性
  3. 选择图形类型:选择一个合适的图形类型,函数名以 geom_ 开头,如 geom_point() 表示散点图。
  4. 设定标题和图例位置等,如 labs(),仍用加号连接。

模板如下:

1
2
3
4
5
6
7
8
p <- ggplot(data = <输入数据框>, 
mapping = aes(<维度>=<变量名>,
<维度>=<变量名>,
<...>=<...>))
p + geom_<图形类型>(<...>) +
scala_<映射>_<类型>(<...>) +
coord_<类型>(<...>) +
labs(<...>)
2.13.1 ggplot2 绘制散点图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
####### ggplot2 绘图
# install.packages("ggplot2") # 安装 ggplot2 绘图库
# install.packages("gapminder") # 安装 gapminder 数据集

library("ggplot2") # 绘图包
library("gapminder") # 数据集包

gapminder

dev.new() # 打开绘图设备
p <- ggplot(data = gapminder,
mapping = aes(
x = gdpPercap,
y = lifeExp))

p <- ggplot(data = gapminder,
aes(gdpPercap, lifeExp))

p + geom_point() # 散点图
p + geom_smooth() # 拟合曲线图
p + geom_point() + geom_smooth()
dev.off() # 关闭绘图设备
2.13.2 ggplot2 绘制面积图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 面积图
# 定义数据框 data.frame
df <- data.frame(
year=c(1700, 1701, 1702, 1703, 1704,
1705, 1706, 1707, 1708, 1709,
1710, 1711, 1712, 1713, 1714,
1715, 1716),
sunspots=c( 5.0, 11.0, 16.0, 23.0, 36.0,
58.0, 29.0, 20.0, 10.0, 8.0,
3.0, 0.0, 0.0, 2.0, 11.0,
27.0, 47.0)
)

df

p <- ggplot(data = df, mapping = aes(year, sunspots))
p + geom_area(fill = "blue", alpha = 0.2, col = "red")
2.13.3 ggplot2 绘制堆叠面积图
1

🎫 3. ECharts - ECharts的简单使用

3.1 ECharts 简介

  • ECharts 概念:一个基于 JavaScript 的开源可视化图表库(别人写好了 .js,我直接使用),可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE 9/10/11ChromeFirefoxSafari 等),底层依赖矢量图形库 *ZRender*,提供直观,交互丰富,可高度个性化定制的数据可视化图表。
  • 官网:ECharts
  • 特性:ECharts Feature
  • 获取 Apache ECharts
    • 通过 GitHub 获取

      • Apache/ECharts 项目的 release 页面可以找到各个版本的链接。点击下载页面下方 Assets 中的 Source code,解压后即为包含完整 ECharts 功能的文件。
      • echarts-5.4.0/echarts-5.4.0/dist/echarts.common.js echarts-5.4.0/echarts-5.4.0/dist/echarts.common.min.js:体积适中,常用版,支持常见的图表和组件
      • echarts-5.4.0/echarts-5.4.0/dist/echarts.jsecharts-5.4.0/echarts-5.4.0/dist/echarts.min.js:体积最大,完整版,包含所有支持的图表和组件。
      • echarts-5.4.0/echarts-5.4.0/dist/echarts.simple.jsecharts-5.4.0/echarts-5.4.0/dist/echarts.simple.min.js:体积较小,精简版,包含最常用的图表和组件。
    • npm 获取

      • npm install echarts
    • CDN 获取

      • <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
    • 在线定制

3.2 第一个 ECharts 程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>第一个 ECharts 程序</title>
<!-- 1. 引入 ECharts.js 类库文件 -->
<!-- <script type="text/javascript" src="js/echarts.js"></script> -->
<script src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>

<script>
window.onload=function() {
// 3. 实例化 ECharts 对象
var dom = document.getElementById("main");
var myChart = echarts.init(dom);

// 4. 指定图表的配置项和数据
var option = {
title: {
text: "ECharts 入门案例"
},
tooltip: { },
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '帽子', '高跟鞋', '袜子']
},
yAxis: { },
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 26, 10, 20, 8]
}]
};

// 5. 使用刚指定的配置项和数据显示图表
myChart.setOption(option);
}
</script>
</head>

<body>
<!-- 2. 准备一个放图表的容器 -->
<div id="main" style="width: 900px; height: 400px"></div>
</body>
</html>

3.3 ECharts 绘图流程

  1. 新建一个 HTML 文件,并在网页头部 <head> ... </head> 部分引入在线的 ECharts 类库或者本地已经下载好的类库。
1
2
3
4
5
6
<head>
<meta charset="utf-8" />
<!-- 引入在线的 ECharts 类库或者本地已经下载好的类库 -->
<!-- <script type="text/javascript" src="js/echarts.js"></script> -->
<script src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
</head>
  1. 在网页创建一个容器,为 ECharts 准备一个具备大小(宽高)的 DOM (Document Object Model, 文件对象模型 )
1
2
3
4
<body>
<!-- 为 ECharts 准备一个具备大小(宽高)的 DOM -->
<div id="main" style="width: 600px; height: 400px"></div>
</body>
  1. 当页面加载时,获取 DOM,并初始化 ECharts 对象
1
2
3
4
5
6
<script>
window.onload=function() {
var dom = document.getElementById("main");
var myChart = echarts.init(dom);
}
</script>
  1. 指定配置项信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
window.onload=function() {
// 省略上一步的操作
var options = {
title: {
text: "ECharts 入门案例"
},
tooltip: { },
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: { },
series: [
{name:'销量', type: 'bar', data: [5, 20, 36, 10, 10, 20]}
]
}
}
</script>
  1. 用刚指定的配置项和数据显示图表
1
2
3
4
5
6
<script>
window.onload=function() {
// 省略上一步的操作...
myChart.setOption(option);
}
</script>

3.4 ECharts 常用配置项详解

完成简单柱状图的绘制需要包含的组件包括:

  • xAxis:X 轴组件
  • yAxis:Y 轴组件
  • series:系列组件(我更喜欢称其序列组件)

完善图形补充组件:

  • title:标题组件
  • legend:图例组件
  • tooltip:提示框组件
  • toolbox:工具栏配置项
  • datazoom:数据区域缩放

3.5 ECharts 样式设置

3.5.1 ECharts 颜色主题(Theme)
  • 内置颜色主题(darklight
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>样式设置</title>
<!-- <script type="text/javascript" src="js/echarts.min.js"></script> -->
<script type="text/javascript" src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div id="main" class="main" style="height=900px; width:400px"></div>
<script>
window.onload=function() {
var dom = document.getElementById("main");
// 内置颜色主题
// var myCharts = echarts.init(dom, "light");
var myCharts = echarts.init(dom, "dark");
var options = {
title: {
text: "ECharts 入门案例"
},
tooltip: { },
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: { },
series: [
{name:'销量', type: 'bar', data: [5, 20, 36, 10, 10, 20]}
]
}
myCharts.setOption(options);
}
</script>
</body>
</html>
  • 自定义颜色主题

    • 通过 JavaScript 定义颜色主题
    1
      
    • 通过 Json + jQuery 定义颜色主题
    1
      
3.5.2 ECharts 调色盘
  • 全局调色盘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ECharts 全局调色盘</title>
<!-- <script type="text/javascript" src="js/echarts.min.js"></script> -->
<script type="text/javascript" src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div id="main" class="main" style="width: 900px; height: 400px"></div>
<script>
window.onload=function() {
var dom = document.getElementById('main'); // 获取到 DOM
var myCharts = echarts.init(dom); // 初始化 ECharts对象
// 定义选项
var options = {
// 颜色组件——全局调色盘
color: ["red", "blue", "yellow"],
xAxis: {
show: true,
position: "bottom",
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
type: 'value',
max: 50
},
// 系列组件
series: [
{ type: "bar", name: "销量1", data: [5, 20, 36, 10, 10, 20] },
{ type: "line", name: "销量2", data: [4, 30, 26, 14, 8, 15] }
]
}
myCharts.setOption(options);
}
</script>
</body>
</html>
  • 系列调色盘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ECharts 系列调色盘</title>
<!-- <script type="text/javascript" src="js/echarts.min.js"></script> -->
<script type="text/javascript" src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div id="main" class="main" style="width: 900px; height: 400px"></div>
<script>
window.onload=function() {
var dom = document.getElementById('main'); // 获取到 DOM
var myCharts = echarts.init(dom); // 初始化 ECharts对象
// 定义选项
var options = {
// 颜色组件——全局调色盘
color: ["red", "blue", "yellow"],
xAxis: {
show: true,
position: "bottom",
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
type: 'value',
max: 50
},
// 系列组件
series: [
{ type: "bar", name: "销量1", data: [5, 20, 36, 10, 10, 20], color: ["red", "green"] },
{ type: "line", name: "销量2", data: [4, 30, 26, 14, 8, 15] }
]
}
myCharts.setOption(options);
}
</script>
</body>
</html>
  • 系列调色盘与全局调色盘同时出现:
    • 当系列调色盘与全局调色盘同时出现时,图表使用系列调色盘中配置的颜色。
3.5.3 ECharts 直接样式设置

直接样式设置(itemStylelineStyleareaStylelabel、…)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ECharts 全局调色盘</title>
<!-- <script type="text/javascript" src="js/echarts.min.js"></script> -->
<script type="text/javascript" src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div id="main" class="main" style="width:900px; height: 400px;"></div>
<script>
window.onload=function() {
var dom = document.getElementById("main");
var myChart = echarts.init(dom);
var options = {
xAxis: {
show: true,
position: 'bottom',
type: 'category',
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {
type: 'value',
max: 50
},
series: [
{
type: 'scatter',
name: '销量1',
data: [5, 20, 36, 10, 10, 20],
symbol: 'triangle',
symbolSize:20,
itemStyle: {
color: 'red',
borderWidth: 2,
borderColor: 'blue',
},
emphasis: {
itemStyle: {
color: 'green'
},
label: {
show: true,
formmatter: "{a}\n{b}{c}", // 这是一个 label
position: 'right'
}
}
},{
type: 'scatter',
name: '销量2',
data: [4, 30, 26, 14, 8, 15]
}
],
title: {
show: true,
text: '主标题-点击访问百度',
link: 'https//www.baidu.com/',
target: 'blank', // 或者 self 也可以
subtext: '副标题-点击访问个人网站',
sublink: 'http://hello-nilera.com/',
subtarget
textStyle: {
color: 'red',
fontSize: 30,
fontStyle: 'italic',
fontWeight: 'normal'
}
}
}

myChart.setOption(options);
}
</script>
</body>
</html>
3.5.4 ECharts 视觉映射(Visual Map)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>样式设置——视觉映射组件</title>
<!-- <script type="text/javascript" src="js/echarts.min.js"></script> -->
<script type="text/javascript" src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div id="main" class="main" style="width: 900px; height: 400px"></div>
<script>
window.onload=function() {
var dom = document.getElementById('main');
var myChart = echarts.init(dom);
var options = {
// 连续型视觉映射组件
/*
visualMap: [
{
type: 'continuous',
min: 0,
max: 40,
range: [4, 15],
calculable: true
}
],
*/
// 分段型视觉映射组件
visualMap: [{
type: 'piecewise',
min: 0,
max: 40
}],
title: {
text: 'ECharts 视觉映射组件'
},
tooltip: { },
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: { },
series: [
{
name: '销量 1',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
},{
name: '销量 2',
type: 'bar',
data: [8, 12, 30, 15, 18, 26]
}
]
}
myChart.setOption(options);
}
</script>
</body>
</html>

3.6 ECharts 中的事件和行为

概念:Apache ECharts 的图表中用户的操作将会触发相应的事件。开发者可以监听这些事件,然后通过回调函数做相应的处理。

事件分类:ECharts 中事件分为两种类型,一种是用户鼠标操作点击,或者 hover 图表的图形时触发的事件,还有一种是用户在使用可以交互的组件后触发的行为事件,例如在切换图例开关时触发的 legendselectchanged 事件(这里需要注意切换图例开关是不会触发事件的),数据区域缩放时触发的 datazoom 事件等等。

3.7 ECharts 实例

3.7.1 柱状图

绘制简单的柱状图,一些配置项:

  • xAxis/yAxis
    • typecategory 类目轴;value 数值轴;time 时间;log 对数轴
    • data:设置类目的数据(数组)
  • series
    • type:设置系列的类型(设置绘制什么图表)
    • name:设置系列名称,会用在 tooltiplegend
    • data:系列中的数据内容数组,定义当前这个系列中每个柱子的高度
    • label:设置图形上的文本标签(把数值显示在每个柱子上)
    • itemStyle:设置图形样式
    • showBackground:设置是否显示背景色(默认不显示)
    • backgroundStyle:设置背景色
3.7.2 折线图、堆叠折线图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- <script type="text/javascript" src="js/echarts.min.js"></script> -->
<script type="text/javascript" src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div id="main" class="main" style="width: 900px; height: 400px"></div>
<script>
window.onload=function() {
var dom = document.getElementById("main");
var myCharts = echarts.init(dom);
var options = {
xAxis: {
type: 'category',
data: ['周一', '周二', '周三', '周四', '周五']
},
yAxis: {
type: 'value'
},
series: [
{ type: 'line', data: [10, 20, 30, 20, 10]}
],
toolbox: {
feature: {
show: true,
dataZoom: { // 数据区域缩放
// 框选型数据区域缩放组件(dataZoomSelect): 提供一个选框进行区域缩放。
// 即 toolbox.feature.dataZoom
yAxisIndex: 'none'
},
dataView: {
readOnly: true
},
magicType: {
type: ['line', 'bar']
},
restore: { // 配置项还原
},
saveAsImage: {
type: 'jpg'
}
}
}
}
myCharts.setOption(options);
}
</script>
</body>
</html>
3.7.3 面积图和堆叠面积图
3.7.4 雷达图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<!-- <script type="text/javascript" src="js/echarts.min.js"></script> -->
<script type="text/javascript" src="http://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
<script type="text/javascript" src="http://cdn.staticfile.org/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<div id="main" class="main" style="width:900px; height: 400px"></div>
<script>
var dom = document.getElementById("main");
var myCharts = echarts.init(dom);
var options = {
radar: {
indicator: [
{ name: '身高' },
{ name: '体重' },
{ name: '年龄' },
{ name: '跑步速度' }
],
},
series: [
{
type: 'radar',
data: [
{ name: '训练前', value: [178, 60, 20, 50] },
{ name: '训练后', value: [178, 58, 20, 52] },
{ name: '训练一年后', value: [180, 55, 21, 55]}
]
},
],
tooltip: {

},
toolbox: {
show:true,
feature:{
saveAsImage:{
show:true
}
}
},
}

myCharts.setOption(options);
</script>
</body>
</html>

HBaseExamReview

HBase 考察知识点复习

知识点(选择)

  1. Region的分裂

    HBase中,Region 是数据管理的基本单位,类似于关系型数据库中的分区。它是 HBase 数据存储和组织的核心概念之一。
    Region 的概念和特点:
    RegionHBase 数据管理的基本单位,它负责存储一定范围内的数据。每个 Region 由一个起始 RowKey 和 终止 RowKey 定义,负责存储该范围内的数据。与关系型数据库中的分区类似,HBase 中的 Region 可以根据数据量的大小进行动态调整。当一个Region 的数据量过大时,它会被 分裂 成两个新的 Region。相反,当数据量较小时,两个或多个 Region 可能会合并成一个新的Region

    MemStore 的数据超过阈值时,将数据溢写磁盘,生成一个 StoreFile 文件。当 Region 中最大Store 的大小超过阈值时,Region 分裂,等分成两个 Region,实现数据访问的负载均衡。新的 Region 的位置由 HMaster 来确定在哪个RegionServer 中。

  2. 查看表结构的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[subowen@bigdata ~]$ hbase shell
2024-07-05 23:04:35,655 WARN [main] util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
HBase Shell
Use "help" to get list of supported commands.
Use "exit" to quit this interactive shell.
For Reference, please visit: http://hbase.apache.org/2.0/book.html#shell
Version 2.1.8, rd8333e556c8ed739cf39dab58ddc6b43a50c0965, Tue Nov 19 15:29:04 UTC 2019
Took 0.0028 seconds
hbase(main):001:0> list
TABLE
logs
logs_20240606
logs_20240607
user
sdutcm:bigdata
5 row(s)
Took 1.2756 seconds
=> ["logs", "logs_20240606", "logs_20240607", "user", "sdutcm:bigdata"]

hbase(main):002:0> describe 'sdutcm:bigdata'
Table sdutcm:bigdata is ENABLED
sdutcm:bigdata
COLUMN FAMILIES DESCRIPTION
{NAME => 'info', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
{NAME => 'msg', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
2 row(s)
Took 0.3826 seconds

hbase(main):003:0> scan 'sdutcm:bigdata'
ROW COLUMN+CELL
2001 column=info:name, timestamp=1719026353219, value=wang
1 row(s)
Took 0.2295 seconds
hbase(main):004:0>
  1. 启动和关闭 HBase Shell 的命令

    启动 HBase Shell 之前需要保证 ZooKeeperHadoop HDFSHBase 已经启动。可以使用 hbase shell 进入 HBase Shell,在 HBase Shell 中可以使用 exitquit 退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[subowen@bigdata ~]$ zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /home/subowen/apps/zookeeper-3.4.12/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

[subowen@bigdata ~]$ zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /home/subowen/apps/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: standalone

[subowen@bigdata ~]$ start-dfs.sh
Starting namenodes on [bigdata]
bigdata: starting namenode, logging to /home/subowen/apps/hadoop-2.7.6/logs/hadoop-subowen-namenode-bigdata.out
bigdata: starting datanode, logging to /home/subowen/apps/hadoop-2.7.6/logs/hadoop-subowen-datanode-bigdata.out
Starting secondary namenodes [0.0.0.0]
0.0.0.0: starting secondarynamenode, logging to /home/subowen/apps/hadoop-2.7.6/logs/hadoop-subowen-secondarynamenode-bigdata.out

[subowen@bigdata ~]$ start-hbase.sh
running master, logging to /home/subowen/apps/hbase-2.1.8/logs/hbase-subowen-master-bigdata.out
bigdata: running regionserver, logging to /home/subowen/apps/hbase-2.1.8/logs/hbase-subowen-regionserver-bigdata.out

[subowen@bigdata ~]$ jps
1746 NameNode
1572 QuorumPeerMain
2071 SecondaryNameNode
2874 Jps
2379 HRegionServer
1852 DataNode
2285 HMaster

[subowen@bigdata ~]$ hbase shell
2024-07-05 22:47:57,331 WARN [main] util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
HBase Shell
Use "help" to get list of supported commands.
Use "exit" to quit this interactive shell.
For Reference, please visit: http://hbase.apache.org/2.0/book.html#shell
Version 2.1.8, rd8333e556c8ed739cf39dab58ddc6b43a50c0965, Tue Nov 19 15:29:04 UTC 2019
Took 0.0051 seconds
hbase(main):001:0> exit

[subowen@bigdata ~]$ hbase shell
2024-07-05 22:52:46,337 WARN [main] util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
HBase Shell
Use "help" to get list of supported commands.
Use "exit" to quit this interactive shell.
For Reference, please visit: http://hbase.apache.org/2.0/book.html#shell
Version 2.1.8, rd8333e556c8ed739cf39dab58ddc6b43a50c0965, Tue Nov 19 15:29:04 UTC 2019
Took 0.0025 seconds
hbase(main):001:0> quit
[subowen@bigdata ~]$
  1. 启动ZooKeeper和查看 ZooKeeper 的运行状态的命令
1
2
3
4
5
6
7
8
9
[subowen@bigdata ~]$ zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /home/subowen/apps/zookeeper-3.4.12/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

[subowen@bigdata ~]$ zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /home/subowen/apps/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: standalone
  1. 修改表结构的命令

    使用 alter 命令可以对表的结构进行修改:

    表名创建时写的所有和列族相关的信息,都可以后续通过alter修改,包括增加删除列族。

    ① 增加列族和修改信息都使用覆盖的方法

    ​ 修改列族的版本,VERSIONS => 6

    1
    2
    3
    4
    5
    hbase(main):001:0> alter 'bigdata:person', NAME => 'name', VERSIONS => 6
    Updating all regions with the new schema...
    1/1 regions updated.
    Done.
    Took 4.0145 seconds

    ​ 添加列族 tel

    1
    2
    3
    4
    5
    hbase(main):002:0> alter 'bigdata:person', NAME => 'tel', VERSIONS => 6
    Updating all regions with the new schema...
    1/1 regions updated.
    Done.
    Took 2.4498 seconds

    ​ 查看修改后的数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    hbase(main):003:0> describe 'bigdata:person'
    Table bigdata:person is ENABLED
    bigdata:person
    COLUMN FAMILIES DESCRIPTION
    {NAME => 'msg', VERSIONS => '6', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fal
    se', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN
    _MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}

    {NAME => 'name', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fa
    lse', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', I
    N_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}

    {NAME => 'tel', VERSIONS => '6', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fal
    se', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN
    _MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
    3 row(s)
    Took 0.0795 seconds

    ② 删除列族

    ​ 删除列族可以用以下两种方式:

    1
    2
    3
    4
    5
    hbase(main):001:0> alter 'bigdata:person', NAME => 'tel', METHOD => 'delete'
    Updating all regions with the new schema...
    1/1 regions updated.
    Done.
    Took 2.1046 seconds
    1
    2
    3
    4
    5
    hbase(main):002:0> alter 'bigdata:person', 'delete' => 'msg'
    Updating all regions with the new schema...
    1/1 regions updated.
    Done.
    Took 2.9721 seconds
  2. HBaseJava API中,数据表描述的对象是什么?TableDescriptor

    1
    TableDescriptor
  3. ZooKeeper 的访问端口号,HBase 的Web访问端口号

    ZooKeeper 的访问端口号为 2181

    HBase 的 Web 访问端口号为 16010

  4. 修改表中列族的Version版本的命令

1
alter 'table_name', {NAME => 'column_family', VERSIONS => number_of_versions}
  1. 删除表中数据的命令

    1
    delete 'ns:tb', 'rk', 'cf:col'
  2. 启动和关闭ZooKeeper的命令

    1
    2
    3
    4
    5
    # 启动 ZooKeeper
    zkServer.sh start

    # 关闭 ZooKeeper
    zkServer.sh stop
  3. 更新表数据的命令

    1
    alter 'ns:tb', {NAME => 'cf:col', VERSIONS => 5}
  4. HBase安装时,配置文件有那几个?

    • hbase-env.sh
    • hbase-site.xml
    • regionservers
    • .bashrc
  5. 关于NoSQL数据库有哪些?

    • HBase
    • Cassandra
    • BigTable
  6. 删除表操作

1
2
disable 'ns:tb'
drop 'ns:tb'
  1. HBase进程名,ZooKeeper 的进程名等相关进程

    • HMaster
    • HRegionServer
    • QuorumPeerMain
  2. HBase 依托于哪个文件存储系统?

    HBase 主要依托于 Hadoop HDFS

  3. 创建表的语法结构

    1
    create 'ns:tb', {NAME => 'cf', VERSIONS=5}
  4. 查询表数据的Shell语法

    1
    2
    3
    get 'r1', {COLUMN => 'cf:col'}

    scan 'ns:tb'
  5. 什么是列族COLUMN FAMILY

  6. HBase的数据类型

    • 数据类型
      • 基本数据类型
        • 字符串(String):用于存储文本数据,如名称、描述等。
        • 整数(Int):用于存储整数数据,如计数、编号等。
        • 浮点数(Float):用于存储小数数据,如金额、比率等。
        • 布尔值(Boolean):用于存储逻辑值,如是否、有效等。
      • 复合数据类型
        • 列族(Column Family):列族是HBase表中数据的组织方式,用于存储一组相关的列。列族是HBase表中数据的基本组织单位,每个列族对应一个数据节点。
        • 列(Column):列是HBase表中数据的基本单位,用于存储一组相关的单元格。列可以包含多个单元格,每个单元格对应一个数据值。
        • 单元格(Cell):单元格是HBase表中数据的基本单位,用于存储一组相关的数据值。单元格包含一个键(Row Key)、一个列(Column)和一个值(Value)。
    • 数据模型
      • NameSpace:命名空间,类似于关系型数据库的 Database 概念,每个命名空间下有多个表。HBase 两个自带的命名空间,分别是 hbasedefaulthbase 中存放的是 HBase 内置的表,default表是用户默认使用的命名空间。
      • Table:类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需要声明具体的列。因为数据存储是稀疏的,所以往 HBase 写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase 能够轻松应对字段变更的场景。
      • Row:HBase 表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是按照 RowKey 的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重要。
      • Column:HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限定,例如 info:nameinfo:age。建表时,只需指明列族,而列限定符无需预先定义。
      • Time Stamp:用于标识数据的不同版本Version,每条数据写入时,系统会自动为其加上该字段,其值为写入 HBase 的时间。
      • Cell:由 {rowkey, column Family:Column Qualifier, timestamp} 唯一确定的单元。Cell 中的数据全部是字节码形式存贮。

判断题

  1. HBase 本身是用 C++ 语言编写的,因此具有很高的执行效率。( × )

    HBase 本身是由 Java 语言编写的,尽管使用 Java,其执行效率还是较高的。

  2. HBase 优化目前主要工作集中于 Rowkey 优化。( × )

    HBase 的优化目前主要工作集中在 RowKey 的设计、参数优化、JVM调优等方面。

  3. 可以想象 HBase 是通过**”三维”**的表来存储数据的,通过时间戳保存数据的历史版本。( √ )

  4. 安装 HBase 完全分布式时,需要确保 SSHJDK 是可以正常使用的。( √ )

    在安装 HBase 的完全分布式部署时,确保 SSHJDK 可以正常使用是非常重要的。具体来说:

    • SSH:在分布式部署中,HBase 的各个节点之间需要进行通信和协作,而 SSH 是用于在不同节点之间建立安全连接的工具。确保节点之间可以互相访问、通信和传输文件是部署 HBase 所必需的。通常需要设置免密登录,以便在节点之间进行信息交换而无需手动输入密码。

    • JDK:HBase 是基于 Java 编程语言开发的,因此必须确保在每个节点上安装并配置了适当的 JDKHBase 运行需要 Java 环境,且为了确保 HBase 能够正常工作,必须保证每个节点都能够使用正确的 JDK 版本。

  5. Hadoop 中引入 HBase 的其中一个原因是:HDFS 在处理连续数据时比较吃力。( × )·

    HDFS 的设计目标之一就是为了有效地处理大规模数据,包括连续数据(如大文件、数据流等)和离散数据(如小文件、批量数据等)。Hadoop 中之所以引入 HBase,是需要 HBase 提供实时访问数据的能力,使得 Hadoop 生态系统更全面(此外还有高可伸缩性、高容错、多版本策略等)。

  6. 利用 HBase 技术可在廉价 PC Server 上搭建起大规模结构化存储集群。( √ )

  7. HBaseHadoop 生态系统的一个组成部分。( √ )

    HBase 是继 Google Bigtable 之后创建的分布式、开源、版本化、非关系型数据库。它是 Hadoop 生态系统的重要组件,利用 HDFS 的容错功能,提供对数据的实时读写访问。 HBase 尽管是数据库,但也可以称为数据存储系统,因为它不提供触发器、查询语言和二级索引等 RDBMS 功能。

  8. 使用 HBase 时,无需启动服务就可以直接在开发环境中调用 HBase 的相关功能。( × )

    HBase 是基于 Hadoop 的,在使用 HBase 之前需要启动 ZooKeeperHadoop 才能使用 HBase

  9. 配置 HBase 分布式部署的过程中,可以通过 cp 命令把文件复制到运行在不同机器上的节点中。( × )

    可以使用 scp 命令、rsync命令,或者基于 rsync 编写工具 xsync 进行传输。

填空题

  1. 判断表是否存在,禁用表,删除表,创建表,查看命名空间等 Shell 命令。

    • 判断表是否存在 :exists 'namespace_name:table_name'
    • 禁用/启用表:disable 'namespace_name:table_name' / enable namespace_name:table_name
    • 删除表:删除表之前需要禁用表,disable 'namespace_name:table_name',然后再删除表,drop 'namespace_name:tablename'
    • 创建表:create 'namespace_name:table_name', {NAME => 'cf1', VERSION=5}, {NAME => 'cf2', VERSION=5}
    • 查看命名空间:list_namespace
  2. HBase 的进程有哪些:关于这些进程的详细说明见 QuickPassHBase简答题 T4

    • HBase 的主要进程:HMasterHRegionServer
    • HBase 所依赖的两个外部的服务:ZooKeeperHDFS
  3. 行键是什么:rowkey

    HBase 中,行键唯一标识了 HBase 表的一行,可以通过单个行键或行键区间的方式访问表。行键保存为 字节数组 ,以字典序 排序存储。

  4. HBase 是一个什么样的分布式存储系统。(非关系型)

    Apache HBase™ 是以 HDFS 为数据存储的,一种分布式可扩展NoSQL 数据库。HBase 是一款面向列存储,用于存储处理海量数据的 NoSQL 数据库。

    HBase 的理论原型是 GoogleBigTable 论文。他是一个高可靠性高性能面向列可伸缩分布式存储系统。

简答题

  1. HBase 的基础核心组件有哪些?分别什么作用?

    • HBase-Client
      客户端,用来访问 HBase 集群。可以和 Hbase 交互,也可以和 HRegionServer 交互。通过 HBase RPC 来访问对应的接口。

      这里的客户端模式有多种,可以是 ThriftAvroRest 等。

      另外,hbase-client 自身会缓存 region 的一些信息。

    • ZooKeeper
      作用:

      • HMaster 的高可用
      • 存储 ROOT 表的地址、HMaster 的地址
      • 存储所有 HRegionServer 的状态,监控 HRegionServer 的上下限
      • 存储 HBase 的一些 SchemaTable 的元数据
    • HMaster

      HMaster可以启动多个,通过选举机制来保证只有一个 HMaster 正常运行并提供服务,其他的 HMaster 作为 standby 来保证高可用。HMaster 主要负责 Region 的管理工作。如:

      • 用户对表的增删改查
      • 管理 RegionServer 的负载均衡,调整 Region 的分布
      • RegionServer 宕机或下线后,负责迁移 RegionServer 上的 Region 到其他的 RegionServer
      • Region 在分裂后,负责分配新的 Region
    • HRegionServer
      HRegionServerHBase 中真正的工作节点,主要负责响应用户的 I/O 请求,向 HDFS 文件系统读写数据,以及 Region 的数据文件的合并和拆分等,是 HBase 中最核心的模块。

      HBase 中,一张表由多个的 HRegion 组成,一个 HRegionServer 中管理着多个 HRegion 对象。而一个 HRegion 由多个HStore 组成,每个HStore对象都对应着表的一个列族 (Column Family)。之后,一个HStore又由一个MemStore和多个StoreFile组成。这些 StoreFile 就是hbase 存储在 HDFS上的数据文件,MemStore 表示还在内存中未刷新到文件上的那些数据。

  2. HBase 的写流程, 读流程?

    • 写流程

      写流程顺序正如 API 编写顺序,首先创建 HBase 的重量级连接。

      ① 首先访问 ZooKeeper,获取 hbase:meta 表位于哪个 Region Server

      ② 访问对应的 Region Server,获取 hbase:meta 表,将其缓存到连接中,作为连接属性 MetaCache,由于 meta 表具有一定的数据量,导致了创建连接比较慢;

      之后使用创建的连接获取 Table,这是一个轻量级的连接,只有在第一次创建的时候会检查表格是否存在访问RegionServer,之后在获取 Table 时不会访问 RegionServer

      ③ 调用 Tableput 方法写入数据,此时还需要解析 RowKey,对照缓存的 MetaCache,查看具体写入的位置有哪个 RegionServer

      ④ 将数据顺序写入(追加)到 WAL,此处写入是直接落盘的,并设置专门的线程控制 WAL 预写日志的滚动(类似 Flume);

      ⑤ 根据写入命令的 RowKeyColumnFamily 查看具体写入到哪个 MemStore,并且在 MemStore 中排序

      ⑥ 向客户端发送 ACK

      ⑦ 等达到 MemStore 的刷写时机后,将数据刷写到对应的 Store 中。

    • 读流程

      读流程创建连接的方式同写流程。创建完连接后:

      ① 创建 Table 对象发送 get 请求。

      ② 优先访问 Block Cache(读缓存),查找是否之前读取过,并且可以读取 HFile 的索引信息和布隆过滤器。

      ③ 不管读缓存中是否已经有数据了(可能已经过期了),都需要再次读取写缓存和 store 中的文件。

      ④ 最终将所有读取到的数据合并版本,按照 get 的要求返回即可。

  3. HBase 的特点有哪些?优点或缺点?

    HBase的特点:(强 / 自动 / 高 / 海集 / 并 / 列 / 多 / 块 / 运行 / 稀)

    • 强一致性的读/写HBase不是”最终一致性”数据库,它非常适合于诸如高速计数器聚合等任务。

    • 自动分片HBase 中的表通过 Region 分布在集群上,而且 Region 会随着数据的增长自动拆分和重新分布。

    • 高可靠性:自动 RegionServer 故障转移,WAL 机制保证了数据写入时不会因集群异常而导致写入数据丢失,Replication 机制保证了在集群出现严重的问题时,数据不会发生丢失或损坏。而且 HBase 底层使用 HDFSHDFS 本身也有备份。

    • Hadoop/HDFS 集成&海量存储:,HBase支持 HDFS开箱即用作为其分布式文件系统。HBase 作为一个开源的分布式 Key-Value 数据库,其主要作用是面向 PB 级别数据的实时入库和快速随机访问。这主要源于上述易扩展的特点,使得 HBase 通过扩展来存储海量的数据。

    • 并行处理HBase 通过 MapReduce 支持大规模并行处理,将 HBase 用作源和接收器。

    • 列式存储HBase 是根据列族来存储数据的。列族下面可以有非常多的列。列式存储的最大好处就是,其数据在表中是按照某列存储的,这样在查询只需要少数几个字段时,能大大减少读取的数据量。(面向列)

    • 多种语言的 APIHBase 支持使用 JavaAPI 来编程进行数据的存取,还支持使用 Thrift 语言和 REST 语言的 API 来编程进行数据的存取。

    • 块缓存和布隆过滤器HBase支持 Block CacheBloom过滤器进行大容量查询优化。

    • 运行管理HBase 为业务洞察和 JMX 度量提供内置网页。

    • 稀疏性:为空的列可以不占存储空间,表可以设计的非常稀疏。

    HBase 的优点和缺点

    • HBase的优点:(动节/海存/负载/并拓)

      • 动态增加&节省空间:在传统的关系数据库中,如果数据结构发生了变化,就需要停机维护,而且需要修改表结构,而在 HBase 中数据表内的列可以做到动态增加,并且列为空的时候不存储数据,从而节省存储空间。
      • 海量数据存储HBase 适合存储 PB 数量级的海量数据,PB 级的数据在只采用廉价 PC 来存储的情况下,也可以在几十到一百毫秒内返回数据。这与 HBase 的极易扩展息息相关,正因如此,HBase 为海量数据的存储提供了便利。
      • 负载均衡:传统的通用关系数据库无法应对在数据规模剧增时导致的系统扩展性问题和性能问题。HBase 可以做到自动切分数据,并且会随着数据的增长自动地拆分和重新分布。
      • 高并发HBase 可以提供高并发的读写操作,而且可以利用廉价的计算机来处理超过 10 亿行的表数据
      • 高拓展性HBase 具有可伸缩性,如果当前集群的处理能力明显下降,可以增加集群的服务器数量来维持甚至提高处理能力。
    • HBase 的缺点:(条查/复杂/JOIN/ACID/SQL)

      • 不支持条件查询:不能支持条件查询,只支持按照 RowKey(行键)来查询,也就是只能按照主键来查询。这样在设计 RowKey 时,就需要完美的方案以设计出符合业务的查询。
      • 架构设计复杂:架构设计复杂,且使用 HDFS 作为分布式存储,因此只是存储少量数据,它也不会很快。在大数据量时,它慢的不会很明显。
      • 不支持 Join 操作HBase 不支持表的关联操作,因此数据分析是 HBase 的弱项。常见的 group byorder by 只能通过编写 MapReduce 来实现。
      • 不支持ACIDHBase部分支持了 ACID
      • 不支持SQL语句查询:查询 HBase 时不支持通过 SQL 语句进行查询。
  4. MasterRegionServer 的作用是什么?

    HBase 包含一个 Master 和许多个 RegionServer

    • Master:实现类为 HMaster,负责监控集群中所有的 RegionServer 实例。主要作用如下:

      • 管理元数据表格 hbase:meta,接收用户对表格创建修改删除的命令并执行

      • 监控 Region 是否需要进行负载均衡,故障转移和 Region 的拆分。

    • RegionServer:实现类为 HRegionServer,主要作用如下:

      • 负责数据 Cell 的处理,例如写入数据 put,查询数据 get 等。

      • 拆分合并 Region 的实际执行者,有 master 监控,有 RegionServer 执行。

编程题

Shell

  1. 创建命名空间的 Shell

    1
    create_namespace 'namespace_name'
  2. 创建表的 Shell

    1
    create 'namespace_name:table_name', {NAME => 'cf1', VERSION => 5} 
  3. 修改表的 Shell

    1
    alter 'namespace_name:table_name', NAME => 'cf1', METHOD => 'delete'
  4. 插入数据的 Shell

    1
    put 'ns:tb', 'rk', 'cf:col', 'value'
  5. 查询数据的 Shell

    • get 最大范围是一行数据,也可以进行列的过滤,读取的结果为多行 CellCell 的格式如下:{rowkey, column Familycolumn Qualifier, time Stamp}
    1
    get 'ns:tb', 'rk' , {COLUMN => 'cf:col'}
    • scan 用于扫描数据,能够读取多行数据,不建议扫描过多的数据,推荐使用 startRowstopRow 来控制读取的数据,默认范围左闭右开
    1
    scan 'namespace_name:table_name', {STARTROW => '1001',STOPROW => '1002'}

API

读写操作时

  1. 加载配置信息
  2. 获取 HBase 的链接对象
  3. 获取 Admin 对象
  4. 构造 TableName 对象
  5. 判断表是否存在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
public class Test {
public static Connection connection = null;
static {
Configuration conf = new Configuration(); // 加载配置信息
conf.set("hbase.zookeeper.quorum", "bigdata");
try {
connection = ConnectionFactory.createConnection(conf);
} catch (IOException e) {
e.printStackTrace();
}
}

public static void closeConntction() throw IOException {
if (connection != null) {
connection.close();
}
}

/**
* @brief 判断表格是否存在
* @param namespace 命名空间名
* @param tableName 表名
* @return true 存在; false 不存在
*/
public static boolean isTableExists(String namespace, String tableName) throws IOException {
// 1. 获得 admin
Admin admin = HBaseConnection.connection.getAdmin();

// 2. 使用对应的方法, 判断表格是否存在
boolean result = false;
try {
result = admin.tableExists(TableName.valueOf(namespace, tableName));
} catch (IOException e) {
e.printStackTrace();
}

// 3. 关闭 admin
admin.close();

// 返回结果
return result;
}

/**
* @brief 向表格中插入数据
* @param namespace 命名空间名
* @param tableName 表名
* @param rowKey 行键
* @param columnFamily 列族名
* @param columnName 列名
* @param value 插入值
*/
public static void putCell(String namespace,
String tableName,
String rowKey,
String columnFamily,
String columnName,
String value) throws IOException {
// 1. 获取 Table
Table table = connection.getTable(TableName.valueOf(namespace, tableName));

// 2. 调用相关方法插入数据
// 2.1 创建 Put 对象
Put put = new Put(Bytes.toBytes(rowKey));

// 2.2. 给 Put 对象添加数据
put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columnName), Bytes.toBytes(value));

// 2.3 将对象写入对应的方法
try {
table.put(put);
} catch (IOException e) {
e.printStackTrace();
}

// 3. 关闭 Table
table.close();
}

/**
* @brief 读取数据 读取对应一行的某一列
* @param namespace 命名空间名
* @param tableName 表名
* @param rowKey 行键
* @param columnFamily 列族名
* @param columnName 列名
*/
public static void getCells(String namespace,
String tableName,
String rowKey,
String columnFamily,
String columnName) throws IOException {
// 1. 获取 Table
Table table = connection.getTable(TableName.valueOf(namespace, tableName));

// 2. 调用相关方法插入数据
// 2.1 创建 Get 对象
Get get = new Get(Bytes.toBytes(rowKey)); // 如果此时不使用 addColumn 进行参数的添加, 此时则读取一整行的数据
// 2.2 如果想读取某一列的数据, 需要添加对应的参数
get.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columnName));

// 2.3 也可以设置读取数据的版本
get.readAllVersions();

try {
// 3. 读取数据, 写入 Result 对象
Result result = table.get(get);

// 4. 处理数据
Cell[] cells = result.rawCells();

// 5. 测试方法: 直接把读取到的数据打印到控制台
// 如果是实际开发, 需要再额外写方法, 对应处理数据
for (Cell cell : cells) {
// Cell 存储数据比较底层, 需要进一步处理
String value = new String(CellUtil.cloneValue(cell));
System.out.println(value);
}
} catch (IOException e) {
e.printStackTrace();
}

// 6. 关闭 Table
table.close();
}

private static main(String[] args) throw IOException {
putCell("sdutcm", "bigdata", "2001", "info", "name", "zhangsan");
connection.close();
}
}

参考文献

Hadoop 综合揭秘——HBase的原理与应用 - 风尘浪子 - 博客园 (cnblogs.com)

HBase的组件

SparkQuickIN

快速入门 Spark

[TOC]

⛳︎ 1. 开始 Spark

Spark官网Apache Spark™ - Unified Engine for large-scale data analytics

1.1 什么是Spark

Spark官网的解释:Apache Spark™ is a unified analytics engine for large-scale data processing.

Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎。Spark是加州大学伯克利分校的 AMP实验室 所开源的类 Hadoop MapReduce 的通用并行计算框架,Spark 拥有 Hadoop MapReduce 所具有的优点,但不同于 MapReduce 的是:Job 中间输出结果可以缓存在内存中,从而不再需要读写 HDFS,减少磁盘数据交互,因此 spark 能更好地适用于数据挖掘与机器学习等需要迭代的算法。

SparkScala 编写,方便快速编程。

其特点是:高速、使用简单、通用、可以在多处运行。

1.2 总体技术栈讲解

Spark 提供了 Sparkcore RDDSpark SQLSpark StreamingSpark MLlibSpark GraphX等技术组件,可以一站式的完成大数据领域的离线批处理、交互式查询、流式计算、机器学习、图计算等常见的任务。这就是Spark一站式开发的特点。

1.3 SparkMapReduce 的区别

1.3.1 MapReduce 的原理

MapReduce 在运算时需要多次进行磁盘 I/O。下面是一个简单的 MapReduce 过程:

视频链接:https://www.bilibili.com/video/BV1TB4y1i7kk/

在这个视频中,可以看出MapReduce 过程中需要多次磁盘 I/O,落地到HDFS上。

1.3.2 Spark 是如何做的

可以看到,MapReduce 的多个 Job 之间相互独立,每个 Job 完成后的数据都需要存储到文件系统中。每个 Job 中也可能会存在大量的磁盘 I/O ,这样会使得 MapReduce 的速度很慢。相比于 MapReduceSpark使用了 DAG 有向无环图。使多个任务串联起来,将结果存储在内存中(当然内存不够还是要将数据缓存在磁盘中)直接进行运算,避免了大量的磁盘I/O。

1.3.3 SparkMapReduce 的一些联系

SparkMapReduce 都是分布式计算框架,Spark 计算中间结果基于内存缓存,MapReduce 基于HDFS存储。也正因此,Spark处理数据的能力一般是 MapReduce的三到五倍以上,Spark 中除了基于内存计算这一个计算快的原因,还有DAG(DAG Schedule)有向无环图来切分任务的执行先后顺序。

1.4 Spark API

Spark API 有多种语言支持,分别包括:ScalaJavaPythonRSQL 等。

1.5 Spark 的运行模式

  • Local:多用于本地测试,如在:EclipseIDEA 中编写测试程序等。
  • StandaloneSpark 自带的资源调度框架,它支持完全分布式。Standalone模式也叫作独立模式其自带完整的服务,可单独部署到一个集群中,无序依赖任何其他资源管理系统。 从一定程度上来说,该模式是 Local 模式和 Yarn 模式的基础。
  • Yarn: Hadoop 生态圈里的一种资源调度框架,Spark也是可以基于 Yarn 来计算的。 若要使用 Yarn 来进行资源调度,必须实现ApplicationMaster 接口,Spark 实现了这个接口,所以可以基于 Yarn 来进行资源调度。
  • Mesos:也是一种资源调度框架(了解即可)。

🥑 2. SparkCore

2.1 RDD

2.1.1 RDD 的概念

RDD(Resilient Distribute Dataset):弹性分布式数据集。

RDD算子Other RDDRDD 经过算子的运算会变成其他的 RDD

(重点)RDD的特点:① 分区的;② 并行操作的;③ 不可变的。

2.1.2 RDD 的五大特性
  1. 每个RDD 由一系列的 Partition 组成。
  2. 函数是作用在每一个 Partition (Split) 上的。
  3. RDD 中有一系列的依赖关系,或者说每个RDD都会依赖其他一系列的RDD
  4. 分区器是作用在 <K, V> 格式的 RDD 上,即:<K, V>RDD 可以通过 Partitioner 进行自定义分区。
  5. RDD提供一系列的最佳计算位置。数据在哪里,计算就在哪里,移动数据不如移动计算。
2.1.3 RDD 理解图

Spark 中读取文件是使用 SparkContext 对象调用 textFile 方法,实际上底层和 MapReduce 读取 HDFS 文件的方式是相同的,读取之前先要进行 split 切片。默认情况下 Split 的大小和 Block 块的大小相同。

一些问题:

  1. RDD的分布式体现在那些方面?

    RDD 由一系列的 Partition 构成,并且 Partition 是分布在不同的节点上的。这就体现了 RDD 的分布式。

  2. 哪里体现了 RDD 的弹性?

    RDD 由一系列的 Partition 组成,其大小和数量都是可以改变的。默认情况下,Partition 的个数和 Block 块的个数相同。

  3. 哪里体现了 RDD 的容错?

    RDD 之间存在一系列的依赖关系,子RDD 可以找到对应的父RDD ,然后通过一系列计算得到得出响应的结果,这就是容错的体现。

    RDD 提供计算最佳位置,体现了数据本地化,体现了大数据中”移动数据不如移动计算“的理念。

一些注意事项:

  1. textFile 方法底层封装的是 MapReduce 读取文件的方式,读取文件之前先进行 Split 切片,默认 Split 大小是一个 Block 的大小。
  2. RDD 实际上不存储数据,但是为了方便理解,可以理解为存储数据。
  3. 什么是 <K, V> 格式的 RDD,如果 RDD 里面存储的数据都是二元组对象,那么这个 RDD 我们就叫做 <K, V> 格式的 RDD
2.1.4 SparkRDD 编程模型
  1. 创建 SparkContext 对象

  2. 创建 RDD

  3. 计算 RDD

  4. 输出结果(如控制台打印测试,存储等)

  5. 关闭 SparkContext

2.1.5 WordCount 案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
object WordCount {
def main(args: Array[String]): Unit = {
// 第一步: 创建 SparkContext 对象
// 对于每个 Spark 程序来说, 最重要的就是两个对象: SparkConf 和 SparkContext
val conf = SparkConf()
conf.setAppName("WordCount").setMaster("Local")
val sparkContext = SparkContext(conf)

// 创建 RDD
val line: RDD[String] = sc.textFile("files/order.csv")

// 计算 RDD
val word: RDD[String] = sc.flatMap(x => x.split(" "))
val pair: RDD[(String, Int)] = word.map(x => (x, 1))
// val result: RDD[(String, Int)] = pair.reduceByKey((x, y) => {x + y})
val result: RDD[(String, Int)] = pair.reduceByKey((x, y) => {
println(x + ":" + y)
x + y
})

// 输出 RDD
result.foreach(println)

// 关闭 SparkContext
sparkContext.stop()
}
}

2.2 Spark 任务执行原理

从下图中,我们可以看到 Spark 的主要角色:

SparkExecutor

一些名词的解释:

  • Master Node 主节点
  • Worker Node 从节点
  • Driver 驱动程序
  • Executor 执行节点
  • Cluster Manager 集群管理者
2.2.1 Spark 架构的类比

我们可以简单的将这个架构和 YARN 对比一下:Master 就相当于 YARN 中的 ResourceManagerWorker 就相当于 YARN 中的 NodeManagerDriver 相当于 YARN 中的 Application

2.2.2 Spark 执行原理详细说明
  1. MasterWorker 节点

    搭建 Spark 集群的时候我们就已经设置好了 Master 节点和 Worker 节点,一个集群有多个Master节点和多个Worker节点。

    • Master 节点常驻 Master 守护进程,负责管理 Worker 节点,我们从 Master 节点提交应用。

    • Worker 节点常驻 Worker 守护进程,与 Master 节点通信,并且管理 Executor 进程。

    一台机器可以同时作为 MasterWorker 节点(e.g. 有四台机器,可以选择一台设置为 Master节点,然后剩下三台设为 Worker节点,也可以把四台都设为 Worker 节点,这种情况下,有一个机器既是 Master 节点又是 Worker 节点)。

    一个 Spark 应用程序分为一个驱动程序 Driver 和多个执行程序 Executors 两种。

  2. DriverExecutor 进程
    Driver 进程就是应用的 main() 函数并且构建 SparkContext 对象,当我们提交了应用之后,便会启动一个对应的 Driver 进程,Driver 本身会根据我们设置的参数占有一定的资源(主要指 CPU CoreMemory)。

    根据部署模式的不同,Driver 可以运行在 Master 上,也可以运行 Worker上,Driver 与集群节点之间有频繁的通信。上图展示了 DriverMaster 上的部署的情况。

    • 如上图所示,Driver首先会向**集群管理者Cluster Manager**,如StandaloneYarnMesos 申请 Spark 应用所需的资源,也就是Executor,然后集群管理者会根据 Spark 应用所设置的参数在各个 Worker 上分配一定数量的 Executor,每个 Executor 都占用一定数量的 CPUMemory

    • 在申请到应用所需的资源以后,Driver 就开始调度和执行我们编写的应用代码了。Driver 进程会将我们编写的 Spark 应用代码拆分成多个 Stage,每个Stage 执行一部分代码片段,并为每个 Stage 创建一批 Tasks,然后将这些 Tasks分配到各个 Executor中执行。这一步即 Driver ---Task--> Worker

    • Executor 进程宿主在 Worker 节点上,一个 Worker可以有多个 Executor。每个 Executor 持有一个线程池,每个线程可以执行一个 TaskExecutor 执行完 Task 以后将结果返回给 Driver,每个 Executor 执行的 Task 都属于同一个应用。此外 Executor 还有一个功能就是为应用程序中要求缓存的 RDD 提供内存式存储,RDD 是直接缓存在 Executor进程内的,因此任务可以在运行时充分利用缓存数据加速运算。这一步即 Worker ---Result--> Driver

    • Driver 负责任务 Tasks 的分发结果Results的回收,即任务的调度。如果 Task 的计算结果非常大就不要回收了,会造成OOM(我们在执行程序时可以通过参数指定 Driver 的内存大小,如 1G,如果一个 Worker 的结果是 510M,那么两个接节点上的结果就会超过 1G,导致 OOM)。

2.2 RDD 算子

RDD有两种操作算子:分别为转换算子(Transformation) 和 **行动算子(Action)**。算子其实就是函数,只不过在 Scala 中称为算子。

下面表格列出了部分 RDD 算子,完整内容可以查看 [SparkRDD](RDD Programming Guide - Spark 3.0.1 Documentation (apache.org)) 文档。

算子类型 算子方法 算子转换
Transformations map(f: T=>U) RDD[T] => RDD[U]
filter(f: T=>Bool) RDD[T] => RDD[T]
flatMap(f: T=>Seq[U]) RDD[T] => RDD[U]
sample(fraction: Float) RDD[T] => RDD[T](Deterministic sampling)
groupByKey() RDD[(K, V)] => RDD[(K, Seq[V])]
reduceByKey(f: (V, V)=>V) RDD[(K, V)] => RDD[(K, V)]
union() (RDD[T], RDD[T]) => RDD[T]
join() (RDD[(K, V)], RDD[(K, W)]) => RDD[(K, (V, W))]
cogroup() (RDD[(K, V)], RDD[(K, W)]) => RDD([K, (Seq[V], Seq[W])])
crossProduct() (RDD[T], RDD[U]) => (RDD[(T, U)])
mapValues(f: V=>W) RDD[(K, V)] => RDD[(K, W)](Preserves Partitioning)
sort(c: Comparator[K]) RDD[(K, V)] => RDD[(K, V)]
partitionBy(p: Partitioner[K]) RDD[(K, V)] => RDD[(K, V)]
... ...
Actions count() RDD[T] => Long
collect() RDD[T] => Seq[T]
reduce(f: (T, T)=>T) RDD[T] => T
lookup(k: K) RDD[(K, V)] => Seq[V](On hash/range partitioned RDDs)
save(path: String) Outputs RDD to a Storage System, e.g. HDFS
foreach(func) -
... ...
2.2.1 Transformation 转换算子

点击快速跳转到转换算子列表:Transformations

Transformation 转换算子有延迟执行的特点,具有**懒加载(Lazy)**的特性。

下面列出常用的行动算子及用法:

  • map(func) 返回一个新的分布式数据集,由每个原元素经过func函数转换后组成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object MapDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("MapDemo")
Logger.getLogger("org.apache.spark").setLevel(Level.OFF)
val sc = new SparkContext(conf)

val list = List(0, 10, 15, 20, 25, 30, 35, 40, 45, 50)
val listRDD = sc.parallelize(list, 5)
listRDD.foreach(println)
println()
val retRDD = listRDD.map(num => num * 7)
retRDD.foreach(num => println(num))

sc.stop()
}
}
  • mapPartition(func) 将函数用在每个RDD的分区上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
object MapPartitionDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("MapPartitionDemo")
Logger.getLogger("org.apache.spark").setLevel(Level.OFF)
val sc = new SparkContext(conf)

val list = List(0, 10, 15, 20, 25, 30, 35, 40, 45, 50)
val listRDD = sc.parallelize(list, 3) // 设置三个分区

// 对比 map 和 mapPartition 的区别
listRDD.map( x => {
println("Map 执行一次")
x + 1
}).foreach(println)

println()

listRDD.mapPartitions( x => {
println("MapPartition 执行一次")
x.map(x => {println("mapPartition 里的map"); x + 1}) // 这个 map 不是 RDD 里的 map, 而是 Iterator 中的 map
}).foreach(println)
}
}
  • filter(func)返回一个新的数据集,由经过func函数后返回值为true的元素组成。
1
2
3
4
5
6
7
8
9
10
11
12
13
object FilterDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("FilterDemo")
Logger.getLogger("org.apache.spark").setLevel(Level.OFF)
val sc = new SparkContext(conf)

val list = List(0, 10, 15, 20, 25, 30, 35, 40, 45, 50)
val listRDD = sc.parallelize(list)
val retRDD = listRDD.filter(num => num % 2 == 0)
retRDD.foreach(println)
sc.stop()
}
}
  • flatMap(func)类似于map,但是每一个输入元素,会被映射为 0 到多个输出元素(因此,func 函数的返回值是一个 Seq,而不是单一元素)。
1
2
3
4
5
6
7
8
9
10
11
12
13
object FlatMapDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("FlatMapDemo")
Logger.getLogger("org.apache.spark").setLevel(Level.OFF)
val sc = new SparkContext(conf)

val list = List("hello you", "hello he", "hello me")
val listRDD = sc.parallelize(list)
val wordsRDD = listRDD.flatMap(line => line.split(" "))
wordsRDD.foreach(println)
sc.stop()
}
}
  • sample(withReplacement, frac, seed)根据给定的随机种子 seed,随机抽样出数量为 frac 的数据。
1

  • union(otherDataset)返回一个新的数据集,由原数据集和参数联合而成。
1

  • groupByKey([numTasks])在一个由 <K, V> 对组成的数据集上调用,返回一个 <K, Seq[V]> 对的数据集。注意:默认情况下,使用 8 个并行任务进行分组,你可以传入 numTask 可选参数,根据数据量设置不同数目的 Task。使用该算子可以将相同Key的元素聚集到一起,最终把所有相同Key的元素合并成一个元素,该元素的Key不变,Value则聚集到一个集合中。
1

  • reduceByKey(func, [numTasks])在一个<K, V>对的数据集上使用,返回一个<K, V>对的数据集,key相同的值,都被使用指定的reduce函数聚合到一起。和 groupByKey类似,任务的个数是可以通过第二个可选参数来配置的。
1

  • join(otherDataset, [numTasks])在类型为<K, V><K, W>类型的数据集上调用,返回一个<K, <V, W>>对,每个key中的所有元素都在一起的数据集。
  • sortByKey()sortBy()sortByKey() 函数需要在类型为 <K, V> 类型的数据集上调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
object SortByKeyDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("SortByKeyDemo")
Logger.getLogger("org.apache.spark").setLevel(Level.OFF)
val sc = new SparkContext(conf)

val list = List(
"1,李 磊,22,175",
"2,刘银鹏,23,175",
"3,齐彦鹏,22,180",
"4,杨 柳,22,168",
"5,敦 鹏,20,175"
)
val listRDD: RDD[String] = sc.parallelize(list)
listRDD.foreach(println)
println()

// 3,齐彦鹏,22,180
// 1,李磊,22,175
// 2,刘银鹏,23,175
val resultRDD = listRDD.map(x => {
val fields = x.split(",")
println(fields(0) + " " + fields(1) + " " + fields(2) + " " + fields(3) + " ")
(fields(0), fields(1), fields(2), fields(3))
}).map(x => {(x._3, x._1)})

// 分区数会影响最终打印结果, 设置分区为 2, 所有分区最终会聚合为两个分区, 打印时显示每个分区的排序
// 这就可能导致打印输出时出现不同分区排序数据交叉的情况。
// 所以我们一般设置分区为 1, 表示排序结果聚合到一个分区
resultRDD.sortByKey(true, 2).foreach(println)

println()
val sortBy_RDD = listRDD.map(x => {
val fields = x.split(",")
// println(fields(0) + " " + fields(1) + " " + fields(2) + " " + fields(3) + " ")
(fields(0), fields(1), fields(2), fields(3))
})
println("SortBy_001")
sortBy_RDD.sortBy(_._1, true, 1).foreach(println)

println("SortBy_002")
sortBy_RDD.sortBy(_._2, true, 1).foreach(println)

println("SortBy_003")
sortBy_RDD.sortBy(_._3, true, 1).foreach(println)

println("SortBy_004")
sortBy_RDD.sortBy(_._4, true, 1).foreach(println)

sc.stop()
}
}
2.2.2 Action 行动算子

Action 行动算子具有触发执行的特点,一个 Application 应用程序有几个 Action 类算子执行,就有几个 Job 运行。

点击快速跳转到 SparkRDD Action 列表: Actions

下面列出常用的行动算子及用法:

  • reduce(func) 通过函数 func 聚集数据集中的所有元素。func 函数接受 2 个参数,返回 1 个值。这个函数必须是关联性的,确保可以被正确的并发执行。关于 reduce 的执行过程,可以对比 Scala 中类似的 reduce函数。

    不同于 Transformation 算子,执行后结果是RDD,执行 Action 算子之后,其结果不再是 RDD,而是一个标量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
object Action_Reduce {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("Action_Reduce")
Logger.getLogger("org.apache.spark").setLevel(Level.OFF)
val sc = new SparkContext(conf)
// val list = List(0, 5, 15, 20, 25, 30) // 如果用这个 list 输出为: 95
val list = List(
"1,李 磊,22,175",
"2,刘银鹏,23,175",
"3,齐彦鹏,22,180",
"4,杨 柳,22,168",
"5,敦 鹏,20,175"
)
val listRDD = sc.parallelize(list)
listRDD.foreach(println)
println()
val ret = listRDD.reduce(
(v1, v2) => {
println("[LOGS] v1 > " + v1 + " ")
println("[LOGS] v2 > " + v2 + " ")
println("[LOGS] v1 + v2 > " + (v1 + v2))
v1 + v2
}
)

println("ret: " + ret)
sc.stop()
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
输出如下:
3,齐彦鹏,22,180
1,李 磊,22,175
4,杨 柳,22,168
2,刘银鹏,23,175
5,敦 鹏,20,175

[LOGS] v1 > 3,齐彦鹏,22,180
[LOGS] v2 > 4,杨 柳,22,168
[LOGS] v1 + v2 > 3,齐彦鹏,22,1804,杨 柳,22,168
[LOGS] v1 > 3,齐彦鹏,22,1804,杨 柳,22,168
[LOGS] v2 > 5,敦 鹏,20,175
[LOGS] v1 + v2 > 3,齐彦鹏,22,1804,杨 柳,22,1685,敦 鹏,20,175
[LOGS] v1 > 1,李 磊,22,175
[LOGS] v2 > 2,刘银鹏,23,175
[LOGS] v1 + v2 > 1,李 磊,22,1752,刘银鹏,23,175
[LOGS] v1 > 1,李 磊,22,1752,刘银鹏,23,175
[LOGS] v2 > 3,齐彦鹏,22,1804,杨 柳,22,1685,敦 鹏,20,175
[LOGS] v1 + v2 > 1,李 磊,22,1752,刘银鹏,23,1753,齐彦鹏,22,1804,杨 柳,22,1685,敦 鹏,20,175
ret: 1,李 磊,22,1752,刘银鹏,23,1753,齐彦鹏,22,1804,杨 柳,22,1685,敦 鹏,20,175
  • collectDriver 的程序中,以数组的形式,返回数据集的所有元素。这通常会在使用 filter 或者其它操作后,返回一个足够小的数据子集再使用,直接将整个 RDDCollect返回,很可能会让 Driver 程序 OOM,这点尤其需要注意。
1

  • count
1

  • take
1

  • first
1

  • saveAsTextFile
1

  • foreach
1

  • saveAsNewAPIHadoopFile
1

2.2.3 其他算子

Spark中还有许多其他种类的算子,体现了 Spark 算子的灵活性,其中包括将数据进行持久化的算子。

  • cache:懒加载执行的,必须有一个 Action 触发算子触发执行。
  • persist:懒加载执行的,必须有一个 Action 触发算子触发执行。
  • checkpoint:算子不仅能将 RDD 持久化到磁盘,还能切断 RDD 之间的依赖关系。
2.2.3 宽依赖和窄依赖
  1. 宽依赖

    ① 子 RDD 的每个分区依赖于所有的父 RDD 分区

    ② 对单个 RDD 基于 Key进行重组和 Reduce,如 groupByKeyreduceByKey

    ③ 对两个 RDD 基于 Key 进行 join 和重组,如 join

    ④ 经过大量 shuffle 生成的 RDD,建议进行缓存。这样避免失败后重新计算带来的开销。

  2. 窄依赖

    ① 子RDD的每个分区依赖于常数个父分区(与数据规模无关)

    ② 输入输出一对一的算子,且结果RDD的分区结构不变。主要是map/flatmap

    ③ 输入输出一对一的算子,但结果RDD的分区结构发生了变化,如union/coalesce

    ④ 从输入中选择部分元素的算子,如 filterdistinctsubstractsample

OnlineTravelBigdataPlatform

🌳 在线旅游大数据平台项目

100%

日期任务清单

  • 项目开始日期: 2024-06-11

    • 了解项目的背景以及整个系统的架构
    • 了解系统需要完成的主要功能
    • 了解系统整个架构
    • 完成数据服务端的部署
    • 完成数据客户端的部署
    • 了解数据集
    • 认识消息队列 Kafka
    • 完成消息队列 Kafka 的部署
    • 了解消息队列 Kafka 的基本应用
    • 使用 Flume 收集数据到 Kafka
  • 2024-06-12

    • 了解实时数据分析所用到的技术
    • 了解 SparkStreamingFlink
    • 了解 SparkStreaming 的核心概念
    • 了解数据源
    • 借助netcat实践Kafka
    • 了解转换操作
    • 具体实施任务:处理数据
  • 2024-06-13

    • 了解数据库连接池
    • 了解如何向 MySQL 数据库中写入数据
    • 借助 alibaba Druid 库实现一个数据库连接工具类
    • 编写案例:WordCount
    • 具体实施任务,将代码中生成的数据写入MySQL
  • 2024-06-14

    • 了解什么是 Kafka Offset
    • 维护 Kafka Offset
    • 具体实施任务,将 Kafka 的 Offset 配合 MySQL 用代码进行维护。
  • 2024-06-17

    • 进行后端开发
    • 进行前端开发
  • 2024-06-18

    • 进行热力图的绘制
    • 进行人流量柱状图的绘制
    • 进行人流量趋势图的绘制
  • 2024-06-19 项目结束日期

文件目录

[TOC]

阅读更多

QuickPassHBase

快速上手HBase

[TOC]

⚙ 1. HBase简介

1.1 HBase的定义

Apache HBase 是以 HDFS 为数据存储的,一种分布式、可扩展的 NoSQL 数据库。

HBase 的设计理念依据 Google 的 BigTable 论文,论文中对于数据模型的首句介绍。

BigTable是一个稀疏的、分布式的、持久的多维排序映射(Map)。该映射由行键、列键和时间戳索引作为键(Key),映射中的每个值(Value)都是一个未解释的字节数组。

HBase 使用与 BigTable 非常相似的数据模型。用户将数据行存储在带标签的表中。数据行具有可排序的键和任意数量的列。该表存储稀疏,因此如果用户喜欢,同一表中的行可以具有疯狂变化的列。

1.2 HBase的数据模型

1.2.1 HBase 的逻辑结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"row_key1": {
"personal_info": {
"name": "ZhangSan",
"city": "Beijing",
"phone": "156****0000"
},
"office_info": {
"tel": "010-1234567",
"address": "Shandong"
}
},
"row_key11": {
"personal_info": {
"city": "Shanghai",
"phone": "133****0000"
},
"office_info": {
"tel": "010-1234567",
}
},
"row_key2": {
...
}
}
列族→ personal_info office_info
RowKey↓ name city phone tel address
row_key1 ZhangSan Beijing 156****0000 010-1234567 Shandong
row_key11 Shanghai 131****0000 010-1234567
row_key2 ... ... ... ... ...

在上面的表格中:

  • personal_infooffice_info称为列族
  • namecityphoneteladdress称为
  • row_key1row_key11称为行键
  • 将一整张大表按照进行拆分,拆分为多个表,拆分后的每个表称为**块(Region)**,用于实现分布式结构。
  • 将一整张大表按照列族进行拆分,拆分为多个**存储(Store)**,用于在底层存储到不同的文件夹中,便于文件对应。

存储数据稀疏,数据存储多维,不同的行具有不同的列。数据存储整体有序,按照RowKey的字典序排列,RowKey为一个Byte数组。

1.2.2 HBase 的物理结构

物理存储结构即为数据映射关系,而在概念视图的空单元格,底层实际根本不存储。

在HDFS中划分好的存储Store如下:

personal_info
RowKey name city phone
row_key1 ZhangSan Beijing 156****0000
row_key11 Shanghai 131****0000
row_key2 ... ... ...

其底层一定是以映射(Map)的方式进行存储的,格式为**(Key, Value)Value一定是“ZhangSan”**这种字段。那么Key是什么呢?

为了确定Value值**”ZhangSan”,我们需要用Key对应到Value**,于是得到存储如下:

Row Key Column Family Column Qualifier Timestamp Type Value
row_key1 personal_info name t1 Put ZhangSan
row_key1 personal_info city t2 Put Beijing
row_key1 personal_info phone t3 Put 156****0000
row_key1 personal_info phone t4 Put 156****0001
row_key1 personal_info phone t5 Delete 156****0001

因为 HDFS 是无法修改数据的,而 HBase 需要修改数据,那么就需要解决这一问题,于是就有了**时间戳(Timestamp)**。不同版本(version)的数据根据 Timestamp 进行区分,读取数据默认读取最新的版本。

在上面的表格中,t4相对于t3来说就是进行了修改,将t3时的**phone156****0000修改为t4时的156****0001,读取时默认读取t4时的phone**值,通过这种方式完成了修改。

同样的,我们也不好删除数据,因此我们只需要插入一条**Type**为Delete的数据即可。

1.2.3 数据模型
  • Name Space 命名空间

    类似于关系型数据库的 Database 概念,每个命名空间下有多个表。HBase 两个自带的命名空间,分别是 hbasedefaulthbase 中存放的是 HBase 内置的表,default表是用户默认使用的命名空间。

  • Table

    类似于关系型数据库的概念。不同的是,HBase 定义表时只需要声明列族即可,不需要声明具体的列。因为数据存储时稀疏的,所有往HBase写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase能够轻松应对字段变更的场景。

    需要注意的是,列族的存在是动态添加列(或称字段)的基础。

  • Row

    HBase 表中的每行数据都由*一个行键(RowKey)多个列(Column)组成,数据是按照 RowKey的字典顺序存储的,*并且查询数据时只能根据 RowKey进行检索**,所以RowKey的设计十分重要。

  • Column

    HBase 中的每个列都由列族(Column Family)列限定符(Column Qualifier)进行限定,例如info:name, info:age。建表时,只需指明列族,而列限定符无需预先定义。列限定符听起来很高端,其实就是列名的意思。

  • Time Stamp

    用于标识数据的**不同版本(Version)**,每条数据写入时,系统会自动为其加上该字段,其值为写入 HBase 的时间。

  • Cell

    {rowkey, Column Family: Column Qualifier, Timestamp} 唯一确定的单元,Cell 中的数据全部是字节码形式存储。

1.3 HBase 基本架构

HBase基本架构
  • Master

    主要进程,具体实现类为HMaster,通常部署在NameNode上。

    主要功能:负责通过 ZK 监控 RegionServer 进程状态,同时是所有元数据变化的接口,内部启动监控执行 region 的故障转移和拆分的线程。

    功能的详细描述

    • 管理元数据表格 hbase:meta:接收用户对表格创建、修改、删除的命令并执行。

    • 监控 RegionServer 是否需要进行负载均衡故障转移Region拆分。通过启动多个后台线程监控实现上述功能:

      • LoadBalancer 负载均衡器

        周期性监控 region分布在 RegionServer 上面是否均衡,由参数 hbase.balancer.period控制周期时间,默认5分钟。

      • CatalogJanitor元数据管理器

        定期检查和清理hbase:meta中的数据。

      • MasterProcWAL Master 预写日志处理器

        把Master需要执行的任务记录到预写日志WAL中,如果Master宕机,则让BackupMaster继续操作。

  • RegionServer

    主要进程,具体实现类为HRegionServer,通常部署在DataNode上。

    功能:主要负责数据 Cell 的处理,同时在执行区域的拆分和合并的时候,由 RegionServer 来实际执行。

    功能的详细描述

    • 负责数据 Cell 的处理,例如写入数据put,查询数据get等。
    • 拆分合并 region 的实际执行者,有 Master 监控,有RegionServer 执行。
  • ZooKeeper

    HBase 通过 ZooKeeper 来做 Master的高可用、记录 RegionServer 的部署信息、并且存储有 meta 表的位置信息。
    HBase 对于数据的读写操作时是直接访问 ZooKeeper 的,在 2.3 版本推出 Master Registry 模式,客户端可以直接访问 Master。使用此功能,会加大对 Master的压力,减轻对 ZooKeeper 的压力。

  • HDFS

    HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高容错的支持。

上图中的Region由三个RegionServer随机管理,尽量均衡。表名hbase:meta是一个特例,他存储在HDFS,但是由Master管理。

🔧 2. 快速上手

2.1 安装部署

2.1.1 分布式部署
  1. 至少 3 台虚拟机

    1
    2
    3
    hadoop101
    hadoop102
    hadoop103
  2. 保证 ZooKeeper 正常部署,并且启动 ZooKeeper

    1
    zkServer.sh start
  3. 保证 Hadoop 正常部署,并且启动 Hadoop

    1
    start-dfs.sh
  4. 配置 HBase 环境

    ① 下载 HBase 安装包(压缩包),这里假设为hbase-2.4.11-bin.tar.gz

    ② 解压 HBase 安装包到一个文件夹

    1
    tar -zxvf /path/to/hbase-2.4.11-bin.tar.gz -C /path/to/module

    ③ 在用户目录下,添加用户环境变量

    1
    vim .bashrc
    1
    2
    3
    #HBase_HOME
    export HBASE_HOME = /path/to/module/hbase-2.4.11
    export PATH = $PATH:$HBASE_HOME/bin

    ④ 使环境变量生效

    1
    source .bashrc

    ⑤ 修改配置文件

    • hbase-env.sh

      1
      2
      3
      # 表示是否需要 HBase 管理维护一个自带的 ZooKeeper, 默认为 true
      # 我们需要使用本机已经配置好的 ZooKeeper, 所以修改为 False
      export HBASE_MANAGES_ZK = false
    • hbase-site.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      <?xml version="1.0"?>
      <?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
      <configuration>
      <!-- ZooKeeper的地址 -->
      <property>
      <name>hbase.zookeeper.quorum</name>
      <value>hadoop101,hadoop102,hadoop103</value>
      </property>

      <!-- HBase数据在HDFS中的存放路径 -->
      <property>
      <name>hbase.rootdir</name>
      <value>hadoop101:8020/hbase</value>
      </property>

      <!-- HBase的运行模式 -->
      <!-- false为单机模式, HBase和ZooKeeper会运行在同一个JVM虚拟机中 -->
      <!-- true 为分布式模式 -->
      <property>
      <name>hbase.cluster.distributed</name>
      <value>true</value>
      </property>

      <!-- ZooKeeper快照的存储位置 -->
      <!-- 这里替换为自己的 /path/to/ZooKeeperDir -->
      <property>
      <name>hbase.zookeeper.property.dataDir</name>
      <value>/opt/module/zookeeper-3.4.6/data</value>
      </property>

      <!-- HBase 安全模式 -->
      <!-- 在分布式模式下, 设置为 false -->
      <property>
      <name>hbase.unsafe.stream.capability.enforce</name>
      <value>false</value>
      </property>
      </configuration>
    • regionservers

      1
      2
      3
      hadoop101
      hadoop102
      hadoop103

    ⑥ 解决 log4j 不兼容的问题,移除 HBase或者 Hadoop.jar

    ⑦ 使用 scp 命令同步 HBase 配置,需要提前设置好免密登录。或者使用 xsync

  5. 启动 HBase 服务

    • 单点启动

      1
      2
      3
      4
      #单点启动HMaster
      hbase-daemon.sh start master
      #单点启动HRegionServer
      hbase-daemon.sh start regionserver
    • 集群启动

      1
      start-hbase.sh
    • 停止服务

      1
      stop-hbase.sh
2.1.2 高可用服务
  1. 如果 HBase 已经启动,先关闭HBase

    1
    stop-hbase.sh
  2. 添加配置文件 backup-masters

    1
    2
    3
    #使用touch命令或者echo命令均可
    touch /path/to/hbase-2.1.4/conf/backup-masters
    vim /path/to/hbase-2.1.4/conf/backup-masters

    添加内容:hadoop102

  3. 使用 scp 命令分发配置文件

  4. 启动HBase,正常启动进程如下:

    1
    2
    3
    hadoop101 -> HMaster HRegionServer
    hadoop102 -> HMaster HRegionServer
    hadoop103 -> HRegionServer

    其中,hadoop101HMaster 先启动作为主节点,hadoop102HMaster后启动,作为**备用节点(Backup-Master)**。

2.2 使用操作

2.2.1 Shell操作

使用命令 hbase shell 启动 HBase 的 Shell 命令界面,所有命令均可以使用 help 查到。

当我们在 hbase shell中输入help命令时,将会弹出HBase的使用提示:

1
hbase shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
hbase(main):001:0> help
HBase Shell, version 2.1.8, rd8333e556c8ed739cf39dab58ddc6b43a50c0965, Tue Nov 19 15:29:04 UTC 2019
Type 'help "COMMAND"', (e.g. 'help "get"' -- the quotes are necessary) for help on a specific command.
Commands are grouped. Type 'help "COMMAND_GROUP"', (e.g. 'help "general"') for help on a command group.

COMMAND GROUPS:
Group name: general
Commands: processlist, status, table_help, version, whoami

Group name: ddl
Commands: alter, alter_async, alter_status, clone_table_schema, create, describe, disable, disable_all, drop, drop_all, enable, enable_all, exists, get_table, is_disabled, is_enabled, list, list_regions, locate_region, show_filters

Group name: namespace
Commands: alter_namespace, create_namespace, describe_namespace, drop_namespace, list_namespace, list_namespace_tables

Group name: dml
Commands: append, count, delete, deleteall, get, get_counter, get_splits, incr, put, scan, truncate, truncate_preserve

Group name: tools
Commands: assign, balance_switch, balancer, balancer_enabled, catalogjanitor_enabled, catalogjanitor_run, catalogjanitor_switch, cleaner_chore_enabled, cleaner_chore_run, cleaner_chore_switch, clear_block_cache, clear_compaction_queues, clear_deadservers, close_region, compact, compact_rs, compaction_state, flush, hbck_chore_run, is_in_maintenance_mode, list_deadservers, major_compact, merge_region, move, normalize, normalizer_enabled, normalizer_switch, split, splitormerge_enabled, splitormerge_switch, stop_master, stop_regionserver, trace, unassign, wal_roll, zk_dump

Group name: replication
Commands: add_peer, append_peer_exclude_namespaces, append_peer_exclude_tableCFs, append_peer_namespaces, append_peer_tableCFs, disable_peer, disable_table_replication, enable_peer, enable_table_replication, get_peer_config, list_peer_configs, list_peers, list_replicated_tables, remove_peer, remove_peer_exclude_namespaces, remove_peer_exclude_tableCFs, remove_peer_namespaces, remove_peer_tableCFs, set_peer_bandwidth, set_peer_exclude_namespaces, set_peer_exclude_tableCFs, set_peer_namespaces, set_peer_replicate_all, set_peer_serial, set_peer_tableCFs, show_peer_tableCFs, update_peer_config

Group name: snapshots
Commands: clone_snapshot, delete_all_snapshot, delete_snapshot, delete_table_snapshots, list_snapshots, list_table_snapshots, restore_snapshot, snapshot

Group name: configuration
Commands: update_all_config, update_config

Group name: quotas
Commands: list_quota_snapshots, list_quota_table_sizes, list_quotas, list_snapshot_sizes, set_quota

Group name: security
Commands: grant, list_security_capabilities, revoke, user_permission

Group name: procedures
Commands: list_locks, list_procedures

Group name: visibility labels
Commands: add_labels, clear_auths, get_auths, list_labels, set_auths, set_visibility

Group name: rsgroup
Commands: add_rsgroup, balance_rsgroup, get_rsgroup, get_server_rsgroup, get_table_rsgroup, list_rsgroups, move_namespaces_rsgroup, move_servers_namespaces_rsgroup, move_servers_rsgroup, move_servers_tables_rsgroup, move_tables_rsgroup, remove_rsgroup, remove_servers_rsgroup

SHELL USAGE:
Quote all names in HBase Shell such as table and column names. Commas delimit
command parameters. Type <RETURN> after entering a command to run it.
Dictionaries of configuration used in the creation and alteration of tables are
Ruby Hashes. They look like this:

{'key1' => 'value1', 'key2' => 'value2', ...}

and are opened and closed with curley-braces. Key/values are delimited by the
'=>' character combination. Usually keys are predefined constants such as
NAME, VERSIONS, COMPRESSION, etc. Constants do not need to be quoted. Type
'Object.constants' to see a (messy) list of all constants in the environment.

If you are using binary keys or values and need to enter them in the shell, use
double-quote'd hexadecimal representation. For example:

hbase> get 't1', "key\x03\x3f\xcd"
hbase> get 't1', "key\003\023\011"
hbase> put 't1', "test\xef\xff", 'f1:', "\x01\x33\x40"

The HBase shell is the (J)Ruby IRB with the above HBase-specific commands added.
For more on the HBase Shell, see http://hbase.apache.org/book.html

根据上述信息,我们可以进一步的操作 HBase 数据库。我们实际开发中常用的**命令组(COMMAND GROUPS)**有:generalnamespaceddldml等,下面依次介绍这些内容:

  • 通用命令 general

    • 查看 HBase 状态 status,提供 HBase 的状态,如服务器的数量等

      1
      2
      3
      hbase(main):001:0> status
      1 active master, 0 backup masters, 1 servers, 0 dead, 4.0000 average load
      Took 0.5268 seconds
    • 查看 HBase 版本 version,提供正在使用 HBase 版本

      1
      2
      3
      hbase(main):002:0> version
      2.1.8, rd8333e556c8ed739cf39dab58ddc6b43a50c0965, Tue Nov 19 15:29:04 UTC 2019
      Took 0.0002 seconds
    • 表引用命令提供帮助 table_help

    • 提供有关用户的信息 whoami

      1
      2
      3
      4
      hbase(main):003:0> whoami
      nilera (auth:SIMPLE)
      groups: nilera
      Took 0.0283 seconds
  • 操作命名空间 Namespace

    **命名空间(Namespace)**,相当于MySQL数据库中的DataBase。Namespace 命令包括:alter namespacecreate_namespacedescribe_namespacedrop_namespacelist_namespacelist_namespace_tables。下面将对一些常用命令进行介绍:

    • 查看全部命名空间 list_namespace

      1
      2
      3
      4
      5
      6
      hbase(main):001:0> list_namespace
      NAMESPACE
      default
      hbase
      2 row(s)
      Took 0.5484 seconds
    • 创建命名空间 create_namespace

      用法:create_namespace 'ns'

      1
      2
      3
      4
      5
      6
      7
      8
      9
      hbase(main):001:0> create_namespace 'bigdata'
      Took 0.0432 seconds
      hbase(main):002:0> list_namespace
      NAMESPACE
      bigdata
      default
      hbase
      3 row(s)
      Took 0.0224 seconds
    • 删除命名空间 drop_namespace

      用法:drop_namespace 'ns',删除命名空间时,命名空间必须为空。

    • 查看命名空间 describe_namespace

      用法:describe_namespace 'ns'

      1
      2
      3
      4
      5
      hbase(main):001:0> describe_namespace 'bigdata'
      DESCRIPTION
      {NAME => 'bigdata'}
      Took 0.0068 seconds
      => 1
    • 查看命名空间下的表 list_namespace_tables

      用法:list_namespace_tables 'ns'

      1
      2
      3
      4
      5
      6
      7
      hbase(main):001:0> list_namespace_tables 'default'
      TABLE
      logs
      user
      2 row(s)
      Took 0.3790 seconds
      => ["logs", "user"]
  • 数据定义语言 ddl

    DDL(Data Definition Language)数据定义语言,主要是进行定义/改变表的结构、数据类型、表之间的链接等操作。ddl 相关命令如下:alteralter_asyncalter_statusclone_table_schemacreatedescribedisabledisable_alldropdrop_allenableenable_allexistsget_tableis_disabledis_enabledlistlist_regionslocate_regionshow_filters。下面将对一些常用命令进行介绍:

    • 创建表 create

      常见用法:

      create 'ns:tb', {NAME => 'cf', VERSIONS => 5}

      ​ 在命名空间 ns 下,创建一张表 tb,定义一个列族 cf

      ② 当在默认命名空间default下创建表时,可以省略 ns

      create 'tb', 'cf1', 'cf2'

      ​ 在默认命名空间default下,创建一张表tb,并定义两个列族 cf1cf2

      create 'tb', {NAME => 'cf1', VERSIONS => 5}, {NAME => 'cf2', VERSIONS => 5}

      ​ 在默认命名空间default下,创建一张表tb,并定义两个列族 cf1cf2,并同时指定两个列族的版本为 5

      1
      2
      3
      4
      hbase(main):001:0> create 'bigdata:person', {NAME => 'name', VERSIONS => 5}, {NAME => 'msg', VERSIONS => 5}
      Created table bigdata:person
      Took 1.5638 seconds
      => Hbase::Table - bigdata:person
    • 查看表的详细信息 describe

      用法describe 'tb'

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      hbase(main):010:0> describe 'bigdata:person'
      Table bigdata:person is ENABLED
      bigdata:person
      COLUMN FAMILIES DESCRIPTION
      {NAME => 'msg', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fal
      se', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN
      _MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
      {NAME => 'name', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fa
      lse', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', I
      N_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
      2 row(s)
      Took 0.1536 seconds
    • 修改表 alter

      表名创建时写的所有和列族相关的信息,都可以后续通过alter修改,包括增加删除列族。

      ① 增加列族和修改信息都使用覆盖的方法

      ​ 修改列族的版本,VERSIONS => 6

      1
      2
      3
      4
      5
      hbase(main):001:0> alter 'bigdata:person', NAME => 'name', VERSIONS => 6
      Updating all regions with the new schema...
      1/1 regions updated.
      Done.
      Took 4.0145 seconds

      ​ 添加列族 tel

      1
      2
      3
      4
      5
      hbase(main):002:0> alter 'bigdata:person', NAME => 'tel', VERSIONS => 6
      Updating all regions with the new schema...
      1/1 regions updated.
      Done.
      Took 2.4498 seconds

      ​ 查看修改后的数据:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      hbase(main):003:0> describe 'bigdata:person'
      Table bigdata:person is ENABLED
      bigdata:person
      COLUMN FAMILIES DESCRIPTION
      {NAME => 'msg', VERSIONS => '6', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fal
      se', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN
      _MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}

      {NAME => 'name', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fa
      lse', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', I
      N_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}

      {NAME => 'tel', VERSIONS => '6', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fal
      se', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN
      _MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
      3 row(s)
      Took 0.0795 seconds

      ② 删除列族

      ​ 删除列族可以用以下两种方式:

      1
      2
      3
      4
      5
      hbase(main):001:0> alter 'bigdata:person', NAME => 'tel', METHOD => 'delete'
      Updating all regions with the new schema...
      1/1 regions updated.
      Done.
      Took 2.1046 seconds
      1
      2
      3
      4
      5
      hbase(main):002:0> alter 'bigdata:person', 'delete' => 'msg'
      Updating all regions with the new schema...
      1/1 regions updated.
      Done.
      Took 2.9721 seconds

      ​ 然后查询修改后的数据:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      hbase(main):003:0> describe 'bigdata:person'
      Table bigdata:person is ENABLED
      bigdata:person
      COLUMN FAMILIES DESCRIPTION
      {NAME => 'name', VERSIONS => '5', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'fa
      lse', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', I
      N_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
      1 row(s)
      Took 0.0391 seconds
    • 禁用表 disable

      用法disable 'ns:tb'disable 'tb'

      1
      2
      hbase(main):001:0> disable 'bigdata:person'
      Took 0.9384 seconds
    • 删除表 drop

      用法drop 'ns:tb'drop 'tb',删除表时需要保证表是禁用的,否则会出现以下错误:

      1
      2
      3
      4
      5
      6
      7
      hbase(main):001:0> drop 'bigdata:person'

      ERROR: Table bigdata:person is enabled. Disable it first.

      For usage try 'help "drop"'

      Took 0.0248 seconds

      ​ 禁用表后再删除表:

      1
      2
      hbase(main):001:0> drop 'bigdata:person'
      Took 1.7106 seconds
  • 数据操纵语言 dml

    DML(Data Manipulation Language)数据操纵语言,主要是对数据进行增加、删除、修改操作。

    • 写入数据 put

      HBase 中如果想要写入数据,只能添加结构中最底层的 Cell。可以手动写入时间戳指定 Cell 的版本,推荐不写,默认使用当前的系统时间。如果重复写入相同 rowKey,相同列的数据,会写入多个版本进行覆盖。所以他同时兼具写入修改的功能。

      用法

      put 'ns:tb', 'rk', 'col', 'value'

      ​ 向命名空间ns中的tb表中的行键为rk,列为col的位置写入值value。其中colcf:col(即列族:列名)的格式。

      ​ 如果重复向相同行号rk,相同col写数据,则会进行覆盖。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      hbase(main):001:0> put 'bigdata:student', '1001', 'info:name', 'zhangsan'
      Took 0.2415 seconds
      hbase(main):002:0> put 'bigdata:student', '1001', 'info:name', 'lisi'
      Took 0.0121 seconds
      hbase(main):003:0> put 'bigdata:student', '1001', 'info:name', 'wangwu'
      Took 0.0342 seconds

      hbase(main):004:0> put 'bigdata:student', '1002', 'info:name', 'zhaoliu'
      Took 0.0082 seconds
      hbase(main):005:0> put 'bigdata:student', '1003', 'info:age', '10'
      Took 0.0050 seconds
      hbase(main):006:0> put 'bigdata:student', '1003', 'info:sex', 'male'
      Took 0.0054 seconds

      put 't1', 'r1', 'c1', 'value'用法同上。

    • 读取数据 get/scan

      读取数据的方法有两个:getscan

      • get最大范围是一行数据,也可以进行列的过滤,读取数据的结果为多行 Cell

      • scan是扫描数据,能够读取多行数据,不建议扫描过多数据,推荐使用 startRowstopRow 来控制读取的数据,默认范围左闭右开。

      get命令

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      Some examples:
      hbase> t.get 'r1' #查看'r1'的数据
      hbase> t.get 'r1', {TIMERANGE => [ts1, ts2]}
      hbase> t.get 'r1', {COLUMN => 'c1'} #过滤单列, 只显示 'c1'
      hbase> t.get 'r1', {COLUMN => ['c1', 'c2', 'c3']} #过滤多列, 只显示 'c1', 'c2', 'c3'
      hbase> t.get 'r1', {COLUMN => 'c1', TIMESTAMP => ts1}
      hbase> t.get 'r1', {COLUMN => 'c1', TIMERANGE => [ts1, ts2], VERSIONS => 4}
      hbase> t.get 'r1', {COLUMN => 'c1', TIMESTAMP => ts1, VERSIONS => 4}
      hbase> t.get 'r1', {FILTER => "ValueFilter(=, 'binary:abc')"}
      hbase> t.get 'r1', 'c1'
      hbase> t.get 'r1', 'c1', 'c2'
      hbase> t.get 'r1', ['c1', 'c2']
      hbase> t.get 'r1', {CONSISTENCY => 'TIMELINE'}
      hbase> t.get 'r1', {CONSISTENCY => 'TIMELINE', REGION_REPLICA_ID => 1}
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      hbase(main):001:0> get 'bigdata:student', '1001'
      COLUMN CELL
      info:name timestamp=1717580289267, value=wangwu
      1 row(s)
      Took 0.0645 seconds

      hbase(main):002:0> get 'bigdata:student', '1001', {COLUMN => 'info:name'}
      COLUMN CELL
      info:name timestamp=1717580289267, value=wangwu
      1 row(s)
      Took 0.0107 seconds

      hbase(main):003:0> get 'bigdata:student', '1003', {COLUMN => 'info:age'}
      COLUMN CELL
      info:age timestamp=1717580366636, value=10
      1 row(s)
      Took 0.0185 seconds

      scan 命令

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      Some examples:
      hbase> scan 'hbase:meta'
      hbase> scan 'hbase:meta', {COLUMNS => 'info:regioninfo'}
      hbase> scan 'ns1:t1', {COLUMNS => ['c1', 'c2'], LIMIT => 10, STARTROW => 'xyz'}
      hbase> scan 't1', {COLUMNS => ['c1', 'c2'], LIMIT => 10, STARTROW => 'xyz'}
      hbase> scan 't1', {COLUMNS => 'c1', TIMERANGE => [1303668804000, 1303668904000]}
      hbase> scan 't1', {REVERSED => true}
      hbase> scan 't1', {ALL_METRICS => true}
      hbase> scan 't1', {METRICS => ['RPC_RETRIES', 'ROWS_FILTERED']}
      hbase> scan 't1', {ROWPREFIXFILTER => 'row2', FILTER => "
      (QualifierFilter (>=, 'binary:xyz')) AND (TimestampsFilter ( 123, 456))"}
      hbase> scan 't1', {FILTER =>
      org.apache.hadoop.hbase.filter.ColumnPaginationFilter.new(1, 0)}
      hbase> scan 't1', {CONSISTENCY => 'TIMELINE'}
      hbase> scan 't1', {ISOLATION_LEVEL => 'READ_UNCOMMITTED'}
      hbase> scan 't1', {MAX_RESULT_SIZE => 123456}
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      hbase(main):001:0> scan 'bigdata:student'
      ROW COLUMN+CELL
      1001 column=info:name, timestamp=1717580289267, value=wangwu
      1002 column=info:name, timestamp=1717580320927, value=zhaoliu
      1003 column=info:age, timestamp=1717580366636, value=10
      1003 column=info:sex, timestamp=1717581149533, value=male
      3 row(s)
      Took 0.0338 seconds

      hbase(main):025:0> scan 'bigdata:student', {STARTROW => '1001', STOPROW => '1003'}
      ROW COLUMN+CELL
      1001 column=info:name, timestamp=1717580289267, value=wangwu
      1002 column=info:name, timestamp=1717580320927, value=zhaoliu
      2 row(s)
      Took 0.0118 seconds
    • 删除数据 delete/deleteall

      删除数据的方式有两个:deletedeleteall

      • delete 表示删除一个版本的数据,即为 1Cell,不填写版本默认删除最新的一个版本。
      • deleteall 表示删除所有版本的数据,即为当前行当前列的多个 Cell。执行命令会标记数据为要删除,不会直接彻底删除,删除只在特定时期清理磁盘时进行。

      delete

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      hbase(main):001:0> put 'bigdata:student', '1001', 'info:name', 'zhangsan'
      Took 0.3910 seconds
      hbase(main):002:0> put 'bigdata:student', '1001', 'info:name', 'lisi'
      Took 0.2024 seconds
      hbase(main):003:0> put 'bigdata:student', '1001', 'info:name', 'wangwu'
      Took 0.1559 seconds

      hbase(main):004:0> scan 'bigdata:student'
      ROW COLUMN+CELL
      1001 column=info:name, timestamp=1717584831277, value=wangwu
      1002 column=info:name, timestamp=1717580320927, value=zhaoliu
      1003 column=info:age, timestamp=1717580366636, value=10
      1003 column=info:sex, timestamp=1717581149533, value=male
      3 row(s)
      Took 0.0083 seconds

      hbase(main):005:0> delete 'bigdata:student', '1001', 'info:name'
      Took 0.0055 seconds

      hbase(main):006:0> scan 'bigdata:student'
      ROW COLUMN+CELL
      1001 column=info:name, timestamp=1717584831277, value=lisi
      1002 column=info:name, timestamp=1717580320927, value=zhaoliu
      1003 column=info:age, timestamp=1717580366636, value=10
      1003 column=info:sex, timestamp=1717581149533, value=male
      3 row(s)
      Took 0.0087 seconds

      deleteall

      1
          
2.2.2 API操作

根据官方 API 介绍,HBase 的客户端连接由 ConnectionFactory 类来创建,用户使用完成之后需要手动关闭连接。同时连接是一个重量级的,推荐一个进程使用一个连接。对 HBase 的命令通过连接中的两个属性 AdminTable 来实现。其中 Admin 主要管理 HBase 的元数据,如创建、修改表格信息,也就是 DDL 操作;Table 主要用于表格的增加、删除数据,也就是 DML 操作。

  • 环境搭建

    使用 IDEA 创建 Maven 项目,并修改 pom.xml 文件,添加 HBase 所需要用到的依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>2.4.11</version>
<!-- 如果报错, 需要排除 javax.el 拓展 -->
<!-- 因为 2.4.11 对应的是一个测试版本的 javax.el 包 -->
<!-- 需要先排除这个包后再添加正式版的 javax.el 包 -->
<exclusions>
<exclusion>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
</exclusion>
</exclusions>
</dependency>

<!-- 添加正式版的 javax.el 包 -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b06</version>
</dependency>
</dependencies>
  • 单线程使用连接

    下面展示了一种单线程使用连接的方式,实际开发中实际上很少这样做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.sdutcm;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.AsyncConnection;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;


public class HBaseConnection {
public static void main(String[] args) throws IOException {
// 1. 创建连接配置对象
Configuration conf = new Configuration();

// 2. 添加配置参数
conf.set("hbase.zookeeper.quorum", "bigdata"); // 这些配置都写在 hbase-site.xml 中

// 3. 创建连接
// 默认创建同步连接
Connection connection = ConnectionFactory.createConnection(conf);

// 也可以创建异步连接: 不推荐使用异步连接
CompletableFuture<AsyncConnection> asyncConnection = ConnectionFactory.createAsyncConnection(conf);

// 4. 使用连接
System.out.println(connection);

// 5. 关闭连接
connection.close();
}
}
  • 多线程使用连接

    实际开发中,因为 HBase 的连接是重量级的,所以我们在每个客户端中一般只创建一个(类似于单例模式)。所以我们对代码进行修改,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.sdutcm;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.AsyncConnection;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;


public class HBaseConnection {
// 声明一个静态属性
public static Connection connection = null;

static {
// 1. 创建连接配置对象: 当完成 resources 目录的配置后, 我们可以直接注释掉创建配置的部分
// 直接进行创建连接操作
// Configuration conf = new Configuration();

// 2. 添加配置参数
// 实际开发中, 不应该在代码中显式的写参数, 而是将参数写在 resources 下的配置文件中
// 将虚拟机的 hbase-site.xml 放到 resources 目录下
// conf.set("hbase.zookeeper.quorum", "bigdata"); // 这些配置都写在 hbase-site.xml 中

// 3. 创建连接
// 默认创建同步连接
try {
// 这里修改为无参构造
// connection = ConnectionFactory.createConnection(conf);
// 这里通过查看 ConnectionFactory.createConnection() -> 查看 create() -> 可以发现 HBase 官方文档添加了两个配置文件
// 分别为 hbase-default.xml 和 hbase-site.xml
// 所以我们可以直接复制虚拟机的 hbase-site.xml 添加到 resources 目录下, 并且将这里改为无参构造
// 无参则默认使用读取本地 hbase-site.xml 文件的方式添加参数
connection = ConnectionFactory.createConnection();
} catch (IOException e) {
e.printStackTrace();
}
}

// 关闭连接方式
public static void closeConnection() throws IOException {
// 判断连接是否为空
if (connection != null) {
connection.close();
}
}

public static void main(String[] args) throws IOException {
// 直接使用创建好的连接, 不要在 main 线程里面单独创建连接
System.out.println(HBaseConnection.connection);

// 使用完连接后需要关闭连接
HBaseConnection.closeConnection();
}
}
  • 获取 Admin
1
2
3
// 获取 Admin
// Admin 的连接式轻量级的, 不是线程安全的, 不推荐池化或者缓存这个连接
Admin admin = connection.getAdmin();
  • 创建命名空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.sdutcm;

import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;

import java.io.IOException;

public class HBaseDDL {
// 声明一个静态属性, 这样我们可以在不同的类中, 调用到同一个对象
public static Connection connection = HBaseConnection.connection;

/**
* @brief 创建命名空间
* @param namespace 命名空间名称
*/
public static void createNamespace(String namespace) throws IOException {
// 1. 获取 Admin
// Admin 的连接式轻量级的, 不是线程安全的, 不推荐池化或者缓存这个连接
Admin admin = connection.getAdmin();

// 2. 调用方法创建命名空间
// 2.1 创建命名空间描述
NamespaceDescriptor.Builder builder = NamespaceDescriptor.create(namespace);

// 2.2 给命名空间添加需求
builder.addConfiguration("user", "sdutcm");

// 2.3 使用 builder 构造出对应的添加完参数的对象, 完成创建
admin.createNamespace(builder.build());

// 关闭 admin
admin.close();
}

public static void main(String[] args) throws IOException {
// 测试创建命名空间
createNamespace("sdutcm");

// 其他代码
System.out.println("其他代码");

// 关闭 HBase 连接
HBaseConnection.closeConnection();
}
}

​ 结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
hbase(main):001:0> list_namespace
NAMESPACE
default
hbase
sdutcm <<< 可以看到 sdutcm 已经被创建出来了
3 row(s)
Took 8.0120 seconds

hbase(main):002:0> describe_namespace "sdutcm"
DESCRIPTION
{NAME => 'sdutcm', user => 'sdutcm'} <<< 这里是我们添加的描述
Took 0.7576 seconds
=> 1
  • 多异常处理
1

  • 判断表格是否存在
  • 创建表格

📕 3. 底层原理

3.1 进程架构

3.1.1 Master架构
3.1.2 RegionServer架构

3.2 写流程

3.2.1 写入顺序
3.2.2 刷新机制

3.3 读流程

3.3.1 读取顺序
3.3.2 合并数据优化

3.4 文件合并

3.4.1 大合并
3.4.2 小合并

Region拆分

自定义预分区
系统拆分

🔧 企业开发

TSDB模式

基础表格模式

自定义API
整合框架
Phoenix 读写数据
Hive 分析数据

IDEA 2022 搭建 Tomcat 环境

[TOC]

Tomcat 环境的搭建

参考教程

下载 Tomcat

Tomcat官网地址
Tomcat官网
在 Tomcat 官网中下载指定版本的 Tomcat,左侧 Download 处有相应版本,这里推荐 Tomcat 9 版本(因为Tomcat 10 在配置时会出现一定的问题)。
TomcatDownload
下载后解压到指定位置即可。

配置环境变量即可

配置 Tomcat 环境变量前一定要配置好 Java 的环境变量,尤其是JAVA_HOME,这里我一开始并没有配置 JAVA_HOME,我的环境变量是JAVA_HOME_180=xxx,这种方式Tomcat是找不到JAVA_HOME的,因此我又重新配置了JAVA_HOME
我的 JAVA_HOME 环境变量为:

1
JAVA_HOME=D:\JDK\jdk1.8.0_231

下面是 Tomcat 的环境变量配置:
新建 CATALINA_HOME 环境变量:

1
CATALINA_HOME=D:\tomcat\apache-tomcat-9.0.89

修改Path,在 Path 后添加(新建)如下环境变量:

1
2
3
%CATALINA_HOME%\lib
%CATALINA_HOME%\bin
%CATALINA_HOME%\lib\servlet-api.jar

验证是否配置成功

在命令行中,执行命令:startup.bat,若正常打印相关配置变量、且 Tomcat 进程被阻塞,即证明环境搭建成功。访问localhost:8080,出现以下界面即证明成功搭建。
TomcatSuccess
使用 shutdown.bat 命令即可使阻塞的 Tomcat 进程被关闭,推荐使用这种方式关闭 Tomcat。

可能会出现的问题

  1. 协议处理程序初始化失败:参考教程
    这个问题有可能是由于8080端口被占用了,在Windows中可以使用如下命令查看端口的占用情况:
    1
    netstat -aon|findstr "8080"
    如果确实被占用了,可以使用如下命令杀死端口号为 <PIDNUM> 的进程。
    1
    taskkill -PID <PIDNUM> -F
  2. 闪退
    可能原因是:环境变量配置不正确,仔细检查环境变量的配置。
  3. 乱码
    问题描述:打开startup.bat后汉字乱码
    解决方法:在.\apache-tomcat-9.0.43\conf下打开logging.properties文件
    java.util.logging.ConsoleHandler.encoding = UTF-8替换为java.util.logging.ConsoleHandler.encoding = GBK

社区版 IDEA 如何配置 Tomcat

CSDN 上大多数教程使用 Maven 创建 Tomcat 项目,但是这种方法实在是过于麻烦,社区版和专业版又有些不同,找不到很多东西。

如何配置 IDEA 2022 社区版中的 Tomcat

  1. 安装插件
    在 File → Settings → Plugin 中安装插件,搜索 Tomcat,安装插件。
    SmartTomcat
  2. 配置Tomcat路径
    安装插件后,在 File → Settings → Plugin → Tomcat Server
    添加配置如下:
    SmartTomcatConfig
  3. 完成

在 Hadoop 高可用的基础上搭建 HBase 高可用

在 Hadoop 高可用的基础上搭建 HBase 高可用

当Hadoop高可用搭建完成后,需要进一步再Hadoop高可用集群上搭建HBase高可用时,过程如下:

  1. 前提说明:

    我的集群为三台机器,每台机器上都有ZooKeeper,使用用户名和主机名(Username@Hostname)分别如下:

    1
    2
    3
    master@master
    master@slaver01
    master@slaver02
  2. 首先保证 ZooKeeper 正常部署

    1
    zkServer.sh start
  3. 需要保证Hadoop正常部署

    1
    2
    [master@master ~]$ start-dfs.sh
    [master@slaver01 ~]$ start-yarn.sh
  4. 解压HBase

  5. 配置环境变量

  6. 生效环境变量

  7. 修改配置文件

    • 修改 hbase-site.xml 文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      <configuration>
      <!-- HBase数据在HDFS中的存放的路径 -->
      <!-- 这里 ns 是 Hadoop 的 nameservice的值, 指向的是一个高可用的通道 -->
      <property>
      <name>hbase.rootdir</name>
      <value>hdfs://ns/hbase</value>
      </property>

      <!-- HBase 的运行模式 -->
      <!-- false是单机模式, 若为 false, HBase 和 ZooKeeper 会运行在同一个 JVM 里面 -->
      <!-- true是分布式模式 -->
      <property>
      <name>hbase.cluster.distributed</name>
      <value>true</value>
      </property>

      <!-- ZooKeeper的地址 -->
      <property>
      <name>hbase.zookeeper.quorum</name>
      <value>master,slaver01,slaver02</value>
      </property>

      <!-- ZooKeeper快照的存储位置 -->
      <property>
      <name>hbase.zookeeper.property.dataDir</name>
      <value>/opt/module/zookeeper-3.4.6/data</value>
      </property>

      <!-- V2.1版本,在伪分布式情况下, 设置为 false -->
      <!-- 当使用 hdfs 时, 设置为 true -->
      <property>
      <name>hbase.unsafe.stream.capability.enforce</name>
      <value>false</value>
      </property>
      </configuration>

      需要注意的是:

      这里使用了ns这个高可用通道,因此需要将Hadoop的 core-site.xmlhdfs-site.xml移动到/opt/module/hbase-2.1.0/conf

    • 修改 regionserver 文件

      1
      2
      3
      master
      slaver01
      slaver02