开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

27-YongGC、MinorGC、 Major GC、Fu

发表于 2021-08-31

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

今天穿插一篇特别说明篇,有些同学在问YongGC、MinorGC、MajorGC以及Old GC、Full GC这么多名词到底谁是谁?搞晕了!本篇文章特别说明下

这里我们把一些名词拿来给大家补充说明下,比如MinorGC 、YongGC、Full GC、OldGC、Major GC、Mixed GC,可能每个人在说的时候都有点混淆和模糊怎么有这么多词呢?到底哪个代表的是新生代、老年代?

这次名词其实在业内并没有一个统一的标准和定义,以及在一些书中也是不同,面试的时候如果不说清楚可能跟面试官的理解也会有差别,因此我们一起说明下:

(1)Minor GC / YongGC

这两个名词都是指的新生代或年轻代,从字面意思也能理解。那么新生代GC,我们就可以说是YongGC或MinorGC,两者都可以。

(2)老年代GC,Old GC?

之前我们说老年代GC 一直都说用的Full GC,没有提过 Old GC ,当然如果要说单独说老年代GC ,那么用Old GC是比较合适的。后续我们提到老年代GC 也都用 Old GC来。(当然如果用Full GC其实也没有问题,一般来讲触发到Old GC都会要么前要么后顺带一次Minor GC,所以我们一般也就直接称为Full GC)

(3)Full GC

首先Full GC是指的针对 新生代、老年代、永久代的全体内存空间的垃圾回收,所以叫做 Full GC。

(4)Major GC

很多书籍或博客里有些说 Major GC == Old GC, 也有说 == Full GC,这个词目前用的很混淆,所以个人建议尽量不提这个词,如果真有面试中面试官说到这个词,那么可以向他明确一下是指的年老代回收还是整堆回收。

(5)Mixed GC

Mixed GC是G1中特有的概念,当老年代内存占据到了45%就户触发Mixed GC,对新生代和老年代都进行回收。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

分布式系统中的副本操作详解!基于Quorum机制实现副本的更

发表于 2021-08-31

这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战

基本约定

  • 更新操作write是一系列顺序的过程
  • 通过其余机制确定更新操作的顺序. 比如由primary-secondary架构中的primary决定顺序
  • 每次更新操作记为wi. 其中i为更新操作单调递增的序号
  • 每个wi执行成功后副本数据都会发生变化,称为不同的数据版本,记为vi
  • 假设每个副本都保存了所有历史版本的数据

write-all-read-one

  • write-all-read-one: WARO
    • 是一种最简单的副本控制规则
    • 在更新时写所有的副本,只有在所有的副本更新成功,才认为更新成功,从而保证所有的副本一致
    • 在读取数据时可以读取任一副本上的数据
  • 对于更新服务,尽管有N个副本,但系统无法容忍任何一个副本异常:
    • 由于更新操作需要在所有的N个副本上都成功,更新操作才能成功,所以一旦有一个副本异常,更新操作失败,更新服务不可用
  • 对于读服务 ,N个副本中只要有一个副本正常,系统就可以提供读服务:
    • 当有N个副本时,系统可以容忍N-1个副本异常
  • 由此可见 ,WARO的读服务的可用性较高,但是更新服务的可用性不高.尽管使用了副本,但是更新服务的可用性等效于没有副本

Quorum定义

  • 在Quorum机制下,当某次更新操作wi如果在所有N个副本中的W个副本上都成功,就认为该更新操作为 “成功提交的更新操作.” 对应的数据为 “成功提交的数据”
  • 令 R > N-W, 由于更新操作wi仅在W个副本上成功,由于W+R > N, 任意R个副本组成的集合一定与成功的W个副本组成的集合有交集,所以读取R个副本一定能读取到wi更新后的数据vi
  • Quorum机制的可用性分析:
    • 限制Quorum参数为W+R=N+1
    • 对于更新服务,由于更新操作需要在W个副本上都成功,更新操作才能成功,所以如果N-W+1个副本异常,更新操作始终无法在W个副本上成功,更新服务不可用
    • 对于读服务,如果N-R+1个副本异常,那么无法保证一定可以读到与W个副本有交集的副本合集,因此读服务的一致性下降
  • 仅仅依赖Quorum机制是无法保证强一致性的:
    • 仅有Quorum机制时无法确定最新已成功提交的版本号
    • 除非将最新已提交的版本号作为元数据由特定的元数据服务器或者元数据集群管理,否则很难确定最新成功提交的版本号
  • Quorum机制的三个系统参数N,W,R控制了系统的可用性,也是系统对用户的服务承诺:
    • 数据最多有N个副本,但是数据更新成功W个副本即返回更新成功
    • 对于一致性较高的Quorum系统,系统还要承诺任何时候不读取未成功提交的数据,即读取到的数据都是曾经在W个副本上成功的数据

读取最新成功提交的数据

  • Quorum机制只需成功更新N个副本中的W个,在读取R个副本时,一定可以读到最新的成功提交的数据
  • 由于有不成功的更新情况存在,仅仅读取R个副本却不一定能确定是哪个版本的数据是最新已提交的数据
  • 对于一个强一致性Quorum系统:
    • 若成功读取的数据少于W个,假设为X个,则继续读取其余副本,直到成功读取到W个该版本的副本,则该数据为最新的成功提交的数据
    • 如果在所有副本中该数据的个数肯定不满足W个,则R中版本号第二大的为最新成功提交的副本
    • 示例:
      • 在读取到 (v2, v1, v1) 时,继续读取剩余的副本:
        • 若读取到剩余的两个副本为 (v2, v2), 那么v2是最新的成功提交的副本
        • 若读取到剩余的两个副本为 (v2, v1) 或者 (v1, v1), 那么v1是最新成功提交的版本
        • 若读取后续两个副本有任一超时或者失败,那么无法判断哪个版本是最新的成功提交的副本
  • 仅仅使用Quorum机制时,如果要确定最新的成功提交的版本,最多需要读取R+(W-R-1)=N个副本,当出现任一副本异常时,读最新的成功提交的版本这一功能都可能不可用
  • 在工程实践中,应该尽量避免通过Quorum机制读取最新的成功提交的版本,可以通过Quorum机制与primary-secondary控制协议结合使用,通过读取primary的方式读取到最新的已提交的副本

基于Quorum机制选择primary副本

  • 读取数据时依照一致性要求的不同可以有不同的做法:
    • 如果要求强一致性的立刻读取到最新的成功提交的数据:
      • 可以直接只读取primary副本上的数据
      • 也可以单纯使用Quorum机制读取最新的成功提交的版本数据
    • 如果要求会话一致性:
      • 可以根据之前已经读取的数据版本号在各个副本上进行选择性读取
    • 如果只需要弱一致性:
      • 可以选择任意副本读取
  • 在primary-secondary协议中:
    • 当primary异常时,需要选出一个新的primary, 之后secondary副本与primary同步数据
    • 通常情况下,选择新的primary的工作是由一个中心节点完成
  • 基于Quorum机制的primary-secondary协议:
    • 常用的primary选择方式与读取数据方式类似: 中心节点读取R个副本,选择R个副本中版本号最高的副本作为新的primary
    • 新primary与至少W个副本完成数据同步后作为新的primary提供读写服务:
      • R个副本中版本号最高的副本一定包含最新成功提交的数据
      • 虽然不能确定最高版本号的数是否为一个成功提交的数据,但是新的primary在随后与secondary同步数据,使得该版本的副本个数达到W, 从而使得该版本的数据成为成功提交的数据
  • 示例:
    • 在N=5, W=3, R=3的系统中,某时刻副本最大版本号为 (v2,v2, v1, v1, v1), 此时v1是系统的最新的成功提交的数据 ,v2是一个处于中间状态未成功提交的数据
    • 假设此刻primary副本异常,中心节点进行primary切换工作
    • 这类 “中间态” 数据是作为 “脏数据” 删除,还是作为新的数据被同步后成为生效的数据,完全取决于这个数据能否参与新的primary的选举:
      • 如果中心节点与其中3个副本通信成功,读取到的版本号为 (v1, v1, v1), 则任选一个副本作为primary, 新的primary以v1作为最新的成功提交的版本并与其余副本同步
        • 当与第1个和第2个副本同步数据时,由于第1个和第2个副本的版本号大于primary, 属于 脏数据, 处理脏数据,可以设计基于undo日志的方式删除脏数据
        • 工程实践中,新的primary也可能会与后面两个副本完成同步后就提供数据服务,随后自身版本号也更新到v2. 如果系统不能保证之后的v2与之前的v2完全一样,则新的primary在与第1个和第2个副本同步数据时不但要比较版本号,还需要比较更新操作的具体内容是否一样
      • 如果中心节点与其中3个副本通信成功,读取到的版本号为 (v2, v1, v1), 则选取版本号为v2的副本作为新的primary
      • 如果新的primary与其余2个副本完成数据同步,则符合v2的副本个数达到W个,成为最新的成功提交的副本,新的primary可以提供正常的读写服务

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

shell脚本-基础篇

发表于 2021-08-31

shell的功能

  1. 自动补全
1
shell复制代码yum -y install bash-completion
  1. 查看历史命令
1
2
3
4
5
bash复制代码history #查看所有历史命令
history -n #查看最近的n条命令
history -d n #删除第n条命令记录
!n #执行第n条命令
!-n #执行底数第n条命令
  1. 别名功能
1
2
3
4
5
6
7
bash复制代码alias h20 = 'head -20' #创建别名,以后就可以用h20 代替后者
unalias h20 #取消别名
#让别名永久生效
vim ~/.bashrc
alias h ='cd /' #添加别名
#然后退出
source ~/.bashrc #刷新文件

