Mysql实战45讲学习详情----为什么你改了我还看不见?
相关词汇:
MyISAM:MySQL原生引擎(不支持事务)
InnoDB:第三方引擎(支持事务)
ACID(Atomicity、Consistency、Isolation、Durability):原子性、一致性、隔离性、持久性
MVCC:数据库的多版本并发控制
事务的概念:
事务就是要保证一组数据库操作,要么全部成功,要么全部失败。
当数据库上有多个事务同时执行时,会出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些就需要“隔离级别”这个概念。
隔离级别:
隔离级别越高效率越低。四种级别依次往下,级别越高,效率越低,安全性越高。
【官方解释】
读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
【个人理解】
1.读未提交:你的事务还没提交,我在我的事务中也能查到你的结果
2.读提交:你的事务提交之后,我在我的事务中才能查到你的结果
3.可重复度:我的事务提交之前,我看到的数据是不会变的,你的事务不论提交还是不提交我都不去管
4.串行化:我的事务没提交,谁都不能动我的数据
实现形式(数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准):
①读未提交:直接返回最新值,没有视图概念
②读提交:在没条sql语句开始执行的时候创建视图
③可重复读:事务启动的时候创建视图
④串行化:用加锁的方式避免并行访问
设置隔离级别:
将启动参数 transaction-isolation 的值设置成 READ-COMMITTED
(将隔离级别设置为读提交)
-- 查看参数“transaction_isolation”的值:
show variables like ‘transaction_isolation‘;
--修改参数“transaction_isolation”的值为“READ-COMMITTED”:
| transaction_isolation | READ-COMMITTED |
【可重复读隔离级别的应用场景】核对账单时,用户发生一笔交易,此时用可重复读的隔离级别,视图不会发生变化,不受其他事物影响。
事务隔离的实现:
每条记录更新的时候都会同时记录一条回滚操作,记录的最新值通过回滚操作,都可以得到前一个状态的值。
同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。
回滚日志什么时候删除呢?
在不需要的时候删除。
那什么时候不需要了呢?
就是当系统里没有比这个回滚日志更早的 read-view 的时候。
那为什么不建议用长事务呢?
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
MySQL的事务启动方式:
1.显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
2.set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。
有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。
因此建议总是使用 set autocommit=1, 通过显式语句的方式来启动事务。
PS:在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。
可以在 information_schema 库的 innodb_trx 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务。
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
附加解释:
脏读:
当数据库中一个事务A正在修改一个数据但是还未提交或者回滚,
另一个事务B 来读取了修改后的内容并且使用了,之后事务A提交了,此时就引起了脏读。
此情况仅会发生在: 读未提交的的隔离级别.
不可重复读:
在一个事务A中多次操作数据,在事务操作过程中(未最终提交),
事务B也才做了处理,并且该值发生了改变,这时候就会导致A在事务操作的时候,发现数据与第一次不一样了。 就是不可重复读。
此情况仅会发生在:读未提交、读提交的隔离级别.
幻读:
一个事务按相同的查询条件重新读取以前检索过的数据,
却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻读。
此情况会回发生在:读未提交、读提交、可重复读的隔离级别.
附加问题:
现在知道了系统里面应该避免长事务,如果你是业务开发负责人同时也是数据库负责人,你会有什么方案来避免出现或者处理这种情况呢?
【个人理解】
开发时缩小事务范围,使用 set autocommit=1, 通过显式语句的方式来启动事务,监控innodb_trx表,发现长事务及时处理。