欧亿体育
工作动态
我的位置: 首页 > 工作动态
数据库锁表和解锁
发布时间:2024-01-08 11:00
  |  
阅读量:
  |  
作者:
欧亿体育

问题描述

在开发或生产环境中,我们经常会遇到数据库锁表的情况,一旦发生锁表,对业务将会产生很大的影响,本篇主要介绍如何判断数据库锁表和锁表后的处理。

mysql锁表处理

一、导致锁表的原因

1、锁表发生在insert update 、delete 中;

2、锁表的原理是 数据库使用独占式封锁机制,当执行上面的语句时,对表进行锁住,直到发生commite 或者 回滚 或者退出数据库用户;

3、锁表的原因 :

1)、A程序执行了对 tableA 的 insert ,并还未 commite时,B程序也对tableA 进行insert 则此时会发生资源正忙的异常 就是锁表;

2)、锁表常发生于并发而不是并行(并行时,一个线程操作数据库时,另一个线程是不能操作数据库的,cpu 和i/o 分配原则)

4、减少锁表的概率:
减少insert 、update 、delete 语句执行 到 commite 之间的时间。
具体点批量执行改为单个执行、优化sql自身的非执行速度
如果异常对事物进行回滚。

二、mysql锁表的解决

#查看进程id,然后用kill id杀掉进程

1)processlist展示了对应的数据库有哪些线程在运行,如果有些语句执行速度慢,可以在里面看到

show processlist;

#根据不同的账号权限可以看到所有用户的,当前用户的对应的连接。只有100条

show full processlist:
展示所有的连接数据
SELECT * FROM information_schema.PROCESSLIST;
#查询正在执行的进程
SELECT * FROM information_schema.PROCESSLIST where length(info) >0 ;
字段说明

ID 连接标识符,这个ID和show processlist 中ID是一样的,也和Performance Schema 中threads表的PROCESSLIST_ID 栏位是一样的,也和CONNECTION_ID()函数返回的是一样的
USER 连接的用户,其中system user代表系统用户,非用户连接,unauthenticated user代表是用户连接,但是未完成认证,event_scheduler 用户代表的是监控定时任务的用户
HOST 连接的主机名,如果用户是system user,则HOST为空
DB 连接的数据库,如果未选择数据库,则为NULL
COMMAND 线程执行的命令类型
TIME 线程在当前状态持续的时间,以秒为单位
STATE 线程当前的状态,如果该状态持续很久,说明有问题,如果是SHOW PROCESSLIST 命令,则状态为NULL
INFO 线程执行的具体命令,如果执行的是call procedure,这里可能显示的是内容的语句,如select

#查询是否锁表
show OPEN  TABLES where In_use > 0;
字段说明

Database 含有该表的数据库。
Table 表名称。
In_use 表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用。
Name_locked 表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作

#查看被锁住的
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

innodb_locks提供有关InnoDB 事务已请求但尚未获取的每个锁的信息,以及事务持有的阻止另一个事务的每个锁。

字段说明

lock_id:锁 ID。
lock_trx_id:拥有锁的事务 ID。可以和 INNODB_TRX 表 JOIN 得到事务的详细信息。
lock_mode:锁的模式。有如下锁类型:行级锁包括:S、X、IS、IX,分别代表:共享锁、排它锁、意向共享锁、意向排它锁。表级锁包括:S_GAP、X_GAP、IS_GAP、IX_GAP 和 AUTO_INC,分别代表共享间隙锁、排它间隙锁、意向共享间隙锁、意向排它间隙锁和自动递增锁。
lock_type:锁的类型。RECORD 代表行级锁,TABLE 代表表级锁。
lock_table:被锁定的或者包含锁定记录的表的名称。
lock_index:当 LOCK_TYPE=’RECORD’ 时,表示索引的名称;否则为 NULL。
lock_space:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的表空间 ID;否则为 NULL。
lock_page:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的页号;否则为 NULL。
lock_rec:当 LOCK_TYPE=’RECORD’ 时,表示一堆页面中锁定行的数量,亦即被锁定的记录号;否则为 NULL。
lock_data:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的主键;否则为NULL。