image-20210831164719583

bash初始化

环境变量的加载

  1. /etc/profile:
  2. /etc/bashrc:
  3. ~/.profile:
  4. ~/.bash_login
  5. ~/.bash_profile
  6. ~/.bashrc
  7. ~/.bash_logout

加载顺序

图形化界面登录时:1 =>3

图形化界面登录后:打开终端时:2=> 5

文本界面登录时: 2 => 1=>5

su进行用户切换:2=>1=>5

常用命令

1
2
3
4
5
bash复制代码command & #让作业进入后台运行
ctrl + z #将当前作业切换到后台
jobs #查看后台作业状态
fg %n #将后台运行作业n切换到前台
bg %n #让指定的作业n在后台运行

重定向

文件描述符

0:标准输入文件

1:标准输出

2:标准错误输出文件

输出重定向:

1
2
3
4
5
bash复制代码command >file #将结果以覆盖的方式写入文件(重定向符号前面没写就默认是1)
command >>file #将结果以追加的方式写入文件
command 2>>file #将command错误信息以追加的方式写入文件
command >file 2>&1 #以覆盖的方式将正确结果和错误信息同时写入同一个文件
command >>file 2>&1 #以追加的方式将正确和错误的结果同时追加到同一个文件中

&表示的是标准标准正确和错误的集合,

&1指的是标准正确通道,

&2指的是标准错误通道,

/dev/null相当于Linux中的回收站,对于一些不必要的信息可以全部重定向到该目录中,放入的垃圾不可恢复;

输入重定向:

1
2
3
4
5
6
7
8
bash复制代码command <file  #将file文件中的内容作为command的输入
command <<END #从标准输入中读取数据,知道遇见分界符END才停止(分界符可自定义)
#案例
while read str
do
echo $str
done < readme.txt #实现将readme.txt中的语句一行一行的输出。
mysqladmin -uroot -p 123 test <test.sql #实现将test.sql中文件内容写到test中
tee

主要是用于将前面结果展示到前端的同时,也可以写入到文件中

1
bash复制代码cat /etc/profile | tee test.txt #将/etc/profile中的内容展示到前台,同时写入test.txt文件中(值得注意的是:写入文件的方式是覆盖整个文件,如果想以追加的方式写入,就用tee -a)

命令排序

&& 、|| 实现逻辑判断

&&:表示只有符号前面的命令执行成功后才会执行符号后面的语句

1
bash复制代码systemctl mysqld start && mysql -uroot -p xxx #只有mysql启动成功才会执行登录命令

||:表示符号前面的命令没有执行成功才会执行符号后面的语句

1
bash复制代码sl || yum install sl #如果sl这个命令使用不成功,就会去安装sl这个命令
; 不具备逻辑判断,只是连接两条命令

编程中正常用法

