事务操作的统计,TPS的计算,隔离级别的读提交

对于事务操作的统计

因为InnoDB存储引擎是支持事务的,因此对于InnoDB存储引擎的应用,在考虑每秒请求数(Question Per Second,QPS)的同时,也许更应该关注每秒事务处理的能力(Transaction Per Second,TPS)。

计算TPS的方法是(com_commit+com_rollback)/time。但是用这种方法计算的前提是:所有的事务必须都是显式提交的,如果存在隐式的提交和回滚(默认autocommit=1),不会计算到com_commit和com_rollback变量中。如:

show global status like 'com_commit'\G  5

insert into t select 3;

select * from t\G

show global status like'com_commit'\G  5

MySQL另外还有2个参数handler_commithandler_rollback。但是我注意到,这两个参数在MySQL 5.1中可以很好地用来统计InnoDB存储引擎显式和隐式的事务提交操作,而在InnoDB Plugin中该参数的表现有些“怪异”,并不能很好地统计事务的次数。所以,如果你的程序都是显式控制事务的提交和回滚,那么可以通过com_commit和com_rollback进行统计。如果不是,那么情况就显得复杂了。

事务的隔离级别

令人惊讶的是,大部分数据库系统都没有提供真正的隔离性,最初或许是因为系统实现者并没有真正理解这些问题。如今这些问题已经弄清楚了,但是数据库实现者在正确性和性能之间做了妥协。ISO和ANIS SQL标准制定了四种事务隔离级别的标准,但是很少有数据库开发厂商遵循这些标准,比如Oracle数据库就不支持read uncommitted和repeatable read的事务隔离级别。

SQL标准定义的四个隔离级别为:

  1. READ UNCOMMITTED
  2. READ COMMITTED
  3. REPEATABLE READ
  4. SERIALIZABLE

READ UNCOMMITTED称为浏览访问(browse access),仅仅只对事务而言的。

READ COMMITTED称为游标稳定(cursor stability)。

REPEATABLE READ是2.9999˚的隔离,没有幻读的保护。

SERIALIZABLE称为隔离,或3˚。SQL和SQL 2标准的默认事务隔离级别是SERIALIZABLE。

InnoDB存储引擎默认的支持隔离级别是REPEATABLE READ,但是与标准SQL不同的是,InnoDB存储引擎在REPEATABLE READ事务隔离级别下,使用Next-Key Lock锁的算法,因此避免幻读的产生。这与其他数据库系统(如Microsoft SQL Server数据库)是不同的。所以说,InnoDB存储引擎在默认REPEATABLE READ的事务隔离级别下已经能完全保证事务的隔离性要求,即达到SQL标准的SERIALIZABLE隔离级别。

隔离级别越低,事务请求的锁越少,或者保持锁的时间就越短。这也是为什么大多数数据库系统默认的事务隔离级别是READ COMMITTED。

在InnoDB存储引擎中,可以使用以下命令来设置当前会话或者全局的事务隔离级别:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL

{

  READ UNCOMMITTED

  |READ COMMITTED

  |REPEATABLE READ

  |SERIALIZABLE

}

如果想在MySQL库启动时就设置事务的默认隔离级别,那就需要修改MySQL的配置文件,在[mysqld]中添加如下行:

[mysqld]

transaction-isolation=READ-COMMITTED

查看当前会话的事务隔离级别,可以使用:

select @@tx_isolation\G

查看全局的事务隔离级别,可以使用:

select @@global.tx_isolation\G

在SERIALIABLE的事务隔离级别,InnoDB存储引擎会对每个SELECT语句后自动加上LOCK IN SHARE MODE,即给每个读取操作加一个共享锁。因此在这个事务隔离级别下,读占用锁了,一致性的非锁定读不再予以支持。因为InnoDB存储引擎在REPEATABLE READ隔离级别下就可以达到3˚的隔离,所以一般不在本地事务中使用SERIALIABLE的隔离级别,SERIALIABLE的事务隔离级别主要用于InnoDB存储引擎的分布式事务。

在READ COMMITTED的事务隔离级别下,除了唯一性的约束检查以及外键约束的检查需要Gap Lock,InnoDB存储引擎不会使用Gap Lock的锁算法。但是使用这个事务隔离级别需要注意一些问题。首先,在MySQL 5.1中,READ COMMITTED事务隔离级别默认只能工作在Replication(复制)的二进制日志为ROW的格式下。如果二进制日志工作在默认的STATEMENT下,则会指出如下的错误:

create table a(b int,primary key(b))engine=innodb;

set @@tx_isolation='read-committed';

select @@tx_isolation\G

begin;

insert into a select 1;

ERROR 1598(HY000):Binary logging not possible.Message:Transaction level

'READ-COMMITTED'in InnoDB is not safe for binlog mode'STATEMENT'

MySQL 5. 0版本之前不支持ROW格式的二进制日志时,也许有人知道,可以通过将参数innodb_locks_unsafe_for_binlog设置为1,来使得可以在二进制日志为STATEMENT下使用READ COMMITTED的事务隔离级别:

select @@version\G

system cat/etc/my.cnf | grep innodb_locks_unsafe_for_binlog

innodb_locks_unsafe_for_binlog=1

show variables like 'innodb_locks_unsafe_for_binlog'\G

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

Variable_name:innodb_locks_unsafe_for_binlog

Value:ON

set @@tx_isolation='read-committed';

begin;

insert into a select 1;

commit;

是的,的确可以通过上述办法使得在MySQL 5.0的版本之前使用READ COMMITED事务隔离级别。但是就像参数innodb_locks_unsafe_for_binlog的名称一样,它是unsafe的。在某些情况下,可能会导致master和slave之间数据的不一致。

接着来演示一种可能导致不同步的情况,首先来看下表a中的数据:

select * from a\G

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

b:1

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

b:2

***************************3.row***************************

b:4

***************************4.row***************************

b:5

4 rows in set(0.00 sec)

接着在master上开启一个会话A执行如下事务,并且不要提交:

#Session A on master

begin;

delete from a where b<=5;

在master上开启另一个会话B,执行如下事务,并且提交:

#Session B on master

begin;

insert into a select 3;

commit;

接着会话A提交,并查看表a中的数据:

#Session A on master

commit;

select * from a\G

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

b:3

但是在slave上看到的结果却是:

#Slave

select * from a;

可以看到,数据产生了不一致。导致这个问题发生的原因有两点:首先,在READ COMMITTED事务隔离级别下,事务是没有Gap Lock锁的,因此我们可以在小于等于5的范围内再插入一条记录;其次,statement记录的是master上产生的SQL语句,因此在master上是先删后插,但是在STATEMENT格式中记录的却是先插后删,逻辑上就产生了不一致。因此,使用READ REPEATABLE事务隔离级别就可以避免第一种情况的发生,因而也就避免了master和slave不一致问题的产生。

在MySQL 5.1的版本之后,因为支持了ROW格式的二进制日志记录格式,所以避免了第二种情况的发生,因此可以放心使用READ COMMITTED的事务隔离级别。即使不使用READ COMMITTED的事务隔离级别,也应该考虑将二进制日志的格式更换成ROW,因为这个格式记录的是行的变更,而不是简单的SQL语句,因此可以避免一些不同步现象的产生。

 

上一篇:阿里云ACA、ACP和ACE认证考试如何选择?有什么区别?


下一篇:MAC 子层 | 带你读《5G 空口设计与实践进阶 》之十二