您的位置:首页 > 数据库 > MySQL

MySQL 温故而知新--Innodb存储引擎中的锁

2017-07-15 18:38 411 查看
近期碰到非常多锁问题。所以攻克了后,细致再去阅读了关于锁的书籍,整理例如以下:

1,锁的种类

Innodb存储引擎实现了例如以下2种标准的行级锁:

?

共享锁(S lock),同意事务读取一行数据。

? 排它锁(X lock)。同意事务删除或者更新一行数据。

当一个事务获取了行r的共享锁。那么另外一个事务也能够马上获取行r的共享锁,由于读取并未改变行r的数据。这样的情况就是锁兼容。

可是假设有事务想获得行r的排它锁,则它必须等待事务释放行r上的共享锁—这样的情况就是锁不兼容。二者兼容性例如以下表格所看到的:

排它锁和共享锁的兼容性

X 排它锁

S 共享锁

X 排它锁

冲突

冲突

S 共享锁

冲突

兼容

2。锁的扩展

Innodb存储引擎支持多粒度锁定,这样的锁定同意在行级别上的锁和表级别上的锁同一时候存在。为了支持在不同粒度上进行加锁操作。InnoDB存储引擎支持一种额外的锁方式,就是意向锁。

意向锁是表级别的锁。其设计目的主要是为了在一个事务中揭示下一行将被请求的锁的类型。它也分为两种:

? 意向共享锁(IS Lock),事务想要获得一个表中某几行的共享锁。

? 意向排它锁(IX Lock),事务想要获得一个表中某几行的排它锁。

由于InnoDB支持的是行级别锁,所以意向锁事实上不大会堵塞除了全表scan下面的不论什么请求。共享锁、排它锁、意向共享锁、意向排它锁相互之前都是有兼容/相互排斥关系的。能够用一个兼容性矩阵表示(y表示兼容,n表示不兼容)。例如以下所看到的:

X 排它锁

S 共享锁

IX 意向排它锁

IS 意向共享锁

X 排它锁

冲突

冲突

冲突

冲突

S 共享锁

冲突

兼容

冲突

兼容

IX 意向排它锁

冲突

冲突

兼容

兼容

IS 意向共享锁

冲突

兼容

兼容

兼容

解析:X和S的相互兼容关系step1描写叙述过了,IX和IS的相互关系全部是兼容,这也非常好理解,由于它们都仅仅是“有意”。还处于YY阶段,没有真干,所以是能够兼容的;

剩下的就是X和IX。X和IS, S和IX, S和IS的关系了,我们能够由X和S的关系推导出这四组关系。

简单的说:X和IX的=X和X的关系。为什么呢?由于事务在获取IX锁后。接下来就有权利获取X锁。假设X和IX兼容的话,就会出现两个事务都获取了X锁的情况。这与我们已知的X与X相互排斥是矛盾的,所以X与IX仅仅能是相互排斥关系。其余的三组关系同理。可用同样的方式推导出来。

3。模拟锁场景

在InnoDB Plugin之前。我们仅仅能通过SHOW FULL PROCESSLIS和SHOW ENGINE INNODB STATUS来查看当前的数据库请求,然后再推断事务中锁的情况。新版本号的InnoDB Plugin中,在information_schema库中加入了3张表,INNODB_LOCKS、INNODB_TRX、INNODB_LOCK_WAITS。通过这3个表,能够更简单的监控当前的事务而且分析可能存在的锁问题。

假设数据库正常运行,这3个表都是空的,没有不论什么记录。

3.1,开启事务t1、t2,模拟锁

开启2个session窗体,而且开启2个事务t1和t2。

在第一个窗体开启事务t1运行一个锁定操作,例如以下t1事务窗体界面:

mysql> set autocommit =0;

Query OK, 0 rows affected (0.00 sec)

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

開始运行锁定操作

mysql> select * from test.t1 where a<5 for update;

+---+----+----+

| a | b | c |

+---+----+----+

| 1 | c2 | c2 |

| 2 | a | |

| 3 | r5 | r3 |

| 4 | r4 | r5 |

+---+----+----+

4 rows in set (0.00 sec)

mysql>