#等待锁定
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

innodb_lock_waits包含每个被阻止InnoDB 事务的一个或多个行,指示它已请求的锁以及阻止该请求的任何锁。

字段说明

requesting_trx_id:请求事务的 ID。
requested_lock_id:事务所等待的锁定的 ID。可以和 INNODB_LOCKS 表 JOIN。
blocking_trx_id:阻塞事务的 ID。
blocking_lock_id:某一事务的锁的 ID,该事务阻塞了另一事务的运行。可以和 INNODB_LOCKS 表 JOIN

#杀掉锁表进程

kill 5601

达梦数据库锁表处理

1. 定义

阻塞:多个线程之间的相互影响,等待临界资源;
死锁:多个线程之间互相等待,形成等待环;

对于死锁,达梦数据库会自动检测,并选择牺牲掉其中的一个事务,保证其它事务正常运行。死锁一般是由于应用程序bug导致的,当有修改多表的事务时,应尽量保证每个事务以相同的顺序锁定表。可以通过V$DEADLOCK_HISTORY查询死锁历史,其中记录了产生死锁后被牺牲掉的事务的事务ID、会话ID、执行的SQL语句以及死锁发生时间(没有记录造成死锁的其它事务)。对于阻塞,达梦数据库不会自动处理,被阻塞的事务会一直挂起,直到获取到所需的资源。阻塞一般是由应用程序bug造成的,在应用编写中,应尽量形成短事务,快速提交。

2. 什么情况下会形成阻塞?

在达梦数据库中,默认采用读提交的机制,查询永远不会被阻塞。查询一般情况下也不会阻塞增删改操作,SELECT FOR UPDATE的情况除外。
INSERT语句被阻塞的情况:多个事务同时向有主键或唯一约束的表中插入相同的数据;
删、改语句被阻塞的情况:所需要操作的数据被其它事务修改过,且一直没有提交或回滚;

3.阻塞的排查

当阻塞发生时,我们可以通过 v$lock 视图查到当前数据库中锁的状态

select * from v$lock;

结果中我们可以看到,事务 1459 被阻塞了,阻塞他的事务为 1450,同样我们也可以通过 V$TRXWAIT 视图排查阻塞,

select * from v$trxwait;

得出同样的结果,等待时间是1814645 毫秒。
接下来,通过 V$SESSIONS 视图查找两个事务对应的会话,可以知道是哪些 SQL 语句产生的阻塞。

select sess_id,sql_text,state,trx_id from v$sessions where trx_id in('1459','1450');

4.阻塞的解决方法

根据需求,可以有两种解决方案。

4.1 提交或回滚产生阻塞的事务。

根据上文,我们可知产生阻塞的事务会话 ID 为 140702994469648。此时,我们只需要在该会话下提交或回滚事务,锁自然会被释放,阻塞解决。

4.2 关闭产生阻塞的会话

同样,我们也可以使用系统过程 SP_CLOSE_SESSION(SESS_ID)来关闭对应的会话

此时,锁被释放,delete 操作也可以顺利进行下去。


也可以用一条语句查看出被阻塞的操作和事务会话 ID(SESS_ID)

select a.*, b.NAME, c.SESS_ID from v$lock a
left join sysobjects b on b.ID=a.TABLE_ID
left join v$sessions c on a.TRX_ID=c.TRX_ID;

然后直接执行SP_CLOSE_SESSION(SESS_ID) 来关闭对应的会话

SQL Server数据库锁表处理

第一步:查询死锁语句

1: 条件是 blocked <> 0

select dbid,* from sys.sysprocesses
where 1=1
and spid >50
and blocked <> 0
--and spid= 62

查询结果如下图所示:

以上查询得出:

受影响的数据库ID 是 5;
受到阻塞的会话有1301、1306、869、109;
阻塞会话是961;

第二步:查询发生阻塞或死锁的当前数据库

1:通过第一步查询,知道数据库ID 为 DBID=5。

2:sp_helpdb 或 Select name,dbid from master.sys.sysdatabases 查询数据库名称

第三步:查询发生阻塞或死锁的SQL语句

输出死锁的执行的语句:

dbcc inputbuffer(961)

语句如下:

select o.tp_Login,o.tp_Title, CONVERT(varchar(10),a.TimeCreated,120)  as accessingdate,count(*) as docnum, 'sps' as type  from UserData u,AllDocs a, userinfo o ,webs w where a.ListId = u.tp_ListId and a.Id = u.tp_docID  and w.Id = a.WebId and w.SiteId = a.SiteId and w.SiteId = o.tp_SiteID and

第四步:杀死锁

kill  961

之后再从第一步进行查询,看是否还存在死锁;

从第二步、第三步,查询出了出问题的锁死数据库及SQL语句,便于分析彻底解决死锁问题。

ORACEL数据库锁表处理

oracle表在什么情况下会被锁住

DML锁又可以分为,行锁、表锁、死锁

行锁: 当事务执行数据库插入、更新、删除操作时,该事务自动获得操作表中操作行的排它锁。

表级锁: 当事务获得行锁后,此事务也将自动获得该行的表锁(共享锁),以防止其它事务进行DDL语句影响记录行的更新。事务也可以在进行过程中获得共享锁或排它锁,只有当事务显示使用LOCK TABLE语句显示的定义一个排它锁时,事务才会获得表上的排它锁,也可使用LOCK TABLE显示的定义一个表级的共享锁(LOCK TABLE具体用法请参考相关文档)。

死锁: 当两个事务需要一组有冲突的锁,而不能将事务继续下去的话,就出现死锁。
如事务1在表A行记录#3中有一排它锁,并等待事务2在表A中记录#4中排它锁的释放,而事务2在表A记录行#4中有一排它锁,并等待事务1在表A中记录#3中排它锁的释放,事务1与事务2彼此等待,因此就造成了死锁。死锁一般是因拙劣的事务设计而产生。
死锁只能使用SQL下:alter system kill session “sid,serial#”;或者使用相关操作系统kill进程的命令,如UNIX下kill -9 sid,或者使用其它工具杀掉死锁进程。

DDL锁又可以分为:排它DDL锁、共享DDL锁、分析锁

排它DDL锁:创建、修改、删除一个数据库对象的DDL语句获得操作对象的 排它锁。如使用alter table语句时,为了维护数据的完成性、一致性、合法性,该事务获得一排它DDL锁。

共享DDL锁:需在数据库对象之间建立相互依赖关系的DDL语句通常需共享获得DDL锁。
如创建一个包,该包中的过程与函数引用了不同的数据库表,当编译此包时,该事务就获得了引用表的共享DDL锁。

分析锁:ORACLE使用共享池存储分析与优化过的SQL语句及PL/SQL程序,使运行相同语句的应用速度更快。一个在共享池中缓存的对象获得它所引用数据库对象的分析锁。分析锁是一种独特的DDL锁类型,ORACLE使用它追踪共享池对象及它所引用数据库对象之间的依赖关系。当一个事务修改或删除了共享池持有分析锁的数据库对象时,ORACLE使共享池中的对象作废,下次在引用这条SQL/PLSQL语句时,ORACLE重新分析编译此语句。

Oracle锁表查询和解锁

第一步:通过管理员权限用户查询被锁表信息

如果怀疑表被锁了,或者事务未被正常关闭,在Oracle数据库中我们可以通过以下语句进行查询获取相关信息:

 select t2.username,    t2.sid,    t2.serial#,    t3.object_name,  t2.OSUSER,  t2.MACHINE,
        t2.PROGRAM,  t2.LOGON_TIME,  t2.COMMAND,   t2.LOCKWAIT,
        t2.SADDR,   t2.PADDR, t2.TADDR,   t2.SQL_ADDRESS,   t1.LOCKED_MODE
   from v$locked_object t1, v$session t2, dba_objects t3
  where t1.session_id = t2.sid
    and t1.object_id = t3.object_id
  order by t2.logon_time;

