连接管理和安全性
每个客户端连接都会在服务器进程中有一个对应的线程,这个连接的查询只会在这个单独的线程中执行。服务器会负责缓存线程,因此不需要为每个新建的连接创建或销毁线程。
优化和执行
优化器并不关心表使用的是什么存储引擎,但存储引擎对优化查询是有影响的。优化器会请求存储引擎提供容量或某个具体操作的开销信息,以及表数据的统计信息等。
锁粒度
一种提高共享资源并发性的方式就是让锁定对象更有选择性。尽量值锁定需要修改的部分数据,而不是
所有的资源。更理想的方式是,只对会修改的数据偏进行精确的锁定。任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可。
为时加锁也需要消耗资源。所的各种操作,包括获得锁、检查锁是否已经解除、释放锁等,都会增加系统的开销。如果系统花费大量的时间来管理锁,而不是存取数据,那么系统的性能可能会因此受到影响。
所谓的锁策略,就是在所的开销和数据的安全性之间寻求平衡,这种平和当然也会影响到性能。大多数商业数据库系统没有提供更多的选择,一般都是在表上施加行级锁,并以各种复杂的方式来实现,以便在锁比较多的情况下尽可能地提供更好的性能。
而MySQL则提供了多种选择。每种MySQL存储引擎都可以实现自己的锁策略和锁粒度。在存储引擎的设计中,锁管理是个非常重要的决定。将锁粒度固定在某个级别,可以为某些特定的应用场景提供更好的性能,但同时却会失去对另外一些应用场景的良好支持。好在MySQL支持多个存储引擎的架构,所以不需要单一的通用解决方案。
表锁
表锁是MySQL中最基本的锁策略,并且是开销最小的策略。在特定的场景中,表锁也可能有良好的性能。例如,READ LOCAL表锁支持某些类型的并发写操作。尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同的目的。例如,服务器会为诸如ALTER TABLE之类的语句使用表锁,而忽略存储引擎的锁机制。
行级锁
行级锁可以最大程度的支持并发处理(同时也带来了最大的锁开销)。行级锁只在存储引擎层实现,而MySQL服务器层没有实现。
事务
就像锁粒度的升级会增加系统开销一样,事务处理过程汇总的额外的安全性,也会需要数据库系统做更多的额外工作。一个实现了ACID的数据库,相比没有实现ACID的数据库,通常会需要更强的CPU处理能力、更大的内存和更多的磁盘空间。这正是MySQL的存储引擎可以发挥优势的地方。用户可以根据业务是否需要食物处理,来选择合适的存储引擎。对于一些不需要事务的查询类应用,选择一个非事务类型的存储引擎,可以获得更高的性能。即使存储引擎不支持事务,也可以通过LOCK TABLES语句为应用提供一定程度的保护,这些选择用户可以自主决定。
隔离级别
在SQL标准中定义了四种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在是屋内和事物间是可见的,哪些是不可见的。较低界别的隔离通常可以执行更高的并发,系统的开销也更低。
READ UNCOMMITTED(未提交读)
在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不会比其他级别好太多,但却缺乏其他级别的很多好处,除非真的有必要的理由,在实际应用中一般很少使用。
READ COMMITTED(提交读)
大多数数据库系统的默认隔离级别都是READ COMMITTED(MySQL不是)。一个事务从开始知道提交之前,所做的任何修改对其他事务都是不可见的。这个级别有的时候也叫做不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。
REPEATABLE READ(可重复读)
REPEATABLE READ解决了脏读的问题。改级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读的问题。所谓幻读,值得是当某个事务在读取某个范围的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。InnoDB和XtraDB存储引擎通过多版本并发控制解决了幻读的问题。
SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读问题。SERIALIZABLE会在读取的每一行数据上都加锁,所以额能导致大量的超时和锁争用的问题。
死锁
死锁发生以后,只有部分或者完全回滚其中一个事务,才能打破死锁。对于事务性的系统,这是无法避免的,所以应用程序在设计时必须考虑如何处理死锁。大多数情况下只需要重新执行因死锁回滚的事务即可。
事务日志
事务日志课帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把改修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志持久以后,内存中被修改的数据在后台可以慢慢地刷回磁盘。
MySQL中的事务
MySQL中提供了两种事务型的存储引擎:InnoDB和NDB Cluster。
自动提交(AUTOCOMMIT)
MySQL偶人采用自动提交模式。也就是说,如果不是显式地开始一个事务,则每个查询都会被当做一个事务执行提交操作。
在事务中混合使用存储引擎
MySQL服务器层不管理事务,事务是由下层的存储引擎实现的。所以在同一个事务中,使用多种存储引擎是不可靠的。如果正常提交,不会有什么问题;但是如果事务要回滚,非事务型表上的变更就无法撤销,这会导致数据库处于不一致的状态,这种情况很难修复,事务的最终结果将无法确定。所以,为每张表选择合适的存储引擎非常重要。
隐式和显式锁定
经常可以发现,应用已经将表从MyISAM换到InnoDB,但是还是使用LOCK TABLES语句。这没有必要而且影响性能,InnoDB的行级锁工作得更好
自动提交
存储引擎
在文件系统中,MySQL将每个数据库(也可以称之为schema)保存为数据目录下的一个子目录。创建表时,MySQL会在数据库子目录下创建一个和表同名的.frm文件中保存该表的定义。不同的存储引擎保存数据和索引的方式是不同的,但表的定义则是在MySQL服务层统一处理的。
可以使用SHOW TABLE STATUS命令显示表的相关信息。例如:
SHOW TABLE STATUA LIKE ‘user’
InnoDB存储引擎
InnoDB是MySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎。它被设计用来处理大量的短期事务,短期事务大部分情况是正常提交的,很少会被回滚。
InnoDB采用MVCC来支持高并发,并且实现了四个标准的隔离级别。其默认级别是REPEATABLE READ(可重复读),并且通过间隙锁策略防止幻读的出现,间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定。
InnoDB表是基于聚簇索引建立的,对主键查询有很高的性能。不过它的二级索引中必须包含主键列。InnoDB的存储格式是平台独立的,也就是说可以将数据和索引文件从Intel平台复制到PowerPC或者Sun SPARC平台。
基准测试的策略
测试何种指标
吞吐量
吞吐量指的是单位时间内处的事务处理数。这类基准测试主要针对在线事务处理(OLTP)的吞吐量,非常适用于多用户的交互式应用。
响应时间或者延迟
这个指标用于测试任务所需的整体时间。包括平均响应时间、最小响应时间、最大响应时间和所占百分比,最大响应时间通常意义不大。通常可以使用百分比响应时间来代替最大响应时间。例如,如果95%的响应时间是5ms,则表示任务在95%的响应时间都是5ms。
并发性
一个设计良好的应用,同时可以打开成百上千个MySQL数据库服务器连接,但可能同时只有少数连接在执行查询。所以说,一个Web站点“同时有50000个用户”访问,却可能只有10-15个并发请求到MySQL数据库。
并发性的测量完全不同于响应时间和吞吐量。它不像是一个结果,而更像是设置基准测试的一种属性。并发性测试通常不是为了测试应用能达到的并发度,而是为了测试应用在不同并发下的性能。当然数据库的并发性还是需要测量的。可以通过sysbench指定32、64或128个线程的测试,然后在测试期间记录MySQL数据库的Treads_running状态值。
归根结底,应该测试那些对用户来说最重要的指标。因此应该尽可能地去收集一些需求,比如,什么样的响应时间是可以接受的,期待多少的并发性,等等。然后基于这些需求来设计基准测试,避免目光短浅只关注部分指标,而忽略其他指标。
MySQL存储引擎
- Memory引擎
Memory表支持Hash索引,因此查找操作非常快。虽然Memory表的速度非常快,但还是无法取代传统的基于磁盘的表。Memory表是表级锁,因此并发写入的性能较低。它不支持BLOB或TEXT类型的列,并且每行的长度是固定的,所以即使指定了VARCHAR列,实际存储时也会转换为CHAR。
如果MySQL在执行查询的过程中需要使用临时表来保存中间结果,内部使用的临时表就是Memory表。如果中间结果太大超出了Memory表的限制,或者含有BLOB或TEXT字段,则临时表会转换成MyISAM表。