这个时候,事务t1已经锁定了表t1的全部a<5的数据行,然后去第二个窗体开启第二个事务t2,例如以下,会看到update语句一直在等待事务t1释放锁资源,过了几秒后,会有报错信息,例如以下t2事务窗体界面:

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update test.t1 set b='t2' where a=1;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql>

3.2,通过3个系统表来查看锁信息

l 1,INNODB_TRX表

先看下表的重要字段以及记录的信息

a) trx_id:innodb存储引擎内部事务唯一的事务id。

b) trx_state:当前事务的状态。

c) trx_started:事务開始的时间。

d) trx_requested_lock_id:等待事务的锁id。如trx_state的状态为LOCK WAIT。那么该值代表当前事务之前占用锁资源的id,假设trx_state不是LOCK WAIT的话,这个值为null。

e) trx_wait_started:事务等待開始的时间。

f) trx_weight:事务的权重,反映了一个事务改动和锁住的行数。

在innodb的存储引擎中。当发生死锁须要回滚时。innodb存储引擎会选择该值最小的事务进行回滚。

g) trx_mysql_thread_id:正在运行的mysql中的线程id。show full processlist显示的记录中的thread_id。

h) trx_query:事务运行的sql语句,在实际中发现,有时会显示为null值,当为null的时候,就是t2事务中等待锁超时直接报错(ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction)后。trx_query就显示为null值

……

由于前面模拟了事务锁场景。开启了t1和t2事务,如今去查看这个表信息,会有2条记录例如以下:

mysql> select * from INNODB_TRX\G

*************************** 1. row ***************************

trx_id: 3015646

trx_state: LOCK WAIT

trx_started: 2014-10-07 18:29:39

trx_requested_lock_id: 3015646:797:3:2

trx_wait_started: 2014-10-07 18:29:39

trx_weight: 2

trx_mysql_thread_id: 18

trx_query: update test.t1 set b='t2' where a=1

trx_operation_state: starting index read

trx_tables_in_use: 1

trx_tables_locked: 1

trx_lock_structs: 2

trx_lock_memory_bytes: 376

trx_rows_locked: 1

trx_rows_modified: 0

trx_concurrency_tickets: 0

trx_isolation_level: READ COMMITTED

trx_unique_checks: 1

trx_foreign_key_checks: 1

trx_last_foreign_key_error: NULL

trx_adaptive_hash_latched: 0

trx_adaptive_hash_timeout: 10000

trx_is_read_only: 0

trx_autocommit_non_locking: 0

*************************** 2. row ***************************

trx_id: 3015645

trx_state: RUNNING

trx_started: 2014-10-07 18:29:15

trx_requested_lock_id: NULL

trx_wait_started: NULL

trx_weight: 2

trx_mysql_thread_id: 17

trx_query: NULL

trx_operation_state: NULL

trx_tables_in_use: 0

trx_tables_locked: 0

trx_lock_structs: 2

trx_lock_memory_bytes: 376

trx_rows_locked: 4

trx_rows_modified: 0

trx_concurrency_tickets: 0

trx_isolation_level: READ COMMITTED

trx_unique_checks: 1

trx_foreign_key_checks: 1

trx_last_foreign_key_error: NULL

trx_adaptive_hash_latched: 0

trx_adaptive_hash_timeout: 10000

trx_is_read_only: 0

trx_autocommit_non_locking: 0

2 rows in set (0.00 sec)

mysql>

这里仅仅是记录了一些当前正在运行的事务,比方事务t2正在运行trx_query: update test.t1 set b='t2' where a=1的sql语句,t1先运行,所以是trx_state: RUNNING先申请的资源一直在运行,而t2后run的所以是trx_state: LOCK WAIT一直在等待t1运行完后释放资源。 可是并不能细致推断锁的一些具体情况,我们须要再去看INNODB_LOCKS表数据。

l 2。INNODB_LOCKS表

a) lock_id:锁的id以及被锁住的空间id编号、页数量、行数量

b) lock_trx_id:锁的事务id。

c) lock_mode:锁的模式。

d) lock_type:锁的类型,表锁还是行锁

e) lock_table:要加锁的表。