大家发现,上面这条SQL语句用到了Oracle的两个视图和一个表,分别是v l o c k e d o b j e c t 、 v locked_object、v lockedobjectvsession、dba_objects:
v l o c k e d o b j e c t 视图中记录了所有 s e s s i o n 中的所有被锁定的对象信息。 v locked_object 视图中记录了所有session中的所有被锁定的对象信息。 v lockedobject视图中记录了所有session中的所有被锁定的对象信息。vsession 视图记录了所有session的相关信息。
dba_objects 为oracle用户对象及系统对象的集合,通过关联这张表能够获取被锁定对象的详细信息。

eg:现在我通过scott用户执行DML语句(eg:select * from emp for update;或者update scott.emp set sal = ‘2000’ where empno=‘7788’; )之后一直不进行提交,然后通过system用户执行上面的查询oracle中被锁表的sql语句,就会找到如下记录:

说明:

username:oracle用户名
sid:进程号
serial#:序列号
object_name:表名
osuser:操作系统用户名
machine:机器名
program:操作工具
logon_time:登陆时间
lockwait:表示当前这张表是否正在等待其他用户解锁这张表
locked_mode:锁表模式(下面详细说明)

注意:这时候如果通过system用户执行select * from scott.emp for update;语句就无法成功执行。

第二步:通过拥有管理员权限的用户解除数据库中被锁住的表(SID,SERIAL)

通过第一步查出来的信息找到被锁的表之后执行如下语句解锁该表:

alter system kill session ‘sid,seial#’;
注意:sid和seial#就是第一步中查询出来的进程号和序列号。

eg:解除第一步中表的锁

alter system kill session ‘10,15’;
现在通过system再次执行DML语句(eg:select * from scott.emp for update;或update scott.emp set sal = ‘2000’ where empno=‘7788’;)就可以了。

锁的模式
v$locked_object中的LOCKED_MODE字段表示锁的模式,oracle中锁的模式有如下几种:
0:none
1:null 空
2:Row-S 行共享(RS):共享表锁,sub share
3:Row-X 行独占(RX):用于行的修改,sub exclusive
4:Share 共享锁(S):阻止其他DML操作,share
5:S/Row-X 共享行独占(SRX):阻止其他事务操作,share/sub exclusive
6:exclusive 独占(X):独立访问使用,exclusive

数字越大锁级别越高, 影响的操作越多。

1级锁有: Select,有时会在v$locked_object出现。
2级锁有: Select for update,Lock For Update,Lock Row Share
select for update当对话使用for update子串打开一个游标时,所有返回集中的数据行都将处于行级(Row-X)独占式锁定,其他对象只能查询这些数据行,不能进行update、delete或select for update操作。
3级锁有: Insert, Update, Delete, Lock Row Exclusive
没有commit之前插入同样的一条记录会没有反应, 因为后一个3的锁会一直等待上一个3的锁, 我们必须释放掉上一个才能继续工作。
4级锁有: Create Index, Lock Share
locked_mode为2,3,4不影响DML(insert,delete,update,select)操作, 但DDL(alter,drop等)操作会提示ora-00054错误。
00054, 00000, “resource busy and acquire with NOWAIT specified”
// *Cause: Resource interested is busy.
// *Action: Retry if necessary.
5级锁有: Lock Share Row Exclusive
具体来讲有主外键约束时update / delete … ; 可能会产生4,5的锁。
6级锁有: Alter table, Drop table, Drop Index, Truncate table, Lock Exclusive

还有一条比较实用的sql:

–查某session 正在执行的sql语句,从而可以快速定位到哪些操作或者代码导致事务一直进行没有结束等.

 SELECT /*+ ORDERED */ 
  sql_text
   FROM v$sqltext a
  WHERE (a.hash_value, a.address) IN
        (SELECT DECODE(sql_hash_value, 0, prev_hash_value, sql_hash_value),
                DECODE(sql_hash_value, 0, prev_sql_addr, sql_address)
           FROM v$session b
          WHERE b.sid = '67')  /* 此处67 为SID*/
  ORDER BY piece ASC;

参考链接:
https://blog.csdn.net/snowfoxmonitor/article/details/7182948
https://www.cnblogs.com/pugang/p/16531352.html
https://blog.csdn.net/qq_26987043/article/details/127640853
http://t.zoukankan.com/smellpawn-p-10799174.html