面试官:MySQL连环炮,你顶不顶得住?

Java面试突击系列(一):消息队列的面试连环炮 消息队列的面试连环炮 前言 你用过消息队列么? 说说你们项目里是怎么用消息队列的? 我们有一个订单系统,订单系统会每次下一个新订单的时候,就会发送一条消息到ActiveMQ里面去,后台有一个库存系统,负责获取消息,然后更新库存。 为什么使用消息队列? 你的订单系统不发送消息到MQ,而是直接调用库存系统的一个接口,然后直接调用成功了,库存也更新了,那就不需要使用消息队列了呀 使用消息队列的主要作用是:异步、解耦、削峰 消息队列都有什么优缺点? Kafka、activeMQ、RibbitMQ、Ro 阅读详情

最近后台好多小伙伴发私信咨询俺,问马上就是金九银十的面试黄金期了,该如何在三个月内突击一下,以便更好地通过面试呢?

我的的想法就是需要对自己掌握的知识进行归纳整理,系统的、分类的去复习相关的知识点。这样也能更好的把自己陌生的内容框出来,在暴漏自己短板的同时,更针对性的做准备。

为了节省大家的时间,我决定帮大家先将 MySQL 面试相关的知识进行汇总整理,一改往日知识点东一棒槌西一榔头的问题。那么,你准备好迎接 MySQL 连环炮了嘛?

  • 1、三大范式

  • 2、DML 语句和 DDL 语句区别

  • 3、主键和外键的区别

  • 4、drop、delete、truncate 区别

  • 5、基础架构

  • 6、MyISAM 和 InnoDB 有什么区别?

  • 7、推荐自增id作为主键问题

  • 8、为什么 MySQL 的自增主键不连续

  • 9、redo log 是做什么的?

  • 10、redo log 的刷盘时机

  • 11、redo log 是怎么记录日志的

  • 12、什么是 binlog

  • 13、binlog 记录格式

  • 14、binlog 写入机制

  • 15、redolog 和 binlog 的区别是什么

  • 16、两阶段提交

  • 17、什么是 undo log.

  • 18、什么是 relaylog

  • 19、索引

  • 20、Hash 索引

  • 21、B树和B+ 树

  • 22、主键索引

  • 23、二级索引

  • 24、聚簇索引与非聚簇索引

  • 25、回表

  • 26、覆盖索引和联合索引

  • 27、最左前缀匹配原则

  • 28、索引下推

  • 29、隐式转换

  • 30、普通索引和唯一索引该怎么选择?

  • 31、避免索引失效

  • 32、建立索引的规则

  • 33、事务极其特性

  • 34、并发事务带来的问题

  • 35、事务的隔离级别

  • 36、MVCC

  • 37、Mysql 中的锁

  • 38、查询语句执行过程

  • 39、更新语句执行过程

  • 40、sql 优化

  • 41、主从同步数据

  • 42、主从延迟要怎么解决

  • 43、为什么不要使用长事务

1、三大范式

1NF(第一范式):属性(对应于表中的字段)不能再被分割,也就是这个字段只能是一个值,不能再分为多个其他的字段了。1NF 是所有关系型数据库的最基本要求 ,也就是说关系型数据库中创建的表一定满足第一范式。

2NF(第二范式):2NF 要求数据库表中的每个实例或行必须可以被惟一地区分,2NF 在 1NF 的基础上增加了一个列,这个列称为主键,非主属性都依赖于主键。

3NF(第三范式):3NF 在 2NF 的基础之上,要求每列都和主键列直接相关,而不是间接相关,即不存在其他表的非主键信息。

在开发过程中,并不一定要满足三大范式,有时候为了提高查询效率,可以在表中冗余其他表的字段。

2、DML 语句和 DDL 语句区别

  • DML 是数据库操作语言(Data Manipulation Language)的缩写,是指对数据库中表记录的操作,主要包括表记录的插入、更新、删除和查询,是开发人员日常使用最频繁的操作。

  • DDL (Data Definition Language)是数据定义语言的缩写,简单来说,就是对数据库内部的对象进行创建、删除、修改的操作语言。它和 DML 语言的最大区别是 DML 只是对表内部数据的操作,而不涉及到表的定义、结构的修改,更不会涉及到其他对象。DDL 语句更多的被数据库管理员(DBA)所使用,一般的开发人员很少使用。

3、主键和外键的区别

  • 主键:用于唯一标识一行数据,不能有重复,不允许为空,且一个表只能有一个主键;

  • 外键:用来和其他表建立联系,外键是另一表的主键,外键是可以有重复的,可以是空值。一个表可以有多个外键;

4、drop、delete、truncate 区别

(1)用法不同

  • drop(丢弃数据): drop table 表名 ,直接将表结构都删除掉,在删除表的时候使用。

  • truncate (清空数据) : truncate table 表名 ,只删除表中的数据,再插入数据的时候自增长 id 又从 1 开始,在清空表中数据的时候使用。

  • delete(删除数据) : delete from 表名 where 列名=值,删除某一行的数据,如果不加 where 子句和truncate table 表名作用类似。

(2)属于不同的数据库语言

  • truncate 和 drop 属于 DDL(数据定义语言)语句,操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发 trigger。

  • delete 语句是 DML (数据库操作语言)语句,这个操作会放到 rollback segment 中,事务提交之后才生效。