f) lock_index:锁的索引。

g) lock_space:innodb存储引擎表空间的id号码

h) lock_page:被锁住的页的数量。假设是表锁,则为null值。

i) lock_rec:被锁住的行的数量,假设表锁,则为null值。

j) lock_data:被锁住的行的主键值,假设表锁,则为null值。

mysql> select * from INNODB_LOCKS\G

*************************** 1. row ***************************

lock_id: 3015646:797:3:2

lock_trx_id: 3015646

lock_mode: X

lock_type: RECORD

lock_table: `test`.`t1`

lock_index: PRIMARY

lock_space: 797

lock_page: 3

lock_rec: 2

lock_data: 1

*************************** 2. row ***************************

lock_id: 3015645:797:3:2

lock_trx_id: 3015645

lock_mode: X

lock_type: RECORD

lock_table: `test`.`t1`

lock_index: PRIMARY

lock_space: 797

lock_page: 3

lock_rec: 2

lock_data: 1

2 rows in set (0.00 sec)

mysql>

这里我们能够看到当前的锁信息了。2个事务都锁定了。看同样的数据lock_space: 797、lock_page: 3、lock_rec: 2能够得出事务t1和事务t2訪问了同样的innodb数据块,再通过lock_data字段信息lock_data: 1,看到锁定的数据行都是主键为1的数据记录。可见2个事务t1和t2都申请了同样的资源,因此会被锁住,事务在等待。

通过lock_mode: X值也能够看出事务t1和t2申请的都是排它锁。

PS:当运行范围查询更新的时候,这个lock_data的值并不是是全然准确。当我们运行一个范围更新时。lock_data仅仅返回最先找到的第一行的主键值id。另外假设当前资源被锁住了,与此同一时候由于锁住的页由于InnoDB存储引擎缓冲池的容量。而导致替换缓冲池页面,再去查看INNODB_LOCKS表时,这个lock_data会显示未NULL值。意味着InnoDB存储引擎不会从磁盘进行再一次查找。

l 3。INNODB_LOCK_WAITS表

当事务量比較少。我们能够直观的查看。当事务量非常大,锁等待也时常发生的情况下。这个时候能够通过INNODB_LOCK_WAITS表来更加直观的反映出当前的锁等待情况:

INNODB_LOCK_WAITS表主要字段例如以下:

1) requesting_trx_id:申请锁资源的事务id。

2) requested_lock_id:申请的锁的id。

3) blocking_trx_id:堵塞的事务id。

4) blocking_lock_id:堵塞的锁的id。

去看下当前锁等待信息,例如以下所看到的:

mysql> select * from INNODB_LOCK_WAITS\G

*************************** 1. row ***************************

requesting_trx_id: 3015646

requested_lock_id: 3015646:797:3:2

blocking_trx_id: 3015645

blocking_lock_id: 3015645:797:3:2

1 row in set (0.00 sec)

mysql>

这里我们能够看到事务t1(3015646)申请了锁资源,而事务t2(3015645)则堵塞了事务t1的申请。我们管理其它表。得到更直观的具体信息,例如以下所看到的:

mysql> SELECT it2.`trx_id` AS waiting_trx_id, it2.`trx_mysql_thread_id` AS waiting_thread,it2.`trx_query` AS waiting_query, it1.`trx_id` AS blocking_trx_id, it1.`trx_mysql_thread_id` blocking_thread, it1.`trx_query` blocking_query FROM `information_schema`.`INNODB_LOCK_WAITS` ilw, `information_schema`.`INNODB_TRX` it1,`information_schema`.`INNODB_TRX` it2 WHERE it1.`trx_id`=ilw.`blocking_trx_id` AND it2.`trx_id`=ilw.`requesting_trx_id`;

+----------------+----------------+-------------------------------------+-----------------+-----------------+----------------+

| waiting_trx_id | waiting_thread | waiting_query | blocking_trx_id | blocking_thread | blocking_query |

+----------------+----------------+-------------------------------------+-----------------+-----------------+----------------+

| 3015647 | 18 | update test.t1 set b='t2' where a>2 | 3015645 | 17 | NULL |

