diff --git a/content/zh/post/dai/1.md b/content/zh/post/dai/1.md new file mode 100644 index 0000000000000000000000000000000000000000..f0867ae17038021317e6b58fe24ec75a176d79a5 --- /dev/null +++ b/content/zh/post/dai/1.md @@ -0,0 +1,980 @@ ++++ +title = "OpenGauss数据库的安装与运行" +date = "2020-05-30" +tags = ["OpenGauss数据库的安装与运行"] +archives = "2021-12" +author = "dai" +summary = "OpenGauss数据库的安装与运行" + ++++ + +## 实验内容 + +本实验主要内容为在CentOS 7.9上安装部署openGauss数据库,并进行简单的数据库相关操作。 + +## 实验环境说明 + +l 组网说明 + +​ 本实验环境为虚拟机VMware Workstation Pro + CentOS 7.9 + openGauss 2.1.0。 + +l 设备介绍 + +​ 我所使用的具体设备: + +​ 设备明细表 + +| 设备名称 | 设备型号 | +| --------------- | ----------------------- | +| Linux操作系统 | CentOS 7.9 | +| windows操作系统 | win10 x86 64位 | +| 虚拟机 | VMware Workstation Pro | +| Python | Python 3.6.X | + +## 实验环境安装 + +官网文档使用的是Oracle VM VirtualBox我安装了一下觉得不太好用,还是推荐用VMware Workstation Pro。在VMware Workstation Pro上创建centos7.9(最新,稍后会进行修改)虚拟机,参考链接如下[超详细虚拟机VMware安装Centos7教程(图文) - 知乎 (zhihu.com)](https://zhuanlan.zhihu.com/p/145102034):不是本文重点,不再赘述。与官方文档不同的是我选择了安装带桌面的centos,更加方便。 + +## openGauss安装 + +\1. 修改操作系统版本 + +如果CentOS版本不是7.6的需要进行修改,如果是7.6则无需修改, + +先vi /etc/redhat-releas 打开编辑文件,然后将内容改为CentOS Linux release 7.6.2003 (Core)。输入”i”切换到编辑模式,移动鼠标到修改位置修改内容,然后按下ESC键退出编辑模式,然后输入”:wq”退出并进行保存,具体如下: + +[root@db1 ~]# **vi /etc/redhat-release** + + + +CentOS Linux release **7.6.**2003 (Core) + +\2. 查看本地ip + +使用命令 : + +Ifconig + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg) + +\3. 使用[一键部署openGauss2.0.1[CentOS 7.6\] - 墨天轮 (modb.pro)](https://www.modb.pro/db/106407)内的代码进行安装,注意将代码里的ip改为自己的ip + + vi auto_install.sh + +然后将代码复制 + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg) + +在命令行中输入以下命令进行运行,开始自动安装 + + sh auto_install.sh + +询问yes/no填yes + +然后输入密码,这里注意输入的密码必须符合要求,否则会安装失败 + +\4. 查看是否安装成功 + +Su - omm + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg) + +安装成功后数据库默认开启 只需要命令 + + gsql -d postgres -p 26000 -r + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image008.jpg) + +而如果数据库关闭了,就需要先执行 + +gs_om -t start + + + +再执行 + + gsql -d postgres -p 26000 -r + +否则会出现failed to connect Unknown:26000 的错误 + +下面对自动安装的代码进行分析 + +\## 1.定义主机信息[请根据实际情况修改] + +export MY_HOSTNAME=node1 ## 主机名 + +export MY_HOSTIP=192.168.244.128 ## IP地址 + +export MY_SOFTWARE_DIRECTORY=/soft/openGauss ## 软件包所在目录 + +export MY_XML=/soft/openGauss/clusterconfig.xml ## 集群配置文件XML + +export openGauss_Download_url=https://opengauss.obs.cn-south-1.myhuaweicloud.com/2.0.1/x86/openGauss-2.0.1-CentOS-64bit-all.tar.gz ## openGauss软件包下载地址 + + + +\## 1. 设置主机名并配置hosts文件 + +hostnamectl set-hostname $MY_HOSTNAME + +sed -i '/$MY_HOSTIP/d' /etc/hosts + +echo "$MY_HOSTIP $MY_HOSTNAME #Gauss OM IP Hosts Mapping" >> /etc/hosts + +cat /etc/hosts + +echo "1.Configure /etc/hosts completed." + +echo -e "\n" + + + +\## 2. 关闭防火墙 + +systemctl disable firewalld.service + +systemctl stop firewalld.service + +echo "Firewalld " `systemctl status firewalld|grep Active` + +echo "2.Disable firewalld service completed." + +echo -e "\n" + + + +\## 3. 关闭SELinux + +sed -i '/^SELINUX=/d' /etc/selinux/config + +echo "SELINUX=disabled" >> /etc/selinux/config + +cat /etc/selinux/config|grep "SELINUX=disabled" + +echo "3.Disable SELINUX completed." + +echo -e "\n" + + + +\## 4. 设置操作系统字符集编码 + +echo "LANG=en_US.UTF-8" >> /etc/profile + +source /etc/profile + +echo $LANG + +echo "4.Configure encoding completed." + +echo -e "\n" + + + +\## 5. 设置操作系统时区 + +rm -fr /etc/localtime + +ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + +date -R + +hwclock + +echo "5.Configure Timezone completed." + +echo -e "\n" + + + +\## 6. 关闭SWAP分区 [对于2G内存的设备,建议待安装完毕后再打开SWAP以间接 “扩容内存容量”] + +sed -i '/swap/s/^/#/' /etc/fstab + +swapoff -a + +free -m + +echo "6.Close swap partition completed." + +echo -e "\n" + + + +\## 7. 配置SSH服务,关闭Banner,允许root远程登录 + +sed -i '/Banner/s/^/#/' /etc/ssh/sshd_config + +sed -i '/PermitRootLogin/s/^/#/' /etc/ssh/sshd_config + +echo -e "\n" >> /etc/ssh/sshd_config + +echo "Banner none " >> /etc/ssh/sshd_config + +echo "PermitRootLogin yes" >> /etc/ssh/sshd_config + +cat /etc/ssh/sshd_config |grep -v ^#|grep -E 'PermitRoot|Banner' + +echo "7.Configure SSH Service completed." + +echo -e "\n" + + + +\## 8. 配置YUM源、安装依赖包、修改默认Python3版本 + +mkdir /etc/yum.repos.d/bak + +mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/bak/ + +wget -O /etc/yum.repos.d/CentOS-Base.repo https://repo.huaweicloud.com/repository/conf/CentOS-7-reg.repo + +yum clean all + +yum install -y wget bzip2 python3 + +yum install -y libaio-devel flex bison ncurses-devel glibc-devel patch redhat-lsb-core readline-devel net-tools tar + +mv /usr/bin/python /usr/bin/python2_bak + +ln -s /usr/bin/python3 /usr/bin/python + +python -V + +echo "8.Configure Install Packages and change default Python version completed." + +echo -e "\n" + + + +\## 9. 配置 sysctl.conf 和 performance.sh + +cat >> /etc/sysctl.conf << EOF + +net.ipv4.tcp_retries1 = 5 + +net.ipv4.tcp_syn_retries = 5 + +net.sctp.path_max_retrans = 10 + +net.sctp.max_init_retransmits = 10 + +EOF + +sysctl -p + +echo "9.Configure sysctl.conf and performance.sh completed." + +echo -e "\n" + + + +\## 10. 配置资源限制 + +echo "* soft stack 3072" >> /etc/security/limits.conf + +echo "* hard stack 3072" >> /etc/security/limits.conf + +echo "* soft nofile 1000000" >> /etc/security/limits.conf + +echo "* hard nofile 1000000" >> /etc/security/limits.conf + +echo "* soft nproc unlimited" >> /etc/security/limits.d/90-nproc.conf + +tail -n 4 /etc/security/limits.conf + +tail -n 1 /etc/security/limits.d/90-nproc.conf + +echo "10.Configure resource limits completed." + +echo -e "\n" + + + +\## 11. 关闭透明大页[Only for CentOS] + +cat >>/etc/rc.d/rc.local< /sys/kernel/mm/transparent_hugepage/enabled + +fi + +if test -f /sys/kernel/mm/transparent_hugepage/defrag; then + + echo never > /sys/kernel/mm/transparent_hugepage/defrag + +fi + +EOF + +chmod +x /etc/rc.d/rc.local + +/usr/bin/sh /etc/rc.d/rc.local + +cat /sys/kernel/mm/transparent_hugepage/enabled + +cat /sys/kernel/mm/transparent_hugepage/defrag + +echo "11.Close transparent_hugepage completed." + +echo -e "\n" + + + +\## 12. 禁用RemoveIPC[Only for openEuler] + +\## sed -i '/^RemoveIPC/d' /etc/systemd/logind.conf + +\## sed -i '/^RemoveIPC/d' /usr/lib/systemd/system/systemd-logind.service + +\## echo "RemoveIPC=no" >> /etc/systemd/logind.conf + +\## echo "RemoveIPC=no" >> /usr/lib/systemd/system/systemd-logind.service + +\## systemctl daemon-reload + +\## systemctl restart systemd-logind + +\## loginctl show-session | grep RemoveIPC + +\## systemctl show systemd-logind | grep RemoveIPC + +\## echo "12.Disable RemoveIPC completed." + +\## echo -e "\n" + + + +\## 13. 下载openGauss软件包 + +mkdir -p $MY_SOFTWARE_DIRECTORY + +cd $MY_SOFTWARE_DIRECTORY + +wget $openGauss_Download_url + +echo "13.openGauss software download completed." + +echo -e "\n" + + + +\## 14. 配置XML文件 + +rm -fr $MY_XML + +cat >> $MY_XML < + + + + + + + +​ + +​ + +​ + +​ + +​ + +​ + +​ + +​ + + + + + + + +​ + +​ + +​ + +​ + +​ + +​ + +​ + +​ + +​ + +​ + +​ + +​ + +​ + +​ + + + + + +EOF + +cat $MY_XML + +echo "14.Configure XML file completed." + +echo -e "\n" + + + +\## 15. 解压安装包并修改目录权限 + +echo "Begin to Uncompress openGauss Package and Modify directory permissions:" + +cd $MY_SOFTWARE_DIRECTORY + +tar -zxvf *all.tar.gz + +tar -zxvf *om.tar.gz + +ls -l + +chmod -R 777 $MY_SOFTWARE_DIRECTORY/../ + +echo "15.Uncompress openGauss Package completed." + +echo -e "\n" + + + +\## 16. 执行 gs_preinstall + +echo "Begin to execute openGauss preinstall:" + +python $MY_SOFTWARE_DIRECTORY/script/gs_preinstall -U omm -G dbgrp -X $MY_XML + +echo "16.openGauss preinstall completed." + +echo -e "\n" + + + +\## 17. 检查预安装环境 + +echo "Begin to Check OS environment:" + +$MY_SOFTWARE_DIRECTORY/script/gs_checkos -i A -h $MY_HOSTNAME --detail + + + +\## 18. 执行 gs_install + +echo "Begin to execute openGauss install:" + +touch /home/omm/install_db + +cat >> /home/omm/install_db <。 + +编写测试程序(修改ip,端口,用户,密码) + +cd /gaussdb/data/db1 + +​ vi pg_hba.conf + +在ipv4最后添加host all all 0.0.0.0/0 sha256 目的为修改网络连接规则,使得一些工具可以连接至该服务器。 + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image040.jpg) + +切换至 omm 用户环境,使用 gs_ctl 将策略生效。 + +su - omm + +​ gs_ctl reload -D /gaussdb/data/db1/ + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image042.jpg) + +登录openGauus,创建新用户(如果已经有则忽略这一步),远程登录不允许使用omm用户. + +CREATE USER jack PASSWORD 'Test@123'; + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image044.jpg) + +如果使用内存表,则需要授予新用户权限: + +grant usage on foreign server mot_server to jack; + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image046.jpg) + +修改数据库监听地址 + +cd /gaussdb/data/db1 + +vi postgresql.conf + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image048.jpg) + +输入“:60”找到对应位置,然后输入“i”切换到 INSERT 模式,将 listen_addresses 的 值修改成为*,修改后按下“ESC”键,退出 INSERT 模式,输入“:wq”后回车保存。 + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image050.jpg) + +修改完成后重启数据库生效(-D后面的数据库默认路径,需要根据实际情况进行修改) + +gs_ctl restart -D /gaussdb/data/db1/ + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image052.jpg) ![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image054.jpg) + + + +//DBtest.java + +//演示基于JDBC开发的主要步骤,会涉及创建数据库、创建表、插入数据等。 + + + +import java.sql.Connection; + +import java.sql.DriverManager; + +import java.sql.PreparedStatement; + +import java.sql.SQLException; + +import java.sql.Statement; + +import java.sql.CallableStatement; + + + +public class DBTest { + + + + //创建数据库连接。 + + public static Connection GetConnection(String username, String passwd) { + + String driver = "org.postgresql.Driver"; + + String sourceURL = "jdbc:postgresql://ip:端口/postgres"; + + Connection conn = null; + + try { + + //加载数据库驱动。 + + Class.forName(driver).newInstance(); + + } catch (Exception e) { + + e.printStackTrace(); + + return null; + + } + + + + try { + + //创建数据库连接。 + + conn = DriverManager.getConnection(sourceURL, username, passwd); + + System.out.println("Connection succeed!"); + + } catch (Exception e) { + + e.printStackTrace(); + + return null; + + } + + + + return conn; + +}; + + + +//执行普通SQL语句,创建customer_t1表。 + +public static void CreateTable(Connection conn) { + + Statement stmt = null; + + try { + + stmt = conn.createStatement(); + + + + //执行普通SQL语句。 + + int rc = stmt + +​ .executeUpdate("CREATE TABLE customer_t1(c_customer_sk INTEGER, c_customer_name VARCHAR(32));"); + + + + stmt.close(); + +} catch (SQLException e) { + + if (stmt != null) { + + try { + + stmt.close(); + + } catch (SQLException e1) { + + e1.printStackTrace(); + + } + + } + + e.printStackTrace(); + +} + +} + + + +//执行预处理语句,批量插入数据。 + +public static void BatchInsertData(Connection conn) { + +PreparedStatement pst = null; + + + +try { + + //生成预处理语句。 + + pst = conn.prepareStatement("INSERT INTO customer_t1 VALUES (?,?)"); + + for (int i = 0; i < 3; i++) { + + //添加参数。 + + pst.setInt(1, i); + + pst.setString(2, "data " + i); + + pst.addBatch(); + + } + + //执行批处理。 + + pst.executeBatch(); + + pst.close(); + +} catch (SQLException e) { + + if (pst != null) { + + try { + + pst.close(); + + } catch (SQLException e1) { + + e1.printStackTrace(); + + } + + } + + e.printStackTrace(); + +} + +} + + + +//执行预编译语句,更新数据。 + +public static void ExecPreparedSQL(Connection conn) { + +PreparedStatement pstmt = null; + +try { + + pstmt = conn + + .prepareStatement("UPDATE customer_t1 SET c_customer_name = ? WHERE c_customer_sk = 1"); + + pstmt.setString(1, "new Data"); + + int rowcount = pstmt.executeUpdate(); + + pstmt.close(); + +} catch (SQLException e) { + + if (pstmt != null) { + + try { + + pstmt.close(); + + } catch (SQLException e1) { + + e1.printStackTrace(); + + } + + } + + e.printStackTrace(); + +} + +} + + + +/** + + \* 主程序,逐步调用各静态方法。 + + \* @param args + +*/ + +public static void main(String[] args) { + + //创建数据库连接。 + +Connection conn = GetConnection("tester", "Password1234"); + + + +//创建表。 + +CreateTable(conn); + + + +//批插数据。 + +BatchInsertData(conn); + + + +//执行预编译语句,更新数据。 + +ExecPreparedSQL(conn); + + + +//关闭数据库连接。 + +try { + + conn.close(); + +} catch (SQLException e) { + + e.printStackTrace(); + + } + +} + +} + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image056.jpg) + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image058.jpg) + +命令行编译,将jdbc驱动的jar包和DBTest.java放在同一个目录下,编译: + +javac -cp postgresql.jar dbtest.java + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image060.jpg) + +运行: + +java -cp .:postgresql.jar dbtest + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image062.jpg) + +### 问题及解决 + +在安装过程中遇到了很多问题,上面安装过程都有体现,下面进行总结。 + +​ i. VM VirtualBox使用过程中遇到了很多问题,建议还是使用VMware + +​ ii. 可以安装centos7.9,但一定要修改/etc/redhat-release文件里的为7.6 + +​ iii. 在设置数据库密码时,注意对密码长度、大小写的要求,不符合要求无法安装成功 + +​ iv. 直接在root用户下运行比较方便 + +​ v. 在运行gs_om -t start时不成功,需要检查一下虚拟机的网络,有时候虚拟机就是连接不上网络,网上看了很多办法都没用,此时建议重启。 + +​ vi. 在运行gsql -d postgres -p 26000 -r时提示错误failed to connect Unknown:26000,说明数据库还没开启,要先运行gs_om -t start,ps -ef |grep gauss可以查看数据库进程是否关闭 + +​ vii. 远程登录不允许使用omm用户!需要创建用户并给他grant \ No newline at end of file diff --git a/content/zh/post/dai/2.md b/content/zh/post/dai/2.md new file mode 100644 index 0000000000000000000000000000000000000000..796d073650ae8f7fbfbc73c56d47f8b3762b7825 --- /dev/null +++ b/content/zh/post/dai/2.md @@ -0,0 +1,646 @@ ++++ +title = "OpenGauss数据库中事务管理源码解析" +date = "2020-05-30" +tags = ["OpenGauss数据库中事务管理源码解析"] +archives = "2021-12" +author = "dai" +summary = "OpenGauss数据库中事务管理源码解析" + ++++ + +### 一、事务 + +1. 事务的定义 + +事务是数据库操作的执行单位,需要满足最基本的ACID(原子性、一致性、隔离性、持久性)属性。 + +(1) 原子性:一个事务提交之后要么全部执行,要么全部不执行。 + +(2) 一致性:事务的执行不能破坏数据库的完整性和一致性。 + +(3) 隔离性:事务的隔离性是指在并发中,一个事务的执行不能被其他事务干扰。 + +(4) 持久性:一旦事务完成提交,那么它对数据库的状态变更就会永久保存在数据库中。 + +本章主要介绍openGauss事务模块是如何实现数据库事务的基本属性,使用户数据不丢不错、修改不乱、查询无错误。 + + + + 2.事务管理器 + +事务管理器:事务系统的中枢,它的实现是一个有限循环状态机,通过接受外部系统的命令并根据当前事务所处的状态决定事务的下一步执行过程。 + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg) + +在openGauss中,事务的实现与存储引擎的实现有很强关联,代码主要集中在src/gausskernel/storage/access/transam及src/gausskernel/storage/lmgr下。 + +(1) 事务管理器:事务系统的中枢,它的实现是一个有限循环状态机,通过接受外部系统的命令并根据当前事务所处的状态决定事务的下一步执行过程。 + +(2) 日志管理器:用来记录事务执行的状态以及数据变化的过程,包括事务提交日志(CLOG)、事务提交序列日志(CSNLOG)以及事务日志(XLOG)。其中CLOG日志只用来记录事务执行的结果状态,CSNLOG记录日志提交的顺序,用于可见性判断;XLOG是数据的redo日志,用于恢复及持久化。 + +(3) 线程管理机制:通过一片内存区域记录所有线程的事务信息,任何一个线程可以通过访问该区域获取其他事务的状态信息。 + +(4) MVCC机制:openGauss系统中,事务执行读流程结合各事务提交的CSN序列号,采用了多版本并发控制机制,实现了元组的读和写互不阻塞。详细可见性判断方法见“5.2 事务并发控制”。 + +(5) 锁管理器:实现系统的写并发控制,通过锁机制来保证事务写流程的隔离性。 + +### 二、Opengauss中的事务 + + 1.层次 + +openGauss将事务系统分为上层(事务块TBlockState)以及底层(TransState)两个层次。 + +通过分层的设计,在处理上层业务时可以屏蔽具体细节,实现灵活支持客户端各类事务执行语句(BEGIN/START TRANSACTION/COMMIT/ROLLBACK/END)。 + +(1) 事务块TBlockState:客户端query的状态,用于提高用户操作数据的灵活性,用事务块的形式支持在一个事务中执行多条query语句。 + +(2) 底层事务TransState:内核端视角,记录了整个事务当前处于的具体状态。 + + + + \2. 具体代码 + +事务块上层状态机结构体代码如下: + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg) + +底层事务块代码: + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg) + + 3.事务状态转换相关函数 + +​ ![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image008.jpg) + +1. 改变事务块上层的状态 + +2. 处理函数,根据相应的状态机调用子函数,在每个query处理前后,或检测到错误后被postgres.c 调用 + +3. 根据当前事务上层状态机,对事务的资源进行相应的申请、回收及清理 + + + +事务处理子函数 + +| 子函数 | 说明 | +| --------------------- | ------------------------------------------------------------ | +| StartTransaction | 开启事务,对内存及变量进行初始化操作,完成后将底层事务状 态置为TRANS_INPROGRESS | +| CommitTransaction | 当前的底层状态机为TRANS_INPROGRESS,然后置为TRANS_COMMIT, 最后将底层状态机置为TRANS_DEFAULT | +| AbortTransaction | 释放LWLock、UnlockBuffers、LockErrorCleanup,当前底层状态为 TRANS_INPROGRESS,设置为TRANS_ABORT,记录相应的CLOG日志, 清空事务槽位信息,释放各类资源 | +| CleanupTransaction | 当前底层状态机应为TRANS_ABORT,继续清理一些资源,一般紧接 着AbortTransaction调用 | +| StartSubTransaction | 开启子事务 | +| CommitSubTransaction | 提交子事务 | +| AbortSubTransaction | 回滚子事务 | +| CleanupSubTransaction | 清理子事务的一些资源信息,类似于CleanupTransaction | + + + +事务执行函数 + +| 函数 | 说明 | +| ------------------------ | ------------------------------------------------------------ | +| StartTransactionCommand | 事务开始时根据上层状态机调用相应的事务执行函数 | +| CommitTransactionCommand | 事务结束时根据上层状态机调用相应的事务执行函数 | +| AbortCurrentTransaction | 事务内部出错,长跳转longjump调用,提前清理掉相应的资源, 并将事务上层状态机置为TBLOCK_ABORT | + +上层事务状态机控制函数 + +| 函数 | 说明 | +| ------------------------- | ------------------------------------------------------------ | +| BeginTransactionBlock | 开启一个显式事务时,将上层事务状态机变为TBLOCK_BEGIN | +| EndTransactionBlock | 显式提交一个事务时,将上层事务状态机变为TBLOCK_END | +| UserAbortTransactionBlock | 显式回滚一个事务时,将上层事务状态机变为TBLOCK_ABORT_PENDING/ TBLOCK_ABORT_END | +| PrepareTransactionBlock | 显式执行PREPARE语句,将上层事务状态机变为TBLOCK_PREPARE | +| DefineSavepoint | 执行savepoint语句,通过调用PushTransaction将子事务上层事务状态机变为 TBLOCK_SUBBEGIN | +| ReleaseSavepoint | 执行release savepoint语句,将子事务上层状态机转变为TBLOCK_SUBRELEASE | +| RollbackToSavepoint | 执行“rollback to”语句,将所有子事务上层状态机转变为 TBLOCK_SUBABORT_PENDING/ TBLOCK_SUBABORT_END,顶层事务的上层状态机转变 为TBLOCK_SUBABORT_RESTART | + + 对其中一个代码进行详细分析,其他代码类似,篇幅原因不再赘述 + +StartTransaction: + +static void StartTransaction(bool begin_on_gtm) + +{ + + TransactionState s; + + VirtualTransactionId vxid; + + GTM_Timestamp gtm_timestamp; + + + + gstrace_entry(GS_TRC_ID_StartTransaction); + + /*清除寄存器信息 */ + + ForgetRegisterStreamSnapshots(); + + + + /*确保状态栈为空*/ + + s = &TopTransactionStateData; + + CurrentTransactionState = s; + + t_thrd.xact_cxt.bInAbortTransaction = false; + + t_thrd.xact_cxt.handlesDestroyedInCancelQuery = false; + + + + StmtRetrySetTransactionCommitFlag(false); + + + + /*检查当前transaction的状态 */ + + if (s->state != TRANS_DEFAULT) { + +​ ereport(WARNING, (errmsg("StartTransaction while in %s state", TransStateAsString(s->state)))); + + } + + /* 开始时对寄存器进行清空 */ + + DestroyCstoreAlterReg(); + + + + /* 设置默认值*/ + + t_thrd.storage_cxt.EnlargeDeadlockTimeout = false; + + /*释放多余空间 */ + + gs_memprot_reset_beyondchunk(); + + + + /* 在开始时设置正确的transaction + + \* state 信息 */ + + s->state = TRANS_START; + +\#ifdef PGXC + + s->isLocalParameterUsed = false; + +\#endif + + s->transactionId = InvalidTransactionId; /* until assigned 赋值*/ + + + + ResetUndoActionsInfo(); + + + + /* + + \* 如果恢复仍在进行中,则将此事务标记为只读。 + + \* 如果恢复仍在进行中,则将此事务标记为只读。 + + \* 我们在 XLogInsert + + \* 和其他地方有较低级别的防御来阻止我们在恢复期间修改数据,但这向用户提供了正常的指示,即事务是只读的。 + + */ + + if (RecoveryInProgress()) { + +​ s->startedInRecovery = true; + +​ u_sess->attr.attr_common.XactReadOnly = true; + + } else { + +​ s->startedInRecovery = false; + +​ u_sess->attr.attr_common.XactReadOnly = u_sess->attr.attr_storage.DefaultXactReadOnly; + +\#ifdef PGXC + +​ if (!u_sess->attr.attr_common.xc_maintenance_mode) + +​ u_sess->attr.attr_common.XactReadOnly = u_sess->attr.attr_common.XactReadOnly || IsPGXCNodeXactReadOnly(); + +\#endif + +​ /*如果事务来自 cm_agent,我们总是把它当作可写的*/ + +​ if (u_sess->libpq_cxt.IsConnFromCmAgent) { + +​ u_sess->attr.attr_common.XactReadOnly = false; + +​ } + + } + + u_sess->attr.attr_storage.XactDeferrable = u_sess->attr.attr_storage.DefaultXactDeferrable; + +\#ifdef PGXC + + if (u_sess->attr.attr_common.DefaultXactIsoLevel == XACT_SERIALIZABLE) + +​ u_sess->attr.attr_common.DefaultXactIsoLevel = XACT_REPEATABLE_READ; + +\#endif + + u_sess->utils_cxt.XactIsoLevel = u_sess->attr.attr_common.DefaultXactIsoLevel; + + t_thrd.xact_cxt.forceSyncCommit = false; + + t_thrd.xact_cxt.MyXactAccessedTempRel = false; + + t_thrd.xact_cxt.MyXactAccessedRepRel = false; + + t_thrd.xact_cxt.XactLocalNodePrepared = false; + + t_thrd.xact_cxt.XactLocalNodeCanAbort = true; + + t_thrd.xact_cxt.XactPrepareSent = false; + + t_thrd.xact_cxt.AlterCoordinatorStmt = false; + + t_thrd.utils_cxt.pRelatedRel = NULL; + + + + /* 重新初始化事务内计数器 */ + + s->subTransactionId = TopSubTransactionId; + + t_thrd.xact_cxt.currentSubTransactionId = TopSubTransactionId; + + t_thrd.xact_cxt.currentCommandId = FirstCommandId; + + t_thrd.xact_cxt.currentCommandIdUsed = false; + +\#ifdef PGXC + + /* + + \* 与事务的全局命令 ID 控制相关的参数。 + + \* 发送第一个命令 ID。 */ + + t_thrd.xact_cxt.isCommandIdReceived = false; + + if (IsConnFromCoord()) { + +​ SetReceivedCommandId(FirstCommandId); + +​ SetSendCommandId(false); + + } + +\#endif + + + + /* 初始化 xid */ + + t_thrd.xact_cxt.nUnreportedXids = 0; + + s->didLogXid = false; + + + + /* 必须首先初始化资源管理的东西 */ + + AtStart_Memory(); + + AtStart_ResourceOwner(); + + + + vxid.backendId = t_thrd.proc_cxt.MyBackendId; + + vxid.localTransactionId = GetNextLocalTransactionId(); + + + + VirtualXactLockTableInsert(vxid); + + + + Assert(t_thrd.proc->backendId == vxid.backendId); + + t_thrd.proc->lxid = vxid.localTransactionId; + + + + TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId); + + + + /* + + \* 初始化当前交易状态字段 * + + */ + + s->subXactLock = false; + + s->nestingLevel = 1; + + s->gucNestLevel = 1; + + s->childXids = NULL; + + s->nChildXids = 0; + + s->maxChildXids = 0; + + GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); + + /* SecurityRestrictionContext 一定要在 transaction内 */ + + Assert(s->prevSecContext == 0); + + + + t_thrd.xact_cxt.xactStartTimestamp = t_thrd.xact_cxt.stmtStartTimestamp; + + t_thrd.xact_cxt.xactStopTimestamp = 0; + + + + s->txnKey.txnHandle = InvalidTransactionHandle; + + s->txnKey.txnTimeline = InvalidTransactionTimeline; + + bool normal_working = begin_on_gtm && IsNormalProcessingMode() && + +​ !(IsAutoVacuumWorkerProcess() && (t_thrd.pgxact->vacuumFlags & PROC_IN_VACUUM)); + + bool update_xact_time = !GTM_FREE_MODE && !u_sess->attr.attr_common.xc_maintenance_mode && normal_working && + +​ IS_PGXC_COORDINATOR && !IsConnFromCoord(); + + if (update_xact_time) { + +​ if (GTM_MODE) { + +​ s->txnKey = BeginTranGTM(>m_timestamp); + +​ t_thrd.xact_cxt.GTMxactStartTimestamp = (TimestampTz)gtm_timestamp; + +​ } else { + +​ t_thrd.xact_cxt.GTMxactStartTimestamp = t_thrd.xact_cxt.xactStartTimestamp; + +​ } + +​ SetCurrentGTMDeltaTimestamp(); + + + +​ SetCurrentStmtTimestamp(); + +​ SetStmtSysGTMDeltaTimestamp(); + + } + + + + /* + + \* CN使用本地时间在gtm_free开启时初始化时间戳 + + \* 如果没有从cn获取时间戳,DN使用本地时间初始化时间戳 */ + + update_xact_time = (GTM_FREE_MODE && IS_PGXC_COORDINATOR && !IsConnFromCoord()) || + +​ (!t_thrd.xact_cxt.timestamp_from_cn && IS_PGXC_DATANODE && normal_working); + + if (update_xact_time) { + +​ t_thrd.xact_cxt.GTMxactStartTimestamp = t_thrd.xact_cxt.xactStartTimestamp; + +​ t_thrd.xact_cxt.GTMdeltaTimestamp = 0; + +​ SetCurrentStmtTimestamp(); + + } + + /* 重置时间戳标志 */ + + t_thrd.xact_cxt.timestamp_from_cn = false; + +\#ifdef PGXC + + pgstat_report_xact_timestamp(t_thrd.xact_cxt.GTMxactStartTimestamp); + +\#else + + pgstat_report_xact_timestamp(t_thrd.xact_cxt.xactStartTimestamp); + +\#endif + + + + /* 为新事务初始化其他子系统*/ + + AtStart_GUC(); + + AtStart_Inval(); + + AtStart_Cache(); + + AfterTriggerBeginXact(); + +\#ifdef ENABLE_MULTIPLE_NODES + + reset_searchlet_id(); + +\#endif + + ResetBCMArray(); + + + + /* 获取节点组状态并保存在缓存中 */ + + InitNodeGroupStatus(); + + + + /* 完成开始处理,将当前事务状态设置为“进行中” */ + + s->state = TRANS_INPROGRESS; + + + +\#ifdef ENABLE_MOT + + CallXactCallbacks(XACT_EVENT_START); + +\#endif + + + + if (module_logging_is_on(MOD_TRANS_XACT)) { + +​ ereport(LOG, (errmodule(MOD_TRANS_XACT), + +​ errmsg("start transaction succ. In Node %s, trans state: %s -> %s.", + +​ g_instance.attr.attr_common.PGXCNodeName, TransStateAsString(TRANS_START), + +​ TransStateAsString(TRANS_INPROGRESS)))); + + } + + + + ShowTransactionState("StartTransaction"); + + + + gstrace_exit(GS_TRC_ID_StartTransaction); + +} + + + +4. 事务状态机 + +![在这里插入图片描述](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image010.jpg) + +在无异常情形下,一个事务块的状态机上图所示按照 + +默认(TBLOCK_DEFAULT) -> 已开始(TBLOCK_STARTED) -> 事务块开启(TBLOCK_BEGIN) -> 事务块运行中(TBLOCK_INPROGRESS) -> 事务块结束(TBLOCK_END) -> 默认(TBLOCK_DEFAULT)循环。 + +剩余的状态机是在上述正常场景下的各个状态点的异常处理分支。 + + 在进入事务块运行中(TBLOCK_INPROGRESS)之前出错,因为事务还没有开启,直接报错并回滚,清理资源回到默认(TBLOCK_DEFAULT)状态。 + +​ 在事务块运行中(TBLOCK_INPROGRESS)出错分为2种情形。 + +​ 事务执行失败: + +事务块运行中(TBLOCK_INPROGRESS) -> 回滚(TBLOCK_ABORT)-> 回滚结束(TBLOCK_ABORT_END) -> 默认(TBLOCK_DEFAULT); + +​ 用户手动回滚执行成功的事务: + +事务块运行中(TBLOCK_INPROGRESS) -> 回滚等待(TBLOCK_ABORT_PENDING) -> 默认(TBLOCK_DEFAULT)。 + +在用户执行COMMIT语句时出错: + +事务块结束(TBLOCK_END) -> 默认(TBLOCK_DEFAULT)。 + +由图5-2可以看出,事务开始后离开默认(TBLOCK_DEFAULT)状态,事务完全结束后回到默认(TBLOCK_DEFAULT)状态。 + +openGauss同时还支持隐式事务块,当客户端执行单条SQL语句时可以自动提交,其状态机相对比较简单: + +默认(TBLOCK_DEFAULT) -> 已开始(TBLOCK_STARTED) -> 默认(TBLOCK_DEFAULT)循环。 + + + + \5. 内核内部底层状态 + +​ 底层状态机的描述见结构体TransState。 + +(1) 在事务开启前事务状态为TRANS_DEFAULT。 + +(2) 事务开启过程中事务状态为TRANS_START。 + +(3) 事务成功开启后一直处于TRANS_INPROGRESS。 + +(4) 事务结束/回滚的过程中为TARNS_COMMIT/ TRANS_ABORT。 + +(5) 事务结束后事务状态回到TRANS_DEFAULT。 + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image011.jpg) + +![在这里插入图片描述](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image013.jpg) + + + +### 三、实例 + +在客户端执行SQL语句: + + + +BEGIN; + +SELECT * FROM TABLE1; + +END; + +整体过程: + + + +任何语句的执行总是先进入事务处理接口事务块中,然后调用事务底层函数处理具体命令,最后返回到事务块中。具体过程见下图 + +![在这里插入图片描述](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image015.png) + + + +BEGIN执行流程,如图所示。 + +(1) 入口函数exec_simple_query处理begin命令。 + +(2) start_xact_command函数开始一个query命令,调用StartTransactionCommand函数,此时事务块上层状态未TBLOCK_DEFAULT,继续调用StartTransaction函数,设置事务底层状态TRANS_START,完成内存、缓存区、锁资源的初始化后将事务底层状态设为TRANS_INPROGRESS,最后在StartTransactionCommand函数中设置事务块上层状态为TBLOCK_STARTED。 + +(3) PortalRun函数处理begin语句,依次向下调用函数,最后调用BeginTransactionBlock函数转换事务块上层状态为TBLOCK_BEGIN。 + +(4) finish_xact_command函数结束一个query命令,调用CommitTransactionCommand函数设置事务块上层状态从TBLOCK_BEGIN变为TBLOCK_INPROGRESS,并等待读取下一条命令。 + +![在这里插入图片描述](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image017.png) + + + +SELECT执行流程,如图5-6所示。 + +(1) 入口函数exec_simple_query处理“SELECT * FROM table1;”命令。 + +(2) start_xact_command函数开始一个query命令,调用StartTransactionCommand函数,由于当前上层事务块状态为TBLOCK_INPROGRESS,说明已经在事务块内部,则直接返回,不改变事务上层以及底层的状态。 + +(3) PortalRun执行SELECT语句,依次向下调用函数ExecutorRun根据执行计划执行最优路径查询。 + +(4) finish_xact_command函数结束一条query命令,调用CommitTransactionCommand函数,当前事务块上层状态仍为TBLOCK_INPROGESS,不改变当前事务上层以及底层的状态。 + +![在这里插入图片描述](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image019.png) + + + +END执行流程,如图5-7所示。 + +(1) 入口函数exec_simple_query处理end命令。 + +(2) start_xact_command函数开始一个query命令,调用StartTransactionCommand函数,当前上层事务块状态为TBLOCK_INPROGESS,表明事务仍然在进行,此时也不改变任何上层及底层事务状态。 + +(3) PortalRun函数处理end语句,依次调用processUtility函数,最后调用EndTransactionBlock函数对当前上层事务块状态机进行转换,设置事务块上层状态为TBLOCK_END。 + +(4) Finish_xact_command函数结束query命令,调用CommitTransactionCommand函数,当前事务块状态TBLOCK_END;继续调用CommitTransaction函数提交事务,设置事务底层状态为TRANS_COMMIT,进行事务提交流程并且清理事务资源;清理后设置底层事务状态为TRANS_DEFAULT,返回CommitTansactionCommand函数;设置事务块上层状态为TBLOCK_DEFAULT,整个事务块结束。 + +![在这里插入图片描述](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image021.png) + +### 四、其他问题 + +1. 多条指令例如: + +BEGIN + +​ SELECT * FROM foo + +​ INSERT INTO foo VALUES (...) + +COMMIT + +调用CommitTransactionCommand后再调用CommandCounterIncrement + +![img](file:///C:/Users/admin/AppData/Local/Temp/msohtmlclip1/01/clip_image023.jpg) + +\2. 子事务块(subtransaction)问题代码位于src/gausskernel/storage/access/transam/xact.c + +子事务块的状态机转换同父事务类似。父子事务的关系类似于一个栈的实现,父事务的子事务相较于父事务后开始先结束。 \ No newline at end of file