(3)执行速度不同

  • delete命令执行的时候会产生数据库的binlog日志,而日志记录是需要消耗时间的,但是也有个好处方便数据回滚恢复。

  • truncate命令执行的时候不会产生数据库日志,因此比delete要快。除此之外,还会把表的自增值重置和索引恢复到初始大小等。

  • drop命令会把表占用的空间全部释放掉。

一般来说:drop > truncate > delete

5、基础架构

下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到客户端的一条 SQL 语句在 MySQL 内部是如何执行的。

img

  • 连接器: 身份认证和权限相关(登录 MySQL 的时候)。

  • 查询缓存: 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。

  • 分析器: 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。

  • 优化器: 按照 MySQL 认为最优的方案去执行。

  • 执行器: 执行语句,然后从存储引擎返回数据。执行语句之前会先判断是否有权限,如果没有权限的话,就会报错。

  • 插件式存储引擎:主要负责数据的存储和读取,采用的是插件式架构,支持 InnoDB、MyISAM、Memory 等多种存储引擎。

6、MyISAM 和 InnoDB 有什么区别?

MySQL 5.5 之前,MyISAM 引擎是 MySQL 的默认存储引擎,MySQL 5.5 版本之后,InnoDB 是 MySQL 的默认存储引擎。

(1)是否支持行级锁

MyISAM 只有表级锁,而 InnoDB 支持行级锁和表级锁,默认为行级锁。

具体的锁详情请参考阿Q的此篇文章:面试必备常见存储引擎与锁的分类,请查收

(2)是否支持事务

MyISAM 不提供事务支持,InnoDB 提供事务支持,实现了 SQL 标准定义的四个隔离级别,具有提交和回滚事务的能力。

InnoDB 默认使用的 REPEATABLE-READ(可重读)隔离级别是可以解决幻读问题发生的(基于 MVCC 和 Next-Key Lock)。

关于 MySQL 事务以及解决幻读的详细介绍,可以看看阿Q写的这篇文章:InnoDB 解决幻读的方案--LBCC&MVCC

(3)是否支持外键

MyISAM 不支持,而 InnoDB 支持。

(4)是否支持数据库异常崩溃后的安全恢复

MyISAM 不支持,而 InnoDB 支持。使用 InnoDB 的数据库在异常崩溃后,数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 redo log 。

(5)是否支持 MVCC

MyISAM 不支持,而 InnoDB 支持。

(6)索引实现

虽然 MyISAM 引擎和 InnoDB 引擎都是使用 B+Tree 作为索引结构,但是两者的实现方式不太一样。

  • InnoDB 引擎中,其数据文件本身就是索引文件。其表数据文件本身就是按 B+Tree 组织的一个索引结构,树的叶子节点 data 域保存了完整的数据记录。

  • MyISAM 索引文件和数据文件是分离的,索引保存的是数据文件的指针。

(7)性能差别

InnoDB 的性能比 MyISAM 更强大,不管是在读写混合模式下还是只读模式下,随着 CPU 核数的增加,InnoDB 的读写能力呈线性增长。MyISAM 因为读写不能并发,它的处理能力跟核数没关系。

InnoDB 和 MyISAM 性能对比

7、推荐自增id作为主键问题

  • 普通索引的 B+ 树上存放的是主键索引的值,如果该值较大,会「导致普通索引的存储空间较大」

  • 使用自增 id 做主键索引新插入数据只要放在该页的最尾端就可以,直接「按照顺序插入」,不用刻意维护

  • 页分裂容易维护,当插入数据的当前页快满时,会发生页分裂的现象,如果主键索引不为自增 id,那么数据就可能从页的中间插入,页的数据会频繁的变动,「导致页分裂维护成本较高」

8、为什么 MySQL 的自增主键不连续

  • 在MySQL 5.7及之前的版本,自增值保存在内存里,并没有持久化;

  • 唯一键冲突:插入数据时先将自增主键+1,然后插入数据时唯一键冲突,插入数据失败,但是未将自增主键改回;

  • 事务回滚:和唯一键冲突类似,回滚操作时自增值也不回退,事实上,这么做的主要原因是为了提高性能。

9、redo log 是做什么的?

redo log(重做日志)是InnoDB存储引擎独有的,它让MySQL拥有了崩溃恢复能力。

比如 MySQL 实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据,保证数据的持久性与完整性。

更新表数据的时候,如果发现 Buffer Pool 里存在要更新的数据,就直接在 Buffer Pool 里更新。然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer)里,接着刷盘到 redo log 文件里。

10、redo log 的刷盘时机

  • 红色部分为 redo log buffer 属于内存

  • 黄色部分为 page cache ,此时已经写入磁盘了,但是未进行持久化

  • 绿色部分是硬盘,已经完成持久化

InnoDB 存储引擎为 redo log 的刷盘策略提供了 innodb_flush_log_at_trx_commit 参数,它支持三种策略

  • 设置为0的时候,表示每次事务提交时不进行刷盘操作,只是保留在 redo log buffer中,mysql 崩溃会丢失1s的数据;

  • 设置为1的时候,表示每次事务提交时都将进行刷盘操作(默认值),持久化到磁盘;

  • 设置为2的时候,表示每次事务提交时都只把redo log buffer内容写入page cache,OS宕机会丢失1s的数据,因为未进行持久化;

innodb_flush_log_at_trx_commit 参数默认为 1 ,也就是说当事务提交时会调用 fsync(同步操作) 对 redo log 进行刷盘。

另外 InnoDB 存储引擎有一个后台线程,每隔1秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。