通配符(和正则略有不同)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bash复制代码*: #匹配0个或者多个任意字符(想起mysql模糊查询的%了吗?)
ls /etc/*.conf #匹配/etc/下面所有以.conf结尾的文件

?: #匹配一个任意字符
ls /etc/???.conf #匹配/etc/下面所有以.conf结尾并且文件名长度是3的文件

[list]: #匹配list中任意单个字符
ls /etc/[list].conf #匹配/etc/下面所有以.conf结尾并且文件名在list中的某个文件

[!list]: #匹配除list中的任意单个字符
ls /etc/[!list].conf #匹配/etc/下面所有以.conf结尾且文件名不在list中的某个文件

[c1-c2]: #匹配c1-c2间的任意单个字符
ls /etc/[c1-c2].conf #匹配/etc/下面所有以.conf结尾且文件名在c1-c2中的某个文件

{string1,string2,......}: #匹配string1,string...中的任意一个字符。
ls /etc/{"1","2","3"}.conf #匹配/etc/下面所有以.conf结尾且文件名不在在{"1","2","3"}中的文件的某个文件

shell规范

1
2
3
4
5
6
7
8
9
10
11
bash复制代码#1.开头必须执行解释器 通过`#!`指定
cat /etc/Shells #默认支持的解释器查看:
/bin/sh
/bin/bash
/user/bin/sh
/user/bin/bash
#2.注释使用
`#`: #单行注释
`:<<! 内容 !`: #多行注释

#其实和java等规范差不多,这里不详说了。

脚本执行检查

1
2
3
bash复制代码sh -n: #不执行script,仅查询语法问题
sh -v: #在执行脚本前,先将脚本的内容输出到屏幕上
sh -x: #将使用的脚本内容输出到屏幕

脚本执行方式:

1
2
3
4
5
bash复制代码sh == bash #不需要添加权限

./: #需要添加权限

/usr/local/test.sh: #也可以以路径的方式执行,同样需要权限:

read

  • read 变量名
  • read -p “提示信息” 变量名
  • read -t n -p”提示信息” 变量名 #-t表示多少秒不输入就算超时
  • read -n n 变量名 #表示读取n个输入字符

引用变量

1
2
3
4
5
6
7
8
bash复制代码"" : 表示弱引用,引用的值将是变量对应的值
test="hello shell"
echo "${test}"
结果是:hello shell
'': 表示强引用,引用的值值中间的字符串
test="hello shell"
echo '${test}'
结果是:${test}

变量运算

整数运算

1
2
3
4
5
6
7
8
9
10
11
12
13
bash复制代码1.使用expr
expr m + n #加
expr m - n #减
expr m / n #除
expr m \* n #乘

2.使用$((运算式))或者$[运算式]
echo $(($a + $b))
echo $[$a + $b]

3.使用let
let sum=2+3; echo $sum
let i++; echo $i

小数运算

1
2
3
4
5
bash复制代码echo "2/4" |bc  #0
echo "2*4" |bc #8
echo "scale=2;6/4" #1.50 指定精确计算到小数点后两位()
awk 'BEGIN(print 1/2)' #0.5
echo "print 5.0/2 |python" #2
  • 乘积小数点位数默认以乘数中小数点位数最多的为准(不指定scale参数)
  • 除法中如果不指定scale参数,则结果没有小数位,小数位数完全按scale值来显示,计算结果的小数位数不足scale的值,则末尾补0

贪婪匹配和非贪婪匹配

一个%为非贪婪匹配,即匹配最短结果。%从右到左进行非贪婪匹配。匹配的内容为以%右边为分割符,分隔符右边的指定字符的最短字符串,然后删掉。

1
2
bash复制代码v=www.baidu.com
echo ${v%.*} #www.baidu

两个%%则为贪婪匹配

1
2
bash复制代码v=www.baidu.com
echo ${v%%.*} # www,匹配到的是以.为定界符的最长字符串

#则和%顺序相反,从左边开始匹配

1
2
3
bash复制代码v=www.baidu.com
echo ${v#*.} #baidu.com
echo ${v##*.} #com

字符串截取还可以用这样的方式:

1
bash复制代码${value: offset:length} #从offset开始截取指定长度

变量替换

使用/

1
2
3
bash复制代码url="www.baidu.com"
echo ${url/baidu/sougou} #www.sougou.com(只会替换一个,属于非贪婪匹配,也就是最短匹配)
echo ${url//w/W} #WWW.baidu.com(会匹配多个,属于贪婪匹配)
1
2
3
bash复制代码${value:-word}: #当value没有定义或者值为空返回Word的内容,否则返回变量值
${value:+word}: #当value已经赋值,才用Word进行替换,否则不进行替换
${value:=word}: #当value没有定义或者为空,返回Word的值,同时将Word的值赋值为value,否则返回变量的值

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

如何设计一个可扩展性高的系统?

发表于 2021-08-31

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。

首先我们来看看今天的话题,扩展性的系统;

现在呢?市面上大多都是处于分布式的系统。而且里面每一个模块。都是称为微服务的一个名词。

你要设计出好的。可以向下沿着或者向上延展的系统,就必须要考虑各个服务之间的:工作状态、通信以及耦合程度。

系统的本质。是做多元化的事情。但也可以定制化的做某件事。系统就相当于一个中央处理器。可以强有力的管理,多路并发

正是因为如此。所以说系统中每一个模块。也就是微服务。中的功能。必须要考虑高度。集中于此服务。一句话概括。就是模块专人专事。只做定制化的东西。

我们来将可扩展的系统这几个名词拆解一下。其实在我看来,系统可扩展。就是一个比较好的高内聚,低耦合的分布式系统。其中微服务的拆分是必不可少的。根据业务的融合程度。可以预见的未来需求。我们将现在的服务切割,划分从而再组合。定制化中的功能。

其实这样讲。可能我们需求理解不太深。我们定制化一个功能或者场景来进行分析?

现在有多个系统同步去对接你的设备,但是每个系统设备功能,和你支持的都不同,你如果保证可扩展性?

对于系统的可扩展性。我们要降低标准在服务中下手。

我们主要的落脚点在于服务中。对于各服务之间的同步通信,出具处理。功能定制。业务扩展。你需要比较。每个系统之间的设备需求,总结出他们的要点,和通用功能。;

如果说你只是对接其中一家,那可以说直接对接就行。定制化的功能。就直接给他添加就行。但是对接到两家。或者两个系统之后。当前系统平台的对接任务明显增大。对于主系统的业务方向。设备的管理。你需要有一个规定。你需要同时对接两个系统。这两个系统中,差异化是你所必须要了解的前提;

最近一个。即使有了标准。也明显觉得不是特别完美。但要是两个这个就发生质变的。;

我们来聊聊方案。关于一个系统当前平台对接多个系统的设备管理。因为每个平台中设备的管理,其中他们的要求以及设备的一些详细信息都是不一样的。我们可以通过当前我们平台的标准。去给对方平台进行对接。

我们作为一个主体,去分发给两个物管平台对接。其中a品牌或许只实现了设备的。影子数据。但是b平台没有。这里面就体现出差异化的可扩展性,不能说没有,我们就不去实现,只能说是我们作为一个平台。去给他一个假象的实现。

也就是说,我们可以用最简单RPC的远程调用方法。

定制化的结果服务去分别。实现一个公共接口。当前a系统实现,,何必系统实现的是同一个接口。但是实现方式是他们自己的。

从而可以做到特别好的延展性和扩展性,,以后有什么添加的新功能,我们可以直接在物管平台的接口中。标准去添加,让这两个平台的系统去分别实现。这两个系统互不干扰。做到最基础的非侵入性。

可扩展高扩展,非侵入性。这是一个系统比较好的特点和特色。

从服务的角度来看,每个服务都是一个个体,在系统的总体把控下。架构。设计下。一次可以完成一个个功能要点的。需求实现。但是各个服务之间的数据交换。是我们需要考虑的要点。

可扩展性高的系统一般具有的特点。就是切分微服务。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

计算机毕业设计-基于JavaWeb的电影评论网站 电影推荐系

发表于 2021-08-31

基于JavaWeb的电影评论网站 电影推荐系统

项目运行视频:点击查看

一、系统截图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、开发环境以及技术架构

系统架构:本系统使用Java作为主要的编程语言编程开发,后台以MVC模式作为主要的技术支撑,数据库采用采用MySQL,前端采用JSP的页面技术配合JavaScript语言,同时引入百度的Ueditor编辑器丰富页面的内容。

开发环境: JDK7+eclipse+tomcat7+mysql5.0

三、系统功能

1.用户身份

用户注册 用户通过输入个人的身份信息以及登录信息完成注册。
用户登录 用户通过输入登陆账号以及密码完成用户登录。
影视资讯 查看发布的关于影视方面的一些时事资讯。
在线影视 查看剧组发布的在线影视资源,可以进行评论,点赞。
影视排行 根据用户对于电影的评论为依据进行排序展示。
影评留言 对于别的用户发布的影评,可以进行留言。
剧组信息 查看剧组发布的影视信息,对剧组进行评价。
个人信息管理 查看管理员发布的系统公告信息。
影评信息管理 发布自己的影评心得,让别的用户查看。
留言信息管理 查看别的用户对自己的留言,以及自己的留言信息。

2.剧组身份

剧组注册 剧组通过输入剧组的认证编号以及登录信息完成注册。
剧组登录 剧组通过输入登陆账号以及密码完成剧组登录。
剧组信息管理 修改剧组的导演,地址等基本信息。
影视信息管理 发布的影视信息进行管理,包括对于内容以及影视主图的添加,删除,查询等操作。
影视评价管理 查看本剧组发布的影视得到用户的评价信息。
剧组评价管理 查看用户对于本剧组的评价信息。

3.管理员身份

密码信息管理 密码信息的修改。

注册用户管理 对于普通的用户登录信息以及个人信息进行管理,对于剧组的登录信息以及个人信息进行管理。
影视资讯管理 发布一些影视资讯,预告,宣传片等资讯。
影视类别管理 对于影视的类别进行管理。
影视评价管理 对于影视资源的评价以及对于发布影视的剧组的评价信息管理,可以删除不良评论。

四、下载链接

点击下载

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

基于 RESTful 的 API 设计规范 RESTful

发表于 2021-08-31

RESTful API 基础

本规范在 API 设计上遵循 REST 架构风格,本部分会针对如何实现 RESTful API,作出说明

简介

REST,全称 Representational State Transfer(表现层状态转化),由 Roy Thomas Fielding 在他2000年的 博士论文 中提出的,是一种被广泛使用的 API 架构风格。

资源 Resource

在REST API的设计中,首先需要面向资源进行建模,其中每个节点是一个“简单资源”或“集合资源”。 为方便起见,它们通常被分别称为资源和集合。

  1. 一个集合包含相同类型的资源列表。 例如,一个用户拥有一组联系人。
  2. 资源具有状态,以及零个或多个子资源。 每个子资源可以是一个简单资源或一个集合资源。

方法 Method

每个资源都会对应一组操作方法,用户通过 API 来完成对应的操作(使用HTTP Method),常见的操作方法如下:

操作类型 HTTP 映射 举例
获取资源集合 GET curl -X GET foo.bar.com/api/v1/cust…
获取单个资源 GET curl -X GET foo.bar.com/api/v1/cust…
创建资源 POST curl -X POST foo.bar.com/api/v1/cust…
更新资源 PUT curl -X PUT foo.bar.com/api/v1/cust…
局部更新资源 PATCH curl -X PATCH foo.bar.com/api/v1/cust…
删除资源 DELETE curl -X DELETE foo.bar.com/api/v1/cust…

其中:POST/PUT 与 PATCH的区别在于全部更新,还是局部信息的更新,POST/PUT为该资源的所有字段均被更新或者覆盖。

RESTful API 设计规范

面向资源设计 URL

面向使用者建模

资源不是数据模型, 也不是领域模型,它的语义应该面向使用者。

反例:

1
2
3
4
bash复制代码# 面向数据模型设计资源,需要多次请求
/customers/123
/customers/123/baseinfo
/customers/123/tags

正例:

1
2
bash复制代码# 面向使用者设计,可以把资源定义为:顾客档案
/customers_archives/123

资源与角色相关

不同角色的资源可以不同,不同角色使用的资源可以是不一样的,比如:

管理员访问某个顾客的订单:

1
bash复制代码GET /customers/123/podcasts

顾客访问自己的订单:

1
bash复制代码GET /my_podcasts

一类资源两个 URL

每个资源都应该只有两个基础 URL(Endpoint),一个 URL 用于集合,另一个用于集合中的某个特定元素。

1
2
bash复制代码/customers      # customer 集合
/customers/1 # customer 集合中的特定元素

使用一致的复数名词

避免混用复数和单数形式,只应该使用统一的复数名词来表达资源。

反例:

1
2
bash复制代码GET /story
GET /story/1

正例:

1
2
bash复制代码GET /stories
GET /stories/1

复杂的查询逻辑使用查询字符串

保持URL简单短小,将复杂或可选参数移动到查询字符串。

1
ini复制代码GET /customers?country=usa&state=ca&city=sfo

表达资源之间的关联

当需要对关联在资源1下的资源2进行操作时,使用该形式构造URL:

resources/:resource_id/sub_resources/:sub_resource_id

反例:

1
2
bash复制代码GET /cusomters/podcasts/123
GET /getCustomerPodcasts?customer_id=123

正例:

1
2
3
bash复制代码GET /cusomters/5678/podcasts        # 获取某个客户的所有播客
GET /cusomters/5678/podcasts/123 # 获取某个客户的某个播客
POST /cusomters/5678/podcasts # 为某个客户创建一个新播客

使用 HTTP Method 表示动作

URL 中不应该包含动词,而是全部使用 Method 来表示动作。

反例:

1
2
3
4
5
bash复制代码GET /getCusomters
GET /getAllMaleCusomters
POST /createCusomter
POST /updateCustomer
POST /customer/create_for_management/

正例:

1
2
3
4
5
6
7
bash复制代码GET /customers                # 获取客户列表
GET /cusomters?gender=male # 获取客户列表(过滤出男性)
GET /customers/5 # 获取ID为5的客户
POST /cusomters # 创建新客户
PUT /cusomters/5 # 更新已存在的客户5(全量字段)
PATCH /cusomters/5 # 更新已存在的客户5(部分字段)
DELETE /cusomters/5 # 删除客户12

使用 HATEOAS

HATEOAS 是 Hypermedia As The Engine Of Application State 的缩写,在 Richardson Maturity Model中,它是 REST 的最高级形态,采用 Hypermedia 的 API 在响应中除了返回资源本身外,还会额外返回一组 Link。 这组 Link 描述了对于该资源,客户端接下来可以做什么以及怎么做,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
json复制代码{

"tracking_id": "123456",
"status": "WAIT_PAYMENT",
"items": [
{
"name": "potato",
"quantity": 1
}
],
"_links": {
"self": {
"href": "http://localhost:57900/orders/123456"
},
"cancel": {
"href": "http://localhost:57900/orders/123456"
},
"payment": {
"href": "http://localhost:57900/orders/123456/payments"
}
}
}

使用 HATEOAS 的好处包括但不限于:

  1. 前端不再需要硬编码绝大多数的后端 API URL,而是由后端在响应中返回,后端在对 API 重命名时可以做到前端无感知。
  2. 将一些业务规则统一收敛到后端,比如:有的功能对某个用户的可见性(权限)

自定义方法

结合实践,使用严格的 RESTful 会有一些语义不易表达(或者说表达起来很拧巴),所以在此基础上,并参考:Google Clould API - 自定义方法,允许使用一些自定义方法来进行表达。这些方法应该仅用于标准方法不易表达的功能。通常情况下,应该尽可能优先考虑使用标准方法,而不是自定义方法,使用方式如下:

  • 为了在表达上和资源区分开,自定义方法使用动词表示,表示针对资源的自定义动作
  • 自定义方法统一只使用 GET / POST 这两种 method。
1
2
3
4
5
bash复制代码# 一些自定义方法举例
POST /cusomters/5/cancel
POST /cusomters/5/undelete
POST /cusomters/5/search # 考虑到搜索通常参数比较长,使用GET可能会导致超出长度
GET /cusomters/batch_get

API 格式约定

URL 前缀

使用如下规则构建 URL:

1
2
3
ruby复制代码https://foo.bar.com/api/ + 业务域 + 版本号 + 资源集合 + 资源ID

例如:https://foo.bar.com/api/mall/v1/customers/1

Response Body 结构

使用相同的 HTTP 响应结构,推荐使用下列结构:

1
2
3
4
5
6
7
8
9
10
11
12
bash复制代码{

"code": 0, # 错误码,请求成功时返回0
"msg": "success", # 错误信息,请求成功时返回"success"
"data": { # 数据内容,结构必须为object,使用 list/string 均不合规范
"id": 1,
"name": "abc"
},
"extra": { # 错误码非0时,data应为空,推荐extra字段返回错误时需要携带的信息

}
}

版本号

  • 当 API 的升级是兼容的时,无需升级版本号。
  • 版本号使用简单的有序数,而不要使用点号(如:V1.2)。
  • 在新版本上线时需要保证旧版本API仍然可用,待旧版本不再有请求量时,才能进行下线。

URI Path 中的版本号

使用在 URI Path 中带版本号,来表示 API 整体的版本,当业务域的 API 发生了重大整体升级时,需要升级该版本号,形如:

1
ruby复制代码https://foo.bar.com/api/mall/v1

HTTP 状态码

使用合适 HTTP Status Code,表达响应的语义

HTTP 描述
200 No error.
400 Client specified an invalid argument. Check error message and error details for more information. (参数错误)Request can not be executed in the current system state (执行操作不满足接口前置条件)
401 Request not authenticated due to missing, invalid, or expired token. (访问身份错误、或者token错误)
403 Client does not have sufficient permission. (无权限)
404 A specified resource is not found, or the request is rejected by undisclosed reasons, such as whitelisting. (操作的资源不存在)
405 The HTTP method in the request is not allowed on the resource. (请求的方法不支持)
409 Concurrency conflict, such as read-modify-write conflict. (服务端出现并发冲突、幂等性冲突、读写冲突等等)
409 The resource that a client tried to create already exists. (要操作的资源已存在)
429 Either out of resource quota or reaching rate limiting. (限流错误)
500 Internal server error. Typically a server bug. (内部异常,不可恢复的)
503 Service unavailable. Typically the server is down.(服务不可用,可恢复异常,短时间之后可以进行重试并恢复的错误码)
504 Request deadline exceeded. This will happen only if the caller sets a deadline that is shorter than the method’s default deadline (i.e. requested deadline is not enough for the server to process the request) and the request did not finish within the deadline. (调用超时)

错误码

在使用 HTTP Status Code 的基础上,还需要有业务错误码,通过code字段返回。错误码由各业务方自行约定,业务内部自行划分区段。

分页

基于 page、page_size 的分页方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bash复制代码curl https://foo.bar.com/api/mall/v1/customers?page=1&page_size=10

{

"code": 0,
"message": "success",
"data": {
"pagination": {
"total": 3465
},
"customers": [
{
"id": 123,
"job_id": 456
}
]
}
}

基于 offset、limit 的分页方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bash复制代码curl https://foo.bar.com/api/mall/v1/customers?offset=20&limit=10
{
"code": 0,
"message": "success",
"data": {
"pagination": {
"total": 3465
},
"customers": [
{
"id": 123,
"job_id": 456
}
]
}
}

基于 page_token 的分页方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bash复制代码curl https://foo.bar.com/api/mall/v1/customers?page_token=xxxxxxx&page_size=10
{
"code": 0,
"message": "success",
"data": {
"pagination": {
"page_token": "yyyyyyyyyy",
"has_more": true
},
"customers": [
{
"id": 123,
"job_id": 456
}
]
}
}

API 度量指标

API 的实现方,需要密切关注以下基础监控指标,以便于:

  1. 及时发现系统的突发情况,如:接口QPS / 耗时激增,依赖的RPC接口耗时激增等。
  2. 为接口优化提供依据

请求量

  • 各接口的请求量,可选口径:QPS / 近7天请求量 / 近1天请求量。
  • 优化方向:在不影响用户体验的前提下,尽可能减少请求量

接口耗时

  • 各接口的响应耗时,可选口径:latency avg / p50 / p95 / p99
  • 优化方向:在满足使用者需求的前提下,尽可能少的耗时

I/O 扩散量(内部 I/O 访问量 & 耗时 & 错误量)

  • 单个接口的各项 I/O 的QPS & 耗时 & 错误量,如:RPC、Mysql、Redis、Mongo、ES 等,当依赖的基础设施出现问题时,可以快速定位原因。
  • 优化方向:尽可能减少一次 API 请求中,各项 IO 的 QPS 与耗时。

API 开发最佳实践

以下部分对一些场景和功能作给出了具体的规范和要求

API-First

在服务端与客户端开发过程中,提前定义好 API,多方依照契约并行开发。

  • 在每次需求编码前,就需要提前定义好API,并在接口平台进行登记
  • 并在后端进行技术方案评审时,需要对 API 接口进行评审

面向使用者设计

仔细定义“资源”

在设计API时,一个重要的前提是对Resource本身进行合理的定义。不应该简单的把服务端内部的存储模型,视为“资源”,而是应该面向使用者,比如:人才详情页也是人才的各种模型的组合,它们应该视为一种(而非多种) 资源。

避免琐碎的 API

尽量避免公开大量小型资源的“琐碎”Web API,此类 API 可能需要客户端(前端)发送多个请求才能拼装它需要的所有数据。尽可能将相关信息合并成单个较大资源,以便于使用方直接使用。

按需返回

应当关注使用方所依赖的具体字段,以及字段的使用方式,只返回使用方依赖数据的最小集,确保返回的字段都是对功能有意义的。

CQRS

CQRS 全称是 Command Query Responsibility Segregation,将应用程序分为两部分:

  • 命令端(Command):处理程序创建,更新和删除请求,并在数据更改时发出事件。
  • 查询端(Query):通过执行查询来处理查询,并且通过订阅数据更改时发出的事件流而保持最新。

CQRS 使用分离的接口,将数据查询操作和数据修改操作分离开来,这也意味着在查询和更新过程中使用的数据模型也是不一样的,这样读和写逻辑就隔离开来了。

相比数据库的读写分离,CQRS 可以理解为是应用层的读写分离,针对读的场景,构建单独的读模型,以提高查询的性能,同时提高系统整体的可维护性。

扩展阅读:

CQRS - Martin Fowler

简单可用的CQRS编码实践

兼容性(Compatibility)

API 的变更必须保证向后兼容,即 API 的升级不会导致 前端/客户端 的出错。

即使某次的升级是前后端同时发布,也不要做不兼容的升级,原因如下:

  • 我们经常并不知道所有的 API 使用方
  • 发布过程需要时间,无法真正实现“同时发布”
  • 使发布各环节耦合,一旦前端需要回滚,则后端也要跟着一起回滚,导致上线方案复杂化

常见的不兼容升级如下:

  • 移除或重命名字段、方法、枚举值
  • 更改字段类型
  • 修改字段的行为和语义

幂等性(Idempotency)

保证 API 的幂等性,能使客户端可以更安全的重试,从而让复杂的流程实现更为简单。

Create 类型的幂等

创建类型的 API,为了实现幂等性,常见的做法是使用一个 client-side generated deduplication token(客户端生成的唯一ID),在反复重试时使用同一个Token,便于服务端识别重复,如果发现重复,应按创建成功返回。

Update 类型的幂等

更新类型的 API,通常有唯一ID对需要更新的资源进行标示,以此可以保证幂等。

对于“Delta”语义的操作,有以下几类方式确保幂等性:

  1. IncrementBy:基于某个数值增加
  2. SetNewTotal:设置新的总量
  3. 使用 Deduplication Token 保证幂等

这几种方式各有优缺点,需要根据场景选择合适的方式。

Delete 类型的幂等

Delete的幂等性问题,往往在于一个对象被删除后,再次试图删除可能会由于数据无法被发现导致出错。这个行为一般来说也没什么问题,虽然严格意义上不幂等,但是也无副作用。

长耗时请求异步化

如果某个 API 方法需要很长时间才能完成,可以通过:

  1. 在服务端异步启动任务,并返回 GUID 标示 “长时间运行的操作”资源
  2. 客户端通过定时轮询 /polling/{guid}, 获取任务进行的状态。
  3. 当任务完成/失败时,客户端可以获取到处理的结果/失败原因。

附录I:Richardson 成熟度模型

Richardson Maturity Model - steps toward the glory of REST

Richardson成熟度模型(Richardson Maturity Model) - 通往真正REST的步骤

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

小册上新 计算机是怎样运行的:从根儿上理解计算机

发表于 2021-08-31

《计算机是怎样运行的:从根儿上理解计算机》, 是MySQL 系列小册(热销超 20000 册)作者小孩子,时隔两年推出的新小册,它完全面向零基础用户,让你一次理解计算机相关的核心概念。

🎤 作者介绍

小孩子是《MySQL是怎样运行的:从根儿上理解MySQL》、《MySQL是怎样使用的:从零蛋开始学习MySQL》小册,以及公众号「我们都是小青蛙」作者。

他致力于研究怎样可以写出深入浅出的技术书籍,觉得把复杂的问题讲清楚是一件很牛逼的事儿。

🚀 这本小册的诞生

作为一名程序员,无论你是从事前端开发、后端开发、数据分析还是算法,还是使用C、Java、PHP、Javascript、Python来编写程序,其实都是和计算机在打交道。

随着计算机的功能越来越强大,你有没有想过这样一个问题:计算机到底是怎样运行的?

我相信大部分人都想过这个问题,在学校可能也学过《计算机组成原理》这样的课程。但是,大部分人提到这个话题的时候,要么认为它太过底层、没必要学,要么认为它太难了、学不懂。

那么,底层的知识真的没必要学吗?其实关于这个问题,已经很多人给出过答案。小孩子想说的只有一句话,「底层知识可以帮助我们在学习新技术的时候,成为那个啥都一点就通的最靓的仔,所以一定要学。」

但是,“难”的问题该怎么解决呢?小孩子认真思考了一下,大家认为“难”的原因无非有以下几种:

  • 概念简直不要太多。刚打开一本讲解计算机原理的书,各种概念像是从加特林机枪里射出来的,打得人喘不上气。而且知识点像是散装起来的,彼此貌似有联系但又没太大联系。
  • 自上向下的讲述策略。即先将计算机分成几个部分,再将它们划分成更小的部分。这是方式非常简单粗暴,对知识的传播者足够友好(因为这样写的确很简单),却对知识的接收者足够不友好(因为人们的认知并不是先整体再具体的)。
  • 引用尚未接触过的概念去解释新概念。这一点可能是因为作者假定读者已经掌握了某些知识,或者是作者一开始就没考虑各个概念之间的因果关系(因为大人长大后很难回忆起自己还是一个小孩子的时候是个啥样了)。
  • 细节!细节!还是细节!细节是魔鬼,细节对于理解问题的本质起着至关重要的作用,学习了缺乏细节的知识会让人觉得好像学了点儿什么,可吃完一顿饭之后又会觉得啥都没学。
  • 枯燥的语言带来了浓浓的“学术”气息,一个个冷冰冰的知识点被我们不情愿的装入脑中,让小伙伴们不禁感叹:学习真是一件苦差事啊。

那么,《计算机是怎样运行》这本小册都做了哪些改进呢?

  • `给各种概念分清层次,尽最大努力保证一次只介绍一个概念。
  • 按照人们的认知顺序讲解。先介绍简单的知识,再在简单知识基础上扩展新的知识,力求为大家打造一个十分平缓的学习曲线。
  • 本小册是完全零基础的(不过需要各位有中学电学知识基础),并且尽最大努力避免引用未介绍过的概念去介绍新概念。
  • 沿着计算机从简单到复杂的发展历程,我们会保留其中最核心的一些东西,并力求给出足够多的细节去实现它们。
  • 小孩子写东西不喜欢那么严肃,大家在读本小册的时候可能会觉得有个人在和你扯淡,在扯淡中学习的过程还是蛮愉悦的嘛!

🏆 小册是如何设计的?

小册的设计借鉴了人们在管理复杂事物时的经验,即把复杂的事物分成若干层次。上层与下层之间只暴露简单的沟通接口,上层无需了解下层是怎么实现的,这样每一层中所面临的问题的复杂性就降低了很多。

比方说治理一个国家很困难,所以将国家分成了省、市、县、乡、村;治理一支军队很困难,所以将军队分成了军、师、旅、团、营、连、排、小工兵(这样旅长只需要给李云龙下命令:干掉狗日的山崎大队!而不用关心李云龙到底是通过集团冲锋还是土工作业的方式把山崎大队干掉的)。

对于降低计算机这个庞然大物的复杂度,我们可以将其拆分为如下图所示的多个层次:

image.png

为什么这么划分呢?假设,我们用C语言敲了一行printf的代码,把它编译运行,在屏幕上把它显示出来。这样的一个简单的操作,其实是经过层层调用得到的。

首先,我们的应用程序会调用操作系统提供的往屏幕上输出一行字的接口,称之为系统调用,应用程序是不关心操作系统将这行字儿输出到屏幕的具体细节的。

然后,操作系统接收到需要往屏幕上输出一行字儿的系统调用之后,会调用相关硬件的驱动程序来真正操作硬件。程序其实是由一条一条的机器指令构成的,我们编写程序时只需要按照我们所使用CPU采用的指令集体系结构中所指定的格式来编写指令即可,而不用关心指令是如何实现的。

接着,针对同一个指令集体系结构来说,不同的厂商可以针对同样的机器指令画出不同的电路图。比方说,对于x86指令集体系结构,Intel公司和AMD公司可以真对同一个机器指令开发出不同的电路图,这个电路图就是所谓的微体系结构。

而微体系结构又是由若干电路组件组成的。比方说,做加法需要加法器,做乘法需要乘法器,存储数据需要寄存器等等。针对同一个电路组件也可以有不同的实现,比方说,对于加法器来说,我们可以使用行波进位加法器,也可以使用先行进位加法器。

其中,电路组件其实是由逻辑门组成的,而完成同样功能的逻辑门,又可以被继电器、真空管或者晶体管这些器件实现。

至于继电器、真空管、晶体管这些器件是怎样导电的,就是物理学家们所要研究的话题了。

总的来说,本小册将从物理的电学知识开始,从最底层逐层向上唠叨,直到计算机体系结构层次。关于操作系统的知识,我们只能留在下一本书里再详细唠叨了。

在《计算机是怎样运行的》这本小册中,小孩子从 0 实现了一个支持 14 条指令的单操作数 miniCPU,并在 FPGA 上做了实现。这些指令分别是:

表格.png

基于这些指令,我们可以进行顺序、分支、循环结构的软件程序编写。除了从头唠叨 CPU 的设计之外,小册还会介绍总线、中断、指令集体系结构(以现实生活中真实使用的 MIPS 为例)等高级概念。

❤️ 写在最后

小孩子创作的《MySQL是怎样运行的:从根儿上理解MySQL》和《MySQL是怎样使用的:从零蛋开始学习MySQL》上线到现在已经有2年多的时间了。目前,一共有两万多名小伙伴和小孩子一起学习过MySQL。

小孩子想用若干年的时间去编写若干本极富小孩子特色的计算机学科的书籍,从而搭建一个计算机学科书籍的金字塔,而这本《计算机是怎样运行的》无疑就是这个金字塔中最底层的那块砖。

💬 互动交流群

小册购买成功后,即可申请加入互动交流群。入群方式如下:

image.png

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

程序员常用的这十个电子书下载网站,你值得拥有!

发表于 2021-08-31

在这里插入图片描述

不管学习什么编程语言,最主要的还是要多看,多记,多动手,去找一些自己感兴趣的脚本,代码去练习,练的越多,对于一些英语单词,特殊符号要比死记硬背要容易些。

以下这些网站,虽说不上全方位的满足你的需求,但是大部分也都能!

1、SoBooks

推荐指数:⭐⭐⭐⭐⭐

简介:SoBooks 图书下载网站是本人最喜欢的一个,它不仅书的种类齐全,而且质量也是相当的高,长期这里获取资源让我受益匪浅。

在这里插入图片描述

2、IT码农网

推荐指数:⭐⭐⭐⭐⭐

简 介:主要包括:计算机专业必读经典书籍下载、工具介绍和下载、各种编程语言教程、机器学习教程等。
在这里插入图片描述

3、Library Genesis

推荐指数:⭐⭐⭐⭐⭐

简介:是俄罗斯人做的超强英文电子书库。收录各大下载网站,书籍、期刊、文献储存量惊人。和B-OK.xyz不同在于支持更多电子书格式,但会有付费资源。

在这里插入图片描述

4、书栈网

推荐指数:⭐⭐⭐⭐

简 介:电子书门类比较全,支持在线查看和下载。

在这里插入图片描述

5、Java知识分享网

推荐指数:⭐⭐⭐

简 介:如网站名字,主要分享Java知识,电子书一般不会挂太久,删的比较快。
在这里插入图片描述

6、码农之家

推荐指数:⭐⭐⭐

简 介:比较多的电子书。

在这里插入图片描述

7、绿色资源网

推荐指数:⭐⭐⭐

简 介:以软件下载为主,也有不少编程电子书,搜索就行了。
在这里插入图片描述

8、脚本之家

推荐指数:⭐⭐⭐

简 介:脚本之家电子书区有不少电子书。

在这里插入图片描述

9、搬书匠

推荐指数:⭐⭐⭐

简 介:可观的技术书籍资源。
在这里插入图片描述

10、ai books

推荐指数:⭐⭐⭐

简 介:比较多的开发技术图书。

在这里插入图片描述
在这里插入图片描述

分享到这结束,想学习Python的朋友可以点开我的头像,看我主页,遇到问题欢迎找我解答,需要获得下面资源的朋友,点击领取福利满满。

①兼职交流,行业咨询

②Python开发环境安装教程

③Python400集自学视频

④大佬在线专业解答

⑤Python学习路线图

⑥3000多本Python电子书

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

手记 Oracle 慢查询排查步骤

发表于 2021-08-31

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜

文章合集 : 🎁 juejin.cn/post/694164…

Github : 👉 github.com/black-ant

CASE 备份 : 👉 gitee.com/antblack/ca…

一 . 前言

记录一次 Oracle 慢查询的排查过程 , 便于以后直接使用.

看了一些文档 , Oracle 中优化的方案和 Mysql 基本上是一致的 , 通常包括一下几个方向 :

  • 基准测试 (吞吐量) : 包括 Oracle 本身吞吐量和磁盘 I/O 吞吐量
  • 硬件分析 (资源情况) : 包括查看服务器 CPU , 硬盘的使用情况
  • SQL分析 : 分析 SQL 中是否存在慢查询 , 是否命中索引
  • 配置优化 : 分析是否可以通过环境配置提高性能

以上几个方面 , 基本上就能将问题定位了 , 通过问题再考虑解决的方法

二 . 排查步骤

2.1 查询慢查询日志

区别于 Mysql 直接写到 log 中的日志 , Oracle 可以通过语句拉出慢查询的 Excle log
@ oracle 慢查询 - 我是属车的 - 博客园 (cnblogs.com)

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
sql复制代码# 慢查询耗时
select *
from (select sa.SQL_TEXT "执行 SQL",
sa.EXECUTIONS "执行次数",
round(sa.ELAPSED_TIME / 1000000, 2) "总执行时间",
round(sa.ELAPSED_TIME / 1000000 / sa.EXECUTIONS, 2) "平均执行时间",
sa.COMMAND_TYPE,
sa.PARSING_USER_ID "用户ID",
u.username "用户名",
sa.HASH_VALUE
from v$sqlarea sa
left join all_users u
on sa.PARSING_USER_ID = u.user_id
where sa.EXECUTIONS > 0
order by (sa.ELAPSED_TIME / sa.EXECUTIONS) desc)
where rownum <= 50;

# 查询次数最多的 SQL
select *
from (select s.SQL_TEXT,
s.EXECUTIONS "执行次数",
s.PARSING_USER_ID "用户名",
rank() over(order by EXECUTIONS desc) EXEC_RANK
from v$sql s
left join all_users u
on u.USER_ID = s.PARSING_USER_ID) t
where exec_rank <= 100;

结果解释 :

image.png

拿到平均执行时间后就可以明显的发现查询时间较长的 SQL , 但是这一类 SQL 不一定是慢查询 , 需要根据情况判断 , 如果出现很离谱的时间 , 就需要分析索引

2.2 查看索引情况

1
2
3
4
5
sql复制代码explain plan for
select * from t_call_records where t_bjhm='123456'

# 查看执行结果
select * from table(dbms_xplan.display)

索引内容补充

image.png

从这里可以明显看到走了全表扫描 , 那么就需要根据情况加索引和校验

  • index unique scan : 索引唯一扫描 (主键索引)
  • index range scan : 索引范围扫描 (组合索引的情况)
  • index full scan : 全索引扫描
  • index fast full scan : 索引快速扫描,扫描索引中的全部的数据块,与全索引扫描的方式基本上类似。
    • 两者之间的明显的区别是,索引快速扫描对查询的数据不进行排序,数据返回的时候不是排序的。

2.3 查看锁的竞争情况

Step 1 : 查看后台锁竞争

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
sql复制代码SELECT
SQ.INST_ID,
SQ.SQL_TEXT, /*SQL文本*/
SE.SID, /*会话的唯一标识,通常要对某个会话进行分析前,首先就需要获得该会话的SID。*/
SE.BLOCKING_SESSION,
SQ.OPTIMIZER_COST AS COST_,/* COST 值*/
SE.LAST_CALL_ET CONTINUE_TIME,/*执行时间*/
SE.EVENT,/*等待事件*/
SE.LOCKWAIT,/*是否等待LOCK(SE,P)*/
SE.MACHINE,/*客户端的机器名。(WORKGROUP\PC-201211082055)*/
SQ.SQL_ID,/*SQL_ID*/
SE.USERNAME,/*创建该会话的用户名*/
SE.LOGON_TIME,/*登陆时间*/
'ALTER SYSTEM KILL SESSION ' || SE.SID || ',' || SE.SERIAL # --若存在锁情况,会用到KILL锁释放~
FROM
gV$SESSION SE,/*会话信息。每一个连接到ORACLE数据库的会话都能在该视图中对应一条记录*/
gV$SQLAREA SQ /*跟踪所有SHARED POOL中的共享CURSOR信息,包括 执行次数,逻辑读,物理读等*/
WHERE
SE.SQL_HASH_VALUE = SQ.HASH_VALUE   
AND SE.STATUS = 'ACTIVE'   
AND SE.SQL_ID = SQ.SQL_ID   
AND SE.USERNAME = SQ.PARSING_SCHEMA_NAME       --过滤条件
AND SE.USERNAME = 'FWSB' --用户名
AND se.BLOCKING_SESSION IS NOT NULL;

