mysql

lobo · 2018-09-28 11:26:56 · 1846 次点击 · 预计阅读时间 3 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2018-09-28 11:26:56 的文章,其中的信息可能已经有所发展或是发生改变。

1、mvcc 多版本控制(可重复读隔离级别)

在Mysql中MVCC是在Innodb存储引擎中得到支持的,Innodb为每行记录都实现了三个隐藏字段:

(1)、事务ID(DB_TRX_ID 6-byte ):最新插入或修改行的事务ID,删除是特殊的修改,会有1bit标示;InnoDB内部维护了一个递增的tx id counter,其当前值可以通过show engine innodb status获得

(2)、回滚指针(DB_ROLL_PTR 7-byte):指向回滚日志的undo log

(3)、隐藏的ID 6-byte

image.png

当事务更改某行的值时,操作如下:

(1)、用排他锁锁定该行

(2)、记录redo log

(3)、把该行修改前的值Copy到undo log

(4)、修改当前行的值,填写事务编号,使回滚指针指向undo log中的修改前的行

image.png

(5)、如果事务1最后执行COMMIT操作,则什么操作都不用做。如果执行ROLLBACK操作,则需要通过回滚指针从undo log中还原修改前的数据。

(6)、释放该行的排它锁

=====================================================================

read view 判断当前版本数据项是否可见:

在InnoDB中,创建一个新事务的时候,InnoDB会将当前系统中的活跃事务列表(trx_sys->trx_list)创建一个副本(read view),副本中保存的是系统当前不应该被本事务看到的其他事务id列表。

当用户在这个事务中要读取该行记录的时候,InnoDB会将该行当前的版本号与该read view进行比较。

注:新建事务(当前事务)与正在内存中commit 的事务不在活跃事务链表中。

函数:read_view_sees_trx_id。

read_view中保存了当前全局的事务的范围:【low_limit_id, up_limit_id】

  1. 当行记录的事务ID小于当前系统的最小活动id,就是可见的。

      if (trx_id < view->up_limit_id) {

        return(TRUE);

      }

  2. 当行记录的事务ID大于当前系统的最大活动id(也就是尚未分配的下一个事务的id),就是不可见的。

      if (trx_id >= view->low_limit_id) {

        return(FALSE);

      }

  3. 当行记录的事务ID在活动范围之中时,判断是否在活动链表中,如果在就不可见,如果不在就是可见的。

      for (i = 0; i < n_ids; i++) {

        trx_id_t view_trx_id       = read_view_get_nth_trx_id(view, n_ids - i - 1);

        if (trx_id <= view_trx_id) {

        return(trx_id != view_trx_id);

        }

      }

理想状态下,事务1的事务id=1,事务2的事务id=2。因为事务2执行时查询时,事务1正处于事务执行状态。

所以read view为{1},事务2读取的数据行 trx_id=1,read view中最早的事务id为trx_id_min=1, 最迟的事务id为trx_id_max=1。

因为trx_id_min <= trx_id <= trx_id_max,并且trx_id_min = trx_id = trx_id_max,说明该行记录所在事务在本次新事务创建的时候处于活动状态,不可见。

所以从该行记录的DB_ROLL_PTR指针所指向的回滚段中取出最新的undo-log的版本号的数据,将该可见行的值返回。所以不会出现脏读的现象。

=====================================================================

InnoDB行锁:

是通过索引上的索引项来实现的,通过在数据中对相应数据行加锁来实现的。

InnoDB这种行锁实现特点意味者:只有通过索引条件检索数据,InnoDB才会使用行级锁,否则,InnoDB将使用表锁!

Mysql中的间隙锁:

1、间隙划分;

2、间隙锁锁定区域,根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B);

3、间隙锁的目的是为了防止幻读:

(1)防止间隙内有新数据被插入;

(2)防止已存在的数据,更新成间隙内的数据(例如防止numer=3的记录通过update变成number=5);

4、nnodb自动使用间隙锁的条件:

(1)必须在RR级别下;

(2)检索条件必须有索引(没有索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加);

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1846 次点击  
加入收藏 微博
3 回复  |  直到 2019-04-03 10:33:40
lobo
lobo · #1 · 6年之前

四种安全隔离级别:

https://www.2cto.com/uploadfile/Collfiles/20171120/2017112009363511.png

脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。

幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

lobo
lobo · #2 · 6年之前

MyISAM与InnoDB区别: 链接:🔗https://www.jianshu.com/p/a957b18ba40d

1、 存储结构

MyISAM:每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。.frm文件存储表定义。数据文件的扩展名为.MYD (MYData)。索引文件的扩展名是.MYI (MYIndex)。 InnoDB:所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB。

2、 存储空间

MyISAM:可被压缩,存储空间较小。支持三种不同的存储格式:静态表(默认,但是注意数据末尾不能有空格,会被去掉)、动态表、压缩表。 InnoDB:需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。

3、 可移植性、备份及恢复

MyISAM:数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作。 InnoDB:免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了。

4、 事务支持 MyISAM:强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持。 InnoDB:提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。

5、表锁差异

MyISAM:只支持表级锁,用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据。 InnoDB:支持事务和行级锁,是innodb的最大特色。行锁大幅度提高了多用户并发操作的新能。但是InnoDB的行锁,只是在WHERE的主键是有效的,非主键的WHERE都会锁全表的。

6、CURD操作

MyISAM:如果执行大量的SELECT,MyISAM是更好的选择。 InnoDB:如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表。DELETE 从性能上InnoDB更优,但DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除,在innodb上如果要清空保存有大量数据的表,最好使用truncate table这个命令。

lobo
lobo · #3 · 6年之前

Limit的执行效率高,是对于一种特定条件下来说的:即数据库的数量很大,但是只需要查询一部分数据的情况。   高效率的原理是:避免全表扫描,提高查询效率。

  比如:每个用户的email是唯一的,如果用户使用email作为用户名登陆的话,就需要查询出email对应的一条记录。   SELECT FROM t_user WHERE email=?;   上面的语句实现了查询email对应的一条用户信息,但是由于email这一列没有加索引,会导致全表扫描,效率会很低。   SELECT FROM t_user WHERE email=? LIMIT 1;   加上LIMIT 1,只要找到了对应的一条记录,就不会继续向下扫描了,效率会大大提高。

使用limit效率低,那就是:只使用limit来查询语句,并且偏移量特别大的情况

  做以下实验: 语句1:   select from table limit 150000,1000;   语句2:   select from table while id>=150000 limit 1000;   语句1为0.2077秒;语句2为0.0063秒   两条语句的时间比是:语句1/语句2=32.968

where...limit....性能基本稳定,受偏移量和行数的影响不大,而单纯采用limit的话,受偏移量的影响很大,当偏移量大到一定后性能开始大幅下降。不过在数据量不大的情况下,两者的区别不大。

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传