redo log buffer占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动刷盘。

11、redo log 是怎么记录日志的

硬盘上存储的 redo log 日志文件不只一个,而是以一个日志文件组的形式出现的,每个的redo日志文件大小都是一样的。

比如可以配置为一组4个文件,每个文件的大小是 1GB,整个 redo log 日志文件组可以记录4G的内容。

它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写,如下图所示。

所以,如果数据写满了但是还没有来得及将数据真正的刷入磁盘当中,那么就会发生「内存抖动」现象,从肉眼的角度来观察会发现 mysql 会宕机一会儿,此时就是正在刷盘了。

12、什么是 binlog

binlog 是归档日志,属于 Server 层的日志,是一个二进制格式的文件,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”。

不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。它的主要作用就是数据备份、主从复制。

binlog会记录所有涉及更新数据的逻辑操作,属于逻辑日志,并且是顺序写。

13、binlog 记录格式

binlog 日志有三种格式,可以通过binlog_format参数指定。

  • statement :记录的内容是SQL语句原文,存在数据一致性问题;

  • row:记录包含操作的具体数据,能保证同步数据的一致性;

  • mixed:记录的内容是前两者的混合,MySQL会判断这条SQL语句是否可能引起数据不一致:如果是,就用row格式,否则就用statement格式。

14、binlog 写入机制

事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。

因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。

我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。

binlog 也提供了 sync_binlog 参数来控制写入 page cache 和磁盘的时机:

  • 0:每次提交事务都只写入到文件系统的 page cache,由系统自行判断什么时候执行fsync,机器宕机,page cache里面的 binlog 会丢失。

  • 1:每次提交事务都会执行fsync,就如同 redo log 日志刷盘流程 一样。

  • N(N>1):每次提交事务都写入到文件系统的 page cache,但累积N个事务后才fsync。如果机器宕机,会丢失最近N个事务的binlog日志。

15、redolog 和 binlog 的区别是什么

  • redolog 是 Innodb 独有的日志,而 binlog 是 server 层的,所有的存储引擎都有使用到;

  • redolog 记录了具体的数值,对某个页做了什么修改,binlog 记录的操作内容;

  • binlog 大小达到上限或者 flush log 会生成一个新的文件,而 redolog 有固定大小只能循环利用;

  • binlog 日志没有 crash-safe 的能力,只能用于归档,而 redo log 有 crash-safe 能力;

  • redo log 在事务执行过程中可以不断写入(刷盘设置为1,后台线程1s执行一次或者 redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候),而 binlog 只有在提交事务时才写入文件缓存系统;

16、两阶段提交

假设执行 sql 过程中写完 redo log 日志后,binlog 日志写期间发生了异常,会出现什么情况呢?

由于 binlog 没写完就异常,这时候 binlog 里面没有对应的修改记录。因此,之后用 binlog 日志恢复数据时,就会少这一次更新,最终数据不一致。

为了解决两份日志之间的逻辑一致问题,InnoDB 存储引擎使用两阶段提交方案。

将 redo log 的写入拆成了两个步骤 prepare 和 commit,这就是两阶段提交。使用两阶段提交后,写入 binlog 时发生异常也不会有影响,因为 MySQL 根据 redo log日志恢复数据时,发现 redo log 还处于 prepare 阶段,并且没有对应 binlog 日志,就会回滚该事务。

再看一个场景,redo log 设置 commit 阶段发生异常,那会不会回滚事务呢?

并不会回滚事务,虽然 redo log 是处于 prepare 阶段,但是能通过事务id找到对应的 binlog 日志,所以 MySQL 认为是完整的,就会提交事务恢复数据。

17、什么是 undo log.

我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作(INSERT、DELETE、UPDATE)进行回滚,在 MySQL 中,恢复机制是通过回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。

每次对记录进行改动都会记录一条 undo log,每条 undo log 也都有一个DB_ROLL_PTR属性,可以将这些 undo log 都连起来,串成一个链表,形成版本链。

版本链的头节点就是当前记录最新的值。

18、什么是 relaylog

relaylog 是中继日志,在主从同步的时候使用到,它是一个中介临时的日志文件,用于存储从 master 节点同步过来的 binlog 日志内容。

master 主节点的 binlog 传到 slave 从节点后,被写入 relay log 里,从节点的 slave sql 线程从 relaylog 里读取日志然后应用到 slave 从节点本地。

从服务器 I/O 线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后 SQL 线程会读取 relay-log 日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。

19、索引

索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据。

索引的作用就相当于书的目录。打个比方: 我们在查字典的时候,如果没有目录,那我们就只能一页一页的去找我们需要查的那个字,速度很慢。如果有目录了,我们只需要先去目录里查找字的位置,然后直接翻到那一页就行了。

20、Hash 索引

哈希表是键值对的集合,通过键(key)即可快速取出对应的值(value),因此哈希表可以快速检索数据(接近 O(1))。

但是!哈希算法有个 Hash 冲突问题,也就是说多个不同的 key 最后得到的 index 相同。通常情况下,我们常用的解决办法是 链地址法。

链地址法就是将哈希冲突数据存放在链表中。就比如 JDK1.8 之前 HashMap 就是通过链地址法来解决哈希冲突的。不过,JDK1.8 以后 HashMap 为了减少链表过长的时候搜索时间过长引入了红黑树。

为了减少 Hash 冲突的发生,一个好的哈希函数应该“均匀地”将数据分布在整个可能的哈希值集合中。

