SaaS多租户系统数据隔离实现方案

参考: https://juejin.cn/post/7234763992333189175

背景

简单来说,一个租户就是一个公司的客户,多个租户共用同一个SaaS系统,一旦SaaS系统不可用,那么所有的租户都不可用,

多租户问题,其实是一种架构设计方式,就是在一台或者一组服务器上运行的SaaS系统,可以为多个租户(客户)提供服务,目的是为了让多个租户在互联网环境下使用同一套程序,且保证租户间的数据隔离。从这种架构设计的模式上,不难看出来,多租户架构的重点就是同一套程序下多个租户数据的隔离。由于租户数据是集中存储的,所以要实现数据的安全性,就是看能否实现对租户数据的隔离,防止租户数据不经意或被他人恶意地获取和篡改。

多租户数据隔离架构设计

目前SaaS多租户系统的数据隔离有三种架构设计,即:

  • 为每个租户提供独立的数据库
  • 为每个租户提供独立的表空间
  • 按字段区分租户

每种方案都有其各自的适用情况。

一个租户一个数据库

一个租户独立使用一个数据库,那就意味着SaaS系统要连接多个数据库,这种实现方案其实和分库分表架构设计是一样的,好处就是数据隔离级别高、安全性好,毕竟一个租户单用一个数据库,但是物理硬件成本、维护成本也变高了。

独立的表空间

所有租户共用一个数据库系统,但是每个租户在数据库系统中拥有一个独立的表空间。

按租户id字段隔离租户

这种方案是多租户方案中最简单的数据隔离方法,即在每张表中都添加一个用于区分租户的字段(如tenant_id或org_id啥的)来标识每条数据属于哪个租户,当进行查询的时候每条语句都要添加该字段作为过滤条件,其特点是所有租户的数据全都存放在同一个表中,数据的隔离性是最低的,完全是通过字段来区分的,很容易把数据搞串或者误操作。

三种数据隔离架构设计的对比如下:

隔离方案 成本 支持租户数量 优点 缺点
独立数据库系统 数据隔离级别高,安全性,可以针对单个租户开发个性化需求 数据库独立安装,物理成本和维护成本都比较高
独立的表空间 较多 提供了一定程度的逻辑数据隔离,一个数据库系统可支持多个租户 数据库管理比较困难,表繁多,同时数据修复稍复杂
按租户id字段区分 维护和购置成本最低,每个数据库能够支持的租户数量最多 隔离级别最低,安全性也最低
2023-06-08    
kill 命令的用途

Linux kill

用途:kill – terminate or signal a process

kill 是向进程发送信号的命令。当然我们可以向进程发送一个终止运行的信号,此时的 kill 命令才是名至实归。事实上如果我们不给 kill 命令传递信号参数,它默认传递终止进程运行的信号给进程!这是 kill 命令最主要的用法,也是本文要介绍的内容。

一般情况下,终止一个前台进程使用 Ctrl + C 就可以了。对于一个后台进程就须用 kill 命令来终止。我们会先使用 ps、top 等命令获得进程的 PID,然后使用 kill 命令来杀掉该进程。

kill命令格式

kill [options] <pid> [...]

<pid> […] : 把信号发送给列出的所有进程。

options :

    -<signal> : 指定发送给进程的信号,指定信号的名称或号码都可以。

    -l : 列出所有信号的名称和号码。

有哪些信号可以发送给进程

[root@centos ~]# kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX

这些信号中只有第9中信号(SIGKILL)才可以无条件的终止进程,其他信号进程都有权忽略。

2023-05-30    
k8s网络

k8s网络模型设计的一个基础原则是:每个Pod都拥有一个独立的IP地址,而且假定所有Pod都在一个可以直接连通的、扁平的网络空间中。

所以不管他们是否运行在同一个Node(宿主机中),都要求他们可以直接通过对方的IP进行访问。 设计这个原则的原因是,用户不需要额外考虑如何建立Pod之间的连接,也不需要考虑将容器端口映射到主机端口等问题。

