锁是计算机协调多个进程或线程并发访问某一资源的机制
特点
显著特点是不同的存储引擎支持不同的锁机制
表级锁:
两种模式:表 共享锁【读锁】(级别) > 表 独占锁【排他锁/写锁】
表级锁的存储引擎:
MyISAM:引擎
MEMORY:引擎
不能访问别的表
特点
- 作用范围:表级别
- 如果加了读锁,对MyISAM表的
读操作
,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写
请求 - 如果加了读锁,可以查询锁定表中的记录,但是更新或者访问其他表都会提示错误【对自己】
- 如果加了写锁,对MyISAM表的
写操作
,会阻塞其他用户对同一表的读 和 写
请求 - 如果加了写锁,可以读取表中的记录,但是更新或者访问其他表都会提示错误,可以写表中的数据【加锁表】【对自己】
如何加锁
MyISAM在执行查询语句(select)前,会自动给涉及的所有表加读锁,在执行更新操作
在执行(update、delete、insert)前,会自动给表加写锁
加锁
lock table 表名 read [local],
Lock table 表名 write [local]
多表加锁:
lock table 表名1,表名2 read/write [local]
解锁
unlock tables;
查看锁的情况
show status like 'table%'
table_locks_immediate:加锁次数
table_locks_waited : 等待次数
show status like '%lock%' # 看到的更全面
show processlist #此命令可以查看那些sql在等待锁
show open tables #当前被锁住的表以及锁的次数
并发插入:由读锁带来的问题解决办法
加锁带来的问题
myisam有一个系统变量concurrent_insert,专门用以控制其并发插入的行为,其值分别对应为0,1,2
concurrent_insert=0,不允许并发插入【NEVER】
concurrent_insert=1,如果myisam没有空洞(即表的中间没有被删除的行),myisam允许在一个进程读表的同时,另一个进程从表尾插入记录,【默认设置】【AUTO】
concurrent_insert=2,无论有没有空洞,都在表尾插入【ALWAYS】
会产生碎片,用optimize table 表名;解决(优化)
读写锁优先级
默认情况下,写 > 读
设置写锁的最多次数
设置:
max_write_lock_count=次数
锁次数=次数值时,执行读操作
有了这样的设置,当系统处理一个写操作后,就会暂停写操作,给读操作执行的机会。
降低写操作的优先级,给读操作更高的优先级
low_priority_updates=1 降低写的优先级
sql_low_priority_updates=1
默认值为0
写语句变更为:
update/insert/delete/... low_priority
再用
设置写内存
max_allowed_packet=1M #限制接受的数据包的大小,大的插入和更新会被限制掉,导致失败
net_buffer_length=2K-16M #设置insert语句缓存值,多数据同时插入(),(),()。。。
bulk_insert_buffer_size=8M # 一次性insert语句插入的大小
优化办法
- concurrent_insert=2
- 用
optimize table 表名
整理碎片 - 设置优先级
- 设置写内存/写的数量次数
使用场景
第一种情况是:事务需要更新大部分或全部数据,表又比较大,如果使用默认的行锁,不仅这个事务执行效率低,而且可能造成其他事务长时间锁等待和锁冲突,这种情况下可以考虑使用表锁来提高该事务的执行速度。
第二种情况是:事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚。这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁、减少数据库因事务回滚带来的开销。
行级锁/事务锁
行级锁模式
读锁/共享锁(S):允许一个事务去读一行,组织其他事务获得相同数据集的排他锁;
允许其他线程上读锁,但是不允许上写锁。
写锁/排他锁(X):允许获得排他锁的事务更新数据,组织其他事务取得相同数据集的共享读锁和排他锁
不允许其他线程上任何锁。
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加锁前必须先取得该表的IS锁;为防止形成死锁
意向排他锁(IX):事务打算给数据加行排他锁,事务在给一个数据行加排他锁前必须取得该表的IX锁;为防止形成死锁【不主张用】
兼容 | X | IX | S | IS |
---|---|---|---|---|
X | × | × | × | × |
IX | × | 兼容 | × | 兼容 |
S | × | × | 兼容 | 兼容 |
IS | × | 兼容 | 兼容 | 兼容 |
如果一个事务请求的锁模式与当前锁兼容,InnoDB就将请求的锁授予该事务;
反之。不兼容,该事务就要等待锁释放
行级锁的存储引擎:InnoDB
特点
- InnoDB行锁时通过给
索引上的索引项
加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将行锁升级为表锁 【操作的行必须有索引】 - 意向锁是InnoDB自动加的,不需要用户干预。
- 对于update、delete、insert,自动给涉及数据集加排他锁;对于普通的select【没有
显示加锁
的情况】,不加任何锁 - 研究行锁时,需要将自动提交关闭,默认开启:set autocommit=0;
- 多个客户端都要设置 set autocommit=0;
select语句加行锁
意向共享锁/读锁:select ***** lock in share mode
意向排他锁/写锁:select ***** for update
释放锁
commit
rollback
innodb注意事项
当我们给某一条数据上了排他锁:
其他人操作不了这条数据;想操作。排队,等其他人释放(commit;)
其他人对这条数据没有任何权限,但是并不影响其他客户修改其他数据
能查询,但是查不到最新的【隔离】
即使字段加了索引,但是你在使用偷换了数据类型,那么索引失效,最终加上表锁【0 》 ‘0 ’】
间隙锁
操作的数据的自增值中间有间隙的时候,将间隙(缺的部分)自动锁起来
也能锁住不存在的条件(一般为子增值)
锁住范围内的所有数据 =》 指定条件的时候给一个确定的范围
造成阻塞
查询行级锁征用情况
show status like 'innobd_row_lock%'
innobd_row_lock_waits innobd_row_lock_time_
优化行级锁
- 尽量用较低的隔离级别,精心设置索引,并尽量使用索引当问数据,使加锁更精确,从而减少锁冲突的机会
- 选择合理的事务大小,小事务发生锁冲突的几率也更小
- 给记录集显示【手动】加锁时,最好一次性请求足够级别的锁,比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时在申请排他锁,这样容易产生死锁
- 尽量用相等条件访问数据,可以避免间隙锁对并发插入的影响
- 对于特定的事务,可以使用
表锁
来提高处理速度或减少死锁的可能