1 锁机制
当前MySQL已经支持 ISAM, MyISAM, MEMORY (HEAP) 类型表的表级锁了,BDB 表支持页级锁,InnoDB 表支持行级锁。很多时候,可以通过经验来猜测什么样的锁对应用程序更合适,不过通常很难说一个锁比别的更好,这全都要依据应用程序来决定,不同的地方可能需要不同的锁。
想要决定是否需要采用一个支持行级锁的存储引擎,就要看看应用程序都要做什么,其中的查询、更新语句是怎么用的。例如,很多的web应用程序大量的做查询,很少删除,主要是基于索引的更新,只往特定的表中插入记录。采用基本的MySQL MyISAM 表就很合适了。
MySQL中对表级锁的存储引擎来说是释放死锁的。避免死锁可以这样做到:在任何查询之前先请求锁,并且按照请求的顺序锁表。
MySQL中用于 WRITE(写) 的表锁的实现机制如下:
如果表没有加锁,那么就加一个写锁。否则的话,将请求放到写锁队列中。 MySQL中用于 READ(读) 的表锁的实现机制如下:
如果表没有加写锁,那么就加一个读锁。否则的话,将请求放到读锁队列中。 当锁释放后,写锁队列中的线程可以用这个锁资源,然后才轮到读锁队列中的线程。
这就是说,如果表里有很多更新 *** 作的话,那么 SELECT 必须等到所有的更新都完成了之后才能开始。
从 MySQL 32333 开始,可以通过状态变量 Table_locks_waited 和 Table_locks_immediate 来分析系统中的锁表争夺情况:
mysql> SHOW STATUS LIKE 'Table%';
+-----------------------+---------+
| Variable_name | Value |
+-----------------------+---------+
| Table_locks_immediate | 1151552 |
| Table_locks_waited | 15324 |
+-----------------------+---------+
兄弟,锁的作用,就是把权限归为私有,其它人用不了。
你自已把表锁了,自已当然还能用。你起另外一个客户端试试。
而且写锁和读锁,是有区别的。
lock table abc read;
lock table abc write;
方法1:用mysql命令锁住表
public void test() {
String sql = "lock tables aa1 write";
// 或String sql = "lock tables aa1 read";
// 如果想锁多个表 lock tables aa1 read ,aa2 write ,
String sql1 = "select from aa1 ";
String sql2 = "unlock tables";
try {
thispstmt = connprepareStatement(sql);
thispstmt1 = connprepareStatement(sql1);
thispstmt2 = connprepareStatement(sql2);
pstmtexecuteQuery();
pstmt1executeQuery();
pstmt2executeQuery();
} catch (Exception e) {
Systemoutprintln("异常" + egetMessage());
}
}
对于read lock 和 write lock官方说明:
1如果一个线程获得一个表的READ锁定,该线程(和所有其它线程)只能从该表中读取。
如果一个线程获得一个表的WRITE锁定,只有保持锁定的线程可以对表进行写入。
其它的线程被阻止,直到锁定被释放时为止。
2当您使用LOCK TABLES时,您必须锁定您打算在查询中使用的所有的表。
虽然使用LOCKTABLES语句获得的锁定仍然有效,但是您不能访问没有被此语句锁定的任何的表。
同时,您不能在一次查询中多次使用一个已锁定的表——使用别名代替,
在此情况下,您必须分别获得对每个别名的锁定。
对与read lock 和 write lock个人说明:
1read lock 和 write lock 是线程级(表级别)
2在同一个会话中加了read lock锁 只能对这个表进行读 *** 作对这个表以外的任何表都无法进行增、删、改、查的 *** 作
但是在不同会话中,只能对加了read lock的表进行读 *** 作但可以对read lock以外的表进行增、删、改、查的 *** 作
3在同一个会话中加了write lock锁只能对这个表进行读、写 *** 作对这个表以外的任何表都无法进行增、删、改、查的 *** 作
但是在不同会话中,无法对加了write lock的表进行读、写 *** 作但可以对write lock以外的表进行增、删、改、查的 *** 作
4如果表中使用了别名(SELECT FROM aa1 AS byname_table)
在对aa1加锁时,必须把别名加上去(lock tables aa1 as byname_table read)
在同一个会话中必须使用别名进行查询
在不同的会话中可以不需要使用别名进行查询
5在多个会话中可以对同一个表进行lock read *** 作但不能在多个会话中对同一个表进行lock write *** 作(这些锁将等待已锁的表释放自身的线程锁)
如果多个会话对同一个表进行lock read *** 作那么在这些会话中,也只能对以锁的表进行读 *** 作
6如果要你锁住了一个表,需要嵌套查询你必须使用别名,并且,要锁定别名
例如lock table aa1 read ,aa1 as byname_table read;
select from aa1 where id in (select from aa1 as xx where id=2);
7解锁必须用unlock tables;
另:
在JAVA程序中,要想解锁,需要调用 unlock tables来解锁
如果没有调用unlock tables
关闭connection 、程序结束 、调用GC 都能解锁
方法2:用记录锁锁表
public void test() {
String sql = "select from aa1 for update";
// select from aa1 lock in share mode;
try {
connsetAutoCommit(false);
thispstmt = connprepareStatement(sql);
pstmtexecuteQuery();
} catch (Exception e) {
Systemoutprintln("异常" + egetMessage());
}
}
1for update 与 lock in share mode 属于行级锁和页级锁
2for update 排它锁,lock in share mode 共享锁
3对于记录锁必须开启事务
4行级锁定事实上是索引记录的锁定只要是用索引扫描的行(或没索引全表扫描的行),都将被锁住
5在不同的隔离级别下还会使用next-key locking算法即所扫描的行之间的“间隙”也会也锁住(在Repeatable read和Serializable隔离级别下有间隙锁)
6在mysql中共享锁的含义是:在被共享锁锁住的行,即使内容被修改且并没有提交在另一个会话中依然看到最新修改的信息
在同一会话中加上了共享锁可以对这个表以及这个表以外的所有表进行增、删、改、查的 *** 作
在不同的会话中可以查到共享锁锁住行的最新消息但是在Read Uncommitted隔离级别下不能对锁住的表进行删,
改 *** 作(需要等待锁释放才能 *** 作)
在Read Committed隔离级别下不能对锁住的表进行删,改 *** 作(需要等待锁释放才能 *** 作)
在Repeatable read隔离级别下不能对锁住行进行增、删、改 *** 作(需要等待锁释放才能 *** 作)
在Serializable隔离级别下不能对锁住行进行增、删、改 *** 作 (需要等待锁释放才能 *** 作)
7在mysql中排他锁的含义是:在被排它锁锁住的行,内容修改并没提交,在另一个会话中不会看到最新修改的信息。
在不同的会话中可以查到共享锁锁住行的最新消息但是Read Uncommitted隔离级别下不能对锁住的表进行删,
改 *** 作(需要等待锁释放才能 *** 作)
在Read Committed隔离级别下不能对锁住的表进行删,改 *** 作(需要等待锁释放才能 *** 作)
在Repeatable read隔离级别下不能对锁住行进行增、删、改 *** 作(需要等待锁释放才能 *** 作)
在Serializable隔离级别下不能对锁住行进行增、删、改 *** 作 (需要等待锁释放才能 *** 作)
8在同一个会话中的可以叠加多个共享锁和排他锁在多个会话中,需要等待锁的释放
9SQL中的update 与 for update是一样的原理
10等待超时的参数设置:innodb_lock_wait_timeout=50 (单位秒)
11任何可以触发事务提交的命令,都可以关闭共享锁和排它锁
当你开始执行一个 ALTER ,而你遇到了可怕的“元数据锁定等待”,我敢肯定你一定遇见过。我最近遇到了一个案例,其中被更改的表要执行一个很小范围的更新(<100行)。ALTER 在负载测试期间一直等待了几个小时。在停止负载测试后,ALTER 按预期在不到一秒的时间内就完成了。那么这里发生了什么?
检查外键
每当有奇数次锁定时,我的第一直觉就是检查外键。当然这张表有一些外键引用了一个更繁忙的表。但是这种行为似乎仍然很奇怪。对表运行 ALTER 时,会针对子表请求一个 SHARED_UPGRADEABLE 元数据锁。还有针对父级的 SHARED_READ_ONLY 元数据锁。
我们来看看如何根据文档获取元数据锁定[1]:
如果给定锁定有多个服务器,则首先满足最高优先级锁定请求,并且与 max_write_lock_count系统变量有关。写锁定请求的优先级高于读取锁定请求。
[1]:>
请务必注意锁定顺序是序列化的:语句逐个获取元数据锁,而不是同时获取,并在此过程中执行死锁检测。
通常在考虑队列时考虑先进先出。如果我发出以下三个语句(按此顺序),它们将按以下顺序完成:
1 INSERT INTO parent2 ALTER TABLE child3 INSERT INTO parent
但是当子 ALTER 语句请求对父进行读取锁定时,尽管排序,但两个插入将在 ALTER 之前完成。以下是可以演示此示例的示例场景:
数据初始化:
CREATE TABLE `parent` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`val` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `child` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`val` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_parent` (`parent_id`),
CONSTRAINT `fk_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB;
INSERT INTO `parent` VALUES (1, "one"), (2, "two"), (3, "three"), (4, "four");
Session 1:
start transaction;update parent set val = "four-new" where id = 4;Session 2:
alter table child add index `idx_new` (val);Session 3:
start transaction;update parent set val = "three-new" where id = 3;此时,会话 1 具有打开的事务,并且处于休眠状态,并在父级上授予写入元数据锁定。 会话 2 具有在子级上授予的可升级(写入)锁定,并且正在等待父级的读取锁定。最后会话 3 具有针对父级的授权写入锁定:
mysql> select from performance_schemametadata_locks;+-------------+-------------+-------------------+---------------+-------------+| OBJECT_TYPE | OBJECT_NAME | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS |+-------------+-------------+-------------------+---------------+-------------+| TABLE | child | SHARED_UPGRADABLE | TRANSACTION | GRANTED | <- ALTER (S2)| TABLE | parent | SHARED_WRITE | TRANSACTION | GRANTED | <- UPDATE (S1)| TABLE | parent | SHARED_WRITE | TRANSACTION | GRANTED | <- UPDATE (S3)| TABLE | parent | SHARED_READ_ONLY | STATEMENT | PENDING | <- ALTER (S2)+-------------+-------------+-------------------+---------------+-------------+请注意,具有挂起锁定状态的唯一会话是会话 2(ALTER)。会话 1 和会话 3 (分别在 ALTER 之前和之后发布)都被授予了写锁。排序失败的地方是在会话 1 上发生提交的时候。在考虑有序队列时,人们会期望会话 2 获得锁定,事情就会继续进行。但是,由于元数据锁定系统的优先级性质,会话 3 具有锁定,会话 2 仍然等待。
如果另一个写入会话进入并启动新事务并获取针对父表的写锁定,则即使会话 3 完成,ALTER 仍将被阻止。
只要我保持一个对父表打开元数据锁定的活动事务,子表上的 ALTER 将永远不会完成。更糟糕的是,由于子表上的写锁定成功(但是完整语句正在等待获取父读锁定),所以针对子表的所有传入读取请求都将被阻止!
另外,请考虑一下您通常如何对无法完成的语句进行故障排除。您查看已经打开较长时间的事务(在进程列表和 InnoDB 状态中)。但由于阻塞线程现在比 ALTER 线程更年轻,因此您将看到的最旧的事务/线程是 ALTER 。
这正是这种情况下发生的情况。在准备发布时,我们的客户端正在运行 ALTER 语句并结合负载测试(一种非常好的做法!)以确保顺利发布。问题是负载测试保持对父表打开一个活动的写事务。这并不是说它只是一直在写,而是有多个线程,一个总是活跃的。 这阻止了 ALTER 完成并阻止对相对静态的子表的随后的读请求。
幸运的是,这个问题有一个解决方案(除了从设计模式中驱逐外键)。变量 max_write_lock_count[2] 可用于允许在写入锁定之后在读取锁定之前授予读取锁定连续写锁。默认情况下,此变量设置为 18446744073709551615,如果你对该表发出 10,000 次写入/秒,那么你的读将被锁定 5800 万年……
以上就是关于mysql数据库truncate的锁机制是什么全部的内容,包括:mysql数据库truncate的锁机制是什么、mysql锁定了数据库表只能写,为什么还可以读为什么不起作用、java程序中如何实现对mysql数据库中表的锁定等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)