mysql分区分表区别
MySql分区、分表和分库数据库的数据量达到一定程度之后,为避免带来系统性能上的瓶颈。需要进行数据的处理,采用的手段是分区、分片、分库、分表。一些问题的解释:1.为什么要分表和分区?日常开发中我们经常...
2024.11.15之前分析一个死锁问题,发现自己对数据库隔离级别理解还不够清楚,所以趁着这几天假期,整理一下MySQL事务的四大隔离级别相关知识,希望对大家有帮助~
事务什么是事务?事务,由一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
假如A转账给B 100 元,先从A的账户里扣除 100 元,再在 B 的账户上加上 100 元。如果扣完A的100元后,还没来得及给B加上,银行系统异常了,最后导致A的余额减少了,B的余额却没有增加。所以就需要事务,将A的钱回滚回去,就是这么简单。
事务的四大特性原子性: 事务作为一个整体被执行,包含在其中的对数据库的操作要么全部都执行,要么都不执行。一致性: 指在事务开始之前和事务结束以后,数据不会被破坏,假如A账户给B账户转10块钱,不管成功与否,A和B的总金额是不变的。隔离性: 多个事务并发访问时,事务之间是相互隔离的,一个事务不应该被其他事务干扰,多个并发事务之间要相互隔离。。持久性: 表示事务完成提交后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。事务并发存在的问题事务并发执行存在什么问题呢,换句话说就是,一个事务是怎么干扰到其他事务的呢?看例子吧~
假设现在有表:
CREATE TABLE `account` (`id` int(11) NOT NULL,`name` varchar(255) DEFAULT NULL,`balance` int(11) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `un_name_idx` (`name`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8;复制代码表中有数据:
脏读(dirty read)假设现在有两个事务A、B:
假设现在A的余额是100,事务A正在准备查询Jay的余额这时候,事务B先扣减Jay的余额,扣了10最后A 读到的是扣减后的余额由上图可以发现,事务A、B交替执行,事务A被事务B干扰到了,因为事务A读取到事务B未提交的数据,这就是脏读。
不可重复读(unrepeatable read)假设现在有两个事务A和B:
事务A先查询Jay的余额,查到结果是100这时候事务B 对Jay的账户余额进行扣减,扣去10后,提交事务事务A再去查询Jay的账户余额发现变成了90事务A又被事务B干扰到了!在事务A范围内,两个相同的查询,读取同一条记录,却返回了不同的数据,这就是不可重复读。
幻读假设现在有两个事务A、B:
事务A先查询id大于2的账户记录,得到记录id=2和id=3的两条记录这时候,事务B开启,插入一条id=4的记录,并且提交了事务A再去执行相同的查询,却得到了id=2,3,4的3条记录了。事务A查询一个范围的结果集,另一个并发事务B往这个范围中插入/删除了数据,并静悄悄地提交,然后事务A再次查询相同的范围,两次读取得到的结果集不一样了,这就是幻读。
事务的四大隔离级别实践既然并发事务存在脏读、不可重复、幻读等问题,InnoDB实现了哪几种事务的隔离级别应对呢?
读未提交(Read Uncommitted)读已提交(Read Committed)可重复读(Repeatable Read)串行化(Serializable)读未提交(Read Uncommitted)想学习一个知识点,最好的方式就是实践之。好了,我们去数据库给它设置读未提交隔离级别,实践一下吧~
先把事务隔离级别设置为read uncommitted,开启事务A,查询id=1的数据
set session transaction isolation level read uncommitted;begin;select * from account where id =1;复制代码结果如下:
这时候,另开一个窗口打开mysql,也把当前事务隔离级别设置为read uncommitted,开启事务B,执行更新操作
set session transaction isolation level read uncommitted;begin;update account set balance=balance+20 where id =1;复制代码接着回事务A的窗口,再查account表id=1的数据,结果如下:
可以发现,在读未提交(Read Uncommitted) 隔离级别下,一个事务会读到其他事务未提交的数据的,即存在脏读问题。事务B都还没commit到数据库呢,事务A就读到了,感觉都乱套了。。。实际上,读未提交是隔离级别最低的一种。
已提交读(READ COMMITTED)为了避免脏读,数据库有了比读未提交更高的隔离级别,即已提交读。
把当前事务隔离级别设置为已提交读(READ COMMITTED),开启事务A,查询account中id=1的数据
set session transaction isolation level read committed;begin;select * from account where id =1;复制代码另开一个窗口打开mysql,也把事务隔离级别设置为read committed,开启事务B,执行以下操作
set session transaction isolation level read committed;begin;update account set balance=balance+20 where id =1;复制代码接着回事务A的窗口,再查account数据,发现数据没变:
我们再去到事务B的窗口执行commit操作:
commit;复制代码最后回到事务A窗口查询,发现数据变了:
由此可以得出结论,隔离级别设置为已提交读(READ COMMITTED) 时,已经不会出现脏读问题了,当前事务只能读取到其他事务提交的数据。但是,你站在事务A的角度想想,存在其他问题吗?
提交读的隔离级别会有什么问题呢?
在同一个事务A里,相同的查询sql,读取同一条记录(id=1),读到的结果是不一样的,即不可重复读。所以,隔离级别设置为read committed的时候,还会存在不可重复读的并发问题。
可重复读(Repeatable Read)如果你的老板要求,在同个事务中,查询结果必须是一致的,即老板要求你解决不可重复的并发问题,怎么办呢?老板,臣妾办不到?来实践一下可重复读(Repeatable Read) 这个隔离级别吧~
哈哈,步骤1、2、6的查询结果都是一样的,即repeatable read解决了不可重复读问题,是不是心里美滋滋的呢,终于解决老板的难题了~
RR级别是否解决了幻读问题呢?
再来看看网上的一个热点问题,有关于RR级别下,是否解决了幻读问题?我们来实践一下:
由图可得,步骤2和步骤6查询结果集没有变化,看起来RR级别是已经解决幻读问题了~ 但是呢,RR级别还是存在这种现象:
其实,上图如果事务A中,没有update account set balance=200 where id=5;这步操作,select * from account where id>2查询到的结果集确实是不变,这种情况没有幻读问题。但是,有了update这个骚操作,同一个事务,相同的sql,查出的结果集不同,这个是符合了幻读的定义~
这个问题,亲爱的朋友,你觉得它算幻读问题吗?
串行化(Serializable)前面三种数据库隔离级别,都有一定的并发问题,现在放大招吧,实践SERIALIZABLE隔离级别。
把事务隔离级别设置为Serializable,开启事务A,查询account表数据
set session transaction isolation level serializable;select @@tx_isolation;begin;select * from account;复制代码另开一个窗口打开mysql,也把事务隔离级别设置为Serializable,开启事务B,执行插入一条数据:
set session transaction isolation level serializable;select @@tx_isolation;begin;insert into account(id,name,balance) value(6,‘Li‘,100);复制代码执行结果如下:
由图可得,当数据库隔离级别设置为serializable的时候,事务B对表的写操作,在等事务A的读操作。其实,这是隔离级别中最严格的,读写都不允许并发。它保证了最好的安全性,性能却是个问题~
MySql隔离级别的实现原理实现隔离机制的方法主要有两种:
读写锁一致性快照读,即 MVCCMySql使用不同的锁策略(Locking Strategy)/MVCC来实现四种不同的隔离级别。RR、RC的实现原理跟MVCC有关,RU和Serializable跟锁有关。
读未提交(Read Uncommitted)官方说法:
select statements are performed in a nonlocking fashion, but a possible earlier version of a row might be used. Thus, using this isolation level, such reads are not consistent.
读未提交,采取的是读不加锁原理。
事务读不加锁,不阻塞其他事务的读和写事务写阻塞其他事务写,但不阻塞其他事务读;串行化(Serializable)官方的说法:
InnoDB implicitly converts all plain select statements to select ... FOR SHARE if autocommit is disabled. If autocommit is enabled, the select is its own transaction. It therefore is known to be read only and can be serialized if performed as a consistent (nonlocking) read and need not block for other transactions. (To force a plain select to block if other transactions have modified the selected rows, disable autocommit.)
所有select语句会隐式转化为select ... FOR SHARE,即加共享锁。读加共享锁,写加排他锁,读写互斥。如果有未提交的事务正在修改某些行,所有select这些行的语句都会阻塞。MVCC的实现原理MVCC,中文叫多版本并发控制,它是通过读取历史版本的数据,来降低并发事务冲突,从而提高并发性能的一种机制。它的实现依赖于隐式字段、undo日志、快照读&当前读、Read View,因此,我们先来了解这几个知识点。
隐式字段对于InnoDB存储引擎,每一行记录都有两个隐藏列DB_TRX_ID、DB_ROLL_PTR,如果表中没有主键和非NULL唯一键时,则还会有第三个隐藏的主键列DB_ROW_ID。
DB_TRX_ID,记录每一行最近一次修改(修改/更新)它的事务ID,大小为6字节;DB_ROLL_PTR,这个隐藏列就相当于一个指针,指向回滚段的undo日志,大小为7字节;DB_ROW_ID,单调递增的行ID,大小为6字节;undo日志事务未提交的时候,修改数据的镜像(修改前的旧版本),存到undo日志里。以便事务回滚时,恢复旧版本数据,撤销未提交事务数据对数据库的影响。 undo日志是逻辑日志。可以这样认为,当delete一条记录时,undo log中会记录一条对应的insert记录,当update一条记录时,它记录一条对应相反的update记录。 存储undo日志的地方,就是回滚段。
多个事务并行操作某一行数据时,不同事务对该行数据的修改会产生多个版本,然后通过回滚指针(DB_ROLL_PTR)连一条Undo日志链。
我们通过例子来看一下~
mysql> select * from account ;+----+------+---------+| id | name | balance |+----+------+---------+|1 | Jay| 100 |+----+------+---------+1 row in set (0.00 sec)复制代码假设表accout现在只有一条记录,插入该该记录的事务Id为100如果事务B(事务Id为200),对id=1的该行记录进行更新,把balance值修改为90事务B修改后,形成的Undo Log链如下:
快照读&当前读快照读:
读取的是记录数据的可见版本(有旧的版本),不加锁,普通的select语句都是快照读,如:
select * from account where id>2;复制代码当前读:
读取的是记录数据的最新版本,显示加锁的都是当前读
select * from account where id>2 lock in share mode;select * fromaccount where id>2 for update;复制代码Read ViewRead View就是事务执行快照读时,产生的读视图。事务执行快照读时,会生成数据库系统当前的一个快照,记录当前系统中还有哪些活跃的读写事务,把它们放到一个列表里。Read View主要是用来做可见性判断的,即判断当前事务可见哪个版本的数据~为了下面方便讨论Read View可见性规则,先定义几个变量
m_ids:当前系统中那些活跃的读写事务ID,它数据结构为一个List。 min_limit_id:m_ids事务列表中,最小的事务ID max_limit_id:m_ids事务列表中,最大的事务ID
如果DB_TRX_ID < min_limit_id,表明生成该版本的事务在生成ReadView前已经提交(因为事务ID是递增的),所以该版本可以被当前事务访问。如果DB_TRX_ID > m_ids列表中最大的事务id,表明生成该版本的事务在生成ReadView后才生成,所以该版本不可以被当前事务访问。如果 min_limit_id =MySql分区、分表和分库数据库的数据量达到一定程度之后,为避免带来系统性能上的瓶颈。需要进行数据的处理,采用的手段是分区、分片、分库、分表。一些问题的解释:1.为什么要分表和分区?日常开发中我们经常...
2024.11.15目录1.redo 日志1.1.为什么需要 REDO 日志?1.2.REDO 日志的好处与特点1.3.redo 的组成1.4.redo 的整体流程1.5.redo log 的刷盘策略1.6.不同刷盘策略...
2024.11.14mysql默认事务隔离级别在可重复读(REPEATABLE READS)隔离级别中,基于锁机制并发控制的DBMS需要对选定对象的读锁(read locks)和写锁(write locks)一直保持到事...
2024.11.15SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。mysql的4种事务隔离级别,如下所示...
2024.11.15作者:古时的风筝经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?本文就帮大家梳理一下。MySQL 事务本文所说的 MySQL 事务...
2024.11.15