(1)在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
(2)事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
(3)事务用来管理 insert,update,delete 语句。
原子性:构成事务的的所有操作必须是一个逻辑单元,要么全部执行,要么全部不执行。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则。
隔离性:并发事务之间不会相互影响。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。事务执行成功后必须全部写入磁盘。
数据库事务会导致脏读、不可重复读、幻读等问题。
1、脏读:事务还没提交,他的修改已经被其他事务看到。
2、不可重复读:在一个事务内读取到了表中的某一行数据,多次读取结果不同。
3、幻读:同一个事务突然发现他以前没发现的数据。幻读是前后读取到表中的记录总数不一样,读取到了其它事务插入的数据。
事务的隔离用是通过锁机制实现的,不同于MyISAM使用表级别的锁,InnoDB采用更细粒度的行级别锁,提高了数据表的性能。InnoDB的锁通过锁定索引来实现,如果查询条件中有主键则锁定主键,如果有索引则先锁定对应索引然后再锁定对应的主键(可能造成死锁),如果连索引都没有则会锁定整个数据表。
MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部键等高级数据库功能。
隔离级别可能发生的问题脏读可重复读幻读读未提交read-uncommitted会会会读已提交read-committed 会会可重复读Repeatable read 会串行化Serializable
3.1、悲观锁(更新多,查询少时用) 如果我们采用悲观锁。就是我们在操作数据库时采用悲观的态度,认为别人会在此时并发访问数据库。我们在查询语句中select * from account where name=‘aaa’ for update; 等于加了排它锁。当A查询余额的时候,select money from account where name=‘aaa’ forupdate; 增加了排它锁,B查询账户余额的时候, select money from account wherename=‘aaa’ forupdate; 也要求对数据库加排它锁,因为A已经拿到了排它锁,导致B不能加锁,所以B只有等待A执行完毕,释放掉锁以后才能继续操作。
3.2、乐观锁(更新少,查询多时用) 如果我们采用乐观锁,就是我们在操作数据库的时候会认为没有其它用户并发访问,但是乐观锁也不是完全乐观的,乐观锁是采用版本号的方式进行控制的。在数据库表中有一列版本号。从数据库中查询的时候,将版本号也查询过来,在进行更新操作的时候,将版本号加1,查询条件的版本号还是查询过来的版本号。比如,A执行查询操作的时候,selectmoney,version from account where name=‘aaa’; 假设此时查询到的版本号为0,A在进行更新操作的时候 update account set money=money+100, version=version+1where name=‘aaa’ and version=0; 未提交时B来查询,查询到的版本号依然是 0,B也执行更新操作update account set money=money+100,version=version+1 wherename=‘aaa’ and version=0;现在A提交了事务,B再提交事务的时候发现版本号为 0的记录没有了,所以就避免了数据丢失的问题。不过这种情况也导致了多个用户更新操作时,只有一个用户的更新被执行。
3.3、行级锁(为某一条记录加锁) 如果想对数据库中的某条记录加行级锁,那么 where 条件后面必须为索引列。否则 for update加的都是表级锁。行级锁就是只对要访问的当前行加锁,其他用户访问其它行记录的时候可以访问。 select * from accountwhere id=1 for update;
3.4、表级锁(为一张表加锁) 在查询语句后增加 for update 时,where 条件后不是索引列,那么此时都是表级锁。select * fromaccount where name=‘aaa’ for update;
在了解脏读,不可重复度,幻读之前,首先要明白这三种情况的出现都是和数据库并发事务有关联的,如果所有的读写都按照队列的形式进行,就不会出现问题。
名词解析和解决方案 脏读
脏读又称无效数据读出(读出了脏数据)。一个事务读取另外一个事务还没有提交的数据叫脏读。
例如:事务T1修改了某个表中的一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,之后事务T1因为某种原因回滚(Rollback)了,那么事务T2读取的数据就是脏的(无效的)。
解决办法:把数据库的事务隔离级别调整到READ_COMMITTED(读提交/不可重复读)
不可重复读
不可重复读是指在同一个事务内,两次相同的查询返回了不同的结果。
例如:事务T1会读取两次数据,在第一次读取某一条数据后,事务T2修改了该数据并提交了事务,T1此时再次读取该数据,两次读取便得到了不同的结果。
解决办法:把数据库的事务隔离级别调整到REPEATABLE_READ(可重复读)
幻读
幻读也是指当事务不独立执行时,插入或者删除另一个事务当前影响的数据而发生的一种类似幻觉的现象。
例如:系统事务A将数据库中所有数据都删除的时候,但是事务B就在这个时候新插入了一条记录,当事务A删除结束后发现还有一条数据,就好像发生了幻觉一样。这就叫幻读。
解决办法:把数据库的事务隔离级别调整到SERIALIZABLE_READ(序列化执行),或者数据库使用者自己进行加锁来保证。
题外话: 不可重复读出现多是因为修改;幻读重点是新增、删除。mysql中的REPEATABLE_READ模式引入了间隙锁(GAP),解决了幻读的问题。不论是什么方式解决幻读,都会付出一定代价的性能让步。所以说在业务需求和技术方案之间权衡也是技术人员最需要掌握得技能之一。
nnoDB和MyISAM是在使用MySQL最常用的两个表类型,各有优缺点,视具体应用而定。下面是已知的两者之间的差别,仅供参考。
innodb InnoDB 给 MySQL 提供了具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。InnoDB 提供了行锁(locking on row level),提供与 Oracle 类型一致的不加锁读取(non-locking read in SELECTs)。这些特性均提高了多用户并发操作的性能表现。在InnoDB表中不需要扩大锁定(lock escalation),因为 InnoDB 的列锁定(row level locks)适宜非常小的空间。InnoDB 是 MySQL 上第一个提供外键约束(FOREIGN KEY constraints)的表引擎。
InnoDB 的设计目标是处理大容量数据库系统,它的 CPU 利用率是其它基于磁盘的关系数据库引擎所不能比的。在技术上,InnoDB 是一套放在 MySQL 后台的完整数据库系统,InnoDB 在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。 InnoDB 把数据和索引存放在表空间里,可能包含多个文件,这与其它的不一样,举例来说,在 MyISAM 中,表被存放在单独的文件中。InnoDB 表的大小只受限于操作系统的文件大小,一般为 2 GB。
在 http://www.innodb.com/ 上可以找到 InnoDB 最新的信息。InnoDB 手册的最新版本总是被放置在那里,并且在那里可以得到 InnoDB 的商业许可(order commercial licenses)以及支持。
InnoDB 现在(2001年十月)在一些大的需高性能的数据库站点上被使用。著名的 Internet 新闻站点 Slashdot.org 就是使用的 InnoDB。 Mytrix, Inc. 在 InnoDB 表上存储了超过 1 TB 的数据,而且另外的一个站点在 InnoDB 表上处理着平均每秒 800 次的插入/更新的负载。
MyISAM MyISAM 是MySQL缺省存贮引擎 .
每张MyISAM 桌被存放在盘在三个文件 。frm 文件存放表格定义。 数据文件是MYD (MYData) 。 索引文件是MYI (MYIndex) 引伸。
以下是一些细节和具体实现的差别:
1.InnoDB不支持FULLTEXT类型的索引。 2.InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含 where条件时,两种表的操作是一样的。 3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。 4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。 5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。
另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”
任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。
MyISAM与InnoDB 的区别(9个不同点)