分布式事务XA解决方案之两阶段提交(2PC)
对于三阶段提交请参考:
https://www.cnblogs.com/binyue/p/3678390.html
https://www.cnblogs.com/charlesblc/p/6289348.html
在 计算机网络 以及 数据库 领域内, 二阶段提交(英语:Two-phase Commit)是指,为了使基于 分布式系统 架构下的所有节点在进行 事务 提交时保持一致性而设计的一种 算法(Algorithm)。通常, 二阶段提交 也被称为是一种 协议(Protocol)。在分布式系统中,每个节点虽然可以知晓自己的操作时成功或者失败,却无法知道其他节点的操作的成功或失败。当一个事务跨越多个节点时,为了保持事务的 ACID 特性,需要引入一个作为 协调者 的组件来统一掌控所有节点(称作 参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。因此,二阶段提交的算法思路可以概括为: 参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。
需要注意的是, 二阶段提交(英文缩写: 2PC)不应该与 并发控制 中的 二阶段锁(英文缩写: 2PL)进行混淆。
目录
[隐藏]
前提[ 编辑]
二阶段提交算法的成立基于以下假设:
该分布式系统中,存在一个节点作为 协调者(Coordinator),其他节点作为 参与者(Cohorts)。且节点之间可以进行网络通信。
所有节点都采用 预写式日志,且日志被写入后即被保持在可靠的存储设备上,即使节点损坏不会导致日志数据的消失。
所有节点不会永久性损坏,即使损坏后仍然可以恢复。
基本算法[ 编辑]
以下对 二阶段提交 算法分阶段进行说明。
第一阶段(提交请求阶段)[ 编辑]
协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。
各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息;如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。
有时候,第一阶段也被称作 投票阶段,即各参与者投票是否要继续接下来的提交操作。
第二阶段(提交执行阶段)[ 编辑]
成功[ 编辑]
当协调者节点从所有参与者节点获得的相应消息都为”同意”时:
协调者节点向所有参与者节点发出”正式提交”的请求。
参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送”完成”消息。
协调者节点收到所有参与者节点反馈的”完成”消息后,完成事务。
失败[ 编辑]
如果任一参与者节点在第一阶段返回的响应消息为”终止”,或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:
协调者节点向所有参与者节点发出”回滚操作”的请求。
参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送”回滚完成”消息。
By admin
read more了解MySQL中的字符集
https://dev.mysql.com/doc/refman/5.7/en/charset.html
平时我们只说了字符集这个概念,另外还有对应的“字符序”。一个字符集(如utf8)对应多个字符序(utf8_general_ci、utf8_german2_ci等),每个字符集都有一个默认“字符序”。
什么是字符集、字符序?简单的来说:
- 字符集(character set):定义了字符以及字符的存储编码。
- 字符序(collation):定义了字符的比较规则。
可以通过命令查看字符集、字符序信息:
SHOW CHARACTER SET;
在我们开发中,一般要保持服务器端的字符集与客户端的字符集保持一致,不然容易出现乱码的情况。
MySQL提供了不同级别的设置,包括server级、database级、table级、column级,可以提供非常精准的设置。
参考文章: https://www.cnblogs.com/chyingp/p/mysql-character-set-collation.html
By admin
read moreLinux下对进程通信管理的信号机制概述
今天看到了篇使用golang实现的系统无感重启的文章, https://gravitational.com/blog/golang-ssh-bastion-graceful-restarts/,一般用来平滑处理一些系统服务,避免先停止再启用导致的服务不可用的情况。其中用到了信号机制,这里找了一些文章主要有来介绍这方面的文章,以便加深理解。 https://blog.csdn.net/junyucsdn/article/details/50519248 https://blog.csdn.net/tiany524/article/details/17048069 https://my.oschina.net/chenliang165/blog/125825
By admin
read moreMySQL中的sql_mode模式
官方文档: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sql-mode-setting
一、模式分类
在MySQL8.0中主要包括以下几种模式
ONLY_FULL_GROUP_BY | 对于GROUP BY聚合操作,如果在SELECT中的列,没有在GROUP BY中出现,那么将认为这个SQL是不合法的,因为列不在GROUP BY从句中 |
---|---|
STRICT_TRANS_TABLES | 在该模式下,如果一个值不能插入到一个事务表中,则中断当前的操作,对非事务表不做任何限制 |
NO_ZERO_IN_DATE | 在严格模式,不接受月或日部分为0的日期。如果使用IGNORE选项,我们为类似的日期插入’0000-00-00’。在非严格模式,可以接受该日期,但会生成警告。 |
NO_ZERO_DATE | 在严格模式,不要将 ‘0000-00-00’做为合法日期。你仍然可以用IGNORE选项插入零日期。在非严格模式,可以接受该日期,但会生成警告 |
ERROR_FOR_DIVISION_BY_ZERO | 在严格模式,在INSERT或UPDATE过程中,如果被零除(或MOD(X,0)),则产生错误(否则为警告)。如果未给出该模式,被零除时MySQL返回NULL。如果用到INSERT IGNORE或UPDATE IGNORE中,MySQL生成被零除警告,但操作结果为NULL。 |
NO_ENGINE_SUBSTITUTION | 如果需要的存储引擎被禁用或未编译,那么抛出错误。不设置此值时,用默认的存储引擎替代,并抛出一个异常。 |
修改当前数据库的模式,有两种办法,一种是全局,一种是会话期间
SET GLOBAL sql_mode = 'modes';
SET SESSION sql_mode = 'modes';
同样,查看方法为
SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;
二、最重要的几种sql_mode
ANSI模式 | 宽松模式,对插入数据进行校验,如果不符合定义类型或长度,对数据类型调整或截断保存,报warning警告。 ANSI模式等于 REAL_AS_FLOAT , PIPES_AS_CONCAT , ANSI_QUOTES , IGNORE_SPACE , and ONLY_FULL_GROUP_BY 几种的组合。 |
---|---|
TRADITIONAL模式 | 严格模式,当向mysql数据库插入数据时,进行数据的严格校验,保证错误数据不能插入,报error错误。用于事物时,会进行事物的回滚。等于STRICT_TRANS_TABLES , STRICT_ALL_TABLES , NO_ZERO_IN_DATE , NO_ZERO_DATE ,ERROR_FOR_DIVISION_BY_ZERO , and NO_ENGINE_SUBSTITUTION .的组合 |
STRICT_TRANS_TABLES模式 | 严格模式,进行数据的严格校验,错误数据不能插入,报error错误。 |
目前新版本MySQL5.7.22的默认模式为 STRICT_TRANS_TABLES, NO_ENGINE_SUBSTITUTION 两种。对于不同版本默认的sql_mode可查看https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_sql_mode
By admin
read more了解MySQL中的驱动表
一、为什么要用小表驱动大表
1、驱动表的定义
当进行多表连接查询时, [驱动表] 的定义为:
1)指定了联接条件时,满足查询条件的记录行数少的表为[驱动表]
2)未指定联接条件时,行数少的表为[驱动表](重要)
忠告:如果你搞不清楚该让谁做驱动表、谁 join 谁,请让 MySQL 运行时自行判断
既然“未指定联接条件时,行数少的表为[驱动表]”了,而且你也对自己写出的复杂的 Nested Loop Join 不太有把握(如下面的实例所示),就别指定谁 left/right join 谁了,请交给 MySQL优化器 运行时决定吧。
2、mysql关联查询的概念:
MySQL 表关联的算法是 Nest Loop Join(嵌套循环),是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。
例: user表10000条数据,class表20条数据
SELECT * FROM user u LEFT JOIN class c u.userid=c.userid
这样则需要用user表循环10000次才能查询出来,而如果用class表驱动user表则只需要循环20次就能查询出来
SELECT * FROM class c LEFT JOIN user u c.userid=u.userid
小结果集驱动大结果集
de.cel 在2012年总结说,不管是你,还是 MySQL,优化的目标是尽可能减少JOIN中Nested Loop的循环次数。
以此保证:永远用小结果集驱动大结果集(Important)!
相关算法参考: https://blog.csdn.net/caomiao2006/article/details/52205177
二、优化联表查询
优化第一步之:根据驱动表的字段排序
left join不变,干嘛要根据非驱动表的字段排序呢?我们前面说过“对驱动表可以直接排序,对非驱动表(的字段排序)需要对循环查询的合并结果(临时表)进行排序!”的。
explain
SELECT mb.id……
FROM mb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid
WHERE 1=1
ORDER BY mb.id DESC
limit 0,10
也满足业务场景,做到了rows最小:
By admin
read more使用docker-compose 快速创建一个mysql 数据库容器
//创建一个独立的容器目录
mkdir docker-db
cd docker-db
前提、创建 docker Compose 配置文件 #vi docker-compose.yml 文件,内容如下
version: '3.6'
services:
db:
image: hub.c.163.com/library/mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wordpress
MYSQL_USER: root
MYSQL_PASSWORD: 123456
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
ports:
- "33061:3306"
image也可以直接写mysql:5.7 或者mysql:latest,不指定获取地址。 上面的 ports 这一块,是指宿主机端口号:容器端口号。在使用的时候,直接访问本机的 33061 端口即可。端口号前也可以指定一个固定的IP 地址。 Compose file version 3 reference: https://docs.docker.com/compose/compose-file/
一、创建并启动容器
$docker-composer up
在 docker-composr.yml 所在的目录里,执行上面的命令,此时会自动从远程服务器拉取容器所需的信息。这时窗口一直处于运行状态,我们通过添加 -d 参数,来实现后台服务运行。
二、停止关闭容器
$ docker-compose stop
关闭后,容器文件仍然在磁盘上存在,重新执行 docker-compose start 即可启动。
三、删除容器
docker-compose rm
上面的命令可以将已经停止运行的容器进行删除,也可以将停止和删除用一条命令代替
docker-compose down
更多命令请执行 docker-compose -h 查看。
By admin
read moreMySQL InnoDB锁机制之Gap Lock、Next-Key Lock、Record Lock解析
InnoDB是一个支持行锁的存储引擎,锁的类型有:共享锁(S)、排他锁(X)、意向共享(IS)、意向排他(IX)。为了提供更好的并发,InnoDB提供了非锁定读:不需要等待访问行上的锁释放,读取行的一个快照。该方法是通过InnoDB的一个特性:MVCC来实现的。
InnoDB有三种行锁的算法
1,Record Lock:单个行记录上的锁。
2,Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
3,Next-Key Lock:1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。
锁的是索引,并不是记录。 记录锁(Record Lock): 单个索引行记录上的锁 间隙锁(Gap Lock):一般是针对非唯一索引而言的. 后码锁(Next-Key Lock):记录锁和间隙锁的结合,对于InnoDB中,更新非唯一索引对应的记录,会加上Next-Key Lock。在RR下如果where未使用索引会使用全表扫描,这个时候会有Next-Key Lock。如果更新记录为空,就不能加记录锁,只能加间隙锁。
Next-Key Lock是行锁与间隙锁的组合,这样,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。如果一个间隙被事务T1加了锁,其它事务是不能在这个间隙插入记录的。
参考资料
By admin
read more聚簇索引概念(Myisam与Innodb索引的区别)转推荐
myisam的主索引和次索引都指向物理行,下面来进行讲解
innodb的主键下存储该行的数据,此索引指向对主键的引用
myisam的索引存储图如下,可以看出,无论是id还是cat_id,下面都存储有存储物理地址的值。通过主键索引或者次索引来查询数据的时候,都是先查找到数据地址,然后再到物理位置上去寻找数据。
[][1]
innodb的索引存储图如下,我们会发现,主键索引下面直接存储有数据,而次索引下,存储的是主键的id(不同于MyISAM,存储的是内容数据的物理地址)。通过主键查找数据的时候,就会很快查找到数据,但是通过次索引查找数据的时候,需要先查找到对应的主键id,然后才能查找到对应的数据。
[][2]
总结: InnoDB的主索引文件上 直接存放该行数据,称为聚簇索引,次索引指向对主键的引用. Myisam中, 主索引和次索引,都指向物理行(磁盘位置).
注意:对InnoDB来说, 1: 主键索引 既存储索引值,又在叶子中存储行的数据 2: 如果没有主键, 则会Unique key做主键 3: 如果没有unique,则系统生成一个内部的rowid做主键. 4: 像innodb中,主键的索引结构中,既存储了主键值,又存储了行数据,这种结构称为”聚簇索引”
参考: https://blog.csdn.net/lisuyibmd/article/details/53004848
转自: [https://blog.csdn.net/qq_25551295/article/details/48901317
By admin
read moreIE浏览器报Promise错误的解决办法
说明ie浏览器不支持Promise,可搜索关键字 polyfill,引入以下库即可
官方文档: https://cdn.polyfill.io/v2/docs/ 参考: https://www.cnblogs.com/XHappyness/p/7919610.html 参考: https://www.jianshu.com/p/2888a8e204e8
By admin
read morelinux下git版本过底引起的无法git clone的解决方案
刚安装的新系统,git版本为1.8.3,使用git clone命令的时候,提示“… Peer reports incompatible or unsupported protocol version”
只需要升级一下基本包即可。
sudo yum update nss curl # nss为名称解析和认证服务 curl为网络请求库
By admin
read moreGo中slice作为参数传递的一些“坑”
看明白了这篇文章,下面的例子基本也就明白了
package main
import "fmt"
func main() {
a := []int{1,2,3}
abc(a)
fmt.Println(a)
}
func abc(a []int) {
a[0] = 2 //修改后还是原来的a
a = append(a, 4) // 此a非原a,使用append导致了重新分配内存地址(存储空间不足,系统自动分配一块新的足够大的内存地址,此时a的物理内存地址已经发行了变化,并将原来a的值copy一份到新的内存地址,所以这里修改的只是新内存地址的值,原来内存地址的值并没有改变),试着删除这行运行一次再看结果
fmt.Println(a)
a[0] = 7 // 新a,因为上面执行了append
fmt.Println(a)
fmt.Printf("\n===\n")
}
解释:
[][1]
By admin
read moregolang中string rune byte 三者的关系
Go
语言中 byte
和 rune
实质上就是 uint8
和 int32
类型。 byte
用来强调数据是 raw data
,而不是数字;而 rune
用来表示 Unicode
的 code point
。参考 规范.
在Golang中 string 底层是用byte字节数组存储的,并且是不可以修改的。
Go语言中的byte和rune区别、对比
例如
s:="Go编程"
fmt.Println(len(s)) //输出结果应该是8因为中文字符是用3个字节存的(2+3*2=8)。
fmt.Printf("%d", len(string(rune('编')))) //经测试一个汉字确实占用3个字节,所以结果是3
如果想要获得字符个数的话,需要先转换为rune切片再使用内置的len函数
fmt.Println(len([]rune(s))) // 结果就是4了。
所以用string存储unicode的话,如果有中文,按下标是访问不到的,因为你只能得到一个byte。 要想访问中文的话,还是要用rune切片,这样就能按下表访问。
总结:
rune
能操作任何字符
byte
不支持中文的操作
示例: https://blog.haohtml.com/archives/14903
在极客时间的 go语言核心36讲 专栏里有一篇文章“ unicode与字符编码”对此介绍的比较详情,包含底层字符集编码。
在 Go 语言中,一个string类型的值既可以被拆分为一个包含多个字符的序列,也可以被拆分为一个包含多个字节的序列。前者可以由一个以 rune 为元素类型的切片来表示,而后者则可以由一个以 byte 为元素类型的切片代表。 rune是 Go 语言特有的一个基本数据类型,它的一个值就代表一个字符,即:一个 Unicode 字符。比如,’G’、’o’、’爱’、’好’、’者’代表的就都是一个 Unicode 字符。
By admin
read more解决mac下brew link python3出错brew Error: Permission denied @ dir_s_mkdir – /usr/local/Frameworks
mac上默认的python版本为2.7.10版本,需要升级到python3 版本,通过brew升级
$brew install python3
提示错误
$ brew install python3 Warning: python3 3.6.3is already installed, it‘s just not linked. You can use
brew link python3
to link this version. $ brew link python3 Linking /usr/local/Cellar/python3/3.6.3… Error: Permission denied @ dir_s_mkdir
发现/usr/local/
下没有路径/usr/local/Frameworks
需要新建该路径,并修改权限
解决:
$ sudo mkdir /usr/local/Frameworks
$ sudo chown $(whoami):admin /usr/local/Frameworks
成功:
$ brew link python3
Linking /usr/local/Cellar/python3/3.6.3... 1 symlinks created
By admin
read more