一、什么是数据的存储引擎?mysql
在实际工做中,选择一个合适的存储引擎是一个很复杂的问题。每种存储引擎都有各自的优点,不能笼统的说谁比谁更好。下面将详解不一样环境常常用到的存储引擎和针对各个存储引擎的特色进行对比,给出不一样的选择建议。web
InnoDB是Mysql数据库的一种存储引擎。InnoDB给Mysql的表提供了 事务、回滚、崩溃修复能力、多版本并发控制的事务安全、间隙锁(能够有效的防止幻读的出现)、支持辅助索引、聚簇索引、自适应hash索引、支持热备、行级锁。还有InnoDB是Mysql上惟一一个提供了外键约束的引擎。sql
InnoDB存储引擎中,建立的表的表结构是单独存储的而且存储在.frm文件中。数据和索引存储在一块儿的而且存储在表空间中。可是默认状况下mysql会将数据库的全部InnoDB表存储在一个表空间中的。其实这种方式管理起来很是的不方便并且还不支持高级功能因此建议每一个表存储为一个表空间实现方式为:使用服务器变量innodb_file_per_table = 1。数据库
若是须要频繁的进行更新、删除操做的数据库也可选择InnoDB存储引擎。由于该存储引擎能够实现事务提交和回滚。缓存
MyISAM存储引擎是Mysql中常见的存储引擎,MyISAM存储引擎是基于ISAM存储引擎发展起来的。MyISAM支持全文索引、压缩存放、空间索引(空间函数)、表级锁、延迟更新索引键。可是MyISAM不支持事务、行级锁、更没法忍受的是崩溃后不能保证彻底恢复(只能手动修复)。安全
MyISAM存储引擎的表存储成3个文件。文件的名字和表的名字相同。扩展名包含frm、MYD、MYI。其中frm为扩展名的文件存储表的结构;MYD为扩展名的文件存储数据,其是MYData的缩写;MYI为扩展名的文件存储索引,其为MYIndex的缩写。服务器
MyISAM存储引擎的插入数据很快,空间和内存使用比较低。若是表主要是用于插入新记录和读出记录,那么选择MyISAM存储引擎可以实现处理的高效率。若是应用的完整性、并发性要求很低,也能够选择MyISAM存储引擎。网络
ARCHIVE,见名之意可看出是归档,因此归档以后不少的高级功能就再也不支持了仅支持插入(insert)和查询(select)两种功能, ARCHIVE存储引擎以前还不支持索引(在Mysql5.5之后开始支持索引了),可是它拥有很好的压缩机制。一般用于作仓库使用。数据结构
ARCHIVE存储引擎适用于存储日志信息或其余按时间序列实现的数据采集类的应用场景中。并发
CSV是将数据文件保存为CSV格式的的文件的,能够方便的导入到其余数据库中去(例如:excel表格,SQLserver等等),由此须要在数据库间自由共享数据时才偶尔建议使用此存储引擎。而且它也不支持索引;我的认为仅适用于数据交换。
BLACKHOLE叫作黑洞,也就是说没有存储机制,任何数据都会被丢弃,可是会记录二进制日志。通常在Mysql复制(中继服务器)中常常用到,这个在Mysql复制博客中将详细介绍,敬请关注。
FEDERATED能够实现跨服务器整理表,简单说就是它能够访问远程服务器上数据的存储引擎,因此说它再也不本地建立数据只会自动的创建一个链接到其余服务器上连接,有点相似于代理的功能,默认都是禁用的。
MEMORY存储引擎是Mysql中的一类特殊的存储引擎。其使用存储在内存中的内存来建立表,并且全部数据保存在内存中。数据安全性很低,可是查找和插入速度很快。若是内存出现异常就会影响到数据的完整性,若是重启或关机,表中的全部数据就会丢失,所以基于MEMORY存储引擎的表的生命周期很短,通常都是一次性的。适用于某些特殊场景像查找和映射,缓存周期性的聚合数据等等。
MRG_MYISAM存储引擎是合并MyISAM表的,就是将多个MyISAM合并为一个(在用户看来是一个进行工做,实际上是多个底层物理文件在运行工做)。
锁定
数据库引擎中的锁定功能决定了如何管理信息的访问和更新。当数据库中的一个对象为信息更新锁定了,在更新完成以前,其它处理不能修改这个数据(在某些状况下还不容许读这种数据)。
锁定不只影响许多不一样的应用程序如何更新数据库中的信息,并且还影响对那个数据的查询。这是由于查询可能要访问正在被修改或者更新的数据。总的来讲,这种延迟是很小的。大多数锁定机制主要是为了防止多个处理更新同一个数据。因为向数据中插入信息和更新信息这两种状况都须要锁定,你能够想象,多个应用程序使用同一个数据库可能会有很大的影响。
不一样的存储引擎在不一样的对象级别支持锁定,并且这些级别将影响能够同时访问的信息。获得支持的级别有三种:表锁定、块锁定和行锁定。支持最多的是表锁定,这种锁定是在MyISAM中提供的。在数据更新时,它锁定了整个表。这就防止了许多应用程序同时更新一个具体的表。这对应用不少的多用户数据库有很大的影响,由于它延迟了更新的过程。
页级锁定使用Berkeley DB引擎,而且根据上载的信息页(8KB)锁定数据。当在数据库的不少地方进行更新的时候,这种锁定不会出现什么问题。可是,因为增长几行信息就要锁定数据结构的最后8KB,当须要增长大量的行,也别是大量的小型数据,就会带来问题。
行级锁定提供了最佳的并行访问功能,一个表中只有一行数据被锁定。这就意味着不少应用程序可以更新同一个表中的不一样行的数据,而不会引发锁定的问题。只有InnoDB存储引擎支持行级锁定。
*修改存储引擎,能够用命令Alter table tableName engine =engineName
假如,若须要将表user的存储引擎修改成archive类型,则可以使用命令alter table user engine=archive。
*建立数据库表时设置存储存储引擎的基本语法是:
Create table tableName(
columnName(列名1) type(数据类型) attri(属性设置),
columnName(列名2) type(数据类型) attri(属性设置),
……..) engine = engineName
事物:
何为数据库事务 “一荣俱荣,一损俱损”这句话很能体现事务的思想,不少复杂的事物要分步进行,但它们组成一个总体,要么总体生效,要么总体失效。这种思想反映到数据库上,就是多个SQL语句,要么全部执行成功,要么全部执行失败。 数据库事务有严格的定义,它必须同时知足四个特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)和持久性(Durabiliy),简称为ACID。下面是对每一个特性的说明: 1. 原子性:表示组成一个事务的多个数据库操做是一个不可分隔的原子单元,只有全部的操做执行成功,整个事务才提交,事务中任何一个数据库操做失败,已经执行的任何操做都必须撤销,让数据库返回到初始状态; 2. 一致性:事务操做成功后,数据库所处的状态和它的业务规则是一致的,即数据不会被破坏。如从A帐户转帐100元到B帐户,无论操做成功与否,A和B的存款总额是不变的; 3. 隔离性:在并发数据操做时,不一样的事务拥有各自数据空间,它们的操做不会对对方产生干扰。准确的说,并不是要求作到彻底无干扰,数据库规定了多种事务隔离级别,不一样隔离级别对应不一样的干扰程度,隔离级别越高,数据一致性越好,但并发性越弱; 4. 持久性:一旦事务提交成功后,事务中全部的数据操做都必须被持久化到数据库中,即便提交事务后,数据库立刻崩溃,在数据库重启时,也必须能保证可以经过某种机制恢复数据。
在这些事务特性中,数据“一致性”是最终目标,其它的特性都是为达到这个目标的措施、要求或手段。 数据库管理系统通常采用重执行日志保证原子性、一致性和持久性,重执行日志记录了数据库变化的每个动做,数据库在一个事务中执行一部分操做后发生错误退出,数据库便可以根据重执行日志撤销已经执行的操做。此外,对于已经提交的事务,即便数据库崩溃,在重启数据库时也可以根据日志对还没有持久化的数据进行相应的重执行操做。
和Java程序采用对象锁机制进行线程同步相似,数据库管理系统采用数据库锁机制保证事务的隔离性。当多个事务试图对相同的数据进行操做时,只有持有锁的事务才能操做数据,直到前一个事务完成后,后面的事务才有机会对数据进行操做。Oracle数据库还使用了数据版本的机制,在回滚段为数据的每一个变化都保存一个版本,使数据的更改不影响数据的读取。
数据并发的问题 一个数据库可能拥有多个访问客户端,这些客户端均可以并发方式访问数据库。数据库中的相同数据可能同时被多个事务访问,若是没有采起必要的隔离措施,就会致使各类并发问题,破坏数据的完整性。这些问题能够归结为5类,包括3类数据读问题(脏读、幻象读和不可重复读)以及2类数据更新问题(第一类丢失更新和第二类丢失更新)。下面,咱们分别经过实例讲解引起问题的场景。
脏读(dirty read) 在讲解脏读前,咱们先讲一个笑话:一个有结巴的人在饮料店柜台前转悠,老板很热情地迎上来:“喝一瓶?”,结巴连忙说:“我…喝…喝…”,老板麻利地打开易拉罐递给结巴,结巴终于憋出了他的那句话:“我…喝…喝…喝不起啊!”。在这个笑话中,饮料店老板就对结巴进行了脏读。 A事务读取B事务还没有提交的更改数据,并在这个数据的基础上操做。若是恰巧B事务回滚,那么A事务读到的数据根本是不被认可的。来看取款事务和转帐事务并发时引起的脏读场景:
时间 | 转帐事务A | 取款事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询帐户余额为1000元 | |
T4 | 取出500元把余额改成500元 | |
T5 | 查询帐户余额为500元(脏读) | |
T6 | 撤销事务余额恢复为1000元 | |
T7 | 汇入100元把余额改成600元 | |
T8 | 提交事务 |
在这个场景中,B但愿取款500元然后又撤销了动做,而A往相同的帐户中转帐100元,就由于A事务读取了B事务还没有提交的数据,于是形成帐户白白丢失了500元。在Oracle数据库中,不会发生脏读的状况。
不可重复读(unrepeatable read) 不可重复读是指A事务读取了B事务已经提交的更改数据。假设A在取款事务的过程当中,B往该帐户转帐100元,A两次读取帐户的余额发生不一致:
时间 | 取款事务A | 转帐事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询帐户余额为1000元 | |
T4 | 查询帐户余额为1000元 | |
T5 | 取出100元把余额改成900元 | |
T6 | 提交事务 | |
T7 | 查询帐户余额为900元(和T4读取的不一致) |
在同一事务中,T4时间点和T7时间点读取帐户存款余额不同。
幻象读(phantom read) A事务读取B事务提交的新增数据,这时A事务将出现幻象读的问题。幻象读通常发生在计算统计数据的事务中,举一个例子,假设银行系统在同一个事务中,两次统计存款帐户的总金额,在两次统计过程当中,恰好新增了一个存款帐户,并存入100元,这时,两次统计的总金额将不一致: 若是新增数据恰好知足事务的查询条件,这个新数据就进入了事务的视野,于是产生了两个统计不一致的状况。
幻象读和不可重复读是两个容易混淆的概念,前者是指读到了其它已经提交事务的新增数据,然后者是指读到了已经提交事务的更改数据(更改或删除),为了不这两种状况,采起的对策是不一样的,防止读取到更改数据,只须要对操做的数据添加行级锁,阻止操做中的数据发生变化,而防止读取到新增数据,则每每须要添加表级锁——将整个表锁定,防止新增数据(Oracle使用多版本数据的方式实现)。 第一类丢失更新 A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能形成很严重的问题,经过下面的帐户取款转帐就能够看出来:
时间 | 取款事务A | 转帐事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询帐户余额为1000元 | |
T4 | 查询帐户余额为1000元 | |
T5 | 汇入100元把余额改成1100元 | |
T6 | 提交事务 | |
T7 | 取出100元把余额改成900元 | |
T8 | 撤销事务 | |
T9 | 余额恢复为1000元(丢失更新) |
A事务在撤销时,“不当心”将B事务已经转入帐户的金额给抹去了。
第二类丢失更新 A事务覆盖B事务已经提交的数据,形成B事务所作操做丢失:
时间 | 转帐事务A | 取款事务B |
T1 | 开始事务 | |
T2 | 开始事务 | |
T3 | 查询帐户余额为1000元 | |
T4 | 查询帐户余额为1000元 | |
T5 | 取出100元把余额改成900元 | |
T6 | 提交事务 | |
T7 | 汇入100元 | |
T8 | 提交事务 | |
T9 | 把余额改成1100元(丢失更新) |
上面的例子里因为支票转帐事务覆盖了取款事务对存款余额所作的更新,致使银行最后损失了100元,相反若是转帐事务先提交,那么用户帐户将损失100元。
数据库锁机制 数据并发会引起不少问题,在一些场合下有些问题是容许的,但在另一些场合下可能倒是致命的。数据库经过锁的机制解决并发访问的问题,虽然不一样的数据库在实现细节上存在差异,但原理基本上是同样的。 按锁定的对象的不一样,通常能够分为表锁定和行锁定,前者对整个表进行锁定,然后者对表中特定行进行锁定。从并发事务锁定的关系上看,能够分为共享锁定和独占锁定。共享锁定会防止独占锁定,但容许其它的共享锁定。而独占锁定既防止其它的独占锁定,也防止其它的共享锁定。为了更改数据,数据库必须在进行更改的行上施加行独占锁定,INSERT、UPDATE、DELETE和SELECT FOR UPDATE语句都会隐式采用必要的行锁定。下面咱们介绍一下ORACLE数据库经常使用的5种锁定: l 行共享锁定:通常经过SELECT FOR UPDATE语句隐式得到行共享锁定,在Oracle中你也能够经过LOCK TABLE IN ROW SHARE MODE语句显式得到行共享锁定。行共享锁定并不防止对数据行进行更改的操做,可是能够防止其它会话获取独占性数据表锁定。容许进行多个并发的行共享和行独占性锁定,还容许进行数据表的共享或者采用共享行独占锁定; l 行独占锁定:经过一条INSERT、UPDATE或DELETE语句隐式获取,或者经过一条LOCK TABLE IN ROW EXCLUSIVE MODE语句显式获取。这个锁定能够防止其它会话获取一个共享锁定、共享行独占锁定或独占锁定; l 表共享锁定:经过LOCK TABLE IN SHARE MODE语句显式得到。这种锁定能够防止其它会话获取行独占锁定(INSERT、UPDATE或DELETE),或者防止其它表共享行独占锁定或表独占锁定,它容许在表中拥有多个行共享和表共享锁定。该锁定可让会话具备对表事务级一致性访问,由于其它会话在你提交或者回溯该事务并释放对该表的锁定以前不能更改这个被锁定的表; l 表共享行独占:经过LOCK TABLE IN SHARE ROW EXCLUSIVE MODE语句显式得到。这种锁定能够防止其它会话获取一个表共享、行独占或者表独占锁定,它容许其它行共享锁定。这种锁定相似于表共享锁定,只是一次只能对一个表放置一个表共享行独占锁定。若是A会话拥有该锁定,则B会话能够执行SELECT FOR UPDATE操做,但若是B会话试图更新选择的行,则须要等待; l 表独占:经过LOCK TABLE IN EXCLUSIVE MODE显式得到。这个锁定防止其它会话对该表的任何其它锁定。
事务隔离级别 尽管数据库为用户提供了锁的DML操做方式,但直接使用锁管理是很是麻烦的,所以数据库为用户提供了自动锁机制。只要用户指定会话的事务隔离级别,数据库就会分析事务中的SQL语句,而后自动为事务操做的数据资源添加上适合的锁。此外数据库还会维护这些锁,当一个资源上的锁数目太多时,自动进行锁升级以提升系统的运行性能,而这一过程对用户来讲彻底是透明的。 ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工做,根据不一样的隔离级别,能够致使不一样的结果。不一样事务隔离级别可以解决的数据并发问题的能力是不一样的。 表 1 事务隔离级别对并发问题的解决状况
隔离级别 | 脏读 |
不可
重复读
|
幻象读 | 第一类丢失更新 | 第二类丢失更新 |
READ UNCOMMITED | 容许 | 容许 | 容许 | 不容许 | 容许 |
READ COMMITTED | 不容许 | 容许 | 容许 | 不容许 | 容许 |
REPEATABLE READ | 不容许 | 不容许 | 容许 | 不容许 | 不容许 |
SERIALIZABLE | 不容许 | 不容许 | 不容许 | 不容许 | 不容许 |
事务的隔离级别和数据库并发性是对立的,二者此增彼长。通常来讲,使用READ UNCOMMITED隔离级别的数据库拥有最高的并发性和吞吐量,而使用SERIALIZABLE隔离级别的数据库并发性最低。
SQL 92定义READ UNCOMMITED主要是为了提供非阻塞读的能力,Oracle虽然也支持READ UNCOMMITED,但它不支持脏读,由于Oracle使用多版本机制完全解决了在非阻塞读时读到脏数据的问题并保证读的一致性,因此,Oracle的READ COMMITTED隔离级别就已经知足了SQL 92标准的REPEATABLE READ隔离级别。
SQL 92推荐使用REPEATABLE READ以保证数据的读一致性,不过用户能够根据应用的须要选择适合的隔离等级。 JDBC对事务的支持 并非全部的数据库都支持事务,即便支持事务的数据库也并不是支持全部的事务隔离级别,你能够经过Connection# getMetaData()方法获取DatabaseMetaData对象,并经过该对象的supportsTransactions()、supportsTransactionIsolationLevel(int level)方法查看底层数据库的事务支持状况。Connection默认状况下是自动提交的,也即每条执行的SQL都对应一个事务,为了可以将多条SQL当成一个事务执行,必须先经过Connection#setAutoCommit(false)阻止Connection自动提交,并可经过Connection#setTransactionIsolation()设置事务的隔离级别,Connection中定义了对应SQL 92标准4个事务隔离级别的常量。经过Connection#commit()提交事务,经过Connection#rollback()回滚事务。下面是典型的JDBC事务数据操做的代码: 代码清单 1 JDBC事务代码
在JDBC 2.0中,事务最终只能有两个操做:要么提交要么回滚。可是,有些应用可能须要对事务进行更多的控制,而不是简单地提交或回滚。JDBC 3.0(JDK 1.4及之后的版本)引入了一个全新的保存点特性,Savepoint 接口容许你将事务分割为多个阶段,你能够指定回滚到事务的特定保存点,而非象JDBC 2.0同样只回滚到开始事务的点,如图 1所示:
图 1 带Savepoint的事务
下面的代码使用了保存点的功能,在发生特定问题时,回滚到指定的保存点,则非回滚整个事务,如代码清单 2所示: 代码清单 2使用保存点的事务代码
并不是全部数据库都支持保存点功能,你能够经过DatabaseMetaData#supportsSavepoints()方法查看是否支持。
小结 数据一致性和访问的并发性二者之间的最佳平衡永远是数据库应用程序开发所追求的终极目录。数据事务是保证数据访问一致性的不二法门,使用API进行数据库事务应用开发时,必须深入了解事务控制属性对应用性能和数据一致性二者的影响,并根据项目实际须要进行合理的设置。