既然哈希表这么快,为什么 MySQL 没有使用其作为索引的数据结构呢? 主要是因为 Hash 索引不支持顺序和范围查询。假如我们要对表中的数据进行排序或者进行范围查询,那 Hash 索引可就不行了,并且每次 IO 只能取一个。

21、B树和B+ 树

  • B 树的所有节点既存放键(key) 也存放数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。

  • B 树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点。

  • B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。

22、主键索引

数据表的主键列使用的就是主键索引,一种特殊的唯一索引。

在 MySQL 的 InnoDB 的表中,当没有显示的指定表的主键时,InnoDB 会自动先检查表中是否有唯一索引且不允许存在 null 值的字段,如果有,则选择该字段为默认的主键,否则 InnoDB 将会自动创建一个 6Byte 的自增主键。

23、二级索引

二级索引又称为辅助索引,是因为二级索引的叶子节点存储的数据是主键。也就是说,通过二级索引,可以定位主键的位置。

唯一索引,普通索引,前缀索引等索引属于二级索引。

  • 唯一索引(Unique Key) :唯一索引也是一种约束。索引列的值必须唯一,但允许有空值;如果是组合索引,则列值的组合必须唯一。一张表允许创建多个唯一索引。建立唯一索引的目的大部分时候都是为了该属性列的数据的唯一性,而不是为了查询效率。

  • 普通索引(Index) :普通索引的唯一作用就是为了快速查询数据,一张表允许创建多个普通索引,并允许数据重复和 NULL。

  • 前缀索引(Prefix) :前缀索引只适用于字符串类型的数据。前缀索引是对文本的前几个字符创建索引,相比普通索引建立的数据更小, 因为只取前几个字符。

  • 组合索引:指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合(后文介绍);

  • 全文索引(Full Text) :全文索引主要是为了检索大文本数据中的关键字的信息,是目前搜索引擎数据库使用的一种技术。Mysql5.6 之前只有 MYISAM 引擎支持全文索引,5.6 之后 InnoDB 也支持了全文索引。

MySQL 中的全文索引,有两个变量,最小搜索长度和最大搜索长度,对于长度小于最小搜索长度和大于最大搜索长度的词语,都不会被索引。

24、聚簇索引与非聚簇索引

聚簇索引即索引结构和数据一起存放的索引,并不是一种单独的索引类型。InnoDB 的主键索引的叶子节点中存放的就是数据行,所以它属于聚簇索引。

在 MySQL 中,InnoDB 引擎的表的 .ibd 文件就包含了该表的索引和数据,对于 InnoDB 引擎表来说,该表的索引(B+树)的每个非叶子节点存储索引,叶子节点存储索引和索引对应的数据。

非聚簇索引即索引结构和数据分开存放的索引,并不是一种单独的索引类型。二级索引(辅助索引)就属于非聚簇索引。MySQL 的 MyISAM 引擎,不管主键还是非主键,使用的都是非聚簇索引。

辅助索引是我们人为创建的索引,它的叶子节点中存放的是主键,当我们通过辅助索引查找到主键之后,再通过查找的主键去回表查找主键索引。

25、回表

回表就是先通过数据库索引扫描出该索引树中数据所在的行,取到主键 id,再通过主键 id 取出主键索引数中的数据,即基于非主键索引的查询需要多扫描一棵索引树。

26、覆盖索引和联合索引

如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。指的是通过索引就能查询到我们所需要的数据,而不需要根据索引再去查询数据表中的数据( 回表),这样就减少了数据库的 io 操作,提高查询效率。

使用表中的多个字段创建索引,就是联合索引,也叫组合索引或复合索引。

27、最左前缀匹配原则

最左前缀匹配原则指的是在使用联合索引时,MySQL 会根据联合索引中的字段顺序,从左到右依次到查询条件中去匹配,如果查询条件中存在与联合索引中最左侧字段相匹配的字段,则就会使用该字段过滤一批数据,直至联合索引中全部字段匹配完成,或者在执行过程中遇到范围查询,如 >、<、between 和 以%开头的like查询 等条件,才会停止匹配。

所以,我们在使用联合索引时,可以将区分度高的字段放在最左边,这也可以过滤更多数据。

28、索引下推

索引下推(Index Condition Pushdown) 是 MySQL 5.6 版本中提供的一项索引优化功能,可以在非聚簇索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表次数。

29、隐式转换

当操作符与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容。某些转换是隐式发生的。例如,MySQL 会根据需要自动将字符串转换为数字,反之亦然。以下规则描述了比较操作的转换方式:

  1. 两个参数至少有一个是NULL时,比较的结果也是NULL,特殊的情况是使用<=>对两个NULL做比较时会返回1,这两种情况都不需要做类型转换;

  2. 两个参数都是字符串,会按照字符串来比较,不做类型转换;

  3. 两个参数都是整数,按照整数来比较,不做类型转换;

  4. 十六进制的值和非数字做比较时,会被当做二进制串;

  5. 有一个参数是TIMESTAMP或DATETIME,并且另外一个参数是常量,常量会被转换为timestamp;

  6. 有一个参数是decimal类型,如果另外一个参数是decimal或者整数,会将整数转换为decimal后进行比较,如果另外一个参数是浮点数,则会把decimal转换为浮点数进行比较;

  7. 所有其他情况下,两个参数都会被转换为浮点数再进行比较;

