数据库锁定机制

数据库锁定机制

话说如果你只是单纯的说 "锁表",总是让人感觉有点 Low ,而我们就直接换个比较高大上一点的名词,锁定机制!

为了保证数据的完整,也就是他的一致性和有效性,所以才会让数据库出现了锁定机制,相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。

  • MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking)
  • BDB存储引擎采用的是页面锁(page-level locking),也支持表级锁
  • InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁

我们先来瞅瞅看一下这些锁都是个什么鬼东西!

行级锁(row-level locking)

  • 锁的对象颗粒度很小
  • 各大数据库中锁粒度最小的
  • 锁定资源占用概率最小

虽然说行级锁的优点是很明显,但是相对的弊端也因为他的优点出现了,

  • 因为锁定的粒度比较小,所以每次获取锁和释放锁需要做更多的内容,带来的消耗必然也就更大了,
  • 行级锁也是最容易发生死锁的。
  • 并发度也最高

表级锁(table-level locking)

  • 和行锁相反,粒度是最大的
  • 逻辑简单,对系统的负面影响比较小
  • 获取锁和释放锁速度快
  • 并发度最低。

弊端也有,因为粒度比较大,锁定资源占用概率也会很大,

页级锁(page-level locking)

  • 比较特殊,介于行锁和表锁之间,所以他的能力都是介于两者之间的,就是

粒度比较 表级锁 > 页锁 > 行锁

而他的并发度也就是一般般了。但是他会出现死锁,这三个当中,看来也就只有表锁不会出现死锁了。

我们在这里已经算是对锁机制大致有了个了解,我们再细致的看一下

表锁

表锁实际上分为2种类型,

  • 读锁定
  • 写锁定

而这两种锁定,又是通过其内部的队列来进行维护的,

  • 当前读锁队列 (Current read-lock queue)
  • 挂起的读锁队列(Pending read-lock queue)
  • 挂起写锁队列(Pending write-lock queue)
  • 当前写锁队列(Current write-lock queue)

什么意思呢?

当前读锁的队列,实际上就是包含了当前所有的持有读锁的线程,这里面的内容就是按照获取锁的时间进行有序的排放,

而挂起的读锁队列中存放的则是等待获取所得线程。

那写锁大家肯定也知道是什么意思了,就像是个4*100接力一样。拿着“棒子”的,就是当前读/写锁队列,等着接 “棒子” 的就是 挂起的读/写锁队列。

行锁

MySQL的 InnoDB 存储引擎支持行级锁,InnoDB 的行锁是通过给索引项加锁实现的。

这句话说明了什么?

说明了一件事:只有通过索引条件检索数据时,InnoDB 才使用行锁,否则使用表锁。

是不是感觉很诧异,但是事实上就是这样的。

InnoDB 级别的行锁也是分成了两种

  • 共享锁
  • 独占锁

共享锁和独占锁(Shared and Exclusive Locks),InnoDB 通过共享锁和独占锁两种方式实现了标准的行锁。共享锁(S 锁):允许事务获得锁后去读数据,独占锁(X 锁):允许事务获得锁后去更新或删除数据。一个事务获取的共享锁 S 后,允许其他事务获取 S 锁,此时两个事务都持有共享锁 S,但是不允许其他事务获取 X 锁。如果一个事务获取的独占锁(X),则不允许其他事务获取 S 或者 X 锁,必须等到该事务释放锁后才可以获取到

很多读者肯定也都了解的很深入,肯定还有其他的,对,还有一种就是意向共享锁和意向独占锁。

这种意向共享锁和意向独占锁的意思就是如果我需要一个共享锁,但是这个共享锁这时候正锁定这资源,那我自己就可以加一个共享锁,只能等这个共享锁释放之后,我才能锁定,这个锁就可以称之为意向共享锁,同理,独占锁也是一样的。

而他们之间的逻辑关系是这个样子的。

数据库锁表的原因

其实最简单的就是会出现在 insert、update、delete 这些操作的并发操作上,当我们使用多个数据库连接的时候,同时对一个表中的数据进行更新的操作的时候,那么速度就会对应的变慢,如果持续一段时间之后,那么就会出现锁表的现象了。

那么都有哪些操作会导致出现锁表呢?

(1) 插入查询的语句

insert into table values select xxxx from table2 

这种情况就会锁住table2.

(2) 更新并发操作

update table1 table2 set table1.name = ‘xxx’ where table1.id = table2.id 

这样也会导致锁表。

实例分析:

1)在不通过索引条件查询的时候,InnoDB确实使用的是表锁,而不是行锁。

在如表20-9所示的例子中,开始tab_no_index表没有索引:
mysql> create table tab_no_index(id int,name varchar(10)) engine=innodb;
Query OK, 0 rows affected (0.15 sec)
mysql> insert into tab_no_index values(1,'1'),(2,'2'),(3,'3'),(4,'4');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0
表20-9   InnoDB存储引擎的表在不使用索引时使用表锁例子
session_1
session_2
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_no_index where id = 1 ;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_no_index where id = 2 ;
+------+------+
| id   | name |
+------+------+
| 2    | 2    |
+------+------+
1 row in set (0.00 sec)
mysql> select * from tab_no_index where id = 1 for update;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)
 
 
mysql> select * from tab_no_index where id = 2 for update;
等待
在如表20 -9所示的例子中,看起来session_1只给一行加了排他锁,但session_2在请求其他行的排他锁时,却出现了锁等待!原因就是在没有索引的情况下,InnoDB只能使用表锁。当我们给其增加一个索引后,InnoDB就只锁定了符合条件的行,如表20-10所示。
创建tab_with_index表,id字段有普通索引:
mysql> create table tab_with_index(id int,name varchar(10)) engine=innodb;
Query OK, 0 rows affected (0.15 sec)
mysql> alter table tab_with_index add index id(id);
Query OK, 4 rows affected (0.24 sec)
Records: 4  Duplicates: 0  Warnings: 0
表20-10   InnoDB存储引擎的表在使用索引时使用行锁例子
session_1
session_2
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 1 ;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 2 ;
+------+------+
| id   | name |
+------+------+
| 2    | 2    |
+------+------+
1 row in set (0.00 sec)
mysql> select * from tab_with_index where id = 1 for update;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)
 
 
mysql> select * from tab_with_index where id = 2 for update;
+------+------+
| id   | name |
+------+------+
| 2    | 2    |
+------+------+
1 row in set (0.00 sec)
(2)由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。
在如表20-11所示的例子中,表tab_with_index的id字段有索引,name字段没有索引:
mysql> alter table tab_with_index drop index name;
Query OK, 4 rows affected (0.22 sec)
Records: 4  Duplicates: 0  Warnings: 0
mysql> insert into tab_with_index  values(1,'4');
Query OK, 1 row affected (0.00 sec)
mysql> select * from tab_with_index where id = 1;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
| 1    | 4    |
+------+------+
2 rows in set (0.00 sec)
表20-11 InnoDB存储引擎使用相同索引键的阻塞例子       
session_1
session_2
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 1 and name = '1' for update;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
+------+------+
1 row in set (0.00 sec)
 
 
虽然session_2访问的是和session_1不同的记录,但是因为使用了相同的索引,所以需要等待锁:
mysql> select * from tab_with_index where id = 1 and name = '4' for update;
等待
(3)当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
在如表20-12所示的例子中,表tab_with_index的id字段有主键索引,name字段有普通索引:
mysql> alter table tab_with_index add index name(name);
Query OK, 5 rows affected (0.23 sec)
Records: 5  Duplicates: 0  Warnings: 0
表20-12  InnoDB存储引擎的表使用不同索引的阻塞例子
session_1
session_2
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tab_with_index where id = 1 for update;
+------+------+
| id   | name |
+------+------+
| 1    | 1    |
| 1    | 4    |
+------+------+
2 rows in set (0.00 sec)
 
 
Session_2使用name的索引访问记录,因为记录没有被索引,所以可以获得锁:
mysql> select * from tab_with_index where name = '2' for update;
+------+------+
| id   | name |
+------+------+
| 2    | 2    |
+------+------+
1 row in set (0.00 sec)
 
由于访问的记录已经被session_1锁定,所以等待获得锁。:
mysql> select * from tab_with_index where name = '4' for update;
(4)即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。关于MySQL在什么情况下不使用索引的详细讨论,参见本章“索引问题”一节的介绍。
在下面的例子中,检索值的数据类型与索引字段不同,虽然MySQL能够进行数据类型转换,但却不会使用索引,从而导致InnoDB使用表锁。通过用explain检查两条SQL的执行计划,我们可以清楚地看到了这一点。
例子中tab_with_index表的name字段有索引,但是name字段是varchar类型的,如果where条件中不是和varchar类型进行比较,则会对name进行类型转换,而执行的全表扫描。
mysql> alter table tab_no_index add index name(name);
Query OK, 4 rows affected (8.06 sec)
Records: 4  Duplicates: 0  Warnings: 0
mysql> explain select * from tab_with_index where name = 1 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tab_with_index
         type: ALL
possible_keys: name
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4
        Extra: Using where
1 row in set (0.00 sec)
mysql> explain select * from tab_with_index where name = '1' \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tab_with_index
         type: ref
possible_keys: name
          key: name
      key_len: 23
          ref: const
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)
 
 

间隙锁(Next-Key锁)

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。
举例来说,假如emp表中只有101条记录,其empid的值分别是 1,2,...,100,101,下面的SQL:
Select * from  emp where empid > 100 for update;
是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。
InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,对于上面的例子,要是不使用间隙锁,如果其他事务插入了empid大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;另外一方面,是为了满足其恢复和复制的需要。有关其恢复和复制对锁机制的影响,以及不同隔离级别下InnoDB使用间隙锁的情况,在后续的章节中会做进一步介绍。
很显然,在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。
还要特别说明的是,InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁!
在如表20-13所示的例子中,假如emp表中只有101条记录,其empid的值分别是1,2,......,100,101。
表20-13                InnoDB存储引擎的间隙锁阻塞例子
session_1
session_2
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
当前session对不存在的记录加for update的锁:
mysql> select * from emp where empid = 102 for update;
Empty set (0.00 sec)
 
 
这时,如果其他session插入empid为102的记录(注意:这条记录并不存在),也会出现锁等待:
mysql>insert into emp(empid,...) values(102,...);
阻塞等待
Session_1 执行rollback:
mysql> rollback;
Query OK, 0 rows affected (13.04 sec)
 
 
由于其他session_1回退后释放了Next-Key锁,当前session可以获得锁并成功插入记录:
mysql>insert into emp(empid,...) values(102,...);
Query OK, 1 row affected (13.35 sec)

恢复和复制的需要,对InnoDB锁机制的影响

MySQL通过BINLOG录执行成功的INSERT、UPDATE、DELETE等更新数据的SQL语句,并由此实现MySQL数据库的恢复和主从复制(可以参见本书“管理篇”的介绍)。MySQL的恢复机制(复制其实就是在Slave Mysql不断做基于BINLOG的恢复)有以下特点。
l  一是MySQL的恢复是SQL语句级的,也就是重新执行BINLOG中的SQL语句。这与Oracle数据库不同,Oracle是基于数据库文件块的。
l  二是MySQL的Binlog是按照事务提交的先后顺序记录的,恢复也是按这个顺序进行的。这点也与Oralce不同,Oracle是按照系统更新号(System Change Number,SCN)来恢复数据的,每个事务开始时,Oracle都会分配一个全局唯一的SCN,SCN的顺序与事务开始的时间顺序是一致的。
从上面两点可知,MySQL的恢复机制要求:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读,这已经超过了ISO/ANSI SQL92“可重复读”隔离级别的要求,实际上是要求事务要串行化。这也是许多情况下,InnoDB要用到间隙锁的原因,比如在用范围条件更新记录时,无论在Read Commited或是Repeatable Read隔离级别下,InnoDB都要使用间隙锁,但这并不是隔离级别要求的,有关InnoDB在不同隔离级别下加锁的差异在下一小节还会介绍。
另外,对于“insert  into target_tab select * from source_tab where ...”和“create  table new_tab ...select ... From  source_tab where ...(CTAS)”这种SQL语句,用户并没有对source_tab做任何更新操作,但MySQL对这种SQL语句做了特别处理。先来看如表20-14的例子。
表20-14                   CTAS操作给原表加锁例子
session_1
session_2
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from target_tab;
Empty set (0.00 sec)
mysql> select * from source_tab where name = '1';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
|  4 | 1    |  1 |
|  5 | 1    |  1 |
|  6 | 1    |  1 |
|  7 | 1    |  1 |
|  8 | 1    |  1 |
+----+------+----+
5 rows in set (0.00 sec)
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from target_tab;
Empty set (0.00 sec)
mysql> select * from source_tab where name = '1';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
|  4 | 1    |  1 |
|  5 | 1    |  1 |
|  6 | 1    |  1 |
|  7 | 1    |  1 |
|  8 | 1    |  1 |
+----+------+----+
5 rows in set (0.00 sec)
mysql> insert into target_tab select d1,name from source_tab where name = '1';
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0
 
 
mysql> update source_tab set name = '1' where name = '8';
等待
commit;
 
 
返回结果
commit;
在上面的例子中,只是简单地读 source_tab表的数据,相当于执行一个普通的SELECT语句,用一致性读就可以了。ORACLE正是这么做的,它通过MVCC技术实现的多版本数据来实现一致性读,不需要给source_tab加任何锁。我们知道InnoDB也实现了多版本数据,对普通的SELECT一致性读,也不需要加任何锁;但这里InnoDB却给source_tab加了共享锁,并没有使用多版本数据一致性读技术!
MySQL为什么要这么做呢?其原因还是为了保证恢复和复制的正确性。因为不加锁的话,如果在上述语句执行过程中,其他事务对source_tab做了更新操作,就可能导致数据恢复的结果错误。为了演示这一点,我们再重复一下前面的例子,不同的是在session_1执行事务前,先将系统变量 innodb_locks_unsafe_for_binlog的值设置为“on”(其默认值为off),具体结果如表20-15所示。
表20-15                   CTAS操作不给原表加锁带来的安全问题例子
session_1
session_2
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql>set innodb_locks_unsafe_for_binlog='on'
Query OK, 0 rows affected (0.00 sec)
mysql> select * from target_tab;
Empty set (0.00 sec)
mysql> select * from source_tab where name = '1';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
|  4 | 1    |  1 |
|  5 | 1    |  1 |
|  6 | 1    |  1 |
|  7 | 1    |  1 |
|  8 | 1    |  1 |
+----+------+----+
5 rows in set (0.00 sec)
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from target_tab;
Empty set (0.00 sec)
mysql> select * from source_tab where name = '1';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
|  4 | 1    |  1 |
|  5 | 1    |  1 |
|  6 | 1    |  1 |
|  7 | 1    |  1 |
|  8 | 1    |  1 |
+----+------+----+
5 rows in set (0.00 sec)
mysql> insert into target_tab select d1,name from source_tab where name = '1';
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0
 
 
session_1未提交,可以对session_1的select的记录进行更新操作。
mysql> update source_tab set name = '8' where name = '1';
Query OK, 5 rows affected (0.00 sec)
Rows matched: 5  Changed: 5  Warnings: 0
mysql> select * from source_tab where name = '8';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
|  4 | 8    |  1 |
|  5 | 8    |  1 |
|  6 | 8    |  1 |
|  7 | 8    |  1 |
|  8 | 8    |  1 |
+----+------+----+
5 rows in set (0.00 sec)
 
更新操作先提交
mysql> commit;
Query OK, 0 rows affected (0.05 sec)
插入操作后提交
mysql> commit;
Query OK, 0 rows affected (0.07 sec)
 
此时查看数据,target_tab中可以插入source_tab更新前的结果,这符合应用逻辑:
mysql> select * from source_tab where name = '8';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
|  4 | 8    |  1 |
|  5 | 8    |  1 |
|  6 | 8    |  1 |
|  7 | 8    |  1 |
|  8 | 8    |  1 |
+----+------+----+
5 rows in set (0.00 sec)
mysql> select * from target_tab;
+------+------+
| id   | name |
+------+------+
| 4    | 1.00 |
| 5    | 1.00 |
| 6    | 1.00 |
| 7    | 1.00 |
| 8    | 1.00 |
+------+------+
5 rows in set (0.00 sec)
mysql> select * from tt1 where name = '1';
Empty set (0.00 sec)
mysql> select * from source_tab where name = '8';
+----+------+----+
| d1 | name | d2 |
+----+------+----+
|  4 | 8    |  1 |
|  5 | 8    |  1 |
|  6 | 8    |  1 |
|  7 | 8    |  1 |
|  8 | 8    |  1 |
+----+------+----+
5 rows in set (0.00 sec)
mysql> select * from target_tab;
+------+------+
| id   | name |
+------+------+
| 4    | 1.00 |
| 5    | 1.00 |
| 6    | 1.00 |
| 7    | 1.00 |
| 8    | 1.00 |
+------+------+
5 rows in set (0.00 sec)
从上可见,设置系统变量innodb_locks_unsafe_for_binlog的值为“on”后,InnoDB不再对source_tab加锁,结果也符合应用逻辑,但是如果分析BINLOG的内容:
......
SET TIMESTAMP=1169175130;
BEGIN;
# at 274
#070119 10:51:57 server id 1  end_log_pos 105   Query   thread_id=1     exec_time=0     error_code=0
SET TIMESTAMP=1169175117;
update source_tab set name = '8' where name = '1';
# at 379
#070119 10:52:10 server id 1  end_log_pos 406   Xid = 5
COMMIT;
# at 406
#070119 10:52:14 server id 1  end_log_pos 474   Query   thread_id=2     exec_time=0     error_code=0
SET TIMESTAMP=1169175134;
BEGIN;
# at 474
#070119 10:51:29 server id 1  end_log_pos 119   Query   thread_id=2     exec_time=0     error_code=0
SET TIMESTAMP=1169175089;
insert into target_tab select d1,name from source_tab where name = '1';
# at 593
#070119 10:52:14 server id 1  end_log_pos 620   Xid = 7
COMMIT;
......
    可以发现,在BINLOG中,更新操作的位置在INSERT...SELECT之前,如果使用这个BINLOG进行数据库恢复,恢复的结果与实际的应用逻辑不符;如果进行复制,就会导致主从数据库不一致!
通过上面的例子,我们就不难理解为什么MySQL在处理“Insert  into target_tab select * from source_tab where ...”和“create  table new_tab ...select ... From  source_tab where ...”时要给source_tab加锁,而不是使用对并发影响最小的多版本数据来实现一致性读。还要特别说明的是,如果上述语句的SELECT是范围条件,InnoDB还会给源表加间隙锁(Next-Lock)。
因此,INSERT...SELECT...和 CREATE TABLE...SELECT...语句,可能会阻止对源表的并发更新,造成对源表锁的等待。如果查询比较复杂的话,会造成严重的性能问题,我们在应用中应尽量避免使用。实际上,MySQL将这种SQL叫作不确定(non-deterministic)的SQL,不推荐使用。
如果应用中一定要用这种SQL来实现业务逻辑,又不希望对源表的并发更新产生影响,可以采取以下两种措施:
¡  一是采取上面示例中的做法,将innodb_locks_unsafe_for_binlog的值设置为“on”,强制MySQL使用多版本数据一致性读。但付出的代价是可能无法用binlog正确地恢复或复制数据,因此,不推荐使用这种方式。
¡  二是通过使用“select * from source_tab ... Into outfile”和“load data infile ...”语句组合来间接实现,采用这种方式MySQL不会给source_tab加锁。

 

怎么样降低锁表的情况?

MyISAM表锁的优化:

  • 缩短锁定的时间:这么说吧,实际上最简单的就是加索引,让你的索引利用最大化,
  • 合理利用读写优先级:写优先,读其次。

Innodb行锁的优化

  • 加索引,让查询走索引
  • 学会控制事务
  • 隔离级别不要随便设置,根据不同情况不同选择就可以了

参考:

https://www.cnblogs.com/sessionbest/articles/8689082.html

posted on 2024-10-07 16:53  myf008  阅读(16)  评论(0编辑  收藏  举报

导航