// 实际运行脚本======================
SELECT
SQ.INST_ID,
SQ.SQL_TEXT,
SE.SID,
SE.BLOCKING_SESSION,
SQ.OPTIMIZER_COST AS COST_,
SE.LAST_CALL_ET CONTINUE_TIME,
SE.EVENT,
SE.LOCKWAIT,
SE.MACHINE,
SQ.SQL_ID,
SE.USERNAME,
SE.LOGON_TIME,
'ALTER SYSTEM KILL SESSION ' || SE.SID || ','
FROM
gV$SESSION SE,
gV$SQLAREA SQ
WHERE
SE.SQL_HASH_VALUE = SQ.HASH_VALUE
AND SE.STATUS = 'ACTIVE'
AND SE.SQL_ID = SQ.SQL_ID
AND SE.USERNAME = SQ.PARSING_SCHEMA_NAME
AND SE.USERNAME = 'FWSB'
AND SE.BLOCKING_SESSION IS NOT NULL;

image.png

补充 : 相关的表结构可以生乳查询 Oracle 官方文档

Step 2 : 查询结果

image.png

这里可以通过 SID 再去查找对应的 SQL , 找到对应的锁对象

2.4 其他锁语句

以下内容参考自 : blog.csdn.net/u011019491/… , 各位可以看看原文