30、普通索引和唯一索引该怎么选择?

  • 查询

    • 当普通索引为条件时查询到数据会一直扫描,直到扫完整张表;

    • 当唯一索引为查询条件时,查到该数据会直接返回,不会继续扫表;

  • 更新

    • 普通索引会直接将操作更新到 change buffer 中,然后结束

    • 唯一索引需要判断数据是否冲突

所以唯一索引更加适合查询的场景,普通索引更适合插入的场景。

31、避免索引失效

索引失效也是慢查询的主要原因之一,常见的导致索引失效的情况有下面这些:

  • 使用 SELECT * 进行查询;

  • 创建了组合索引,但查询条件未准守最左匹配原则;

  • 在索引列上进行计算、函数、类型转换等操作;

  • 以 % 开头的 LIKE 查询比如 like '%abc';

  • 查询条件中使用 or,且 or 的前后条件中有一个列没有索引,涉及的索引都不会被使用到

  • match() 函数中的指定的列必须与全文索引中指定的列完全相同,否则会报错,无法使用全文索引。

  • 全文索引时要注意搜索长度会导致索引失效

32、建立索引的规则

  • 不为 NULL 的字段 :索引字段的数据应该尽量不为 NULL,因为对于数据为 NULL 的字段,数据库较难优化。如果字段频繁被查询,但又避免不了为 NULL,建议使用 0,1,true,false 这样语义较为清晰的短值或短字符作为替代。

  • 被频繁查询的字段 :我们创建索引的字段应该是查询操作非常频繁的字段。

  • 被作为条件查询的字段 :被作为 WHERE 条件查询的字段,应该被考虑建立索引。

  • 频繁需要排序的字段 :索引已经排序,这样查询可以利用索引的排序,加快排序查询时间。

  • 被经常频繁用于连接的字段 :经常用于连接的字段可能是一些外键列,对于外键列并不一定要建立外键,只是说该列涉及到表与表的关系。对于频繁被连接查询的字段可以考虑建立索引,提高多表连接查询的效率。

  • 被频繁更新的字段应该慎重建立索引;

  • 尽可能的考虑建立联合索引而不是单列索引;

  • 考虑在字符串类型的字段上使用前缀索引代替普通索引;

  • 删除长期未使用的索引;

33、事务极其特性

一个事情由n个单元组成,这n个单元在执行过程中,要么同时成功,要么同时失败,这就把n个单元放在了一个事务之中。举个简单的例子:在不考虑试题正确与否的前提下,一张试卷由多个题目构成,当你答完题交给老师的时候是将一整张试卷交给老师,而不是将每道题单独交给老师,在这里试卷就可以理解成一个事务。

事务的特性:

  • A:原子性(Atomicity),原子性是指事务是一个不可分割的工作单位,事务中的操作,要么都发生,要么都不发生。

  • C:一致性(Consistency),在一个事务中,事务前后数据的完整性必须保持一致。

  • I:隔离性(Isolation),存在于多个事务中,事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。

  • D:持久性(Durability),持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

34、并发事务带来的问题

  • 脏读(Dirty read):B事务读取到了A事务尚未提交的数据;

  • 丢失修改(Lost to modify):在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。

  • 不可重复读(Unrepeatable read):B事务读到了A事务已经提交的数据,即B事务在A事务提交之前和提交之后读取到的数据内容不一致(AB事务操作的是同一条数据);

  • 幻读/虚读:B事务读到了A事务已经提交的数据,即A事务执行插入操作,B事务在A事务前后读到的数据数量不一致。

35、事务的隔离级别

为了解决以上隔离性引发的并发问题,数据库提供了事务的隔离机制。

  • read uncommitted(读未提交): 一个事务还没提交时,它做的变更就能被别的事务看到,读取尚未提交的数据,哪个问题都不能解决;

  • read committed(读已提交):一个事务提交之后,它做的变更才会被其他事务看到,读取已经提交的数据,可以解决脏读 ---- oracle默认的;

  • repeatable read(可重复读):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的,可以解决脏读和不可重复读 ---mysql默认的;

  • serializable(串行化):顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。可以解决脏读、不可重复读和虚读---相当于锁表。

虽然 serializable 级别可以解决所有的数据库并发问题,但是它会在读取的每一行数据上都加锁,这就可能导致大量的超时和锁竞争问题,从而导致效率下降。所以我们在实际应用中也很少使用 serializable,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。

36、MVCC

锁的粒度过大会导致性能的下降, MySQL 的 InnoDB 引擎下存在一种性能更优越的 MVCC 方法。

MVCC 是 Multi-Version Concurremt Control 的简称,意思是基于多版本的并发控制协议,通过版本号避免同一数据在不同事务间的竞争。它主要是为了提高数据库的并发读写性能,不用加锁就能让多个事务并发读写。

MVCC 的实现依赖于隐藏列、Undo log、 Read View 。

从上面对 SQL 标准定义了四个隔离级别的介绍可以看出,标准的 SQL 隔离级别定义里,REPEATABLE-READ(可重复读)是不可以防止幻读的。

但是 InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,主要有下面两种情况:

  • 快照读 :由 MVCC 机制来保证不出现幻读。

  • 当前读 :使用 Next-Key Lock(临键锁) 进行加锁来保证不出现幻读,Next-Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的结合,行锁只能锁住已经存在的行,为了避免插入新行,需要依赖间隙锁。

InnoDB 存储引擎在分布式事务的情况下一般会用到 SERIALIZABLE 隔离级别。