Tips

  1. 查看一个网卡是否开启了混杂模式
[root@centos ~]# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,PROMISC,MULTICAST>  mtu 1500
        inet 172.17.36.2  netmask 255.255.240.0  broadcast 172.17.47.255
        ether 00:16:3e:03:f4:76  txqueuelen 1000  (Ethernet)

当输出包含PROMISC时,表明该网络接口处于混杂模式。

ifconfig eth0 promisc # 启用网卡的混杂模式
ifconfig eth0 -promisc # 关闭网卡的混杂模式

将网络设备加入Linux bridge后,会自动进入混杂模式。

2023-05-08    
《k8s权威指南学习》--ConfigMap

ConfigMap

使用ConfigMap 的限制条件如下。

  • ConfigMap 必须在Pod 之前创建。

  • ConfigMap 受Namespace 限制,只有处于相同Namespaces 中的Pod 可以引用它。

  • ConfigMap 中的配额管理还未能实现。

  • kubelet 只支持可以被API Server 管理的Pod 使用ConfigMap 。kubelet 在本Node 上通过 --manifest-url--config 自动创建的静态Pod将无法引用ConfigMap

  • 在Pod 对ConfigMap 进行挂载( volumeMount )操作时,容器内部只能挂载为“目录”,无法挂载为“文件”。在挂载到容器内部后,目录中将包含Co nfigMap 定义的每个item,如果该目录下原来还有其他文件,则容器内的该目录将会被挂载的ConfigMap 覆盖。如果应用程序需要保留原来的其他文件,则需要进行额外的处理。可以将ConfigMap挂载到容器内部的临时目录,再通过启动脚本将配置文件复制或者链接到( cp 或link命令)应用所用的实际配置目录下。

2023-03-26    
《k8s权威指南学习》--k8s核心原理

k8s核心原理

API Server