查询那些用户,操纵了那些表造成了锁机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sql复制代码SELECT
s.username,
decode(l.TYPE, 'TM', 'TABLE LOCK', 'TX', 'ROW LOCK', NULL ) LOCK_LEVEL,
o.owner,
o.object_name,
o.object_type,
s.sid,
s.terminal,
s.machine,
s.program,
s.osuser
FROM
v$session s,
v$lock l,
all_objects o
WHERE
l.sid = s.sid
AND l.id1 = o.object_id(+)
AND s.username is NOT Null

详情参考 :—> V$Lock

image.png

查出被锁的表,和锁住这个表的会话ID

1
sql复制代码select a.session_id ,b.* from v$locked_object a,all_objects b where a.object_id=b.object_id

查出对应的SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sql复制代码SELECT
vs.SQL_TEXT,
vsess.sid,
vsess.SERIAL #,
vsess.MACHINE,
vsess.OSUSER,
vsess.TERMINAL,
vsess.PROGRAM,
vs.CPU_TIME,
vs.DISK_READS
FROM
v$sql vs,
v$session vsess
WHERE
vs.ADDRESS = vsess.SQL_ADDRESS
AND vsess.sid = 36

image.png

补充语句 :

1
2
3
4
5
6
7
8
9
java复制代码// 查哪个过程被锁 -> 查V$DB_OBJECT_CACHE视图:
SELECT * FROM V$DB_OBJECT_CACHE WHERE OWNER='过程的所属用户' AND LOCKS!='0';