关于 MySQL 事务以及解决幻读的详细介绍,可以看看阿Q写的这篇文章:InnoDB 解决幻读的方案--LBCC&MVCC

37、Mysql 中的锁

具体的锁详情请参考阿Q的此篇文章:面试必备常见存储引擎与锁的分类,请查收

38、查询语句执行过程

select * from tb_student  s where s.age='18' and s.name=' 张三 ';
  • 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 SQL 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。

  • 通过分析器进行词法分析,提取 SQL 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student,需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 SQL 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。

  • 接下来就是优化器进行确定执行方案,上面的 SQL 语句,可以有两种执行方案:

    • a.先查询学生表中姓名为“张三”的学生,然后判断是否年龄是 18。

    • b.先找出学生中年龄 18 岁的学生,然后再查询姓名为“张三”的学生。那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。

  • 进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。

查询语句的执行流程如下:权限校验(如果命中缓存)--->查询缓存--->分析器--->优化器--->权限校验--->执行器--->引擎

39、更新语句执行过程

update tb_student A set A.age='19' where A.name=' 张三 ';

这条语句基本上也会沿着上一个查询的流程走,只不过执行更新的时候要记录日志,这就会引入日志模块了,MySQL 自带的日志模块是 binlog(归档日志) ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 redo log(重做日志),我们就以 InnoDB 模式下来探讨这个语句的执行流程。

  • 先查询到张三这一条数据,如果有缓存,也是会用到缓存。

  • 然后拿到查询的语句把 age 改为 19,然后调用引擎 API 接口写入这一行数据,InnoDB 引擎把数据保存在内存中同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器执行完成了随时可以提交。

  • 执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。

  • 更新完成。

更新语句执行流程如下:分析器---->权限校验---->执行器--->引擎---redo log(prepare 状态)---> binlog --->redo log(commit状态)

40、sql 优化

  1. 应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引;

  2. 应尽量避免在 where 子句中使用以下语句,否则将导致引擎放弃使用索引而进行全表扫描;

    • 对字段进行 null 值判断,

    • 使用!=或<>

    • or 来连接条件(使用union all代替)

    • in 和 not in 也要慎用

    • 不要使用模糊查询(可用全文索引)

    • 减少表达式操作

    • 函数操作

  3. 任何地方都不要使用 select * from t ,用具体的字段列表代替“* ”,不要返回用不到的任何字段;

  4. 一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要;

  5. 很多时候用 exists 代替 in 是一个好的选择;

  6. 尽量减少多表联合查询;

  7. 分页优化;

  8. 正确使用索引;

41、主从同步数据

  • master 主库将此次更新的事件类型写入到主库的 binlog 文件中

  • master 创建 log dump 线程通知 slave 需要更新数据

  • slave 向 master 节点发送请求,将该 binlog 文件内容存到本地的 relaylog 中

  • slave 开启 sql 线程读取 relaylog 中的内容,将其中的内容在本地重新执行一遍,完成主从数据同步

同步策略:

  • 全同步复制:主库强制同步日志到从库,等全部从库执行完才返回客户端,性能差;

  • 半同步复制:主库收到至少一个从库确认就认为操作成功,从库写入日志成功返回 ack 确认;

42、主从延迟要怎么解决

  • MySQL 5.6 版本以后,提供了一种并行复制的方式,通过将 SQL 线程转换为多个 work 线程来进行重放

  • 提高机器配置(王道)

  • 在业务初期就选择合适的分库、分表策略,避免单表单库过大带来额外的复制压力

  • 避免长事务

  • 避免让数据库进行各种大量运算

  • 对于一些对延迟很敏感的业务直接使用主库读

43、为什么不要使用长事务

  • 并发情况下,数据库连接池容易被撑爆

  • 容易造成大量的阻塞和锁超时,长事务还占用锁资源,也可能拖垮整个库

  • 执行时间长,容易造成主从延迟

  • 回滚所需要的时间比较长,事务越长整个时间段内的事务也就越多

  • undolog 日志越来越大,长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

今天的内容到这里就结束了,跪求一键三连,期望靓仔在评论区打出老铁666,鼓励一下。

自监督学习与上下文赌博机融合:视觉决策中的协同优化与实战指南 在机器学习领域,自监督学习(Self-Supervised Learning)通过设计前置任务从无标注数据中学习通用表征,已成为解决标注数据稀缺问题的关键技术。其核心原理是利用数据自身的结构生成监督信号,训练编码器提取鲁棒特征。上下文赌博机(Contextual Bandits)作为强化学习的简化分支,专注于在给定上下文信息下做出单步最优决策,平衡探索与利用,广泛应用于推荐系统、广告投放等场景。将两者结合,构建端到端的协同训练框架,能让视觉表征的优化直接服务于最终的决策目标,同时利用自监督任务作为正则化项提 阅读详情

相关推荐

CHI里的Error handling机制

本文讲解了CHI里的Error handling机制。

W1Z1Q的专栏 2502

面试官:MySQL连环炮,你扛得住嘛?

binlog 是归档日志,属于 Server 层的日志,是一个二进制格式的文件,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”。不管用什么存储引擎,只要发生了表数据更新,都会产生binlog日志。它的主要作用就是数据备份、主从复制。binlog会记录所有涉及更新数据的逻辑操作,属于逻辑日志,并且是顺序写。我们知道如果想要保证事务的原子性。

2401_83384536的博客 810

