1.事务
事务是要保证一组数据库操作,要么全部成功 要么全部失败,事务支持是在存储引擎层面实现的,myIsAM是不支持事务的,InnoDB支持事务。
事务的四大特性:
原子性: 一个事务本身是一个不可分割的最小单元,一组操作,要么全部执行,要么全部不执行,因此需要支持回滚 ,比如执行到某个sql出了异常,那么就回滚之前的所有的操作。
一致性: 状态必须从一个一致性状态 转移到另一个一致性状态,不能存在中间状态,比如说在执行到一半,数据库因为意外崩溃了,恢复之后
数据也还是原来的状态,因为这个事务并没有提交
隔离性:事务之间 不能互相影响,一个事务的执行不受其他正在执行的事务的影响,但是因为效率的问题,因此有了隔离级别的存在。
持久性:事务一旦提交,对数据的改变是“永久性”的,一般是指写入到磁盘中,意外丢失之后可以通过备份恢复,事实上是没有真正的永久的东西的。
隔离级别:
不同的隔离级别,会出现了 脏读,幻读,不可重复读等的问题,
隔离级别越高,效率越低,但事务互相间的影响越小,因此需要在两者之间找到平衡点。
脏读:读了另一个事务未提交的数据,如果那个事务回滚了,这个数据就是无效的脏数据, 在读未提交的隔离级别下会出现。
幻读: 在一个事务中,两次读相同条件下,读到的数据条数不同,也叫幻行, 可重复读隔离级别 也不能解决这个问题,只有串行能解决。
mysql的innoDB通过并发版本控制解决了这个问题。
不可重复读:在一个事务中,对相同的记录,多次读,读到的数据不同,可重复读级别可以解决这个问题。
sql标准的事务隔离级别有:
1.读未提交
一个事务还没提交的时候,另一个事务就已经可以读到这个事务做的更改,隔离级别最低,会出现脏读,幻读,不可重复读
2.读已提交
一个事务可以读到另一个事务提交后的更改 ,也因此无法保证两次查询的结果一致,因为第一次查询和第二次查询之间,可能有其他事务提交了修改。可以避免脏读,但是无法避免 幻读,不可重复读。
3.可重复读 ,这个也是mysql默认的隔离级别。
一个事务中,多次读同样的记录,读到的数据一致 ,但是在读一个范围的数据时候,无法保证读到的数据总行数一致,因为这个期间,可能有其他事务插入了新的数据。也叫幻行,属于幻读的一种,innodb通过并发版本控制解决了这个问题,
4.串行化
通过对事务中用到的每一行数据进行加锁,保证了事务的串行执行,效率最低,而且可能导致锁竞争引发的一系列问题。
隔离级别的实现:
1.读未提交,直接将记录上的最新值返回
2.读已提交,可重复读,则是创建了一个逻辑视图,返回的结果以逻辑视图为准
3.串行化,通过对数据的加锁避免并行访问
在mysql中,事务的回滚
每条记录在更新的时候都会记录一条回滚操作,记录上最新的值,都可以通过回滚操作,返回到上一个状态的值。 (这个回滚操作的日志叫undolog)
每个数据记录,都对应一个undolog ,如果依次有事务A B C 分别先后对这个数据记录进行了操作,那么会在undolog上面依次记录
但是不同的事务分别有自己的read view ,也就是有自己的视图。
回滚日志会在不需要的时候进行删除,不需要的条件就是,没有比这个回滚日志更早的readview的时候。
例如:
事务A 将 1改成了2 , 再2改成了3,然后将3改成4 此时4为当前值
回滚日志里面记录的是回滚操作,也就是 将2改成1, 将3改成2,将4改成3
如果事务A要回滚成1,那么就要对当前值4 依次执行上述的所有回滚操作,最终回滚成1.
没有比这个回滚日志更早的readview的时候,回滚日志可以被删除,
比如说 事务A提交了, 就没有更早的read view了,,这个回滚日志就可以删除了。
因此,尽量避免使用长事务,因为持续时间越长的事务,就会有越古老的事务视图,期间会操作的数据也可能越多,回滚日志也大,会占用大量的存储空间。长事务还会占用锁资源,搞不好会拖垮整个库。
同一个数据记录可以有多个版本,在mvvc(并发版本控制里面),不同的事务可能持有不同版本的数据,因此也会产生不同的回滚日志。
可以在information_schema库的innodb_trx这个表中查询长事务。
1.1 防止长事务的方式
1.设置 auto commit的值为1 ,也就是打开自动提交,忘了提交导致事务超长
2.去掉没有必要的只读事务,也就是在纯select里面加事务的
3.连接的时候 通过set max_execute_time 设置最大执行时间,避免意外执行很长时间
4.监控infomation_schema.innodb_trx表,发现长事务就报警或者kill