// 查是哪一个SID,通过SID可知道是哪个SESSION. -> 查V$ACCESS视图:
SELECT * FROM V$ACCESS WHERE OWNER='过程的所属用户' AND NAME='刚才查到的过程名';

// 查出SID和SERIAL# -> 查V$SESSION视图 + 查V$PROCESS视图
SELECT SID,SERIAL#,PADDR FROM V$SESSION WHERE SID='刚才查到的SID'
SELECT SPID FROM V$PROCESS WHERE ADDR='刚才查到的PADDR';

三 . 慢查询优化

3.1 SQL 部分

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
java复制代码// 避免 in 操作
Oracle 中 in 会被试图转换成多个表的连接 , 转换不成功会先进行 in 中的子查询 , 再进行外部查询

// 避免 not in
不管哪个数据库 , 一般都是不推荐的 ,这种写法会跳过索引 (同理还有 is null 和 not null)

// 避免使用 <>
类似 , 不走索引

// **采用函数处理的字段不能利用索引**

// 关联查询
- 多用 Where 语句把单个表的结果集最小化,多用聚合函数汇总结果集后再与其它表做关联
- 多用 右连接

// 过滤多用 where ,避免使用 having
- 这个和 mysql 是一致的 , having 是对 where 的数据进行过滤组处理 , 对于数据的过滤 , 优先用 where
- 总结 : 先过滤小的结果集,然后通过这个小的结果集和其他表做关联

// like 操作符
like 操作可以通过 instr 代替

// union操作符
- 通常不会产生重复结果 , 而 union 会额外触发一次排序
- 采用union ALL操作符替代union,因为union ALL操作只是简单的将两个结果合并后就返回

// SQL 执行保证统一性
涉及到 SGA 的概念

// where后面的条件顺序影响
这里不是全表索引的问题 , 而是由于 where 多个条件时 , 比较带来的 cpu 占用率问题

// 询表顺序的影响
- 表的顺序不对会产生十分耗服务器资源的数据交叉

// 其他的方案还包括以下方式
@ https://www.jb51.net/article/97515.htm

@ https://www.jb51.net/article/23071.htm

@ https://www.jb51.net/article/40281.htm

四 . 性能优化

挺不好意思的!!!

都是抄的书上的 !!!

而且大多数还没实践过 !!!

Oracle 毕竟接触有限 , 就算碰到了多数是SQL 问题 , 性能优化也就碰到过几次 , 导致方法学到不少 , 实际就用过几个 , 但是我都记下来了!!! 😜😜😜

4.1 整体性能优化流程

这里直接引用别人文章的结果 , 没有测试 , 仅供参考 !

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
java复制代码// PS : 初始化时间 49.41

// 增大 SGA Buffer Cache 和 SGA Shared Pool -> 48.57
- 增大 SGA 已经缓冲看来对于性能的提升并不显著,加载时间只提升了 1.73%

// 增大 SGA Redo Cache 和 Redo Log Files -> 41.39
- 加载时间提升了 17.35%,TPS 也提升了 9.33%。因为加载和同时插入,更新,删除需要比 8M 大的空间
- 但是看起来增加内存性能并没有显著提升