Notion访问卡顿?一键优化DNS与Hosts提升加载速度

本文针对Notion访问卡顿问题,提供了两种核心优化方案:修改Hosts文件与配置DNS。详细解析了卡顿根源在于网络链路,并手把手指导用户在Windows、Mac、iOS及Android设备上实施优化,旨在通过替换为更优线路,显著提升Notion的页面加载与同步速度。

fff88的博客 1198

710-MySQL原理篇

MySQL原理篇 MySQL的四种隔离级别 SQL标准定义了4类隔离级别,包括一些具体规则,用来限定事务之间的隔离性。 这四种级别分别是读未提交、读已提交、可重复读、串型化。 读未提交,顾名思义,就是可以读到还没有提交的数据;读已提交会读到其它事务已经提交的数据;可重复读确保了同一事务中,读取同一条数据时,会看到同样的数据行;串型化通过强制事务排序,使其不可能相互冲突。 重点介绍下Repeatable Read Repeatable Read就是可重复读。它确保了在同一事务中,读取同一条数据时,会看到同样的

LINZEYU666的博客 363

载波同步技术(一):BPSK的Costas Loop 载波同步

Author:McCrocodile 2010/11/04          载波的提取在相干解调中往往比较重要,根据信号中是否包含载频分量,载波同步一般也可以分为两种方法。包含载频分量的可以采用窄带滤波或者基本的锁相环环路来实现,不包含的则比较麻烦需要采用特殊的环路实现载波的同步。这里介绍的BPSK的载波恢复就属于后面一种。 对于不包含载频分量的信号载波同步一般有:同相-正交环(就是常说的Costas环)、判决反馈环(几乎可以适用所有调制方式)、同相-正交环衍生环(QPSK松尾环、N相同相-正交环)。

McCrocodile Blog 1万+

关于Vscode配置Unity环境时的一些报错问题(持续更新)

3.找到设置文件,添加如下代码,用于告诉VSCode)在处理 C# 项目时优先使用 C# 扩展来加载和管理项目,而不是使用 C# Dev Kit 或其他扩展。1.很可能是unity的插件版本问题,打开window > package manager将。2.很可能是项目文件的问题,建议unity打开edit > perfercenes。3.手动重装其他版本的.Net框架(去官网找,多下几个无所谓的)解决:(由于造成问题不一样,所以建议都尝试一次)下载net请求超时(一般都会超时很正常的)

bug制造机 1890

疯了吧!这谁招架的住,5连问我MySQL的索引底层采用是B树还是B+树?

最近很多小伙伴都是在家办公,相对时间充裕了很多,很多同学都在疯狂的充电,但是有好多小伙伴都在后台留言问有没有跟mysql底层原理的相关文章,并且提出了很多面试中被问到的问题,各位小伙伴可...

欢迎关注公众号:【码农突围】,回复9999,可以获取一份LeetCode刷题笔记 473

继电保护整定值计算软件_「全套69份」整定值计算与设计方案

【全套69份】整定值计算与设计方案,输电线路、变压器、发电机、电容器、母线保护等下载方式见文末内容预览:10kV高压电机保护原理及整定计算35kV环网运行线路继电保护整定计算方案分析35KV以下继电保护及三段式电流保护整定计算110 kv电网计算机整定计算新方案220kV两相式电铁牵引站供电线路的短路故障计算及其保护整定原则分析220kV周潮线接地距离保护整定计算特殊方案750 kV变压器保护配置...

weixin_30825953的博客 5020

分库分表

为什么要分库分表?(设计高并发系统的时候,数据库层面该如何设计?) 说白了,分库分表是两回事儿,大家可别搞混了,可能是光分库不分表,也可能是光分表不分库,都有可能。 我先给大家抛出来一个场景。 假如我们现在是一个小创业公司(或者是一个 BAT 公司刚兴起的一个新部门),现在注册用户就 20 万,每天活跃用户就 1 万,每天单表数据量就 1000,然后高峰期每秒钟并发请求最多就 10。天,就这...

ai_goodStudent的博客 649

Wider Face+YOLOV7人脸检测

使用Wider Face数据集训练YOLOV7

chen_znn的博客 8414

推理篇第3节:TensorRT基础——Builder、Engine、ExecutionContext生命周期

TensorRT模型优化与推理流程摘要 TensorRT是NVIDIA的深度学习推理优化器,通过Builder、Engine和ExecutionContext三阶段实现高效推理。Builder负责编译模型,执行图优化和kernel选择;Engine是优化后的推理计划;ExecutionContext管理运行时执行。编译期进行重量级优化(如算子融合、kernel调优),运行时快速执行。TensorRT 10.x引入新API,推荐使用buildSerialized方法。构建流程包括创建Builder、网络定义、

PeterPan的博客 503

Mysql的连环炮面试题

目录1 MySQL存储引擎和索引介绍2 数据库设计原则3 数据库是事务4 SQL优化5 简单说一说drop、delete与truncate的区别9 什么是视图7 什么是内联接、左外联接、右外联接?8 并发事务带来哪些问题?9 事务隔离级别有哪些?MySQL的默认隔离级别是?10 分表分库相关11 数据库并发策略12 MySQL 中有哪几种锁?13 MySQL 中 InnoDB 支持的四种事务隔离级别名称,以及逐级之 间的区别?14 CHAR 和 VARCHAR 的区别?15 主键和候选键有什么区别?16 m

赵广陆 857

从零开始学习PX4源码20(遥控器模式切换如何执行)

