时间:2021-07-01 10:21:17 帮助过:28人阅读
锁机制: 解决因资源共享而出现的并发控制问题
??锁机制主要包含两个部分【锁类型 - 对数据集合操作的权力】和【锁粒度 - 操作数据集合的大小】
- 示例:买最后一件衣服X
- A:
- X
- 买 : X加锁 ->试衣服...下单..付款..打包 ->X解锁
- B:
- X 买:发现X已被加锁,等待X解锁, X已售空
- 分类:
- ??操作类型(锁类型):
- a.读锁(共享锁): 对同一个数据(衣服),多个读操作可以同时进行,互不干扰。
- b.写锁(排他锁): 如果当前写操作没有完毕(买衣服的一系列操作),则无法进行其他的读操作、写操作
- ??操作范围(锁粒度\范围):
- a.表锁 :一次性对一张表整体加锁。如MyISAM存储引擎使用表锁,开销小、加锁快、并发能力小;无死锁;但锁的范围大,容易发生锁冲突、并发度低。
- b.行锁 :一次性对一条数据加锁。如InnoDB存储引擎使用行锁,开销大,加锁慢、并发能力大;容易出现死锁;锁的范围较小,不易发生锁冲突,并发度高(很小概率 发生高并发问题:脏读、幻读、不可重复度、丢失更新等问题)。
- c.页锁
【数据准备】
- <code>create table tablelock
- (
- id int primary key auto_increment ,
- name varchar(20)
- )engine myisam;
- insert into tablelock(name) values(‘a1‘);
- insert into tablelock(name) values(‘a2‘);
- insert into tablelock(name) values(‘a3‘);
- insert into tablelock(name) values(‘a4‘);
- insert into tablelock(name) values(‘a5‘);
- commit;
- -- ??增加锁
- lock table 表1 read/write ,表2 read/write ,...
- mysql> lock table tablelock read;
- Query OK, 0 rows affected (0.00 sec)
- -- ??查看加锁表
- mysql> SHOW OPEN TABLES;
- +--------------------+----------------------------------------------+--------+-------------+
- | Database | Table | In_use | Name_locked |
- +--------------------+----------------------------------------------+--------+-------------+
- | mysql | time_zone_transition_type | 0 | 0 |
- | performance_schema | events_waits_summary_global_by_event_name | 0 | 0 |
- | performance_schema | setup_timers | 0 | 0 |
- | performance_schema | events_waits_history_long | 0 | 0 |
- | mysql | time_zone_transition | 0 | 0 |
- | performance_schema | mutex_instances | 0 | 0 |
- | performance_schema | events_waits_summary_by_instance | 0 | 0 |
- | mysql | tables_priv | 0 | 0 |
- | mysql | procs_priv | 0 | 0 |
- | mysql | func | 0 | 0 |
- | performance_schema | events_waits_history | 0 | 0 |
- | mysql | time_zone_name | 0 | 0 |
- | mysql | user | 0 | 0 |
- | performance_schema | setup_consumers | 0 | 0 |
- | performance_schema | file_instances | 0 | 0 |
- | performance_schema | cond_instances | 0 | 0 |
- | mysql | plugin | 0 | 0 |
- | mysql | db | 0 | 0 |
- | mysql | proxies_priv | 0 | 0 |
- | mysql | time_zone | 0 | 0 |
- | performance_schema | rwlock_instances | 0 | 0 |
- | performance_schema | events_waits_current | 0 | 0 |
- | mysql | event | 0 | 0 |
- | mysql | columns_priv | 0 | 0 |
- | performance_schema | performance_timers | 0 | 0 |
- | performance_schema | threads | 0 | 0 |
- | mysql | host | 0 | 0 |
- | performance_schema | events_waits_summary_by_thread_by_event_name | 0 | 0 |
- | performance_schema | file_summary_by_event_name | 0 | 0 |
- | mysql | time_zone_leap_second | 0 | 0 |
- | performance_schema | file_summary_by_instance | 0 | 0 |
- | learn_demo | tablelock | 1 | 0 | -- ?
- | mysql | servers | 0 | 0 |
- | performance_schema | setup_instruments | 0 | 0 |
- +--------------------+----------------------------------------------+--------+-------------+
- 34 rows in set (0.00 sec)
- </code>
会话:session :每一个访问数据的dos命令行、数据库客户端工具 都是一个会话
- <code>-- 会话1:
- lock table tablelock read;
- select * from tablelock; -- 读(查) 可以
- delete from tablelock where id = 1; -- 写(增删改) 不可以
- select * from emp; -- 读 不可以
- delete from emp where id = 1; --- 写(增删改) 不可以
- -- ??如果一个会话对一个表施加了读锁,则该会话只能对该表进行读的操作不能进行写操作而且也不能对其他的表进行读或写。
- -- 会话2:
- select * from tablelock; -- 读(查) 可以
- delete from tablelock where id = 1; -- 写,??会等待会话1将锁释放
- select * from emp; -- 读(查) 可以
- delete from emp where id = 1; -- 写 可以
- -- ??如果某一个会话对A表加了读锁则其他的会话可以对A表进行读的操作但写需要等待A表上的锁释放,其他会话对其他表的读写没有影响。
- </code>
释放锁: UNLOCK TABLES
【数据准备】
- <code>mysql> LOCK TABLE tablelock WRITE; --
- Query OK, 0 rows affected (0.00 sec)
- mysql> SHOW OPEN TABLES;
- +--------------------+----------------------------------------------+--------+-------------+
- | Database | Table | In_use | Name_locked |
- +--------------------+----------------------------------------------+--------+-------------+
- | mysql | time_zone_transition_type | 0 | 0 |
- | performance_schema | events_waits_summary_global_by_event_name | 0 | 0 |
- | performance_schema | setup_timers | 0 | 0 |
- | performance_schema | events_waits_history_long | 0 | 0 |
- | mysql | time_zone_transition | 0 | 0 |
- | performance_schema | mutex_instances | 0 | 0 |
- | performance_schema | events_waits_summary_by_instance | 0 | 0 |
- | mysql | tables_priv | 0 | 0 |
- | mysql | procs_priv | 0 | 0 |
- | mysql | func | 0 | 0 |
- | performance_schema | events_waits_history | 0 | 0 |
- | mysql | time_zone_name | 0 | 0 |
- | mysql | user | 0 | 0 |
- | performance_schema | setup_consumers | 0 | 0 |
- | performance_schema | file_instances | 0 | 0 |
- | performance_schema | cond_instances | 0 | 0 |
- | mysql | plugin | 0 | 0 |
- | mysql | db | 0 | 0 |
- | mysql | proxies_priv | 0 | 0 |
- | mysql | time_zone | 0 | 0 |
- | performance_schema | rwlock_instances | 0 | 0 |
- | performance_schema | events_waits_current | 0 | 0 |
- | mysql | event | 0 | 0 |
- | mysql | columns_priv | 0 | 0 |
- | performance_schema | performance_timers | 0 | 0 |
- | performance_schema | threads | 0 | 0 |
- | mysql | host | 0 | 0 |
- | performance_schema | events_waits_summary_by_thread_by_event_name | 0 | 0 |
- | performance_schema | file_summary_by_event_name | 0 | 0 |
- | mysql | time_zone_leap_second | 0 | 0 |
- | performance_schema | file_summary_by_instance | 0 | 0 |
- | learn_demo | tablelock | 1 | 0 |
- | mysql | servers | 0 | 0 |
- | performance_schema | setup_instruments | 0 | 0 |
- +--------------------+----------------------------------------------+--------+-------------+
- 34 rows in set (0.00 sec)
- mysql> SELECT *
- -> FROM tablelock;
- +----+------+
- | id | name |
- +----+------+
- | 1 | a1 |
- | 2 | a2 |
- | 3 | a3 |
- | 4 | a4 |
- | 5 | a5 |
- | 6 | wyt |
- +----+------+
- 6 rows in set (0.00 sec)
- mysql> UPDATE tablelock
- -> SET name = "wzz"
- -> WHERE id = 5;
- Query OK, 1 row affected (0.03 sec)
- Rows matched: 1 Changed: 1 Warnings: 0
- mysql> SELECT *
- -> FROM tablelock;
- +----+------+
- | id | name |
- +----+------+
- | 1 | a1 |
- | 2 | a2 |
- | 3 | a3 |
- | 4 | a4 |
- | 5 | wzz |
- | 6 | wyt |
- +----+------+
- 6 rows in set (0.00 sec)
- mysql> unlock tables;
- Query OK, 0 rows affected (0.00 sec)
- -- 如果一个会话对A表加了写锁,则该会话可以对A表进行读写(增删改查), 其他会话对A表进行读写的前提是A表上的锁释放
- </code>
- 分析表锁定:
- 查看哪些表加了锁: show open tables ; 1代表被加了锁
- 分析表锁定的严重程度: show status like ‘table%‘ ;
- mysql> SHOW STATUS LIKE "table%";
- +-----------------------+-------+
- | Variable_name | Value |
- +-----------------------+-------+
- | Table_locks_immediate | 49 |
- | Table_locks_waited | 0 |
- +-----------------------+-------+
- 2 rows in set (0.00 sec)
- Table_locks_immediate :即可能获取到的锁数
- Table_locks_waited:需要等待的表锁数(如果该值越大,说明存在越大的锁竞争)
- 一般建议:
- Table_locks_immediate/Table_locks_waited > 5000, 建议采用InnoDB引擎,否则MyISAM引擎
【数据准备】
- <code>mysql> create table linelock(
- -> id int(5) primary key auto_increment,
- -> name varchar(20)
- -> )engine=innodb ;
- Query OK, 0 rows affected (0.04 sec)
- mysql> insert into linelock(name) values(‘1‘) ;
- Query OK, 1 row affected (0.02 sec)
- mysql> insert into linelock(name) values(‘2‘) ;
- Query OK, 1 row affected (0.00 sec)
- mysql> insert into linelock(name) values(‘3‘) ;
- Query OK, 1 row affected (0.00 sec)
- mysql> insert into linelock(name) values(‘4‘) ;
- Query OK, 1 row affected (0.00 sec)
- mysql> insert into linelock(name) values(‘5‘) ;
- Query OK, 1 row affected (0.00 sec)
- mysql> SELECT *
- -> FROM linelock;
- +----+------+
- | id | name |
- +----+------+
- | 1 | 1 |
- | 2 | 2 |
- | 3 | 3 |
- | 4 | 4 |
- | 5 | 5 |
- +----+------+
- 5 rows in set (0.00 sec)
- </code>
为了研究行级锁,暂时将自动的commit关闭;
SET AUTOCOMMIT = 0; 研究之后需要commit
- <code>-- 【行锁 - 操作同一条记录 eg: id = 6】
- -- 会话1: 写操作
- mysql> INSERT INTO linelock VALUES(6, ‘a6‘);;
- Query OK, 1 row affected (0.00 sec)
- mysql> commit; -- 如果一直不commit即结束此事务则造成其他会话无法对该行数据进行读写
- -- ★ ??表锁 是通过unlock tables,也可以通过事务解锁 ; 行锁 是通过事务解锁??
- -- 会话2: 写操作同样的数据
- mysql> update linelock set name=‘ax‘ where id = 6;
- -- 总结: 如果会话x对某条数据a进行 DML操作(研究时:关闭了自动commit的情况下),则其他会话必须等待会话x结束事务(commit/rollback)后 才能对数据a进行操作。
- -- 【行锁 - 操作不同记录 eg: name = ax 】
- 会话1: 写操作
- insert into linelock values(8,‘a8‘) ;
- 会话2: 写操作, 不同的数据
- update linelock set name=‘ax‘ where id = 5;
- 行锁,一次锁一行数据;因此 如果操作的是不同数据,则不干扰。
- </code>
?? 如果没有索引,则行锁会转为表锁
- <code> 【按理来说,两个会话操作不同的记录相互之间不会发送阻塞的现象但下面的情况发生了阻塞】
- 会话0: 写操作
- update linelock set name = ‘ai‘ where name = ‘3‘ ;
- 会话1: 写操作, 不同的数据
- update linelock set name = ‘aiX‘ where name = ‘4‘ ;
- commit
- -- ?????? 原因:如果索引类 发生了类型转换,则索引失效。 因此 此次操作,会从行锁转为表锁。
- </code>
- <code>
- b.行锁的一种特殊情况:间隙锁:值在范围内,但却不存在
- --此时linelock表中 没有id=7的数据
- update linelock set name =‘x‘ where id >1 and id<9 ; --即在此where范围中,没有id=7的数据,则id=7的数据成为间隙。
- 间隙:Mysql会自动给 间隙 加索 ->间隙锁。即 本题 会自动给id=7的数据加 间隙锁(行锁)。
- 行锁:如果有where,则实际加索的范围 就是where后面的范围(不是实际的值)
- 如何仅仅是查询数据,能否加锁? 可以 for update
- 研究学习时,将自动提交关闭:
- set autocommit =0 ;
- start transaction ;
- begin ;
- select * from linelock where id =2 for update ;
- 通过for update对query语句进行加锁。
- </code>
行锁分析:
- <code>show status like ‘%innodb_row_lock%‘ ;
- Innodb_row_lock_current_waits :当前正在等待锁的数量
- Innodb_row_lock_time:等待总时长。从系统启到现在 一共等待的时间
- Innodb_row_lock_time_avg :平均等待时长。从系统启到现在平均等待的时间
- Innodb_row_lock_time_max :最大等待时长。从系统启到现在最大一次等待的时间
- Innodb_row_lock_waits :等待次数。从系统启到现在一共等待的次数
- </code>
SQL锁机制 - 基础概念+示例分析
标签:src 进程 次数 ons 关闭 数据集 集合 相互 其它