// 增大 Database Block Size (2K-4K) -> 17.35
- 加载时间提升了 138%!而对 TPS 值没有很大的影响

// 使用 Tablespaces Local -> 15.07
- TPS 轻微提升

// Database Block Size 增大 (4K-8K) -> 11.42
- TPS 继续提升 , 区别较大

// 添加 io_slaves -> 10.48
dbwr_io_slaves 4\
lgwr_io_slaves (derived) 4

// 优化Linux 内核 -> 9.40
可以看到 , 内核版本优化后 , 性能是有一定提升的

// 调整虚拟子内存 -> 5.58
- /ect/sysctl.cong
-> vm.bdflush = 100 1200 128 512 15 5000 500 1884 2

这个流程不能作为标杆 , 但是可以作为优化 Oracle 的思路 , 可以看到 , 性能提升很大

4.2 硬件优化

此处是使用IO校准(I/O Calibration),可以用于评测一下数据库的I/O性能 , 通过 分析 IO 结果判断采用不同的策略

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
java复制代码// Step 1 : 确定并行度配置 (通常是核数的2倍)
show parameters parallel_thread

// Step 2 : 确定并行策略 (auto : Oracle将依据要执行的操作的特性和对象的大小来确定并行度)
- 查询策略 : show parameters parallel_degree_policy
- 设置策略 : alter session set parallel_degree_policy = 'auto'

// Step 3 : 查看并行度数据
- 打开系统默认设置的输出功能 : set serveroutput on
- 查看详情 :
set serveroutput on
DECLARE
lat INTEGER;
iops INTEGER;
mbps INTEGER;
BEGIN
-- DBMS_RESOURCE_MANAGER.CALIBRATE_IO (disk_count,max_latency , iops, mbps, lat);
DBMS_RESOURCE_MANAGER.CALIBRATE_IO (2, 10, iops, mbps, lat);

DBMS_OUTPUT.PUT_LINE ('max_iops = ' || iops);
DBMS_OUTPUT.PUT_LINE ('latency = ' || lat);
dbms_output.put_line('max_mbps = ' || mbps);
end;
/


// 问题补充 : ORA-56708: 找不到任何具有异步 I/O 功能的数据文件
- 确定 sync : show parameter filesystemio_options
- 设置 sync : filesystemio_options
- ASYNCH: 使Oracle支持文件的异步(Asynchronous)IO
- DIRECTIO:使Oracle支持文件的Direct IO
- SETALL:使Oracle同时支持文件的Asynchronous IO和Direct IO
- NONE:使Oracle关闭对Asynchronous IO和Direct IO的支持
1> alter system set filesystemio_options=setall scope=spfile;
2> shutdown immediate;
3> startup
// PS : 注意其中管理员权限问题


alter system set filesystemio_options=none scope=spfile;

五 . 概念补充

5.1 SGA

系统全局区域(SGA) 是一组共享内存结构,称为 SGA 组件,包含一个 Oracle 数据库实例的数据和控制信息。SGA 由所有服务器和后台进程共享。SGA 中存储的数据示例包括缓存的数据块和共享的 SQL 区域。

组成部分 :

  • Database buffer cache : 数据缓存
    • 在查询或修改数据库中存储的数据之前,必须从磁盘读取数据并将其存储在缓冲区缓存中。
    • 所有连接到数据库的用户进程都共享对缓冲区缓存的访问。
    • 为了获得最佳性能,缓冲区缓存应该足够大,以避免频繁的磁盘 I/O 操作。
  • Shared pool : 共享池缓存用户共享的信息 , 包括如下内容
    • 可重用的 SQL 语句
    • 来自数据字典的信息,例如用户帐户数据、表和索引描述以及特权
    • 存储过程,它是存储在数据库中的可执行代码
  • Redo log buffer : 这个缓冲区通过缓存重做信息来提高性能,直到可以将它写入存储在磁盘上的物理在线重做日志文件
  • Large pool : 这个可选区域用于为各种服务器进程缓冲大型 I/O 请求
  • Java pool : Java 池是用于 Java 虚拟机(JVM)中所有特定于会话的 Java 代码和数据的内存区域
  • Streams pool : Streams 池是 Oracle Streams 特性使用的内存区域
  • Result cache : 结果缓存缓冲区查询结果。如果运行的查询将结果存储在结果缓存中,那么数据库将从结果缓存返回查询结果,而不是重新运行查询。

总结

笔者只是基于通过业务要求的角度进行 Oracle 优化 , 并没有深入 Oracle 业务优化 , 感兴趣的可以看看 <Oracle数据库性能优化方法论和最佳实践> , 对数据库进行系统的优化

后续可能会继续深入 Oracle 细节 , 拭目以待.

查文档属实太类了 , 不过 Oracle 文档很完善 , 操作起来也简单

参考文档 (非常感谢)

www.cnblogs.com/wolfplan/p/…

www.cnblogs.com/pizicai17/p…

www.cnblogs.com/sunxiuwen/p…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

带你走进MySQL全新高可用解决方案-MGR 一、初识MGR

发表于 2021-08-31

​一、初识MGR

相信很多人对MGR这个词比较陌生,其实MGR(全称 MySQL Group Replication 【MySQL 组复制】)是Oracle MySQL于2016年12月发布MySQL 5.7.17推出的一个全新高可用和高扩展的解决方案。具备以下特性:

  • 高一致性,基于原生复制及Paxos协议的组复制技术,并以插件的方式提供,提供一致数据安全保证;
  • 高容错性,只要不是大多数节点坏掉就可以继续工作,有自动检测机制,当不同节点产生资源争用冲突时,不会出现错误,按照先到者优先原则进行处理,并且内置了自动化脑裂防护机制;
  • 高扩展性,节点的新增和移除都是自动的,新节点加入后,会自动从其他节点上同步状态,直到新节点和其他节点保持一致,如果某节点被移除了,其他节点自动更新组信息,自动维护新的组信息;
  • 高灵活性,有单主模式(图1)和多主模式(图2),单主模式下,会自动选主,所有更新操作都在主上进行;多主模式下,所有server都可以同时处理更新操作。

单主模式(图1)

多主模式(图2)

MGR架构图如下所示:主要包括APIs层,组件层,负责协议模块和API+Paxos引擎层构成。

二、MGR技术演进

2.1 主从复制

传统MySQL复制默认提供了一种简单的主从复制方法,这种架构有一个主,以及一个或者多个从,当主节点执行提交事务,然后异步的方式发送到其他从节点,从库重新执行relay log日志内容达到主副本一致的目的,在默认情况下集群所有节点数据都是一致的。

MySQL异步复制

2.2 半同步复制

异步复制存在一定的数据丢失风险,MySQL又在5.6版本中推出半同步复制,在同步数据协议中添加了一个同步操作,这样意味主节点在commit操作,需要确认最少一个从节点确认接收到并且返回ACK,只有这样主节点才能正确提交数据。

MySQL半同步复制

2.3 组复制

MySQL MGR 集群最少3个server节点共同组成的分布式集群,一种share-nothing复制方案,每个server节点都有完整的副本。

MySQL组复制协议

三、MGR技术特性

3.1 故障检测

组复制自带提供一种故障检测机制,这个机制能报告哪个组成员是无响应的,并且如何判断该成员是否排除集群组。在组复制中故障检测是一种分布式服务。假设服务器A在预定时间段内未收到来自服务器B的消息,如果组内其他成员也同样未收到来自服务器B的消息,那么确认判断B发生故障,这样由其他成员判定将失联组成员从集群中剔除。

此时服务器B与其他服务节点都无法联系。由于无法达成最小仲裁成员数,处于独立状态,无法对外提供服务。

3.2 容错

MySQL组复制构建在Paxos分布式算法基础上实现的,以提供不同server之间的分布式协调。因此,它需要大多数server处于活动状态以达到仲裁成员数,从而做出决定。这对系统可以容忍的不影响其自身及其整体功能的故障数量有直接影响。容忍f个故障所需的server数量(n)n = 2 * f + 1。

实践中,这意味着为了容忍一个故障,组必须有三个server。如果一个服务器故障, 仍然有两个服务器形成大多数(三分之二)来允许系统自动地继续运行。但是,如果第二个server意外地宕掉,则该组锁定(只有一个server),因为没有达到多数可以达成选举(不能自己选举自己)。以下是说明上述公式的小表:

3.3 成员管理

组复制以组视图(Group View,后续简称视图)为基础来进行成员管理,视图一般在Group在一段时间内的成员状态,如果这段时间没有成员变化,也就是说没有成员的加入和退出,一旦有成员加入或者退出组,则视图就发生变化,并且使用视图ID(view id)进行跟踪变化区分先后时间,下面我们来看一张图演示一下:

序号部分,初始化时,第一个视图的序号从1开始,成员只有引导主一个,为进行初始化节点,以后出现的任何成员的加入和退出这个序号都需要增加1,可以通过performance_schema系统库下的replication_group_member_stats表中查询当前视图。

四、MGR安装体验

了解任何一个新技术从部署开始,安装比较简单,我们准备如下测试节点:

10.10.1.214

10.10.1.217

10.10.6.91

安装版本均为最新版本8.0.24,将安装包解压后进行初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码su - mysql

wget http://mirrors.ustc.edu.cn/mysql-ftp/Downloads/MySQL-8.0/mysql-8.0.24-linux-glibc2.12-x86_64.tar

tar -xf mysql-8.0.24-linux-glibc2.12-x86_64.tar

cd mysql-8.0.24-linux-glibc2.12-x86_64