+----------------+----------------+-------------------------------------+-----------------+-----------------+----------------+

1 row in set (0.00 sec)

mysql>

4。一致性的非锁定读操作

4.1。CNR原理解析

一致性的非锁定行读(consistent nonlocking read,简称CNR)是指InnoDB存储引擎通过行多版本号控制(multi versioning)的方式来读取当前运行时间数据库中运行的数据。

假设读取的行正在运行delete、update操作。这时读取操作不会因此而会等待行上锁的释放,相反。InnoDB存储引擎会去读取行的一个快照数据,例如以下图所看到的:


非锁定读。是由于不须要等待訪问的行上X锁的释放,快照数据是指该行之前版本号的数据。该实现是通过Undo段来实现,而Undo用来在事务中回滚数据。因此快照本身是没有额外的开销。此外读取快照是不须要上锁的。由于没有必要对历史的数据进行改动。

非锁定读大大提高了数据读取的并发性,在InnoDB存储引擎默认设置下,这是默认的读取方式。既读取不会占用和等待表上的锁。可是不同事务隔离级别下。读取的方式不同,不是每个事务隔离级别下的都是一致性读。同样,即使都是使用一致性读,可是对于快照数据的定义也不同样。

快照数据事实上就是当前数据之前的历史版本号。可能有多个版本号。如上图所看到的。一个行可能不止有一个快照数据。我们称这样的技术为行多版本号技术。

由此带来的并发控制。称之为多版本号并发控制(Multi Version Concurrency Control,MVCC)。

在Read Committed和Repeatable Read模式下。innodb存储引擎使用默认的非锁定一致读。

在Read Committed隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在Repeatable Read隔离级别下,对于快照数据,非一致性读总是读取事务開始时的行数据版本号。

4.2,CNR实例开启2个Session A和B。

Session A:

mysql> begin;

Query OK, 0 rows affected (0.01 sec)

mysql> select * from t1 where a=1;

+---+----+----+

| a | b | c |

+---+----+----+

| 1 | c2 | c2 |

+---+----+----+

1 row in set (0.00 sec)

mysql>

Session A中事务已经開始,读取了a=1的数据,可是还没有结束事务。这时我们再开启一个Session B,以此模拟并发的情况,然后对Session B做例如以下操作:

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set a=111 where a=1;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0

mysql>

Session 中将a=1的行改动为a=111,可是事务同样没有提交。这样a=1的行事实上加了一个X锁。

这时假设再在Session A中读取a=1的数据。根据innodb存储引擎的特性。在Read Committed和Repeatable Read事务隔离级别下。会使用非锁定的一致性读。回到Session A。节着上次未提交的事务,运行select * from t1 where a=1;的操作。显示的数据应该都是原来的数据:

mysql> select * from t1 where a=1;

+---+----+----+

| a | b | c |

+---+----+----+

| 1 | c2 | c2 |

+---+----+----+

1 row in set (0.00 sec)

mysql>

由于当前a=1的数据被改动了1次,所以仅仅有一个版本号的数据,接着我们在Session B中commit上次的事务。

如:

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

mysql>

Session B提交事务后。这时再在Session A中运行select * from t1 where a=1;的sql语句,在READ-COMMITTED和REPEATABLE-READ事务隔离级别下。得到的结果就会不一样,对于READ-COMMITTED模事务隔离级别,它总是读取行的最新版本号。假设行被锁定了,则读取该行的最新一个快照(fresh snapshot)。

因此在这个样例中。由于Session B已经commit了事务,所以在READ-COMMITTED事务隔离级别下会得到例如以下结果。查询a=1就是为null记录,由于a=1的已经被commit成了a=111。可是假设查询a=111的记录则会被查到。例如以下所看到的:

mysql> show variables like 'tx_isolation';

+---------------+----------------+

| Variable_name | Value |

+---------------+----------------+

| tx_isolation | READ-COMMITTED |

+---------------+----------------+

1 row in set (0.00 sec)

mysql> select * from t1 where a=1;

Empty set (0.00 sec)

mysql> select * from t1 where a=111;

+-----+----+----+

| a | b | c |

+-----+----+----+

