欢迎关注“数据库运维之道”公众号,一起学习数据库技术! 本期将为大家分享“数据库锁等待排查方法、命令行安装数据库及授权文件更新”的运维技能。
关键词:锁等待、V$LOCK、V$TRXWAIT、死锁、锁超时、命令行部署达梦、授权文件更新
当用户反馈执行SQL语句时卡死或是应用运行缓慢时,首先会想到,在数据库中有没有阻塞的发生,然后找到它,记录它,杀掉它。让应用恢复正常,再来分析SQL的合理性并去优化它。
达梦数据库通过多版本并发控制MVCC和锁机制实现事务的并发控制和保持数据的一致性及正确性。在实际工作中,可能出现多个事务同时访问、修改相同数据的情况,当一个事务正在占用某个资源的锁,此时另一个事务正在请求这个资源上与第一个锁相冲突的锁类型时,就会发生阻塞。相信每位DBA都会遇到类似的事件,因此如何使用动态性能视图查看锁等待以及解决锁阻塞的问题是一项必备技能。
场景描述:模拟更新表记录出现锁等待,一步步定位到阻塞源头,并清理会话信息。
实验步骤:
1、新建lock_ywzd测试表,其中ID字段为主键,表结构参考如下:
1、登录数据库
./disql SYSDBA/SYSDBA@localhost:5237
服务器[localhost:5237]:处于普通打开状态
登录使用时间 : 5.803(ms)
disql V8
2、创建用户
create user ywzd identified by ywzdXX_1_2;
grant dba to ywzd;
create tablespace ywzd datafile '/dmdata/data/DMTEST/ywzd.DBF' size 128 autoextend on
maxsize 32768 CACHE = NORMAL;
alter user ywzd default tablespace ywzd;
3、登录数据库
./disql ywzd/'"ywzdXX_1_2"'@localhost:5237
服务器[localhost:5237]:处于普通打开状态
登录使用时间 : 5.803(ms)
disql V8
4、准备数据
create table lock_ywzd(id number primary key, name varchar(10));
insert into lock_ywzd values(10,'ywzd1');
insert into lock_ywzd values(20,'ywzd2');
insert into lock_ywzd values(30,'ywzd3');
commit;
5、查询SYSOBJECTS获取表的对象ID为1015
SQL> select name,id from sysobjects where name='LOCK_YWZD';
行号 NAME ID
---------- --------- -----------
1 LOCK_YWZD 1015
2、查询v$lock该表的锁(使用对象ID查询该表的锁),可以看到读操作时有IS锁(意向共享锁)。
SQL> select * from lock_ywzd;
行号 ID NAME
---------- -- -----
1 10 ywzd1
2 20 ywzd2
3 30 ywzd3
SQL> select trx_id,ltype,lmode,blocked,table_id,tid from v$lock where table_id=1015;
行号 TRX_ID LTYPE LMODE BLOCKED TABLE_ID TID
---------- -------------------- ------ ----- ----------- ----------- --------------------
1 6756 OBJECT IS 0 1015 6756
3、打开一个新会话(会话二),对lock_ywzd表id=10的记录进行更新。此时查看v$lock,新增表对象的IS(意向共享锁)和IX(意向排他锁)。
SQL> update lock_ywzd set name='ywzd4' where id=10;
影响行数 1
已用时间: 3.258(毫秒). 执行号:63401.
SQL> select trx_id,ltype,lmode,blocked,table_id,tid from v$lock where table_id=1015;
行号 TRX_ID LTYPE LMODE BLOCKED TABLE_ID TID
---------- -------------------- ------ ----- ----------- ----------- --------------------
1 6756 OBJECT IS 0 1015 6756
2 6757 OBJECT IX 0 1015 6757--》新事务6757
4、打开一个新会话(会话三),当UPDATE修改的记录已经被另一个事务修改过(未提交),将会发生阻塞,直到未提交的造成阻塞的事务提交或回滚。
SQL> update lock_ywzd set name='ywzd5' where id=10;
执行UPDATE DML语句,发现卡住,很长时间都没有执行完成。
很明显是会话二的事务未提交导致行记录锁住,阻塞了本事务的执行,导致等待。
5、会话一此时查看v$lock 或v$trxwait,可以看到会话被阻塞。
SQL> select trx_id,ltype,lmode,blocked,table_id,tid from v$lock where table_id=1015;
行号 TRX_ID LTYPE LMODE BLOCKED TABLE_ID TID
---------- -------------------- ------ ----- ----------- ----------- --------------------
1 6756 OBJECT IS 0 1015 6756
2 6757 OBJECT IX 0 1015 6757
3 6758 OBJECT IX 0 1015 6758 --》新事务6758
4 6758 TID X 1 1015 6757 --》新事务6758
SQL> select * from v$trxwait;
行号 ID WAIT_FOR_ID WAIT_TIME THRD_ID
---------- -------------------- -------------------- ----------- -----------
1 6758 6757 67191 25366
6、会话一根据字段WAIT_FOR_ID的阻塞事务ID=6757找到会话ID,可以看到会话id,sql信息。
SQL> select sess_id,trx_id,thrd_id,sql_text from v$sessions where trx_id=6757;
行号 SESS_ID TRX_ID THRD_ID SQL_TEXT
---------- -------------------- -------------------- ----------- ----------------------------------------------
1 140275042432184 6757 25366 update lock_ywzd set name='ywzd4' where id=10;
或者
SQL> select sf_get_session_sql(sess_id),trx_id,thrd_id from v$sessions where trx_id=6757;
上述查询结果有可能是SELECT语句,那是因为该事务做了数据修改后没有提交,然后又执行了其他的操作。可以开启SQL日志记录查询真正的阻塞SQL,具体查看“推荐阅读”。
7、会话一根据会话ID,使用sp_close_session系统过程即可杀死会话。
SQL> sp_close_session(140275042432184);
8、会话一再次查询v$lock可以看到会话阻塞问题解决:
SQL> select trx_id,ltype,lmode,blocked,table_id,tid from v$lock where table_id=1015;
行号 TRX_ID LTYPE LMODE BLOCKED TABLE_ID TID
---------- -------------------- ------ ----- ----------- ----------- --------------------
1 6756 OBJECT IS 0 1015 6756
2 6758 OBJECT IX 0 1015 6758
3 6758 TID X 0 1015 6757
9、会话三,执行commit提交命令,可以看到6758事务已释放。
SQL> select trx_id,ltype,lmode,blocked,table_id,tid from v$lock where table_id=1015;
行号 TRX_ID LTYPE LMODE BLOCKED TABLE_ID TID
---------- -------------------- ------ ----- ----------- ----------- --------------------
1 6756 OBJECT IS 0 1015 6756
2 0 OBJECT IX 0 1015 6758
场景描述:随着国产数据库替代不断推进,运维工程师会遇到达梦数据库安装工作,那么掌握Linux环境下达梦数据库部署也是一项必备技能。
实验步骤:
1、安装前准备。在安装 DM 数据库之前需要检查或修改操作系统的配置,以保证 DM 数据库能够正确安装和运行。
1、查看操作系统版本
# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
2、查看CPU架构
# lscpu
Architecture: x86_64
3、在官方网下载好安装包
达梦下载地址:https://www.dameng.com/list_110.html
dm8_20240116_x86_rh7_64.zip
信创环境安装部署也可以参考此篇文章,但需注意 CPU 和操作系统对应的 DM 数据库版本。
4、新建 dmdba 用户
注意安装前必须创建 dmdba 用户,禁止使用 root 用户安装数据库。
创建用户所在的组,命令如下:
groupadd dinstall -g 2001
创建用户,命令如下:
useradd -G dinstall -m -d /home/dmdba -s /bin/bash -u 2001 dmdba
修改用户密码,命令如下:
passwd dmdba
5、修改文件打开最大数
在 Linux、Solaris、AIX 和 HP-UNIX 等系统中,操作系统默认会对程序使用资源进行限制。如果不取消对应的限制,则数据库的性能将会受到影响。
使用 root 用户打开 /etc/security/limits.conf 文件进行修改,命令如下:
vi /etc/security/limits.conf
在最后添加四条语句,需添加的语句如下:
dmdba soft nice 0
dmdba hard nice 0
dmdba soft as unlimited
dmdba hard as unlimited
dmdba soft fsize unlimited
dmdba hard fsize unlimited
dmdba soft nproc 65536
dmdba hard nproc 65536
dmdba soft nofile 65536
dmdba hard nofile 65536
dmdba soft core unlimited
dmdba hard core unlimited
dmdba soft data unlimited
dmdba hard data unlimited
注意修改配置文件后重启服务器生效。
切换到 dmdba 用户,查看是否生效,命令如下:
su - dmdba
ulimit -a
参数配置已生效。
6、目录规划
可根据实际需求规划安装目录,本示例使用默认配置 DM 数据库安装在 /home/dmdba 文件夹下。
规划创建实例保存目录、归档保存目录、备份保存目录。
##实例保存目录
mkdir -p /dmdata/data
##归档保存目录
mkdir -p /dmdata/arch
##备份保存目录
mkdir -p /dmdata/dmbak
注意使用 root 用户建立文件夹,待 dmdba 用户建立完成后需将文件所有者更改为 dmdba 用户,否则无法安装到该目录下
7、修改目录权限
将新建的路径目录权限的用户修改为 dmdba,用户组修改为 dinstall。命令如下:
chown -R dmdba:dinstall /dmdata/data
chown -R dmdba:dinstall /dmdata/arch
chown -R dmdba:dinstall /dmdata/dmbak
给路径下的文件设置 755 权限。命令如下:
chmod -R 755 /dmdata/data
chmod -R 755 /dmdata/arch
chmod -R 755 /dmdata/dmbak
2、数据库安装,DM 数据库在 Linux 环境下支持命令行安装和图形化安装,本章节将对命令行安装进行详细介绍。
1、挂载镜像
切换到 root 用户,将 DM 数据库的 iso 安装包保存在任意位置,例如 /opt 目录下,执行如下命令挂载镜像:
cd /opt
unzip dm8_20240116_x86_rh7_64.zip
mount -o loop dm8_20240116_x86_rh7_64.iso /mnt
2、切换至 dmdba 用户下,在 /mnt 目录下使用命令行安装数据库程序,依次执行以下命令安装 DM 数据库。
su - dmdba
cd /mnt
执行如下命令进行安装。
./DMInstall.bin -i
安装语言选择“1-简体中文”
是否输入Key文件路径? (Y/y:是 N/n:否) [Y/y]:n
是否设置时区? (Y/y:是 N/n:否) [Y/y]:y
时区按需求选择一般选择 “21-中国标准时间”:
安装类型选择“1-典型安装”,安装目录按实际情况配置,这里示例使用默认安装位置。
数据库安装大概 1~2 分钟,数据库安装完成后,显示如下信息。
[INFO] 安装达梦数据库完成。
请以root系统用户执行命令:
/home/dmdba/dmdbms/script/root/root_installer.sh
安装结束
3、数据库安装完成后,需要切换至 root 用户执行如上中的命令/home/dmdba/dmdbms/script/root/root_installer.sh,创建 DmAPService,否则会影响数据库备份。
移动 /home/dmdba/dmdbms/bin/dm_svc.conf 到/etc目录
创建DmAPService服务
Created symlink from /etc/systemd/system/multi-user.target.wants/DmAPService.service to /usr/lib/systemd/system/DmAPService.service.
创建服务(DmAPService)完成
启动DmAPService服务
4、配置环境变量
切换到 root 用户进入 dmdba 用户的根目录下,配置对应的环境变量。DM_HOME 变量和动态链接库文件的加载路径在程序安装成功后会自动导入。
编辑 .bash_profile,使其最终效果如下所示:
cd /home/dmdba/
vim .bash_profile
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/home/dmdba/dmdbms/bin"
export DM_HOME="/home/dmdba/dmdbms"
export PATH=$PATH:$DM_HOME/bin:$DM_HOME/tool
切换至 dmdba 用户下,执行以下命令,使环境变量生效。
su - dmdba
source .bash_profile
3、命令行方式初始化实例
1、使用 dmdba 用户配置实例,进入到 DM 数据库安装目录下的 bin 目录中。
su - dmdba
cd /home/dmdba/dmdbms/bin
2、使用 dminit 命令初始化实例,dminit 命令可设置多种参数,可执行如下命令查看可配置参数。
./dminit help
3、以下命令设置页大小为 32 KB,簇大小为 32 KB,大小写敏感,字符集为 utf_8,数据库名为 DMTEST,实例名为 DBSERVER,端口为 5237。
./dminit path=/dmdata/data PAGE_SIZE=32 EXTENT_SIZE=32 CASE_SENSITIVE=y CHARSET=1 DB_NAME=DMTEST INSTANCE_NAME=DBSERVER PORT_NUM=5237
4、注册服务,DM 提供了将 DM 服务脚本注册成操作系统服务的脚本,同时也提供了卸载操作系统服务的脚本。注册和卸载脚本文件所在目录为安装目录的“/script/root”子目录下。
注册服务脚本为 dm_service_installer.sh,用户可以使用注册服务脚本将服务脚本注册成为操作系统服务。注册服务需使用 root 用户进行注册,使用 root 用户进入数据库安装目录的 /script/root 下,如下所示:
cd /home/dmdba/dmdbms/script/root/
注册实例服务,如下所示:
[root@localhost root]# ./dm_service_installer.sh -t dmserver -dm_ini /dmdata/data/DMTEST/dm.ini -p DMTEST
部分参数说明:
-t:注册服务类型,支持一下服务类型:dmap、dmamon、dmserver、dmwatcher、dmmonitor、dmasmsvr、dmasmsvrm、dmcss、dmcssm。
-dm_ini:指定服务所需要的 dm.ini 文件路径。
-p:服务名后缀,指定服务名后缀,生成的操作系统服务名为“服务脚本模板名,称 + 服务名后缀”。
5、命令行启停数据库
服务注册成功后中,启动数据库。
使用 dmdba 用户进入 DM 安装目录下的 bin 目录下,启动数据库,如下所示:
[dmdba@localhost ~]$ cd /home/dmdba/dmdbms/bin
[dmdba@localhost bin]$ ls
[dmdba@localhost bin]$ ./DmServiceDMTEST start
停止数据库
[dmdba@localhost bin]$ ./DmServiceDMTEST stop
重启数据库
[dmdba@localhost bin]$ ./DmServiceDMTEST restart
查看数据库状态,如下所示:
[dmdba@localhost bin]$ ./DmServiceDMTEST status
登录数据库:
[dmdba@localhost bin]$ ./disql SYSDBA/SYSDBA@localhost:5237
服务器[localhost:5237]:处于普通打开状态
登录使用时间 : 6.805(ms)
disql V8
SQL>
6、查看版本信息和授权信息
1、查看版本信息
SQL> select * from v$version;
行号 BANNER
---------- ---------------------------------
1 DM Database Server 64 V8
2 DB Version: 0x7000c
3 03134284132-20240115-215128-20081
2、查看授权信息,正常情况只有一年有效期
select project_name,
authorized_customer,
product_type,
server_ver,
date_gen,
expired_date,
os_type,
cpu_type
from v$license;
行号 PROJECT_NAME AUTHORIZED_CUSTOMER PRODUCT_TYPE SERVER_VER DATE_GEN EXPIRED_DATE OS_TYPE CPU_TYPE
---------- ------------ ------------------- ------------ ---------- ---------- ------------ ------- --------
1 DEVELOP USER DM8 X.X.x.x 1900-01-01 2025-01-15 Others Others
7、更新授权文件
1、授权文件所在目录为:$DM_HOME/bin,也即dmserver运行进程所在的目录,如下所示,授权文件目录为:/home/dmdba/dmdbms/bin
[dmdba@db ~]$ ps -ef |grep dmserver
/home/dmdba/dmdbms/bin/dmserver path=/dmdata/data/DMTEST/dm.ini -noconsole
2、将收到的授权文件(例如:dmB701088516.key)重命名为dm.key,放置到授权文件所在目录。
设置文件属主权限为:chown dmdba:dmdba dm.key
3、无需重启数据库,用SYSDBA用户连接数据库实例(disql SYSDBA/密码@IP:端口)
执行函数:sp_load_lic_info(),需要连接每个数据库实例执行:
$ disql SYSDBA/SYSDBA@localhost:5237
SQL>sp_load_lic_info();
4、更新完成之后,查询以下表可以看到授权信息,过期时间(expired_date)为空即表示永不过期。
select * from v$license;
推荐阅读一:开启SQL日志记录
执行SP_SET_PARA_VALUE(1,'SVR_LOG',1);开启SVR_LOG。开启svr_log后,默认会在dm.ini的存放目录下生成一个sqllog.ini文件。同时在软件安装目录下的log目录中生成 dmsql 开头的 log 日志文件。详见:性能诊断 | 达梦技术文档
执行SP_SET_PARA_VALUE(1,'SVR_LOG',0);关闭SVR_LOG。
根据事务编号,搜索日志文件中的SQL语句信息
[dmdba@ log]$ cat dmsql_DBSERVER_20240413_16*|grep "trxid:6760"
2024-04-13 16:18:53.733 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:NULL appname:disql) TRX: START
2024-04-13 16:18:53.734 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:NULL appname:disql) trx[6760] alloc pseg page[0, 1023], page_lsn[41182], n_pages[1]
2024-04-13 16:18:53.734 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:0x8c20e88 appname:disql ip:::1) [UPD] update lock_ywzd set name='ywzd4' where id=10; EXECTIME: 0(ms) ROWCOUNT: 1(rows) EXEC_ID: 64100.
2024-04-13 16:19:01.404 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:0x8c20e88 appname:disql ip:::1) [ORA]: update lock_ywzd set name='ywzd4' where id=10;
2024-04-13 16:19:01.404 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:0x8c20e88 appname:disql ip:::1) [UPD] update lock_ywzd set name='ywzd4' where id=10; EXECTIME: 0(ms) ROWCOUNT: 1(rows) EXEC_ID: 64101.
2024-04-13 16:29:41.514 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:0x8c20e88 appname:disql ip:::1) [ORA]: select 8
2024-04-13 16:29:41.514 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:0x8c20e88 appname:disql ip:::1) [ERR(-2007)]: select 8
2024-04-13 16:29:41.514 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:0x8c20e88 appname:disql ip:::1) [UNKNOWN] EXECTIME: 0(ms) EXEC_ID: -1.
2024-04-13 16:29:49.140 (EP[0] sess:0x8bfce88 thrd:25723 user:YWZD trxid:6760 stmt:0x8c20e88 appname:disql ip:::1) [ORA]: select * from v$trxwait;
数据库的DDL锁超时时间是由参数DDL_WAIT_TIME指定,默认十秒,可根据实际需要修改。
SQL> select para_value from v$dm_ini where para_name='DDL_WAIT_TIME';
行号 PARA_VALUE
---------- ----------
1 10
--查询实例中已执行未提交的 SQL
SELECT t1.sess_id,t1.sql_text, t1.state, t1.trx_id
FROM v$sessions t1, v$trx t2
WHERE t1.trx_id = t2.id AND t1.state = 'IDLE' AND t2.status = 'ACTIVE';
行号 SESS_ID SQL_TEXT STATE TRX_ID
---------- -------------------- ---------------------------------------------- ----- --------------------
1 140274974138584 update lock_ywzd set name='ywzd5' where id=10; IDLE 6758
推荐阅读二:阻塞和死锁的区别
阻塞和死锁是会与并发事务一起发生的两个事件,它们都与锁相关。当一个事务正在占 用某个资源的锁,此时另一个事务正在请求这个资源上与第一个锁相冲突的锁类型时,就会 发生阻塞。被阻塞的事务将一直挂起,直到持有锁的事务放弃锁定的资源为止。死锁与阻塞 的不同之处在于死锁包括两个或者多个已阻塞事务,它们之间形成了等待环,每个都等待其他事务释放锁。
阻塞和死锁发生的情形?
在DM数据库中,INSERT、UPDATE、DELETE 是最常见的会产生阻塞和死锁的语句:
(1) INSERT 发生阻塞的唯一情况是,当多个事务同时试图向有主键或 UNIQUE 约束的表中 插入相同的数据时,其中的一个事务将被阻塞,直到另外一个事务提交或回滚。一个事务提交时,另一个事务将收到唯一性冲突的错误;一个事务回滚时,被阻塞的事务可以继续执行。
(2) 当 UPDATE 和 DELETE 修改的记录,已经被另外的事务修改过,还未commit时,将会发生阻塞,直到另一个事务提交或回滚。
以上就是本期关于“数据库锁等待排查方法、命令行安装数据库及授权文件更新”的运维技能。希望能给大家带来帮助。
欢迎关注“数据库运维之道”公众号,一起学习数据库技术!