# 创建配置文件和数据目录

mkdir conf data

初始化数据库并且启动

./bin/mysqld --initialize --datadir=/home/mysql/mysql-8.0.24-linux-glibc2.12-x86_64/data --basedir=/home/mysql/mysql-8.0.24-linux-glibc2.12-x86_64

./bin/mysqld_safe --defaults-file=conf/my.cnf &

4.1 通用配置说明

配置代码

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
java复制代码[mysqld]
bind-address=0.0.0.0
datadir=/home/mysql/mysql-8.0.24-linux-glibc2.12-x86_64/data
basedir=/home/mysql/mysql-8.0.24-linux-glibc2.12-x86_64
port=3306
socket=/home/mysql/mysql-8.0.24-linux-glibc2.12-x86_64/data/mysqld.sock
user=mysql
# 每个节点要求不一样
server_id=1
gtid_mode=ON
enforce_gtid_consistency=ON
master_info_repository=TABLE
relay_log_info_repository=TABLE
binlog_checksum=NONE
log_slave_updates=ON
log_bin=binlog
binlog_format=ROW
innodb_buffer_pool_size=1g
# 8.0 默认值XXHASH64,针对写事务进行哈希处理
transaction_write_set_extraction=XXHASH64
# 启动加载组复制插件
plugin_load_add='group_replication.so'
# 集群唯一ID
group_replication_group_name="8d3cebd8-b132-11eb-8529-0242ac130003"
# 是否启动MySQL服务时启动组复制,建议值:off
group_replication_start_on_boot=off
# 本地IP后面端口33061可自定义,集群通信端口,建议统一端口
group_replication_local_address= "10.10.1.214:33061"
# 初始化集群成员列表,可动态修改
group_replication_group_seeds= "10.10.1.214:33061,10.10.1.217:33061,10.10.6.91:33061"
# 判断是否为引导组
group_replication_bootstrap_group=off
# 设置白名单,这里特别注意,如果是同网段可以不用设置,如果是不同网段则需要修改否则通信端口不可访问
loose-group_replication_ip_whitelist='10.10.1.214,10.10.1.217,10.10.6.91'

4.2 单主模式部署

4.2.1 引导节点初始化

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
java复制代码# 创建用户和安装插件

mysql> SET SQL_LOG_BIN=0;

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'password';

Query OK, 0 rows affected (0.01 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';

Query OK, 0 rows affected (0.00 sec)

mysql> GRANT BACKUP_ADMIN ON *.* TO rpl_user@'%';

Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;

Query OK, 0 rows affected (0.00 sec)

mysql> SET SQL_LOG_BIN=1;

Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE REPLICATION SOURCE TO SOURCE_USER='rpl_user', SOURCE_PASSWORD='password' FOR CHANNEL 'group_replication_recovery';

Query OK, 0 rows affected, 2 warnings (0.05 sec)

mysql> INSTALL PLUGIN group_replication SONAME 'group_replication.so';

mysql> SHOW PLUGINS;

+---------------------------------+----------+--------------------+----------------------+---------+

| Name | Status | Type | Library | License |

+---------------------------------+----------+--------------------+----------------------+---------+

| group_replication | ACTIVE | GROUP REPLICATION | group_replication.so | GPL |

+---------------------------------+----------+--------------------+----------------------+---------+

# 启动引导节点

mysql> SET GLOBAL group_replication_bootstrap_group=ON;

Query OK, 0 rows affected (0.00 sec)

mysql> START GROUP_REPLICATION;

Query OK, 0 rows affected, 1 warning (2.33 sec)

mysql> SET GLOBAL group_replication_bootstrap_group=OFF;

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM performance_schema.replication_group_members;

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| group_replication_applier | 4cf69361-b22b-11eb-a2c9-fa163ebefc6a | 10-10-1-214 | 3306 | ONLINE | PRIMARY | 8.0.24 |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

4.2.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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
java复制代码mysql> SET SQL_LOG_BIN=0;

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE USER rpl_user@'%' IDENTIFIED BY 'password';

Query OK, 0 rows affected (0.01 sec)

mysql> GRANT REPLICATION SLAVE ON *.* TO rpl_user@'%';

Query OK, 0 rows affected (0.03 sec)

mysql> GRANT BACKUP_ADMIN ON *.* TO rpl_user@'%';

Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;

Query OK, 0 rows affected (0.00 sec)

mysql> SET SQL_LOG_BIN=1;

Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE REPLICATION SOURCE TO SOURCE_USER='rpl_user', SOURCE_PASSWORD='password' FOR CHANNEL 'group_replication_recovery';

Query OK, 0 rows affected, 2 warnings (0.05 sec)

mysql> START GROUP_REPLICATION;

Query OK, 0 rows affected, 1 warning (3.33 sec)

# 检查状态

mysql> SELECT * FROM performance_schema.replication_group_members;

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| group_replication_applier | 4cf69361-b22b-11eb-a2c9-fa163ebefc6a | 10-10-1-214 | 3306 | ONLINE | PRIMARY | 8.0.24 |

| group_replication_applier | 53f39dba-b22b-11eb-bfdb-fa163e42784d | 10-10-1-217 | 3306 | ONLINE | SECONDARY | 8.0.24 |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

2 rows in set (0.00 sec)

其他一个节点执行上述即可,执行完成后检查

mysql> SELECT * FROM performance_schema.replication_group_members;

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| group_replication_applier | 4cf69361-b22b-11eb-a2c9-fa163ebefc6a | 10-10-1-214 | 3306 | ONLINE | PRIMARY | 8.0.24 |

| group_replication_applier | 53f39dba-b22b-11eb-bfdb-fa163e42784d | 10-10-1-217 | 3306 | ONLINE | SECONDARY | 8.0.24 |

| group_replication_applier | 56779526-b22b-11eb-a28e-fa163e1f9809 | 10-10-6-91 | 3306 | ONLINE | SECONDARY | 8.0.24 |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

3 rows in set (0.00 sec)

4.3 多主模式部署

多主模式和单主部署方式差不多,只在加入集群时多执行:

1
java复制代码set global group_replication_single_primary_mode=off;

单主的都是ON。

4.3.1 引导节点初始化

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
java复制代码mysql> set global group_replication_single_primary_mode=off;

Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL group_replication_bootstrap_group=ON;

Query OK, 0 rows affected (0.00 sec)

mysql> start group_replication;

Query OK, 0 rows affected, 1 warning (2.16 sec)

mysql> SET GLOBAL group_replication_bootstrap_group=OFF;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from performance_schema.replication_group_members;

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| group_replication_applier | 4cf69361-b22b-11eb-a2c9-fa163ebefc6a | 10-10-1-214 | 3306 | ONLINE | PRIMARY | 8.0.24 |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

1 row in set (0.00 sec)

4.3.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
java复制代码mysql> set global group_replication_single_primary_mode=off;

Query OK, 0 rows affected (0.00 sec)

mysql> START GROUP_REPLICATION;

Query OK, 0 rows affected, 1 warning (3.26 sec)

mysql> select * from performance_schema.replication_group_members;

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

| group_replication_applier | 4cf69361-b22b-11eb-a2c9-fa163ebefc6a | 10-10-1-214 | 3306 | ONLINE | PRIMARY | 8.0.24 |

| group_replication_applier | 53f39dba-b22b-11eb-bfdb-fa163e42784d | 10-10-1-217 | 3306 | ONLINE | PRIMARY | 8.0.24 |

| group_replication_applier | 56779526-b22b-11eb-a28e-fa163e1f9809 | 10-10-6-91 | 3306 | ONLINE | PRIMARY | 8.0.24 |

+---------------------------+--------------------------------------+-------------------------------------------------+-------------+--------------+-------------+----------------+

3 rows in set (0.00 sec)

4.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
java复制代码# 在任意节点执行

mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| mysql |

| performance_schema |

| sys |

+--------------------+

4 rows in set (0.00 sec)

mysql> create database test;

Query OK, 1 row affected (0.01 sec)

# 任意节点查询
mysql> show databases;

+--------------------+

| Database |

+--------------------+

| information_schema |

| mysql |

| performance_schema |

| sys |

| test |

+--------------------+

5 rows in set (0.00 sec)

五、应用场景

  • MGR采用多副本模式,在2N+1个集群中,集群只要N+1个节点还存活,数据库就能稳定对外提供服务,适用于金融场景,因为这些场景数据必须零丢失,可用性在4个9甚至5个9。
  • 适用于替代当前主从高可用版本,解决单点写入问题。
  • 针对业务需要弹性扩展节点的基础架构环境,例如私有云。

六、总结

尽管MySQL在2016年就推出了MGR该功能,同时我们也知道有很多好处,并且有大胆的公司采用进行测试甚至部署线上环境,据公开资料网易、滴滴都有使用,国内部分商业银行也有使用,但仍然有不少人处于观望状态,主要有以下几点原因导致:

需求不是特别强烈

  • 很多业务情况使用MySQL半同步和异步复制足够满足业务要求,配合MHA第三方组件满足了绝大部分场景需求。

分布式新事物

  • 本身分布式这个概念已经存在多年,但是由于MGR推出年限较短,且我们搜索官方bug库任然存在较多未解决的bug。用户使用排查问题较为困难,且由于分布式设计导致问题复现难也是一种阻碍。

生态不成熟

  • 官方几乎没有完全成熟用来构建整套高可用架构的解决方案,如果想要大规模使用还是需要更加成熟的生态。

任何新鲜事物都有一个被大众接受过程,只是需要时间筛选和磨砺。

参考文档

dev.mysql.com/doc/refman/…

作者:vivo互联网数据库团队-Liu Shilin

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…542543544…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%