‘壹’ 大表,update,where条件没有索引,会加多少锁
正常情况应碰弯该只有银橡一个锁 exclusive table lock,这是笑搏闷由lock promotion HWM 决定的, ASE escalates the locks to table lock whenever over 200 rows (DOL) or 200pages (APL) are involved for updates。估计你遇到的是out of locks通常情型之一:one process was holding the lock(s) and it prevented your update process from escalating the row/page locks to table lock. "select" holds exclusive locks under isolation level 2 (repeatable read).只有运用one of the best ASE practices: small batch size. 分批提交update,例如1000条记录一提交,就可防止out of the locks。
‘贰’ mysql 发生死锁问题请求帮助
这是我见的一个文档,虽然我看不懂,你看看有没有帮助
MySQL死锁问题的相关知识是本文我们主要要介绍的内容,接下来我们就来一一介绍这部分内容,希望能够对您有所帮助。
1、MySQL常用存储引擎的锁机制
MyISAM和MEMORY采用表级锁(table-level locking)
BDB采用页面锁(page-level locking)或表级锁,默认为页面锁
InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
2、各种锁特点
表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高
页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
3、各种锁的适用场景
表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如Web应用
行级锁则更适合于有大量按索引条件并发更新数据,同时又有并发查询的应用,如一些在线事务处理系统
4、死锁
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
表级锁不会产生死锁。所以解决死锁主要还是针对于最常用的InnoDB。
5、死锁举例分析
在MySQL中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。
在UPDATE、DELETE操作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking。
例如,一个表db。tab_test,结构如下:
id:主键;
state:状态;
time:时间;
索引:idx_1(state,time)
出现死锁日志如下:
?***(1) TRANSACTION:
?TRANSACTION 0 677833455, ACTIVE 0 sec, process no 11393, OSthread id 278546 starting index read
?mysql tables in use 1, locked 1
?LOCK WAIT 3 lock struct(s), heap size 320
?MySQL thread id 83, query id 162348740 dcnet03 dcnet Searching rows for update
?update tab_test set state=1064,time=now() where state=1061 and time < date_sub(now(), INTERVAL 30 minute) (任务1的sql语句)
?***(1) WAITING FOR THIS LOCK TO BE GRANTED: (任务1等待的索引记录)
?RECORD LOCKS space id 0 page no 849384 n bits 208 index `PRIMARY` of table `db/tab_test` trx id 0 677833455 _mode X locks rec but not gap waiting
?Record lock, heap no 92 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
?0: len 8; hex 800000000097629c; asc b ;; 1: len 6; hex 00002866eaee; asc (f ;; 2: len 7; hex 00000d40040110; asc @ ;; 3: len 8; hex 80000000000050b2; asc P ;; 4: len 8; hex 800000000000502a; asc P*;; 5: len 8; hex 8000000000005426; asc T&;; 6: len 8; hex 800012412c66d29c; asc A,f ;; 7: len 23; hex 8616e642e706870; asc xxx.com/;; 8: len 8; hex 800000000000042b; asc +;; 9: len 4; hex 474bfa2b; asc GK +;; 10: len 8; hex 8000000000004e24; asc N$;;
?*** (2) TRANSACTION:
?TRANSACTION 0 677833454, ACTIVE 0 sec, process no 11397, OS thread id 344086 updating or deleting, thread declared inside InnoDB 499
?mysql tables in use 1, locked 1
?3 lock struct(s), heap size 320, undo log entries 1
?MySQL thread id 84, query id 162348739 dcnet03 dcnet Updating update tab_test set state=1067,time=now () where id in (9921180) (任务2的sql语句)
?*** (2) HOLDS THE LOCK(S): (任务2已获得的锁)
?RECORD LOCKS space id 0 page no 849384 n bits 208 index `PRIMARY` of table `db/tab_test` trx id 0 677833454 lock_mode X locks rec but not gap
?Record lock, heap no 92 PHYSICAL RECORD: n_fields 11; compact format; info bits 0
?0: len 8; hex 800000000097629c; asc b ;; 1: len 6; hex 00002866eaee; asc (f ;; 2: len 7; hex 00000d40040110; asc @ ;; 3: len 8; hex 80000000000050b2; asc P ;; 4: len 8; hex 800000000000502a; asc P*;; 5: len 8; hex 8000000000005426; asc T&;; 6: len 8; hex 800012412c66d29c; asc A,f ;; 7: len 23; hex 8616e642e706870; asc uploadfire.com/hand.php;; 8: len 8; hex 800000000000042b; asc +;; 9: len 4; hex 474bfa2b; asc GK +;; 10: len 8; hex 8000000000004e24; asc N$;;
?*** (2) WAITING FOR THIS LOCK TO BE GRANTED: (任务2等待的锁)
?RECORD LOCKS space id 0 page no 843102 n bits 600 index `idx_1` of table `db/tab_test` trx id 0 677833454 lock_mode X locks rec but not gap waiting
?Record lock, heap no 395 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
?0: len 8; hex 8000000000000425; asc %;; 1: len 8; hex 800012412c66d29c; asc A,f ;; 2: len 8; hex 800000000097629c; asc b ;;
?*** WE ROLL BACK TRANSACTION (1)
?(回滚了任务1,以解除死锁)
原因分析:
当“update tab_test set state=1064,time=now() where state=1061 and time < date_sub(now(), INTERVAL 30 minute)”执行时,MySQL会使用idx_1索引,因此首先锁定相关的索引记录,因为idx_1是非主键索引,为执行该语句,MySQL还会锁定主键索引。
假设“update tab_test set state=1067,time=now () where id in (9921180)”几乎同时执行时,本语句首先锁定主键索引,由于需要更新state的值,所以还需要锁定idx_1的某些索引记录。
这样第一条语句锁定了idx_1的记录,等待主键索引,而第二条语句则锁定了主键索引记录,而等待idx_1的记录,这样死锁就产生了。
6、解决办法
拆分第一条sql,先查出符合条件的主键值,再按照主键更新记录:
?select id from tab_test where state=1061 and time < date_sub(now(), INTERVAL 30 minute);
?update tab_test state=1064,time=now() where id in(......);
‘叁’ 数据库事务使用方法
一个大型、稳健、成熟的分布式系统的背后,往往会涉及众多的支撑系统,我们将这些支撑系统称为分布式系统的基础设施。除了前面所介绍的分布式协作及配置管理系统ZooKeeper,我们进行系统架构设计所依赖的基础设施,还包括分布式缓存系统、持久化存储、分布式消息系统、搜索引擎,以及CDN系统、负载均衡系统、运维自动化系统等,还有后面章节所要介绍的实时计算系统、离线计算系统、分布式文件系统、日志收集系统、监控系统、数据仓库等。
分布式缓存主要用于在高并发环境下,减轻数据库的压力,提高系统的响应速度和并发吞吐。当大量的读、写请求涌向数据库时,磁盘的处理速度与内存显然不在一个量级,因此,在数据库之前加一层缓存,能够显着提高系统的响应速度,并降低数据库的压力。作为传统的关系型数据库,MySQL提供完整的ACID操作,支持丰富的数据类型、强大的关联查询、where语句等,能够非常客易地建立查询索引,执行复杂的内连接、外连接、求和、排序、分组等操作,并且支持存储过程、函数等功能,产品成熟度高,功能强大。但是,对于需要应对高并发访问并且存储海量数据的场景来说,出于对性能的考虑,不得不放弃很多传统关系型数据库原本强大的功能,牺牲了系统的易用性,并且使得系统的设计和管理变得更为复杂。这也使得在过去几年中,流行着另一种新的存储解决方案——NoSQL,它与传统的关系型数据库最大的差别在于,它不使用SQL作为查询语言来查找数据,而采用key-value形式进行查找,提供了更高的查询效率及吞吐,并且能够更加方便地进行扩展,存储海量数据,在数千个节点上进行分区,自动进行数据的复制和备份。在分布式系统中,消息作为应用间通信的一种方式,得到了十分广泛的应用。消息可以被保存在队列中,直到被接收者取出,由于消息发送者不需要同步等待消息接收者的响应,消息的异步接收降低了系统集成的耦合度,提升了分布式系统协作的效率,使得系统能够更快地响应用户,提供更高的吞吐。
当系统处于峰值压力时,分布式消息队列还能够作为缓冲,削峰填谷,缓解集群的压力,避免整个系统被压垮。垂直化的搜索引擎在分布式系统中是一个非常重要的角色,它既能够满足用户对于全文检索、模糊匹配的需求,解决数据库like查询效率低下的问题,又能够解决分布式环境下,由于采用分库分表,或者使用NoSQL数据库,导致无法进行多表关联或者进行复杂查询的问题。
‘肆’ 数据库锁表是什么意思
oracle数据库 锁表和死锁的区别
死锁指的是a,b两个事务对同一对象进行dml或ddl操作(即修改表结构或者增删改数据),出现了相互等待被锁定的对象的情况,即类似于红绿灯十字路口红灯方向堵住路口,绿灯方向却红灯车辆挡在路口不能过去,这样无论红绿灯如何变化都无法通行。一般像oracle这样的dbms是有死锁检测的,然后把锁定对象抛出来按照预定规则处理或者让程序处理。 锁等待指的是a事务锁定了操作对象,而b事务也要对其进行dml或ddl操作(即修改表结构或者增删改数据)时,需要等待a事务完成。这个和死锁不同,只要a事务完成后,b事务就可以正常进行了。类似于正常的红绿灯十字路口通行状态:红灯方向就是等待锁释放的b事务,绿灯方向就是锁定路口的a事务。待红绿灯互换,则a事务执行完毕,b事务也就可以正常执行啦。
MySQL锁表是什么意思?有什么用?什么情况下用?好处?缺点?
白话解说如下:
简单说,就是lock table,不让别人动
锁分共享锁和排它锁。
共享锁时,别人能读,不能改变量表数据
排它锁时,别人既不能读,也不能改表数据
根据以上特点,应该就知道何时使用锁了。不想让别人变更数据,对自己产生影响,就加锁。一定要在不用之后,进行锁释放,不然,应历搭用系统会一直因为读取数据而报错。
好处就是,保证数据的原子性,完整性,一致性。 只有加锁者释放了锁,别人才能改变数据。
缺点就是,增加了系统开销,有可能产生锁等待,造成数据库运行异常。这都是不正常的使用锁带来的问题。
什么情况下造成数据库锁表? 如何解决?
./question/180766896
两个SQL的锁表问题
不同的数据库,多版本的实现机制不同,上述语句执行情况也就不一样,下面以oracle为例说明:
1.insert/delete语句可以并发执行,陆慧不会锁等待
2.并发insert不会锁等待
3.并发update,如果不是操作同一条记录,不会锁等待
=================================================
对真实存在的数据进行并发操作才有可能发生写冲突,所以楼主供要把握住这点就可以早烂答判断是否会冲突了。
建议楼主构造简单数据,开两个客户端,在不同的隔离级下去模拟并发操作,理论和实践相结合,你会理解的更透彻。
oracle 数据库 为什么锁表
简单地说,锁是为了保证数据的一致性,锁不止存在于oracle,其他数据库一样有,只不过机制上可能大相径庭。 至于什么样的操作会锁表,其实锁的种类很多,你所说的锁表大概说的是行级锁——也就是事务锁吧
如何将数据库中被锁表解锁
在操作数据库的时候,有时候会由于操作不当引起数据库表被锁定,这么我们经常不知所措,不知怎么给这些表解锁,在pl/sql Developer工具的的菜单“tools”里面的“sessions”可以查询现在存在的会话,但是我们很难找到那个会话被锁定了,想找到所以被锁的会话就更难了,下面这叫查询语句可以查询出所以被锁的会话。如下:
SELECT sn.username, m.SID,sn.SERIAL#, m.TYPE,
DECODE (m.lmode,
0, 'None',
1, 'Null',
2, 'Row Share',
3, 'Row Excl.',
4, 'Share',
5, 'S/Row Excl.',
6, 'Exclusive',
lmode, LTRIM (TO_CHAR (lmode, '990'))
) lmode,
DECODE (m.request,
0, 'None',
1, 'Null',
2, 'Row Share',
3, 'Row Excl.',
4, 'Share',
5, 'S/Row Excl.',
6, 'Exclusive',
request, LTRIM (TO_CHAR (m.request, '990'))
) request,
m.id1, m.id2
FROM v$session sn, v$lock m
WHERE (sn.SID = m.SID AND m.request != 0) --存在锁请求,即被阻塞
OR ( sn.SID = m.SID --不存在锁请求,但是锁定的对象被其他会话请求锁定
AND m.request = 0
AND lmode != 4
AND (id1, id2) IN (
SELECT s.id1, s.id2
FROM v$lock s
WHERE request != 0 AND s.id1 = m.id1
AND s.id2 = m.id2)
)
ORDER BY id1, id2, m.request;
通过以上查询知道了sid和 SERIAL#就可以开杀了
alter system kill session 'sid,SERIAL#';
怎么知道数据库表已经锁表了
通过查询结果中的object_id,可以查询到具体被锁的对象再给你看看我查到的一些关于锁的资料:锁有以下几种模式: 0:none 1:null 空 2:Row-S 行共享(RS):共享表锁 3:Row-X 行专用(RX):用于行的修改 4:Share 共享锁(S):阻止其他DML操作 5:S/Row-X 共享行专用(SRX):阻止其他事务操作 6:exclusive 专用(X):独立访问使用数字越大锁级别越高, 影响的操作越多。一般的查询语句如select ... from ... ;是小于2的锁, 有时会在v$locked_object出现。 select ... from ... for update; 是2的锁。当对话使用for update子串打开一个游标时,所有返回集中的数据行都将处于行级(Row-X)独占式锁定,其他对象只能查询这些数据行,不能进行update、delete或select...for update操作。 insert / update / delete ... ; 是3的锁。没有mit之前插入同样的一条记录会没有反应, 因为后一个3的锁会一直等待上一个3的锁, 我们必须释放掉上一个才能继续工作。创建索引的时候也会产生3,4级别的锁。 locked_mode为2,3,4不影响DML(insert,delete,update,select)操作, 但DDL(alter,drop等)操作会提示ora-00054错误。有主外键约束时 update / delete ... ; 可能会产生4,5的锁。 DDL语句时是6的锁。以DBA角色, 查看当前数据库里锁的情况可以用如下SQL语句: select object_id,session_id,locked_mode from v$locked_object; select t2.username,t2.sid,t2.serial#,t2.logon_time from v$locked_object t1,v$session t2 where t1.session_id=t2.sid order by t2.logon_time; 如果有长期出现的一列,可能是没有释放的锁。我们可以用下面SQL语句杀掉长期没有释放非正常的锁: alter system kill session 'sid,serial#'; 如果出现了锁的问题, 某个DML操作可能等待很久没有反应。当你采用的是直接连接数据库的方式,也不要用OS系统命令 $kill process_num 或者 $kill -9 process_num来终止用户连接,因为一个用户进程可能产生一个以上的锁, 杀OS进程并不能彻底清除锁的问题。
如何实现数据库锁表及解锁
锁表:
LOCK TABLES tablename WRITE;LOCK TABLES tablename READ;解锁
UNLOCK TABLES;
数据库中如何释锁表进程
锁表查询的代码有以下的形式:
select count(*) from v$locked_object;
select * from v$locked_object;
查看哪个表被锁
select b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_object a,dba_objects b where b.object_id = a.object_id;查看是哪个session引起的
select b.username,b.sid,b.serial#,logon_time from v$locked_object a,v$session b where a.session_id = b.sid order by b.logon_time;杀掉对应进程
执行命令:alter system kill session'1025,41';
其中1025为sid,41为serial#.
怎么知道数据库表已经锁表了
先回答你的问题:
select *from v$locked_object;
可以获得被锁的对象的object_id及产生锁的会话sid。
通过查询结果中的object_id,可以查询到具体被锁的对象
再给你看看我查到的一些关于锁的资料:
锁有以下几种模式:
0:none
1:null 空
2:Row-S 行共享(RS):共享表锁
3:Row-X 行专用(RX):用于行的修改
4:Share 共享锁(S):阻止其他DML操作
5:S/Row-X 共享行专用(SRX):阻止其他事务操作
6:exclusive 专用(X):独立访问使用
数字越大锁级别越高, 影响的操作越多。
一般的查询语句如select ... from ... ;是小于2的锁, 有时会在v$locked_object出现。
select ... from ... for update; 是2的锁。
当对话使用for update子串打开一个游标时,
所有返回集中的数据行都将处于行级(Row-X)独占式锁定,
其他对象只能查询这些数据行,不能进行update、delete或select...for update操作。
insert / update / delete ... ; 是3的锁。
没有mit之前插入同样的一条记录会没有反应,
因为后一个3的锁会一直等待上一个3的锁, 我们必须释放掉上一个才能继续工作。
创建索引的时候也会产生3,4级别的锁。
locked_mode为2,3,4不影响DML(insert,delete,update,select)操作,
但DDL(alter,drop等)操作会提示ora-00054错误。
有主外键约束时 update / delete ... ; 可能会产生4,5的锁。
DDL语句时是6的锁。
以DBA角色, 查看当前数据库里锁的情况可以用如下SQL语句:
select object_id,session_id,locked_mode from v$locked_object;
select t2.username,t2.sid,t2.serial#,t2.logon_time
from v$locked_object t1,v$session t2
where t1.session_id=t2.sid order by t2.logon_time;
如果有长期出现的一列,可能是没有释放的锁。
我们可以用下面SQL语句杀掉长期没有释放非正常的锁:
alter system kill session 'sid,serial#';
如果出现了锁的问题, 某个DML操作可能等待很久没有反应。
当你采用的是直接连接数据库的方式,
也不要用OS系统命令 $kill process_num 或者 $kill -9 process_num来终止用户连接,
因为一个用户进程可能产生一个以上的锁, 杀OS进程并不能彻底清除锁的问题。
记得在数据库级别用alter system kill session 'sid,serial#';杀掉不正常的锁。
这里还讲了一些:
......>>
‘伍’ mysql 多列唯一索引在事务中select for update是不是行锁
会的,for
update就等于是加上了一个写锁,会把表锁住无法修改
‘陆’ mysql死锁场景整理
本文死锁场景皆为工作中遇到(或同事遇到)并解决的死锁场景,写这篇文章的目的是整理和分享,欢迎指正和补充,本文死锁悉拿场景包括:
注 :以下场景隔离级别均为默认的Repeatable Read;
前提 :表 t_user 的 uid 字段创建了唯一索引,并拥有可更新字段age。
场景复现 :
相应业务案例和解决方案 :
该场景常见于事务中存在for循环更新某条记录厅桐的情况,死锁日志显示 lock_mode X locks rec but not gap waiting (即行锁而非间隙锁),解扮陆坦决方案:
表结构 :
场景复现 :
首先查询表中目前存在的记录:
执行两个事务的操作:
死锁原因分析 :
解决方案 :
t_user结构改造为:
场景复现操作(几率不高) :
假设存在以下数据 :
死锁分析 :
事务1 :
① 锁住zone_id=1对应的间隙锁: zoneId in (1,2)
② 锁住索引zone_id=1对应的主键索引行锁id = [1,2]
③ 锁住uid=1对应的间隙锁: uid in (1, 2)
④ 锁住uid=1对应的主键索引行锁: id = [1, 3]
事务2 :
① 锁住zone_id=2对应的间隙锁: zoneId in (1,2)
② 锁住索引zone_id=2对应的主键索引行锁id = [3,4]
③ 锁住uid=2对应的间隙锁: uid in (1, 2)
④ 锁住uid=2对应的主键索引行锁: id = [2, 4]
解决方案 :创建联合索引,使执行计划只会用到一个索引。
测试表结构 :
场景复现操作 :
解决办法:尽量避免这种插入又回滚的场景。
避免死锁的原则:
‘柒’ 什么是数据库索引
第二次回答:
问题补充:能不能具体点,新建一个索引就可以了吗
基本上可以这么说,不过你也可以修改索引。
记住:
索引其实关键目的是为了加快检索速度而建立的,所以,怎么用索引是数据库系统本身的事情,作为数据库设计或使用者,设计并创建好索引然后体验加上索引后的查询变快的感觉就行了。所以,索引怎么用就变为了“怎么创建合适的索引”
以下回答是否符合你的要求?你还有什么问题?
第一次回答:
一、索引是什么
索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。索引包含由表或视图中的一列或多列生成的键。这些键存储在一个结构(B 树)中,使 SQL Server 可以快速有效地查找与键值关联的行。
表或视图可以包含以下类型的索引:
* 聚集
o 聚集索引根据数据行的键值在表或视图中排序和存储这些数据行。索引定义中包含聚集索引列。每个表只能有一个聚集索引,因为数据行本身只能按一个顺序排序。
o 只有当表包含聚集索引时,表中的数据行才按排序顺序存储。如果表具有聚集索引,则该表称为聚集表。如果表没有聚集索引,则其数据行存储在一个称为堆的无序结构中。
* 非聚集
o 非聚集索引具有独立于数据行的结构。非聚集索引包含非聚集索引键值,并且每个键值项都有指向包含该键值的数据行的指针。
o 从非聚集索引中的索引行指向数据行的指针称为行定位器。行定位器的结构取决于数据页是存储在堆中还是聚集表中。对于堆,行定位器是指向行的指针。对于聚集表,行定位器是聚集索引键。
o 您可以向非聚集索引的叶级添加非键列以跳过现有的索引键限制(900 字节和 16 键列),并执行完整范围内的索引查询。
聚集索引和非聚集索引都可以是唯一的。这意味着任何两行都不能有相同的索引键值。另外,索引也可以不是唯一的,即多行可以共享同一键值。
每当修改了表数据后,都会自动维护表或视图的索引。
索引和约束
对表列定义了 PRIMARY KEY 约束和 UNIQUE 约束时,会自动创建索引。例如,如果创建了表并将一个特定列标识为主键,则 数据库引擎自动对该列创建 PRIMARY KEY 约束和索引。有关详细信息,请参阅创建索引(数据库引擎)。
二、索引有什么用
与书中的索引一样,数据库中的索引使您可以快速找到表或索引视图中的特定信息。索引包含从表或视图中一个或多个列生成的键,以及映射到指定数据的存储位置的指针。通过创建设计良好的索引以支持查询,可以显着提高数据库查询和应用程序的性能。索引可以减少为返回查询结果集而必须读取的数据量。索引还可以强制表中的行具有唯一性,从而确保表数据的数据完整性。
设计良好的索引可以减少磁盘 I/O 操作,并且消耗的系统资源也较少,从而可以提高查询性能。对于包含 SELECT、UPDATE、DELETE 或 MERGE 语句的各种查询,索引会很有用。例如,在 AdventureWorks 数据库中执行的查询 SELECT Title, HireDate FROM HumanResources.Employee WHERE EmployeeID = 250。执行此查询时,查询优化器评估可用于检索数据的每个方法,然后选择最有效的方法。可能采用的方法包括扫描表和扫描一个或多个索引(如果有)。
扫描表时,查询优化器读取表中的所有行,并提取满足查询条件的行。扫描表会有许多磁盘 I/O 操作,并占用大量资源。但是,如果查询的结果集是占表中较高百分比的行,扫描表会是最为有效的方法。
查询优化器使用索引时,搜索索引键列,查找到查询所需行的存储位置,然后从该位置提取匹配行。通常,搜索索引比搜索表要快很多,因为索引与表不同,一般每行包含的列非常少,且行遵循排序顺序。
查询优化器在执行查询时通常会选择最有效的方法。但如果没有索引,则查询优化器必须扫描表。您的任务是设计并创建最适合您的环境的索引,以便查询优化器可以从多个有效的索引中选择。SQL Server 提供的数据库引擎优化顾问可以帮助分析数据库环境并选择适当的索引。
三、索引怎么用
索引其实关键目的是为了加快检索速度而建立的,所以,怎么用索引是数据库系统本身的事情,作为数据库设计或使用者,设计并创建好索引然后体验加上索引后的查询变快的感觉就行了。所以,索引怎么用就变为了“怎么创建合适的索引”,以下说明这个问题:
索引设计不佳和缺少索引是提高数据库和应用程序性能的主要障碍。设计高效的索引对于获得良好的数据库和应用程序性能极为重要。为数据库及其工作负荷选择正确的索引是一项需要在查询速度与更新所需开销之间取得平衡的复杂任务。如果索引较窄,或者说索引关键字中只有很少的几列,则需要的磁盘空间和维护开销都较少。而另一方面,宽索引可覆盖更多的查询。您可能需要试验若干不同的设计,才能找到最有效的索引。可以添加、修改和删除索引而不影响数据库架构或应用程序设计。因此,应试验多个不同的索引而无需犹豫。
SQL Server 中的查询优化器可在大多数情况下可靠地选择最高效的索引。总体索引设计策略应为查询优化器提供可供选择的多个索引,并依赖查询优化器做出正确的决定。这在多种情况下可减少分析时间并获得良好的性能。若要查看查询优化器对特定查询使用的索引,请在 SQL Server Management Studio 中的“查询”菜单上选择“包括实际的执行计划”。
不要总是将索引的使用等同于良好的性能,或者将良好的性能等同于索引的高效使用。如果只要使用索引就能获得最佳性能,那查询优化器的工作就简单了。但事实上,不正确的索引选择并不能获得最佳性能。因此,查询优化器的任务是只在索引或索引组合能提高性能时才选择它,而在索引检索有碍性能时则避免使用它。
建议的索引设计策略包括以下任务:
1. 了解数据库本身的特征。例如,它是频繁修改数据的联机事务处理 (OLTP) 数据库,还是主要包含只读数据的决策支持系统 (DSS) 或数据仓库 (OLAP) 数据库?
2. 了解最常用的查询的特征。例如,了解到最常用的查询联接两个或多个表将有助于决定要使用的最佳索引类型。
3. 了解查询中使用的列的特征。例如,某个索引对于含有整数数据类型同时还是唯一的或非空的列是理想索引。筛选索引适用于具有定义完善的数据子集的列。
4. 确定哪些索引选项可在创建或维护索引时提高性能。例如,对现有某个大型表创建聚集索引将会受益于 ONLINE 索引选项。ONLINE 选项允许在创建索引或重新生成索引时继续对基础数据执行并发活动。
5. 确定索引的最佳存储位置。非聚集索引可以与基础表存储在同一个文件组中,也可以存储在不同的文件组中。索引的存储位置可通过提高磁盘 I/O 性能来提高查询性能。例如,将非聚集索引存储在表文件组所在磁盘以外的某个磁盘上的一个文件组中可以提高性能,因为可以同时读取多个磁盘。
或者,聚集索引和非聚集索引也可以使用跨越多个文件组的分区方案。在维护整个集合的完整性时,使用分区可以快速而有效地访问或管理数据子集,从而使大型表或索引更易于管理。有关详细信息,请参阅已分区表和已分区索引。在考虑分区时,应确定是否应对齐索引,即,是按实质上与表相同的方式进行分区,还是单独分区。
# 设计索引。
索引设计是一项关键任务。索引设计包括确定要使用的列,选择索引类型(例如聚集或非聚集),选择适当的索引选项,以及确定文件组或分区方案布置。
# 确定最佳的创建方法。按照以下方法创建索引:
* 使用 CREATE TABLE 或 ALTER TABLE 对列定义 PRIMARY KEY 或 UNIQUE 约束
SQL Server 数据库引擎自动创建唯一索引来强制 PRIMARY KEY 或 UNIQUE 约束的唯一性要求。默认情况下,创建的唯一聚集索引可以强制 PRIMARY KEY 约束,除非表中已存在聚集索引或指定了唯一的非聚集索引。默认情况下,创建的唯一非聚集索引可以强制 UNIQUE 约束,除非已明确指定唯一的聚集索引且表中不存在聚集索引。
还可以指定索引选项和索引位置、文件组或分区方案。
创建为 PRIMARY KEY 或 UNIQUE 约束的一部分的索引将自动给定与约束名称相同的名称。
* 使用 CREATE INDEX 语句或 SQL Server Management Studio 对象资源管理器中的“新建索引”对话框创建独立于约束的索引
必须指定索引的名称、表以及应用该索引的列。还可以指定索引选项和索引位置、文件组或分区方案。默认情况下,如果未指定聚集或唯一选项,将创建非聚集的非唯一索引。若要创建筛选索引,请使用可选的 WHERE 子句。
# 创建索引。
要考虑的一个重要因素是对空表还是对包含数据的表创建索引。对空表创建索引在创建索引时不会对性能产生任何影响,而向表中添加数据时,会对性能产生影响。
对大型表创建索引时应仔细计划,这样才不会影响数据库性能。对大型表创建索引的首选方法是先创建聚集索引,然后创建任何非聚集索引。在对现有表创建索引时,请考虑将 ONLINE 选项设置为 ON。该选项设置为 ON 时,将不持有长期表锁以继续对基础表的查询或更新。
简单的创建索引,可采用如下语句:
CREATE INDEX IX_ProctVendor_VendorID
ON Purchasing.ProctVendor (VendorID, VendorName);
GO
‘捌’ 使用SQL Server数据库,如何对数据项加S锁或X锁呢
锁的概述
一. 为什么要引入锁
多个用户同时对数据库的并发操作时会带来以下数据不一致的问题:
丢失更新
A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统
脏读
A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致
不可重复读
A用户读取数据,随后B用户读出该数据并修改,此时A用户再读取数据时发现前后两次的值不一致
并发控制的主要方法是封锁,锁就是在一段时间内禁止用户做某些操作以避免产生数据不一致
二 锁的分类
锁的类别有两种分法:
1. 从数据库系统的角度来看:分为独占锁(即排它锁),共享锁和更新锁
MS-SQL Server 使用以下资源锁模式。
锁模式 描述
共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。
更新 (U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。
排它 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。
意向锁 用于建立锁的层次结构。意向锁的类型为:意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。
架构锁 在执行依赖于表架构的操作时使用。架构锁的类型为:架构修改 (Sch-M) 和架构稳定性 (Sch-S)。
大容量更新 (BU) 向表中大容量复制数据并指定了 TABLOCK 提示时使用。
共享锁
共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁。
更新锁
更新 (U) 锁可以防止通常形式的死锁。一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。
若要避免这种潜在的死锁问题,请使用更新 (U) 锁。一次只有一个事务可以获得资源的更新 (U) 锁。如果事务修改资源,则更新 (U) 锁转换为排它 (X) 锁。否则,锁转换为共享锁。
排它锁
排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据。
意向锁
意向锁表示 SQL Server 需要在层次结构中的某些底层资源上获取共享 (S) 锁或排它 (X) 锁。例如,放置在表级的共享意向锁表示事务打算在表中的页或行上放置共享 (S) 锁。在表级设置意向锁可防止另一个事务随后在包含那一页的表上获取排它 (X) 锁。意向锁可以提高性能,因为 SQL Server 仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁。而无须检查表中的每行或每页上的锁以确定事务是否可以锁定整个表。
意向锁包括意向共享 (IS)、意向排它 (IX) 以及与意向排它共享 (SIX)。
锁模式 描述
意向共享 (IS) 通过在各资源上放置 S 锁,表明事务的意向是读取层次结构中的部分(而不是全部)底层资源。
意向排它 (IX) 通过在各资源上放置 X 锁,表明事务的意向是修改层次结构中的部分(而不是全部)底层资源。IX 是 IS 的超集。
与意向排它共享 (SIX) 通过在各资源上放置 IX 锁,表明事务的意向是读取层次结构中的全部底层资源并修改部分(而不是全部)底层资源。允许顶层资源上的并发 IS 锁。例如,表的 SIX 锁在表上放置一个 SIX 锁(允许并发 IS 锁),在当前所修改页上放置 IX 锁(在已修改行上放置 X 锁)。虽然每个资源在一段时间内只能有一个 SIX 锁,以防止其它事务对资源进行更新,但是其它事务可以通过获取表级的 IS 锁来读取层次结构中的底层资源。
独占锁:只允许进行锁定操作的程序使用,其他任何对他的操作均不会被接受。执行数据更新命令时,SQL Server会自动使用独占锁。当对象上有其他锁存在时,无法对其加独占锁。
共享锁:共享锁锁定的资源可以被其他用户读取,但其他用户无法修改它,在执行Select时,SQL Server会对对象加共享锁。
更新锁:当SQL Server准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server确定要进行更新数据操作时,他会自动将更新锁换为独占锁,当对象上有其他锁存在时,无法对其加更新锁。
2. 从程序员的角度看:分为乐观锁和悲观锁。
乐观锁:完全依靠数据库来管理锁的工作。
悲观锁:程序员自己管理数据或对象上的锁处理。
MS-SQLSERVER 使用锁在多个同时在数据库内执行修改的用户间实现悲观并发控制
三 锁的粒度
锁粒度是被封锁目标的大小,封锁粒度小则并发性高,但开销大,封锁粒度大则并发性低但开销小
SQL Server支持的锁粒度可以分为为行、页、键、键范围、索引、表或数据库获取锁
资源 描述
RID 行标识符。用于单独锁定表中的一行。
键 索引中的行锁。用于保护可串行事务中的键范围。
页 8 千字节 (KB) 的数据页或索引页。
扩展盘区 相邻的八个数据页或索引页构成的一组。
表 包括所有数据和索引在内的整个表。
DB 数据库。
四 锁定时间的长短
锁保持的时间长度为保护所请求级别上的资源所需的时间长度。
用于保护读取操作的共享锁的保持时间取决于事务隔离级别。采用 READ COMMITTED 的默认事务隔离级别时,只在读取页的期间内控制共享锁。在扫描中,直到在扫描内的下一页上获取锁时才释放锁。如果指定 HOLDLOCK 提示或者将事务隔离级别设置为 REPEATABLE READ 或 SERIALIZABLE,则直到事务结束才释放锁。
根据为游标设置的并发选项,游标可以获取共享模式的滚动锁以保护提取。当需要滚动锁时,直到下一次提取或关闭游标(以先发生者为准)时才释放滚动锁。但是,如果指定 HOLDLOCK,则直到事务结束才释放滚动锁。
用于保护更新的排它锁将直到事务结束才释放。
如果一个连接试图获取一个锁,而该锁与另一个连接所控制的锁冲突,则试图获取锁的连接将一直阻塞到:
将冲突锁释放而且连接获取了所请求的锁。
连接的超时间隔已到期。默认情况下没有超时间隔,但是一些应用程序设置超时间隔以防止无限期等待
五 SQL Server 中锁的自定义
1 处理死锁和设置死锁优先级
死锁就是多个用户申请不同封锁,由于申请者均拥有一部分封锁权而又等待其他用户拥有的部分封锁而引起的无休止的等待
可以使用SET DEADLOCK_PRIORITY控制在发生死锁情况时会话的反应方式。如果两个进程都锁定数据,并且直到其它进程释放自己的锁时,每个进程才能释放自己的锁,即发生死锁情况。
2 处理超时和设置锁超时持续时间。
@@LOCK_TIMEOUT 返回当前会话的当前锁超时设置,单位为毫秒
SET LOCK_TIMEOUT 设置允许应用程序设置语句等待阻塞资源的最长时间。当语句等待的时间大于 LOCK_TIMEOUT 设置时,系统将自动取消阻塞的语句,并给应用程序返回"已超过了锁请求超时时段"的 1222 号错误信息
示例
下例将锁超时期限设置为 1,800 毫秒。
SET LOCK_TIMEOUT 1800
3) 设置事务隔离级别。
4 ) 对 SELECT、INSERT、UPDATE 和 DELETE 语句使用表级锁定提示。
5) 配置索引的锁定粒度
可以使用 sp_indexoption 系统存储过程来设置用于索引的锁定粒度
六 查看锁的信息
1 执行 EXEC SP_LOCK 报告有关锁的信息
2 查询分析器中按Ctrl+2可以看到锁的信息
七 使用注意事项
如何避免死锁
1 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务;
2 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;
3 优化程序,检查并避免死锁现象出现;
4 .对所有的脚本和SP都要仔细测试,在正是版本之前。
5 所有的SP都要有错误处理(通过@error)
6 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁
解决问题 如何对行 表 数据库加锁
八 几个有关锁的问题
1 如何锁一个表的某一行
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT * FROM table ROWLOCK WHERE id = 1
2 锁定数据库的一个表
SELECT * FROM table WITH (HOLDLOCK)
加锁语句:
sybase:
update 表 set col1=col1 where 1=0 ;
MSSQL:
select col1 from 表 (tablockx) where 1=0 ;
oracle:
LOCK TABLE 表 IN EXCLUSIVE MODE ;
加锁后其它人不可操作,直到加锁用户解锁,用commit或rollback解锁
几个例子帮助大家加深印象
设table1(A,B,C)
A B C
a1 b1 c1
a2 b2 c2
a3 b3 c3
1)排它锁
新建两个连接
在第一个连接中执行以下语句
begin tran
update table1
set A='aa'
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran
在第二个连接中执行以下语句
begin tran
select * from table1
where B='b2'
commit tran
若同时执行上述两个语句,则select查询必须等待update执行完毕才能执行即要等待30秒
2)共享锁
在第一个连接中执行以下语句
begin tran
select * from table1 holdlock -holdlock人为加锁
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran
在第二个连接中执行以下语句
begin tran
select A,C from table1
where B='b2'
update table1
set A='aa'
where B='b2'
commit tran
若同时执行上述两个语句,则第二个连接中的select查询可以执行
而update必须等待第一个事务释放共享锁转为排它锁后才能执行 即要等待30秒
3)死锁
增设table2(D,E)
D E
d1 e1
d2 e2
在第一个连接中执行以下语句
begin tran
update table1
set A='aa'
where B='b2'
waitfor delay '00:00:30'
update table2
set D='d5'
where E='e1'
commit tran
在第二个连接中执行以下语句
begin tran
update table2
set D='d5'
where E='e1'
waitfor delay '00:00:10'
update table1
set A='aa'
where B='b2'
commit tran
同时执行,系统会检测出死锁,并中止进程
补充一点:
Sql Server2000支持的表级锁定提示
HOLDLOCK 持有共享锁,直到整个事务完成,应该在被锁对象不需要时立即释放,等于SERIALIZABLE事务隔离级别
NOLOCK 语句执行时不发出共享锁,允许脏读 ,等于 READ UNCOMMITTED事务隔离级别
PAGLOCK 在使用一个表锁的地方用多个页锁
READPAST 让sql server跳过任何锁定行,执行事务,适用于READ UNCOMMITTED事务隔离级别只跳过RID锁,不跳过页,区域和表锁
ROWLOCK 强制使用行锁
TABLOCKX 强制使用独占表级锁,这个锁在事务期间阻止任何其他事务使用这个表
UPLOCK 强制在读表时使用更新而不用共享锁
应用程序锁:
应用程序锁就是客户端代码生成的锁,而不是sql server本身生成的锁
处理应用程序锁的两个过程
sp_getapplock 锁定应用程序资源
sp_releaseapplock 为应用程序资源解锁
注意: 锁定数据库的一个表的区别
SELECT * FROM table WITH (HOLDLOCK) 其他事务可以读取表,但不能更新删除
SELECT * FROM table WITH (TABLOCKX) 其他事务不能读取表,更新和删除
‘玖’ 数据库事务的四个隔离级别,mysql在哪一个级别
术式之后皆为逻辑,一切皆为需求和实现。希望此文能从需求、现状和解决方式的角度帮大家理解隔离级别。
隔离级别的产生
在串型执行的条件下,数据修改的顺序是固定的、可预期的结果,但是并发执行的情况下,数据的修改是不可预期的,也不固定,为了实现数据修改在并发执行的情况下得到一个固定、可预期的结果,由此产生了隔离级别。
所以隔离级别的作用是用来平衡数据库并发访问与数据一致性的方法。
事务的4种隔离级别
READ UNCOMMITTED 行竖 未提交读,可以读取未提交的数据。READ COMMITTED 已提交读,对于锁定读(select with for update 或者 for share)、update 和 delete 语句, InnoDB 仅锁定索引记录,而不锁定它们之间的间隙,因此允许在锁定的记录旁边自由插入新记录。 Gap locking 仅用于外键约束检查和重复键检查。REPEATABLE READ 可重复读,事务中的一致性读取读取的是事务第一次读取所建立的快照。SERIALIZABLE 序列化
在了解了 4 种隔离级别的需求后,在采用锁控制隔离级别的基础上,我们需要了解加锁的对象(数据本身&间隙),以及了解整个数据范围的全集组成。
数据范围全集组成
SQL 语句根据条件判断不需要扫描的数据范围(不加锁);
SQL 语句根据条件扫描到的可能需要加锁的数据范围;
以单个数据范围为例,数据范围全集包含:(数据范围不一定是连续的值,也可能是间隔的值组成)
1. 数据已经填充了整个数据范围:(被完全填充的数据范围,不存在数据间隙)
整形,对值具有唯一约束条件的数据范围 1~5 ,
已有数据1、2、3、4、5,此时数据范围已被完全填充;
整形,对值具有唯一约束条件的数据范围 1 和 5 ,
已有数据1、5,此时数据范围已被完全填充;
整形的数据范围 1~5 ,
已有数据 1、2、3、4、5,但是因为没有唯一约束,
所以数据范围可以继续被 1~5 的数据重复填充;
整形,具有唯一约束条件的数据范围 1~5 ,
已有数据 2,5,此时数据范围未被完全填充,还可以填充 1、3、4 ;
整形的数据范围 1~5 ,数据范围内当前没有任何数据。
更新丢失:当多个事务选择了同一行,然后基于最初选定的值更新该行时,
由于每个事物不知道其陆塌他事务的存在,最后的更新就会覆盖其他事务所做的更新;
脏读: 一个事务正在对一条记录做修改,这个事务完成并提交前,这条记录就处于不一致状态。
这时,另外一个事务也来读取同一条记录,如果不加控制,
第二个事务读取了这些“脏”数据,并据此做了进一步的处理,就会产生提交的数据依赖关系。
这种现象就叫“脏读”。
不可重复读:一个事早带圆务在读取某些数据后的某个时间,再次读取以前读过的数据,
却发现其读出的数据已经发生了改变,或者某些记录已经被删除了。
这种现象就叫“不可重复读”。
幻读:一个事务按相同的查询条件重新读取以前检索过的数据,
却发现其他事务插入了满足其查询条件的新数据,这种现象称为“幻读”。
可以简单的认为满足条件的数据量变化了。
Record Locks
索引记录锁,索引记录锁始终锁定索引记录,即使表中未定义索引,
这种情况下,InnoDB 创建一个隐藏的聚簇索引,并使用该索引进行记录锁定。
Gap Locks
间隙锁是索引记录之间的间隙上的锁,或者对第一条记录之前或者最后一条记录之后的锁。
间隙锁是性能和并发之间权衡的一部分。
对于无间隙的数据范围不需要间隙锁,因为没有间隙。
Next-Key Locks
索引记录上的记录锁和索引记录之前的 gap lock 的组合。
假设索引包含 10、11、13 和 20。
可能的next-key locks包括以下间隔,其中圆括号表示不包含间隔端点,方括号表示包含端点:
(负无穷大, 10] (10, 11] (11, 13] (13, 20] (20, 正无穷大) 对于最后一个间隔,next-key将会锁定索引中最大值的上方,
2. 数据填充了部分数据范围:(未被完全填充的数据范围,是存在数据间隙)
3. 数据范围内没有任何数据(存在间隙)
如下:
在了解了数据全集的组成后,我们再来看看事务并发时,会带来的问题。
无控制的并发所带来的问题
并发事务如果不加以控制的话会带来一些问题,主要包括以下几种情况。
1. 范围内已有数据更改导致的:
2. 范围内数据量发生了变化导致:
因为无控制的并发会带来一系列的问题,这些问题会导致无法满足我们所需要的结果。因此我们需要控制并发,以实现我们所期望的结果(隔离级别)。
MySQL 隔离级别的实现
InnoDB 通过加锁的策略来支持这些隔离级别。
行锁包含:
左右滑动进行查看
"上确界"伪记录的值高于索引中任何实际值。
上确界不是一个真正的索引记录,因此,实际上,这个 next-key 只锁定最大索引值之后的间隙。
基于此,当获取的数据范围中,数据已填充了所有的数据范围,那么此时是不存在间隙的,也就不需要 gap lock。
对于数据范围内存在间隙的,需要根据隔离级别确认是否对间隙加锁。
默认的 REPEATABLE READ 隔离级别,为了保证可重复读,除了对数据本身加锁以外,还需要对数据间隙加锁。
READ COMMITTED 已提交读,不匹配行的记录锁在 MySQL 评估了 where 条件后释放。
对于 update 语句,InnoDB 执行 "semi-consistent" 读取,这样它会将最新提交的版本返回到 MySQL,
以便 MySQL 可以确定该行是否与 update 的 where 条件相匹配。
总结&延展:
唯一索引存在唯一约束,所以变更后的数据若违反了唯一约束的原则,则会失败。
当 where 条件使用二级索引筛选数据时,会对二级索引命中的条目和对应的聚簇索引都加锁;所以其他事务变更命中加锁的聚簇索引时,都会等待锁。
行锁的增加是一行一行增加的,所以可能导致并发情况下死锁的发生。
例如,
在 session A 对符合条件的某聚簇索引加锁时,可能 session B 已持有该聚簇索引的 Record Locks,而 session B 正在等待 session A 已持有的某聚簇索引的 Record Locks。
session A 和 session B 是通过两个不相干的二级索引定位到的聚簇索引。
session A 通过索引 idA,session B通过索引 idB 。
当 where 条件获取的数据无间隙时,无论隔离级别为 rc 或 rr,都不会存在间隙锁。
比如通过唯一索引获取到了已完全填充的数据范围,此时不需要间隙锁。
间隙锁的目的在于阻止数据插入间隙,所以无论是通过 insert 或 update 变更导致的间隙内数据的存在,都会被阻止。
rc 隔离级别模式下,查询和索引扫描将禁用 gap locking,此时 gap locking 仅用于外键约束检查和重复键检查(主要是唯一性检查)。
rr 模式下,为了防止幻读,会加上 Gap Locks。
事务中,SQL 开始则加锁,事务结束才释放锁。
就锁类型而言,应该有优化锁,锁升级等,例如rr模式未使用索引查询的情况下,是否可以直接升级为表锁。
就锁的应用场景而言,在回放场景中,如果确定事务可并发,则可以考虑不加锁,加快回放速度。
锁只是并发控制的一种粒度,只是一个很小的部分:
从不同场景下是否需要控制并发,(已知无交集且有序的数据的变更,MySQL 的 MTS 相同前置事务的多事务并发回放)
并发控制的粒度,(锁是一种逻辑粒度,可能还存在物理层和其他逻辑粒度或方式)
相同粒度下的优化,(锁本身存在优化,如IX、IS类型的优化锁)
粒度加载的安全&性能(如获取行锁前,先获取页锁,页锁在执行获取行锁操作后即释放,无论是否获取成功)等多个层次去思考并发这玩意。
‘拾’ MySQL从入门到精通(九) MySQL锁,各种锁
锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,除传统的计算资源(CPU、RAM、I/O)争用外,数据也是一种供许多用户共享的资源,如何保证数据并发访问的一致性,有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素,从这个角度来说,锁对数据库而言是尤其重要,也更加复杂。MySQL中的锁,按照锁的粒度分为:1、全局锁,就锁定数据库中的所有表。2、表级锁,每次操作锁住整张表。3、行级锁,每次操作锁住对应的行数据。
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将阻塞。其典型的使用场景就是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。但是对数据库加全局锁是有弊端的,如在主库上备份,那么在备份期间都不能执行更新,业务会受影响,第二如果是在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志,会导致主从延迟。
解决办法是在innodb引擎中,备份时加上--single-transaction参数来完成不加锁的一致性数据备份。
添加全局锁: flush tables with read lock; 解锁 unlock tables。
表级锁,每次操作会锁住整张表.锁定粒度大,发送锁冲突的概率最高,并发读最低,应用在myisam、innodb、BOB等存储引擎中。表级锁分为: 表锁、元数据锁(meta data lock, MDL)和意向锁。
表锁又分为: 表共享读锁 read lock、表独占写锁write lock
语法: 1、加锁 lock tables 表名 ... read/write
2、释放锁 unlock tables 或者关闭客户端连接
注意: 读锁不会阻塞其它客户端的读,但是会阻塞其它客户端的写,写锁既会阻塞其它客户端的读,又会阻塞其它客户端的写。大家可以拿一张表来测试看看。
元数据锁,在加锁过程中是系统自动控制的,无需显示使用,在访问一张表的时候会自动加上,MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML和DDL冲突,保证读写的正确性。
在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作时,加MDL写锁(排他).
查看元数据锁:
select object_type,object_schema,object_name,lock_type,lock_ration from performance_schema_metadata_locks;
意向锁,为了避免DML在执行时,加的行锁与表锁的冲突,在innodb中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。意向锁分为,意向共享锁is由语句select ... lock in share mode添加。意向排他锁ix,由insert,update,delete,select。。。for update 添加。
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_lock;
行级锁,每次操作锁住对应的行数据,锁定粒度最小,发生锁冲突的概率最高,并发读最高,应用在innodb存储引擎中。
innodb的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁,对于行级锁,主要分为以下三类:
1、行锁或者叫record lock记录锁,锁定单个行记录的锁,防止其他事物对次行进行update和delete操作,在RC,RR隔离级别下都支持。
2、间隙锁Gap lock,锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事物在这个间隙进行insert操作,产生幻读,在RR隔离级别下都支持。
3、临键锁Next-key-lock,行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap,在RR隔离级别下支持。
innodb实现了以下两种类型的行锁
1、共享锁 S: 允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
2、排他锁 X: 允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。
insert 语句 排他锁 自动添加的
update语句 排他锁 自动添加
delete 语句 排他锁 自动添加
select 正常查询语句 不加锁 。。。
select 。。。lock in share mode 共享锁 需要手动在select 之后加lock in share mode
select 。。。for update 排他锁 需要手动在select之后添加for update
默认情况下,innodb在repeatable read事务隔离级别运行,innodb使用next-key锁进行搜索和索引扫描,以防止幻读。
间隙锁唯一目的是防止其它事务插入间隙,间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用的间隙锁。