本节主要学习PX4的手动遥控器切换模式,具体是如何实现的,具体改变了哪些变量,和模式管理有什么联系。

陌城烟雨 319

面试连环炮之Mysql

1、MySQL体系结构 应用层、MySQL服务层、存储引擎层 应用层:连接处理、用户鉴权、安全管理服务层:MySQL Management Server & utilities(系统管理)、SQL Interface(SQL 接口)、SQL Parser(SQL 解析器)、Optimizer (查询优化器)、Caches & buffers(缓存)存储引擎层:存储引擎、物理文件 2、mysql常用存储引擎? mysql常用存储引擎InnoDB 、MyISAM 、Memory等

dehuisun的专栏 3992

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Java面试被MySQL问哭了,面试官一个连环炮提问,我一个都讲不清

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。 既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!由于文件比较多,这里只是将部分目录截图出来,全套

2401_84024148的博客 957

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

面试连环炮(上)

我最近从大厂离职之后在合肥呆了个把月,之前已经准备了半个多月,从7月底开始投简历面试,目前是java高级职位,到现在为止已经面了24+公司了,手上也有一些意向offer。现在把跟面试官对垒的面试问题总体记录一下。请注意这里只有问题,没有答案,也没有标准答案。参考答案在我的思维导图里。本系列会分为三个系列博客去讲我面过的一些公司出的一些题,以某个知识点或者框架出发扩展深入。来,看你能撑多少炮! ##一、 redis连环炮 redis数据结构 redis有哪几种数据结构?zset用过吗,底层数据结构是什么?.

程序男的专栏 2695

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

阿里面试官一个Mysql连环炮提问,一下给我干懵 多亏这份“豪华面试笔记”我才缓过来

终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2401_84438959的博客 1837

Java面试被MySQL问哭了,面试官一个连环炮提问

查询语句不同元素(where、jion、limit、group by、having等等)执行先后顺序?MySQL的MyISAM与InnoDB两种存储引擎在,事务、锁级别,各自的适用场景?MySQL常见的三种存储引擎(InnoDB、MyISAM、MEMORY)的区别?事务隔离级别,每个级别会引发什么问题,MySQL默认是哪个级别?mysql都有什么锁,死锁判定原理和具体场景,死锁怎么解决?事务四大特性(ACID)原子性、一致性、隔离性、持久性?数据库的读写分离、主从复制,主从复制分析的 7 个问题?

m0_55030688的博客 830

面试连环炮(中)

上一篇面试连环炮整体讲述了面试官会问的java基础和高级相关的知识点以及一些数据库和缓存相关的理论和实践,包括消息队列。发到各个技术群里引起了广泛的讨论,也支持我去继续写剩下的两篇文章。本篇将从项目实战的角度去看面试官会从哪些方面去考察应聘者的知识深度和广度。 一、业务中间件&组件连环炮 Tomcat 项目部署用的是什么容器启动的?说一下tomcat的架构模型?说一下Tomcat的线程模型?底层原理是什么?跟Netty的线程模型相比有什么区别?做过Tomcat的调优吗?你们服务部署的时候Tomc.

程序男的专栏 2303

吊打面试官!Mysql数据库83道连环炮,你能答对多少?

MySQL数据库83题 1、数据库存储引擎 2、InoDB (B+树) 2、TokuDB ( Fractal Tree-节点带数据) 3、MyIASM 4、Memory 5、数据库引擎有哪些 6、InoDB与MyISM的区别 7、索引 8、常见索引原则有 9、数据库的三范式是什么 10、第一范式(1st NF -列都是不可再分) 11、第二范式(2nd NF-每个表只描述一件事情) 12、第三范式(3rd NF -不存在对非主键列的传递依赖) 13、数据库是事务 14、SQL优化 15、简单说一说drop、

资料免费分享,点击名片 714

面试官关于MySQL事务、锁、MVCC的N个连环炮,见招拆招!

文章来源:https://juejin.cn/post/7017969370206830623面试官:要不你来讲讲你们对MySQL是怎么调优的?候选者:哇,这命题很大阿…我认为,对于开发者而言,对MySQL的调优重点一般是在「开发规范」、「数据库索引」又或者说解决线上慢查询上。候选者:而对于MySQL内部的参数调优,由专业的DBA来搞。面试官:扯了这么多,你就是想表达你不会MySQL参数调优,对吧...

石杉的架构笔记 287

备战金九银十:大厂面试官必问MySQL连环炮全梳理,你扛得住嘛?

最近后台好多小伙伴发私信咨询阿Q,问马上就是金九银十的面试黄金期了,该如何在三个月内突击一下,以便更好地通过面试呢? 阿Q的想法就是需要对自己掌握的知识进行归纳整理,系统的、分类的去复习相关的知识点。这样也能更好的把自己陌生的内容框出来,在暴漏自己短板的同时,更针对性的做准备。 为了节省大家的时间,阿Q决定帮大家先将 MySQL 面试相关的知识进行汇总整理,一改往日知识点东一棒槌西一榔头的问题。那么,你准备好迎接 MySQL 连环炮了嘛?

Javatutouhouduan的博客 386
上一篇: 耗时3个月 暗访40+安全大牛 终于总结了2023各安全大厂面经(包含护网红蓝队面试题)
下一篇: 内网渗透| 隧道穿透的搭建从简单到高阶
AI小模型
博客等级 码龄6年 6863粉丝 487原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI小模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值