| 111 | c2 | c2 |

+-----+----+----+

1 row in set (0.01 sec)

mysql>

可是假设在REPEATABLE-READ事务隔离级别下,总是读取事务開始时的数据。所以得到的结果截然不同。例如以下所看到的:

mysql> show variables like 'tx_isolation';

+---------------+-----------------+

| Variable_name | Value |

+---------------+-----------------+

| tx_isolation | REPEATABLE-READ |

+---------------+-----------------+

1 row in set (0.00 sec)

mysql> select * from t1 where a=1;

+---+----+----+

| a | b | c |

+---+----+----+

| 1 | c0 | c2 |

+---+----+----+

1 row in set (0.00 sec)

mysql> select * from t1 where a=111;

Empty set (0.00 sec)

mysql>

对于READ-COMMITTED的事务隔离级别而言,从数据库理论的角度来看。事实上违反了事务ACID的I的特性。既是隔离性,整理成时序表,例如以下图所看到的。

Time

Session A

Session B

| time 1

Begin;

Select * from t1 where a=1;有记录

| time 2

Begin;

Update t1 set a=111 where a=1;

| time 3

Select * from t1 where a=1;有记录

| time 4

Commit;

| time 5

Select * from t1 where a=1; 无记录

V time 6

Commit;

假设依照ACID原理中的I原理隔离性。在整个会话中Session A中,Select * from t1 where a=1;应该查询出来的数据保持一直,可是在time 5那一刻 Session A未结束的时候,查询出来的结果已经变化了和time 1、time 3已经不一致了,不满足ACID的隔离性。

5,SELECT ... FOR UPDATE && SELECT ... LOCK IN SHARE MODE

默认情况下,innodb存储引擎的select操作使用一致性非锁定读。可是在某些情况下。须要对读操作进行加锁。

Innodb存储引擎对select语句支持2种加入锁操作;

? SELECT ... FOR UPDATE 对于读取的行记录加一个X排它锁,其它事务假设要对这些行进行dml或者select操作都会被堵塞。

? SELECT ... LOCK IN SHARE MODE 对于读取的行记录加入一个S共享锁。其它事务能够向被锁定的行加S锁。可是不同意加入X锁,否则会被堵塞。

对于一致性 非锁定读,即使读取的行数已经被SELECT ... FOR UPDATE了,可是也是能够进行读取的。

PS:… FOR UPDATE以及LOCK IN SHARE MODE必须在一个事务中,假设事务commit了,锁也就释放了,所以在使用的时候务必加上begin;start transaction或者set autocommit=0;

样例例如以下:

会话A:开启事务,运行LOCK IN SHARE MODE;锁定

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM t1 WHERE a=1 LOCK IN SHARE MODE;

+---+----+----+

| a | b | c |

+---+----+----+

| 1 | c0 | c2 |

+---+----+----+

1 row in set (0.00 sec)

mysql>

同一时候在另外一个窗体开启会话B,运行dml操作

mysql> begin;

Query OK, 0 rows affected (0.00 sec)

mysql> update t1 set a=111 where a=1;

…这里会卡住,没有信息。

再开启一个会话C。查询INNODB_LOCKS、INNODB_TRX、INNODB_LOCK_WAITS表,就会看到锁的具体信息:

mysql> select * from INNODB_LOCKS;

+-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+

| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |

+-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+

| 3015708:797:3:2 | 3015708 | X | RECORD | `test`.`t1` | PRIMARY | 797 | 3 | 2 | 1 |

| 3015706:797:3:2 | 3015706 | S | RECORD | `test`.`t1` | PRIMARY | 797 | 3 | 2 | 1 |

+-----------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+

2 rows in set (0.00 sec)

mysql>

mysql> select * from INNODB_LOCK_WAITS;

+-------------------+-------------------+-----------------+------------------+

| requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id |

+-------------------+-------------------+-----------------+------------------+

| 3015708 | 3015708:797:3:2 | 3015706 | 3015706:797:3:2 |

+-------------------+-------------------+-----------------+------------------+

1 row in set (0.00 sec)

mysql>