总体来看, Kubemetes API Server 的核心功能是提供了Kubemetes 各类资源对象(如Pod 、RC 、Service 等〉的增、删、改、查及Watch 等HTTP Rest 接口,成为集群内各个功能模块之间数据交互和通信的中心枢纽,是整个系统的数据总线和数据中心。 除此之外,它还有以下一些功能特性。

  1. 是集群管理的API入口。
  2. 是资源配额控制的入口。
  3. 提供了完备的集群安全机制。
2023-03-26    
《k8s权威指南学习》--Pod

Pod 生命周期

Pod 在整个生命周期过程中被系统定义为各种状态 Pod 的状态如表2.14 所示。 表2.14 Pod 的状态

状态值 描述
Pending API Server已经创建该Pod,但Pod内还有一个或多个容器的镜像没有创建,包括正在下载镜像的过程
Running Pod 内所有容器均己创建,且至少有一个容器处于运行状态、正在启动状态或正在重启状态
Succeeded Pod 内所有容器均成功执行退出, 且不会再重启
Failed Pod 内所有容器均已退出,但至少有一个容器退出为失败状态
Unknown 由于某种原因无法获取该Pod 的状态, 可能由于网络通信不畅导致

Pod重启策略

Pod 的重启策略( RestartPolicy )应用于Pod 内的所有容器,井且仅在Pod 所处的Node上由kubelet 进行判断和重启操作。当某个容器异常退出或者健康检查(详见下节)失败时, kubelet将根据RestartPolicy 的设置来进行相应的操作。 Pod 的重启策略包括Always、OnFailure和Never, 默认值为Always:

  • Always:当容器失效时,由kubelet自动重启该容器。
  • OnFailure:当容器终止运行且退出码不为0时,由kubelet自动重启该容器。
  • Never :不论容器运行状态如何, kubelet 都不会重启该容器。

kubelet 重启失效容器的时间间隔以sync-frequency 乘以2n 来计算;例如1、2 、4 、8 倍等, 最长延时5min ,并且在成功重启后的10min后重置该时间。

Pod健康检查

可以通过两类探针来检查: LivenessProbe 和ReadinessProbe

  • LivenessProbe 探针:用于判断容器是否存活( running 状态),如果LivenessProbe 探针探测到容器不健康,则kubelet 将杀掉该容器,并根据容器的重启策略做相应的处理。如果一个容器不包含LivenessProbe 探针,那么kubelet 认为该容器的LivenessProbe 探针返回的值永远是“ Success"
  • ReadinessProbe 探针:用于判断容器是否启动完成( ready 状态),可以接收请求。如果ReadinessProbe 探针检测到失败,则Pod 的状态将被修改。Endpoint Con位oiler 将从Service 的Endpoint 中删除包含该容器所在Pod 的Endpoint 。

对于每种探测方式,都需要设置initialDelaySeconds和timeoutSeconds两个参数,它们的含 义分别如下。

2023-03-26    
《k8s权威指南学习》--Service

Service

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
2023-03-26    
linux cron表达式

解析crontab表达式的网站:https://crontab.guru/

Cron是什么?

简单来讲,cron是基于Unix的系统上的一个实用程序。它使用户能够安排任务在指定的【日期/时间】定期运行。它自然是一个伟大的工具,可以自动运行大量流程,不需要人工干预。

Cron作为守护进程运行。这意味着它只需要启动一次,并将在后台继续运行。此过程使用crontab读取计划条目并启动任务。

随着时间的推移,cron表达式格式被广泛采用,许多其他程序和库也使用它。

Cron表达式

既然使用定时任务,那必定离不开定时表达式,其命名规则有如下部分组成:

<minute> <hour> <day-of-month> <month> <day-of-week> <command>

其中每一个域可出现的字符如下。

  • minute:可出现,.*/这4 个字符,有效范围为0-59的整数。
  • hour:可出现,.*/这4 个字符,有效范围为0-23整数。
  • day-of-month:可出现,.*/?LWC这8个字符,有效范围为0-31的整数。
  • month :可出现,.*/这4个字符,有效范围为1-12的整数或JAN-DEC 。
  • day-of-week:可出现,.*/?LC#这8个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一,以此类推。

特殊字符

(1)*:表示匹配该域的任意值。假如在Minutes域使用*, 即表示每分钟都会触发事件。

(2)?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。

(3)-:表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次

(4)/:表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着第一次触发是在第5分钟时,接下来每20分钟触发一次,将在第25,45时刻等分别触发一次.

(5),:表示列出枚举值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。

(6)L:表示最后,只能出现在DayofWeek和DayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。

(7)W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。

(8)LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。

(9)#:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

常见例子

# 每天凌晨1点执行
0 1 * * * /usr/local/test.sh

# 每10分钟执行
*/10 * * * * /usr/local/test.sh

# 每天晚上21:30执行一次
30 21 * * * /usr/local/test.sh

# 每个月1号、10号、22号凌晨4:45执行
45 4 1,10,22 * * /usr/local/test.sh

# 每天18点至23点之间,每隔30分钟执行
0,30 18-23 * * * /usr/local/test.sh

参考:https://blog.csdn.net/IndexMan/article/details/120801069

2023-03-26    
ASCII码表

ASCII码,使用7位二进制数,表示128个标准ASCII字符,使用8位二进制数,表示256 个标准及扩展ASCII字符;

ASCII编码字符分类:

控制字符:0~32、127表示,共33个,如CR(回车)、LF(换行)、FF(换页)、BS(退格)、DEL(删除)、Space(空格)等。

特殊符号:33-47表示,如+(加)、-(减)、*(乘)、/(除)、!(感叹号)。

数字:48~57表示,0-9阿拉伯数字。

字母:65~90为26个大写英文字母,97~122号为26个小写英文字母。

DEC OCT HEX BIN 缩写/符号 HTML实体 描述
0 000 00 00000000 NUL Null char (空字符)
1 001 01 00000001 SOH Start of Heading (标题开始)
2 002 02 00000010 STX Start of Text (正文开始)
3 003 03 00000011 ETX End of Text (正文结束)
4 004 04 00000100 EOT End of Transmission (传输结束)
5 005 05 00000101 ENQ Enquiry (请求)
6 006 06 00000110 ACK Acknowledgment (收到通知)
7 007 07 00000111 BEL Bell (响铃)
8 010 08 00001000 BS Back Space (退格)
9 011 09 00001001 HT Horizontal Tab (水平制表符)
10 012 0A 00001010 LF Line Feed (换行键)
11 013 0B 00001011 VT Vertical Tab (垂直制表符)
12 014 0C 00001100 FF Form Feed (换页键)
13 015 0D 00001101 CR Carriage Return (回车键)
14 016 0E 00001110 SO Shift Out / X-On (不用切换)
15 017 0F 00001111 SI Shift In / X-Off (启用切换)
16 020 10 00010000 DLE Data Line Escape (数据链路转义)
17 021 11 00010001 DC1 Device Control 1 (设备控制1)
18 022 12 00010010 DC2 Device Control 2 (设备控制2)
19 023 13 00010011 DC3 Device Control 3 (设备控制3)
20 024 14 00010100 DC4 Device Control 4 (设备控制4)
21 025 15 00010101 NAK Negative Acknowledgement (拒绝接收)
22 026 16 00010110 SYN Synchronous Idle (同步空闲)
23 027 17 00010111 ETB End of Transmit Block (传输块结束)
24 030 18 00011000 CAN Cancel (取消)
25 031 19 00011001 EM End of Medium (介质中断)
26 032 1A 00011010 SUB Substitute (替补)
27 033 1B 00011011 ESC Escape (溢出)
28 034 1C 00011100 FS File Separator (文件分割符)
29 035 1D 00011101 GS Group Separator (分组符)
30 036 1E 00011110 RS Record Separator (记录分离符)
31 037 1F 00011111 US Unit Separator (单元分隔符)
32 040 20 00100000 Space (空格)
33 041 21 00100001 ! ! Exclamation mark
34 042 22 00100010 " " Double quotes
35 043 23 00100011 # # Number
36 044 24 00100100 $ $ Dollar
37 045 25 00100101 % % Procenttecken
38 046 26 00100110 & & Ampersand
39 047 27 00100111 ' Single quote
40 050 28 00101000 ( ( Open parenthesis\
41 051 29 00101001 ) ) Close parenthesis
42 052 2A 00101010 * * Asterisk
43 053 2B 00101011 + + Plus
44 054 2C 00101100 , , Comma
45 055 2D 00101101 - - Hyphen
46 056 2E 00101110 . . Period, dot or full stop
47 057 2F 00101111 / / Slash or divide
48 060 30 00110000 0 0 Zero
49 061 31 00110001 1 1 One
50 062 32 00110010 2 2 Two
51 063 33 00110011 3 3 Three
52 064 34 00110100 4 4 Four
53 065 35 00110101 5 5 Five
54 066 36 00110110 6 6 Six
55 067 37 00110111 7 7 Seven
56 070 38 00111000 8 8 Eight
57 071 39 00111001 9 9 Nine
58 072 3A 00111010 : : Colon
59 073 3B 00111011 ; ; Semicolon
60 074 3C 00111100 < < Less than
61 075 3D 00111101 = = Equals
62 076 3E 00111110 > > Greater than
63 077 3F 00111111 ? ? Question mark
64 100 40 01000000 @ @ At symbol
65 101 41 01000001 A A Uppercase A
66 102 42 01000010 B B Uppercase B
67 103 43 01000011 C C Uppercase C
68 104 44 01000100 D D Uppercase D
69 105 45 01000101 E E Uppercase E
70 106 46 01000110 F F Uppercase F
71 107 47 01000111 G G Uppercase G
72 110 48 01001000 H H Uppercase H
73 111 49 01001001 I I Uppercase I
74 112 4A 01001010 J J Uppercase J
75 113 4B 01001011 K K Uppercase K
76 114 4C 01001100 L L Uppercase L
77 115 4D 01001101 M M Uppercase M
78 116 4E 01001110 N N Uppercase N
79 117 4F 01001111 O O Uppercase O
80 120 50 01010000 P P Uppercase P
81 121 51 01010001 Q Q Uppercase Q
82 122 52 01010010 R R Uppercase R
83 123 53 01010011 S S Uppercase S
84 124 54 01010100 T T Uppercase T
85 125 55 01010101 U U Uppercase U
86 126 56 01010110 V V Uppercase V
87 127 57 01010111 W W Uppercase W
88 130 58 01011000 X X Uppercase X
89 131 59 01011001 Y Y Uppercase Y
90 132 5A 01011010 Z Z Uppercase Z
91 133 5B 01011011 [ [ Opening bracket
92 134 5C 01011100 \|\|Backslash
93 135 5D 01011101 ] ] Closing bracket
94 136 5E 01011110 ^ ^ Caret - circumflex
95 137 5F 01011111 _ _ Underscore
96 140 60 01100000 ` ` Grave accent
97 141 61 01100001 a a Lowercase a
98 142 62 01100010 b b Lowercase b
99 143 63 01100011 c c Lowercase c
100 144 64 01100100 d d Lowercase d
101 145 65 01100101 e e Lowercase e
102 146 66 01100110 f f Lowercase f
103 147 67 01100111 g g Lowercase g
104 150 68 01101000 h h Lowercase h
105 151 69 01101001 i i Lowercase i
106 152 6A 01101010 j j Lowercase j
107 153 6B 01101011 k k Lowercase k
108 154 6C 01101100 l l Lowercase l
109 155 6D 01101101 m m Lowercase m
110 156 6E 01101110 n n Lowercase n
111 157 6F 01101111 o o Lowercase o
112 160 70 01110000 p p Lowercase p
113 161 71 01110001 q q Lowercase q
114 162 72 01110010 r r Lowercase r
115 163 73 01110011 s s Lowercase s
116 164 74 01110100 t t Lowercase t
117 165 75 01110101 u u Lowercase u
118 166 76 01110110 v v Lowercase v
119 167 77 01110111 w w Lowercase w
120 170 78 01111000 x x Lowercase x
121 171 79 01111001 y y Lowercase y
122 172 7A 01111010 z z Lowercase z
123 173 7B 01111011 { { Opening brace
124 174 7C 01111100
125 175 7D 01111101 } } Closing brace
126 176 7E 01111110 ~ ~ Equivalency sign (tilde)
127 177 7F 01111111 Delete

———————————————— 版权声明:本文为CSDN博主「火腿肠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_39511050/article/details/126809454

2023-02-23    
Raft

可理解的共识算法研究

(In Search of an Understandable Consensus Algorithm–Extended Version)

作者:Diego Ongaro and John Ousterhout–Stanford University

摘要

Raft是管理复制日志的共识算法, 比Paxos牛.

1. 介绍

复制状态机,如图1

图1

共识算法管理

2. Raft把共识算法分解为3个独立的子问题:

2.1 Leader选举

当前Leader挂掉后必须进入一个新Leader的选举

集群中每个节点,任意时刻处于Leader, Follower, Candidate这三个角色之一。选举特点如下:

  • 当集群初始化时候,每个节点都是Follower角色;
  • 集群中存在至多1个有效的主节点,通过心跳与其他节点同步数据;
  • 当Follower在一定时间内没有收到来自主节点的心跳,会将自己角色改变为Candidate,并发起一次选主投票;当收到包括自己在内超过半数节点赞成后,选举成功;当收到票数不足半数选举失败,或者选举超时。若本轮未选出主节点,将进行下一轮选举(出现这种情况,是由于多个节点同时选举,所有节点均未获得过半选票)。
  • Candidate节点收到来自主节点的信息后,会立即终止选举过程,进入Follower角色。

为了避免陷入选主失败循环,每个节点未收到心跳发起选举的时间是一定范围内的随机值,这样能够避免2个节点同时发起选主。

2.2 日志复制

所谓日志复制,是指Leader将每次操作形成日志条目,并持久化到本地磁盘,然后通过网络IO发送给其他节点。其他节点根据日志的逻辑时钟(TERM)和日志编号(INDEX)来判断是否将该日志记录持久化到本地。 当Leader收到包括自己在内超过半数节点成功返回,那么认为该日志是可提交的(committed),并将日志输入到状态机,将结果返回给客户端。

这里需要注意的是,每次选Leader都会形成一个唯一的TERM编号,相当于逻辑时钟。每一条日志都有全局唯一的编号。

Leader通过网络IO向其他节点追加日志。若某节点收到日志追加的消息,首先判断该日志的TERM是否过期,以及该日志条目的INDEX是否比当前已经提交的日志的INDEX更早。若已过期,或者比提交的日志更早,那么就拒绝追加,并返回该节点当前的已提交的日志的编号。否则,将日志追加,并返回成功。

当Leader收到其他节点关于日志追加的回复后,若发现有拒绝,则根据该节点返回的已提交日志编号,发送其编号下一条日志。

Leader向其他节点同步日志,还作了拥塞控制。具体地说,主节点发现日志复制的目标节点拒绝了某次日志追加消息,将进入日志探测阶段,一条一条发送日志,直到目标节点接受日志,然后进入快速复制阶段,可进行批量日志追加。

按照日志复制的逻辑,我们可以看到,集群中慢节点不影响整个集群的性能。另外一个特点是,数据只从Leader复制到Follower,这样大大简化了逻辑流程。

2.3 安全性

Leader不会删除或复写log entries;只增加新的entries

选主以及日志复制并不能保证节点间数据一致。试想,当一个某个节点挂掉了,一段时间后再次重启,并当选为主节点。而在其挂掉这段时间内,集群若有超过半数节点存活,集群会正常工作,那么会有日志提交。这些提交的日志无法传递给挂掉的节点。当挂掉的节点再次当选主节点,它将缺失部分已提交的日志。在这样场景下,按Raft协议,它将自己日志复制给其他节点,会将集群已经提交的日志给覆盖掉。

这显然是不可接受的。

其他协议解决这个问题的办法是,新当选的主节点会询问其他节点,和自己数据对比,确定出集群已提交数据,然后将缺失的数据同步过来。这个方案有明显缺陷,增加了集群恢复服务的时间(集群在选举阶段不可服务),并且增加了协议的复杂度。

Raft解决的办法是,在选主逻辑中,对能够成为主的节点加以限制,确保选出的节点已定包含了集群已经提交的所有日志。如果新选出的主节点已经包含了集群所有提交的日志,那就不需要从和其他节点比对数据了。简化了流程,缩短了集群恢复服务的时间。

这里存在一个问题,加以这样限制之后,还能否选出主呢?答案是:只要仍然有超过半数节点存活,这样的主一定能够选出。因为已经提交的日志必然被集群中超过半数节点持久化,显然前一个主节点提交的最后一条日志也被集群中大部分节点持久化。当主节点挂掉后,集群中仍有大部分节点存活,那这存活的节点中一定存在一个节点包含了已经提交的日志了。

3. Raft 节点

一般来说,Raft集群节点个数为5个,此时可以容忍两个失效。

3.1 节点角色

Leader, Follower, Candidate,一般来说,只有一个Leader,其余都是Follower。

图4 图4:各节点的状态。

3.1.1 Followers

只响应来自其他节点的请求【只对Leader和Candidate做出响应】,如果没有收到其他节点的信息,则会变成Candidate并且初始化一个选举过程。若该节点收到了客户端的请求,则会重定向至leader节点。

3.1.2 Candidate

用来选举新的Leader。如果收到大多数节点的投票,则会变成Leader。

2023-02-23