查看表的存储引擎:mysql> show create table myLock;mysql
修改当前表的存储引擎:mysql> alter table myLock engine=myisam;sql
查看数据库当前默认的存储引擎:mysql> show variables like '%storage_engine%';数据库
读锁(共享锁):对于同一条记录,多个读操做能够同时进行而不会互相影响,会阻塞写操做。安全
写锁(排他锁):当前写操做没有完成前,会阻碍其余写锁与读锁。服务器
表锁:表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特色是实现逻辑很是简单,带来的系统负面影响最小。因此获取锁和释放锁的速度很快。因为表级锁一次会将整个表锁定,因此能够很好的避免困扰咱们的死锁问题。固然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的几率也会最高,导致并大度大打折扣。(MyISAM存储引擎支持表锁)session
建立数据:并发
mysql> create table myLock(id int not null primary key auto_increment,name varchar(20))engine myisam; Query OK, 0 rows affected (0.01 sec) mysql> insert into myLock (name) values("A"); Query OK, 1 row affected (0.00 sec) mysql> insert into myLock (name) values("B"); Query OK, 1 row affected (0.00 sec)
给表myLock加读锁:mysql> lock table myLock read;高并发
在查看表的锁状态:性能
mysql> show open tables from jalja_deal; +------------+------------+--------+-------------+ | Database | Table | In_use | Name_locked | +------------+------------+--------+-------------+ | jalja_deal | jalja_user | 0 | 0 | | jalja_deal | myLock | 1 | 0 | | jalja_deal | deal_pay | 0 | 0 | | jalja_deal | deal_order | 0 | 0 | +------------+------------+--------+-------------+
解除表myLock的读锁:大数据
mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) mysql> show open tables from jalja_deal; +------------+------------+--------+-------------+ | Database | Table | In_use | Name_locked | +------------+------------+--------+-------------+ | jalja_deal | jalja_user | 0 | 0 | | jalja_deal | myLock | 0 | 0 | | jalja_deal | deal_pay | 0 | 0 | | jalja_deal | deal_order | 0 | 0 | +------------+------------+--------+-------------+ 4 rows in set (0.00 sec)
第一个session:
mysql> lock table myLock read; Query OK, 0 rows affected (0.00 sec) mysql> select * from myLock; +----+------+ | id | name | +----+------+ | 1 | A | | 2 | B | +----+------+ 2 rows in set (0.00 sec)
第二个session:
mysql> select * from myLock; +----+------+ | id | name | +----+------+ | 1 | A | | 2 | B | +----+------+ 2 rows in set (0.00 sec)
从这里能够看出读锁之间是共享的。
咱们的第一个session在不释放锁的状况下进行如下操做:
修改当前表的信息:
mysql> update myLock set name='C' where id =1; ERROR 1099 (HY000): Table 'myLock' was locked with a READ lock and can't be updated
查询其余表:
mysql> select * from jalja_user; ERROR 1100 (HY000): Table 'jalja_user' was not locked with LOCK TABLES
第二个session修改myLock表的数据:没有错误也没有执行结果,应为他被阻塞了正在排队等待获取myLock表的锁
mysql> update myLock set name="N" where id =1;
模拟写锁:
#加锁
mysql> lock table myLock write; Query OK, 0 rows affected (0.00 sec) //读数据 mysql> select * from myLock; +----+------+ | id | name | +----+------+ | 1 | N | | 2 | B | +----+------+ 2 rows in set (0.00 sec) //写数据 mysql> update myLock set name="N" where id=1; Query OK, 0 rows affected (0.03 sec) Rows matched: 1 Changed: 0 Warnings: 0 //操做其余表 mysql> select * from jalja_user; ERROR 1100 (HY000): Table 'jalja_user' was not locked with LOCK TABLES
第二个session:
读数据组阻塞
mysql> select * from myLock;
写数据阻塞
mysql> update myLock set name="G" where id=1;
结论:
行锁:行级锁定最大的特色就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。因为锁定颗粒度很小,因此发生锁定资源争用的几率也最小,可以给予应用程序尽量大的并发处理能力而提升一些须要高并发应用系统的总体性能。虽然可以在并发处理能力上面有较大的优点,可是行级锁定也所以带来了很多弊端。因为锁定资源的颗粒度很小,因此每次获取锁和释放锁须要作的事情也更多,带来的消耗天然也就更大了。此外,行级锁定也最容易发生死锁。使用行级锁定的主要是InnoDB存储引擎。
A、查看数据的锁状态 (数据库 jalja_deal)
mysql> show open tables from jalja_deal; +------------+----------------+--------+-------------+ | Database | Table | In_use | Name_locked | +------------+----------------+--------+-------------+ | jalja_deal | InnoDB_monitor | 0 | 0 | | jalja_deal | jalja_user | 0 | 0 | | jalja_deal | myLock | 0 | 0 | | jalja_deal | deal_pay | 0 | 0 | | jalja_deal | deal_order | 0 | 0 | +------------+----------------+--------+-------------+
In_use =0 说明没有表被锁
B、分析表的锁定状况
mysql> show status like 'table%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Table_locks_immediate | 75 | | Table_locks_waited | 0 | | Table_open_cache_hits | 3 | | Table_open_cache_misses | 5 | | Table_open_cache_overflows | 0 | +----------------------------+-------+
Table_locks_immediate:产生表级锁定的次数,表示能够当即获取锁的查询次数,每当即获取锁时该值加1
Table_locks_waited:出现表级锁争用而发生等待的次数(不能当即获取锁的次数,每等待一次该值加1),该值高则说明存在较为严重的表级锁争用的状况。
InnoDB存储引擎中对锁的观察:
mysql> show status like 'InnoDB_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 0 | | Innodb_row_lock_time_avg | 0 | | Innodb_row_lock_time_max | 0 | | Innodb_row_lock_waits | 0 | +-------------------------------+-------+
InnoDB 的行级锁定状态变量不只记录了锁定等待次数,还记录了锁定总时长,每次平均时长,以及最大时长,此外还有一个非累积状态量显示了当前正在等待锁定的等待数量。对各个状态量的说明以下:
InnoDB_row_lock_current_waits:当前正在等待锁定的数量;
InnoDB_row_lock_time:从系统启动到如今锁定总时间长度;
InnoDB_row_lock_time_avg:每次等待所花平均时间;
InnoDB_row_lock_time_max:从系统启动到如今等待最常的一次所花的时间;
InnoDB_row_lock_waits:系统启动后到如今总共等待的次数;
对于这5个状态变量,比较重要的主要是InnoDB_row_lock_time_avg(等待平均时长),InnoDB_row_lock_waits(等待总次数)以及InnoDB_row_lock_time(等待总时长)这三项。尤为是当等待次数很高,并且每次等待时长也不小的时候,咱们就须要分析系统中为何会有如此多的等待,而后根据分析结果着手指定优化计划。
监控:
若是发现锁争用比较严重,如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比较高,还能够经过设置InnoDB Monitors来进一步观察发生锁冲突的表、数据行等,并分析锁争用的缘由。锁冲突的表、数据行等,并分析锁争用的缘由。具体方法以下:
mysql> create table InnoDB_monitor(a INT) engine=InnoDB;
而后就能够用下面的语句来进行查看:
mysql> show engine InnoDB status;
监视器能够经过发出下列语句来中止查看:
mysql> drop table InnoDB_monitor;
设置监视器后,会有详细的当前锁等待的信息,包括表名、锁类型、锁定记录的状况等,便于进行进一步的分析和问题的肯定。可能会有读者朋友问为何要先建立一个叫InnoDB_monitor的表呢?由于建立该表实际上就是告诉InnoDB咱们开始要监控他的细节状态了,而后InnoDB就会将比较详细的事务以及锁定信息记录进入MySQL的errorlog中,以便咱们后面作进一步分析使用。打开监视器之后,默认状况下每15秒会向日志中记录监控的内容,若是长时间打开会致使.err文件变得很是的巨大,因此用户在确认问题缘由以后,要记得删除监控表以关闭监视器,或者经过使用“--console”选项来启动服务器以关闭写日志文件。
InnoDB行锁是经过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不一样,后者是经过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特色意味着:只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁!
一个支持事务的数据库必须必须具有ACID的属性
原子性是指事务包含的全部操做要么所有成功,要么所有失败回滚,这和前面两篇博客介绍事务的功能是同样的概念,所以事务的操做若是成功就必需要彻底应用到数据库,若是操做失败则不能对数据库有任何影响。
一致性(Consistency):
一致性是指事务必须使数据库从一个一致性状态变换到另外一个一致性状态,也就是说一个事务执行以前和执行以后都必须处于一致性状态。
例如:
假设用户A和用户B二者的钱加起来一共是5000,那么无论A和B之间如何转帐,转几回帐,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性是当多个用户并发访问数据库时,好比操做同一张表时,数据库为每个用户开启的事务,不能被其余事务的操做所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始以前就已经结束,要么在T1结束以后才开始,这样每一个事务都感受不到有其余事务在并发地执行。
关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。
一旦事务提交,则其所作的修改就会永久保存到数据库中。此时即便系统崩溃,修改的数据也不会丢失。(持久性的安全性与刷新日志级别也存在必定关系,不一样的级别对应不一样的数据安全级别。)
2、高并发操做会引起的问题
一、丢失更新(Lost Update)
撤销一个事务时,把其余事务已提交的更新数据覆盖
例子:A和B事务并发执行,A事务执行更新后,提交;B事务在A事务更新后,B事务结束前也作了对该行数据的更新操做,而后回滚,则两次更新操做都丢失了
二、脏读(Dirty Reads)
一个事务读到另外一个事务未提交的更新数据
例子:A和B事务并发执行,B事务执行更新后,A事务查询B事务没有提交的数据,B事务回滚,则A事务获得的数据不是数据库中的真实数据。也就是脏数据,即和数据库中不一致的数据,不符合一致性要求。
三、不可重复读(Non-Repeatable Reads)
一个事务读到另外一个事务已提交的更新数据
例子:A和B事务并发执行,A事务查询数据,而后B事务更新该数据,A再次查询该数据时,发现该数据变化了
四、覆盖更新
这是不可重复读中的特例,一个事务覆盖另外一个事务已提交的更新数据
例子:A事务更新数据,而后B事务更新该数据,A事务查询发现本身更新的数据变了
五、虚读(幻读 Phantom Reads)
一个事务读到另外一个事务已提交的新插入的数据
例子:A和B事务并发执行,A事务查询数据,B事务插入或者删除数据,A事务再次查询发现结果集中有之前没有的数据或者之前有的数据消失了
隔离级别 | 读数据一致性 | 脏读 | 不可重复读 | 幻读 |
未提交读(Read uncommitted) | 最低级别隔离,只能保证不读取物理上损坏的数据 | 是 | 是 | 是 |
已提交读(Read committed) | 语句级别 | 否 | 是 | 是 |
可重复读(Repeatable read) | 事务级别 | 否 | 否 | 是 |
可序列化(Serializable) | 最高级别,事务级 | 否 | 否 | 否 |
事务的隔离级别越高,并发引起的反作用就越小,但付出的代价越大,由于事务隔离实质上就是使事务在必定程度上“串行化”进行,这显然与“并发”是矛盾的。同时,不一样的应用对读一致性和事务隔离程度的要求也是不一样的,好比许多应用对“不可重复读” 和“幻读”并不敏感,可能更关心数据库并发访问能力。
查看当前数据库的隔离级别:
mysql> show variables like 'tx_isolation'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | tx_isolation | REPEATABLE-READ | +---------------+-----------------+