mysql> SELECT it2.`trx_id` AS waiting_trx_id, it2.`trx_state` AS waiting_trx_status,it2.`trx_mysql_thread_id` AS waiting_thread,it2.`trx_query` AS waiting_query,

-> it1.`trx_id` AS blocking_trx_id, it1.`trx_mysql_thread_id` blocking_thread, it1.`trx_query` blocking_query

-> FROM `information_schema`.`INNODB_LOCK_WAITS` ilw, `information_schema`.`INNODB_TRX` it1,`information_schema`.`INNODB_TRX` it2

-> WHERE it1.`trx_id`=ilw.`blocking_trx_id` AND it2.`trx_id`=ilw.`requesting_trx_id`;

+----------------+--------------------+----------------+-------------------------------+-----------------+-----------------+----------------+

| waiting_trx_id | waiting_trx_status | waiting_thread | waiting_query | blocking_trx_id | blocking_thread | blocking_query |

+----------------+--------------------+----------------+-------------------------------+-----------------+-----------------+----------------+

| 3015708 | LOCK WAIT | 18 | update t1 set a=111 where a=1 | 3015706 | 21 | NULL |

+----------------+--------------------+----------------+-------------------------------+-----------------+-----------------+----------------+

1 row in set (0.00 sec)

mysql>

会话A开启的事务1(事务id:3015706)运行了SELECT * FROM t1 WHERE a=1 LOCK IN SHARE MODE;

语句已经在a=1的行上加了S锁。所以会话B开启的事务2(事务id:23015708)运行的update t1 set a=111 where a=1;sql语句往a=1的行上加X锁。就被会话A的事务1堵塞了。所以事务2的状态值就是LOCK WAIT,一直在等待。直到事务等待超时。报错例如以下:

mysql> update t1 set a=111 where a=1;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql>

此时,会话B中的事务2就终止了update t1 set a=111 where a=1;的dml请求操作。

6,自增长和锁

自增长在数据库中是非经常见的一种属性,在Innodb的存储引擎的内存结构中。对每个含有自增长值的表都有一个自增长计数器(auto-increment counter)。当对有自增长字段的表进行insert时候,这个计数器会被初始化。运行例如以下的sql语句来得到计数器的值。

SELECT MAX(auto_inc_col) FROM tablename FOR UPDATE;

插入操作会根据这个自增长的计数器值+1赋予自增长列,这个实现方式称为AUTO-INC Locking。这样的锁事实上是一种特殊的表锁机制,为了提高插入的性能,锁不是在一个事务完毕后才释放。而是在完毕对自增长值插入的sql语句后马上释放。

从mysql 5.1.22版本号開始。提供了一些轻量级相互排斥的自增长实现机制,这样的机制大大提高自增长值插入的性能。

还提供了一个參数innodb_autoinc_lock_mode,默认值为1.

自增长的分类:

在mysql的innodb表中,自增长列必须是索引,而且必须为索引的第一列,假设是第二个列会报错例如以下所看到的:

mysql> CREATE TABLE t(a INT AUTO_INCREMENT ,b VARCHAR(10),KEY (b,a));

ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key

mysql>

mysql> CREATE TABLE t(a INT AUTO_INCREMENT ,b VARCHAR(10),PRIMARY KEY (a),KEY (b,a));

Query OK, 0 rows affected (0.01 sec)

mysql>

而在myisam表中。则没有这样的限制,例如以下所看到的:

mysql> CREATE TABLE t_myisam(a INT AUTO_INCREMENT ,b VARCHAR(10),KEY (b,a))engine=myisam;

Query OK, 0 rows affected (0.01 sec)

mysql>

7,MySQL外键和锁

在innodb存储引擎中,对于一个外键列,假设没有显式的针对这个列加入索引Innodb存储引擎会自己主动的对其加入一个索引,这样能够避免表锁,这点比oracle做的较好一些,oracle须要自己手动加入外键锁。


----------------------------------------------------------------------------------------------------------------

<版权全部,文章同意转载,但必须以链接方式注明源地址,否则追究法律责任!>
原博客地址: http://blog.itpub.net/26230597/viewspace-1315111/
原作者:黄杉 (mchdba)


----------------------------------------------------------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: