复杂的深分页问题优化

简介: 基础

MySQL是怎么解决幻读的问题的?

幻读就是事务执行过程中,在查询一个范围的数据时,有新的数据插入到这个范围,导致两次查询的数据不一致。因为读分为快照读和实时读,

快照读

我们普通的SELECT语句都是普通读,也就是读取的数据都是事务开始时那个状态的数据,普通读的幻读问题主要是通过MVCC来解决的,具体可以看上面的MVCC中的查询操作。

实时读

SELECT *** FOR UPDATE 在查询时会先申请X锁

SELECT *** IN SHARE MODE 在查询时会先申请S锁

就是实时读,就是读取的是实时的数据,而不快照数据,读的时候会加Next-Key Lock锁住当前的记录,以及左右两个区间的间隙,这样在读的时候就不能往我们的查询范围插入数据了。

MySQL中有哪些锁?

全局锁

就是对整个数据库加锁,让整个数据库处于只读状态,所有更新操作停止。(如果是主库就不能执行更新语句,从库也不能执行同步过来的bin log)

最常用的场景是对数据库加锁,让数据库只能读,然后对整个数据库做逻辑备份(就是将所有数据生成SQL写入备份文件。)

做逻辑备份有三种方式:

1.全局锁

对数据库执行

Flush tables with read lock命令让整个库处于只读状态。

2.利用innodb的事务隔离性(可重复读)

就是通过官方自带的逻辑备份工具mysqldump来进行逻辑备份时,可以设置一个参数-single-transaction,这样导数据的时候就会开启一个事务,这样利用innodb的mvcc机制可以保证在事务执行过程中,读到的数据都跟事务开始时的一致,并且执行过程中,其他事务可以执行更新操作, 不会对他造成影响。这种方法必须要求数据库所有表的引擎都是innodb才行。

3.set global readonly=true

执行这个命令也可以让全库只能读,但是第一有些系统会使用readonly来做一个操作,例如根据readonly是否为true判断数据库是否是从库,第二是如果执行这个命令后,客户端断开连接后,数据库会一直处于只读状态,如果是FTWRL命令发送异常会释放全局锁。(如果是从库,设置read-only对super user权限无效)

表级锁

表级别的锁有两种,一种是表锁,一种是元数据锁MDL。

表锁 lock table

就是使用lock table user_table read/write命令来对表进行加读锁或者写锁 加读锁后,表对所有线程都是只能读,即便是当前线程也只能读表,不然会数据不一致。 加写锁后,表是对当前线程写,其他线程不能读,不然会数据不一致。 可以通过unlock tables来解锁,客户端断开时也会自动释放锁,但是影响所有线程,影响面太大了。

元数据锁MDL(MetaData Lock)

分为读锁和写锁,加读锁时,所有的线程都可以读表,加写锁时,只能一个线程写,其他的不能读。 锁不用显式使用,是访问一个表时,自动加上的。 对表执行普通SQL语句对表数据进行增删改查时,会加读锁。 对表结构做修改时,会加写锁。

元数据锁是为了修改表结构不会出现问题而设计的,因为一边修改表结构一边读数据可能会读到脏数据,所以在增删改查时会申请读锁,在这个期间不能修改表结构,要修改表结构需要先申请写锁,申请成功后对表结构进行修改,在这个期间不能进行增删改查。

自增锁

插入语句主要分为两种: 1.能确定插入行数的,例如插入一条或者多条数据,INSERT... 2.不能确定行数的,例如从一个表查询出满足条件的数据,然后插入另外一个表,INSERT...SELECT

在所有模式中,如果一个事务回滚,这些自增值将被“丢失”。

innodb_autoinc_lock_mode为0 这种是tradition模式,每次执行一条插入语句时都会去申请表级别的auto_increment锁

innodb_autoinc_lock_mode为1 这种是consecutive模式,执行不确定数量的插入语句时,才会去申请表级别的auto_increment锁, 执行确定数量的插入语句时,只需要执行前去获取 AUTO_INCREMENT 计数器的互斥锁并在获取主键后直接释放, 不需要等待当前语句执行完成。

innodb_autoinc_lock_mode为2 交叉模式 所有的插入语句都不需要获取表级别的 AUTO_INCREMENT 锁, 如果binlog_format为statement模式,如果从服务器上的计数器的值可能会与主服务器不一致, 可能会有同一行数据在主从数据库上id不一样的情况,如果binlog_format为row模式,那么就不影响。

意向锁

意向锁定的主要目的是表明有人正在锁定表中的行,或者打算锁定表中的行。意向锁的作用主要在于,当一个事务去申请表级别的排斥锁X,共享锁S时,需要去判断是否有其他事务在修改数据行,或者让数据行处于只读状态。假如没有意向锁,可能需要查询每一行数据,判断是否加了行锁。而如果有意向锁的情况下,可以快速进行判断,只需要判断当前表是否有加意向锁就可以了,减小了性能开销。

意向共享锁(IS锁)

事务让一行数据只能读,需要申请对这行数据加行级别的锁共享锁S,在申请S锁之前会主动申请表级别的共享意向锁IS锁。

意向排斥锁(IX锁)

事务在更新某一行数据时,需要申请对这行数据加行级别的锁排斥锁X,在申请X锁之前会申请IX

意向锁之间是兼容的,IS锁和IX是兼容,因为可能我们对第一行数据加S锁,那么会申请IS锁,对第二行数据加X锁,此时跟第一行的数据的S锁不冲突,所以也会先申请IX锁,由此可见,IS锁和IX之间不冲突,IS锁,IX锁与行级别的S,行级别的X之间也不冲突。

意向锁只是跟表级别的S,X锁可能会冲突,

场景1:假设一个事务要加表级别的S锁,让整个表只能被读。那么如果当前有意向锁IX,说明有其他事务在改数据,那么不能加,只能进行等待,等事务改完是否意向锁IX。

场景2:假设当前事务要加表级别的S锁时,让整个表只能被读。只有IS意向锁,没有IX锁,说明只是有其他事务在让数据只能被读取,不能被修改,那么加表级别S锁,也不会其他事务造成影响。

场景3:假设当前事务要加表级别的X锁时,让整个表只能被这个事务写,不能被其他事务读。如果现在有其他事务加了意向读锁IS,说明有其他事务在让一些数据行只能被读,或者是一些写锁IX,说明其他事务让一些数据行正在被修改。那么当前要加表级别的X锁就不行,会跟其他事务冲突,只能等其他事务执行完毕才能申请成功。

表级别的S锁 表级别的X锁
意向读锁IS 兼容 不兼容
意向写锁IX 不兼容 不兼容
那么意向锁的作用是什么呢?

假如没有意向锁,我们执行lock table read命令来申请表锁,让整个表只能读,在获得表级别的只读锁之前,需要执行的步骤是:

1.数据库会先判断当前表是否加了表级别的排斥锁,因为这个时候要是加了排斥锁,是只能由加了那个排斥锁的事务来更新数据,其他事务都不能读数据,只能阻塞等待。

2.如果当前表没有加表级别的排斥锁,那么就需要对每一行数据进行判断,判断是否加了行级别的X锁,如果加了只能阻塞等待,这样需要对一行进行判断,性能开销太大了。

所以才有了意向锁,在获得表级别的只读锁之前,需要执行的步骤是:

1.第一步还是跟上面的步骤一一样

2.第二步只需要判断当前锁是否加了表级别的意向排斥锁,因为如果加了意向排斥锁,说明正在有事务在对数据加行锁,对数据进行更新,这样避免了对每一行数据进行判断,判断是否加了行锁。

相关文章
|
1月前
|
SQL Java 数据库连接
谈谈mybatispllus的分页原理
【5月更文挑战第23天】MyBatis 是一个流行的持久层框架,它支持自定义 SQL、存储过程以及高级映射。分页是开发中常见的需求,尤其是在处理大量数据时,合理的分页可以有效提升系统性能和用户体验。MyBatis 提供了几种不同的分页方式,本文将对这些方式进行介绍。
24 2
|
1月前
|
SQL 存储 关系型数据库
深分页怎么导致索引失效了?提供6种优化的方案!
深分页怎么导致索引失效了?提供6种优化的方案!
|
1月前
【从浅入深,全面掌握数组的操作与优化技巧】
【从浅入深,全面掌握数组的操作与优化技巧】
|
1月前
|
存储 缓存 大数据
深度分页问题
深度分页问题
|
1月前
|
存储 SQL 前端开发
解决深度分页问题
解决深度分页问题
|
6月前
|
前端开发 Java UED
通用分页集模糊,全部查询,分页查询为一体(2)演示,优化上篇通用查询分页
通用分页集模糊,全部查询,分页查询为一体(2)演示,优化上篇通用查询分页
|
9月前
|
SQL 关系型数据库 MySQL
数据库深分页介绍及优化方案
在前端页面显示,为了避免一次性展示全量数据,通过上下翻页或指定页码的方式查看部分数据,就像翻书一样,这就利用了 MySQL 的分页查询。
152 0
|
Java 数据库
ES深度分页问题解决方案
ES深度分页问题
274 0
|
缓存 前端开发 数据可视化
前端基础向--空表格处理与分页调整,优化用户体验
前端基础向--空表格处理与分页调整,优化用户体验
160 0
|
SQL 算法 关系型数据库
[MySQL优化案例]系列 — 分页优化
[MySQL优化案例]系列 — 分页优化
128 0