MySql之灵魂拷问


在这里插入图片描述

传统关系型数据库架构

  1. 客户端:你懂的。
  2. 连接器:管理连接,权限验证。(server层)
  3. 查询缓存:命中🎯则直接返回结果。(server层)
  4. 分析器:词法分析,语法解析。(server层)
  5. 优化器:执行计划生成,选择索引,优化语法。(server层)
  6. 执行器:操作引擎,执行语法,返回结果。(server层)
  7. 存储引擎:存储数据,提供读写接口。(引擎层)

传统关系型数据库事务

为什么传统关系型数据库需要事务?

  • 用户下订单,是一个独立的完整的事件。要么下订单成功,要么下订单失败。如果下订单失败,应该等效于没有下过订单
  • 所以,在实际业务场景看,要想服务好用户,就要保证:1. 要么下单成功,要么下单失败。2. 订单成功后的状态是确定唯一的,即成功的状态应该是完整的订单记录存储,而不是半个订单的信息混淆视听。3. 不同用户之间的订单互不影响,你是你,我是我。4. 订单是持久生效的,有迹可循,有据可依。(服务好客户的4个条件
  • 在技术层面解读,事务的4个特性,刚好对应上述4个条件:1. 原子性(atomicity)2. 一致性(consistency)3. 隔离性(isolation)4. 持久性(durability),简称:ACID,事务的4个特性。

传统关系型数据库事务的4个特性是如何实现的?

  1. 原子性(atomicity):依靠undo log日志实现回滚机制。要么成功全部提交执行,要么失败全部回滚。比如,InnoDB引擎的MySql数据库,其事务日志包括redo logundo log,分别对应前滚操作回滚操作redo log中记录的是数据库每个页的修改,是物理格式上的日志,用于重现已提交的物理数据页,且只能恢复到最后一次提交的位置,即重做,复现到最后一次提交的位置,是向前滚动。而undo log是逻辑日志,比如,执行insert时,undo log记录的是delete(回滚段里面针对修改和插入存储旧的值),即记录实际操作的反向逻辑操作,撤销之前的操作,向回滚动。(本质依靠redo logundo log
  2. 一致性(consistency):数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。例如对银行转帐事务,不管事务成功还是失败,应该保证事务结束后ACCOUNTS表中Tom和Jack的存款总和不变。(本质依靠redo logundo log
  3. 隔离性(isolation):依靠每个事务都有自己的专属数据空间。在并发情况下,不同事务在各自的专属空间中操作相同数据,互不干扰。(本质依靠Lock锁
  4. 持久性(durability):依靠对数据状态变更的永久保存。无论机器宕机还是系统崩溃,数据库重启后,都会恢复到事务成功结束的状态(crash safe)。(本质依靠redo logundo log)备注:Innodb通过force log at commit机制实现事务的持久性,即在事务提交的时候,必须先将该事务的所有事务日志写入到磁盘上的redo log file和undo log file中进行持久化。

传统关系型数据库的日志究竟是咋回事?

日志是第一公民
  • 在传统关系型数据库中,日志比数据重要,因为只要有日志,就有数据。当然,前提是选择了先持久化日志的策略(WAL:Write Ahead Log),预写日志。
介绍下传统关系型数据库日志写入相关概念及关键参数:
  1. redo log:前滚日志。有点宕机重播、恢复现场的感觉。
  2. redo log buffer:前滚日志缓冲区,磁盘批量同步,用于提速。
  3. 关键参数 innodb_flush_log_at_trx_commit:值可以取0,1或者2,其中默认为1,控制commit动作是否刷新log buffer到磁盘。
    当设置为1的时候,事务每次提交都会将log buffer中的日志写入os buffer并调用fsync()刷到log file on disk中。相当于强一致保障日志和磁盘的同步,宕机也不怕,代价是频繁I/O导致性能下降。(最安全)
    当设置为0的时候,事务提交时不会将log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到log file on disk中。相当于是在性能与一致性上的取舍平衡,秒级同步,减少I/O。问题是宕机丢失1s的数据,别小看,这一秒可能是上万条数据哦!(最快)
    当设置为2的时候,每次提交都仅写入到os buffer(与设置0相比实时写入os buffer),然后是每秒调用fsync()将os buffer中的日志写入到log file on disk。咋一看和设置0的时候好像没啥区别?其实,性能上设置为2和设置为0的确差不多(设置为0更快),但是设置为2更安全一些,因为commit时写入了os buffer,所以,只有在操作系统崩溃或者系统断电的情况下,上一秒钟所有事务数据才可能丢失。(较快)
  4. redo dirty log:redo log buffer 未同步刷新到磁盘的日志。
  5. undo log:回滚日志。撤销。
  6. undo log buffer:回滚日志缓冲区,磁盘批量同步,用于提速。
  7. lsn(log sequence number):日志逻辑序列号。
  8. dirty data:内存(buffer pool)中未刷新到磁盘的数据。
  9. checkpoint:触发后会同步内存中的 dirty data 和 dirty log,刷新到磁盘。其中,分为sharp checkpoint(刷新同步所有) 和 fuzzy checkpoint(刷新同步部分)。
  10. buffer pool:缓存池,以页为单位,用于缓存数据和索引等数据,对应表空间中的页。更新一条数据,会先找到数据所在页,将该页加载到buffer pool中,在buffer pool中对数据进行修改,最后通过IO线程再以页为单位将缓存中的数据刷入磁盘。
redo log 循环写入图解:
  • 一般空间固定,4个g,一个g为一组。
  • write pos为当前记录写入的位置,顺时针不断行进。一边写一边后移。
  • checkpoint是当前要擦除的位置,擦除记录前要把记录更新到数据文件。
    在这里插入图片描述
简述一下预写日志和落盘数据的过程:

Demo:update T set a = a - 1 where ID = 6;

  1. 执行器 找 引擎 去 树搜索 定位ID=6数据行。(执行器说:“引擎兄,小明在哪?”)
  2. 如果ID=6数据行所在页在内存中(命中缓存),则直接返回。否则,查询磁盘,载入缓存,返回。(引擎找了找,一楼不在,在二楼。说:“在楼上打牌。”)
  3. 执行器 执行 minus 1 操作,得到新的数据行,调用引擎写入接口进行更新。(执行器说:“她老婆(客户端)让我把他赢的钱拿出1元放到柜台,一会他儿子来取。”)
  4. 引擎 拿到新数据行,先更新到内存。同时将更新操作记录到 redo log 中,redo log 处于 prepare状态。然后告诉执行器执行完成,可以提交事务。(引擎先自己拿出1元放到柜台,以便小明儿子来了可以拿到。同时,给小明记了个账,欠1元,打完牌清算。并告诉执行器办完了。)
  5. 执行器 生成 binlog,并将binlog写入磁盘。(执行器记录了下,小明儿子一会过来取钱的事办完了。)
  6. 执行器 调用 引擎的提交事务接口,引擎 将 redo log 的prepare状态 改成 commit状态,更新完成。(执行器告诉引擎,ok啦!多谢!)
  7. InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做。(此处,即打完牌清算,还账。)
不同类型日志功能小结:
  1. redo log:位于引擎层,InnoDB特有,且InnoDB为后续引入MySQL。用于宕机,恢复没有来得及写入磁盘的数据的状态。保证事务持久性。是循环写入方式,即空间固定会用完。
  2. undo log:用于回滚和MVCC。
  3. binlog:二进制日志,是逻辑日志,位于Server层,只是用于归档、主从复制和审计SQL注入,本身不具备crash safe的能力。(recovery恢复、replication复制、audit审计)是执行器 生成 binlog,并将binlog写入磁盘。是追加写入的方式,达到一定空间会生成下一个文件。
两阶段提交的理解:

在这里插入图片描述

  • 日志写入次序为:写redo log->事务状态prepare->写binlog->提交事务->修改redo log事务状态为commit。
    两阶段的目的为:保证redo log 和 binlog一致性。
    黄色部分经过分析器分析、优化器优化、最终由执行器执行。
    绿色部分为执行器执行内容。
    紫色部分为引擎执行内容。
  1. 写入redo log(prepare)之前,mysql crash down,则下一次重新执行,重新写入redo log 和 binlog日志。
  2. 写入redo log(prepare)之后,mysql crash down,则下一次执行会判断binlog的写入是否已经成功(XID_EVENT)。顺序扫描redo log日志,遇到redo log(prepare),则查看binlog(XID_EVENT)是否写入已成功。
    2.1 如果binlog(XID_EVENT)写入成功,则直接提交redo log(prepare)将其变为redo log(commit)。
    2.2 如果binlog(XID_EVENT)写入失败(mysql crash down导致未完成),则回滚事务。后续的复现执行过程会purge覆盖掉binlog 最后一次成功的XID_EVENT之后的内容。
事务4个隔离级别是什么意思?
  • 多线程->并发操作->无锁情况可能出现数据一致性问题,包括:
  1. 第一类丢失更新/撤销覆盖(Lost update):读的都是相同的初始值,但事务A的失败撤销导致事务B的更新丢失。(解法:行锁。)
    第二类丢失更新/提交覆盖(Lost update):读的都是相同的初始值,但事务A的成功执行导致事务B的更新丢失。(解法:行锁。)
  2. 脏读(Dirty Read):事务A读到的是事务B未提交的值,事务B回滚导致事务A读到的是脏数据。(解法:其他事务不可以读取当前事务未提交的修改过的值。)
  3. 不可重复读(Non-repeatable Reads) :事务A连续读了两次值不同。(解法:修改事务提交完成之后,其他事务才被允许读取。)
  4. 幻读(Phantom Reads):统计两次,结果不同。(解法:统计操作事务完成之前,其他事务不可以插入和删除新数据。)
4个隔离级别逐步递进,一致性与并发性的折衷:
  1. read uncommitted(读未提交):一个事务写数据,其他事务不可以同时写。避免第一类丢失更新问题。(同一行数据,我没写完你别写)
    注意:我没写完,你别写。可以避免第一类丢失更新,但是无法避免第二类丢失更新,因为可以读未提交。
    实现:对需要修改的数据上面(就是在发生修改的瞬间) 对其加共享锁(其他事务不能更改,但是可以读取-导致“脏读”),直到事务结束才释放。事务结束包括正常结束(COMMIT)和非正常结束(ROLLBACK)。
  2. read committed(读已提交):不同读数据事务共存,但写数据事务不允许其他事务读取当前被操作行。避免脏读。(同一行数据,我没写完你别读)
    注意:同一行数据,我没写完你别读。可以避免脏读和第一类丢失更新,但依然无法避免第二类丢失更新,因为两个事务依然可以读到两个相同的初始值。
    实现:1)事务在对需要更新的数据上(就是发生更新的瞬间) 加排他锁(直到事务结束),防止其他事务读取未提交的数据,这样,也就避免了 “脏读” 的情况。2)事务对当前被读取的数据上面加共享锁(当读到时加上共享锁),一旦读完该行,立即 释放该该行的共享锁。上面只能防止不读脏数据。
  3. repeatable read(可重复读,默认选项):读数据事务共存,但读数据事务不允许其他事务修改当前被读取行。避免不可重复读问题。(同一行数据,我没写完你别读,我没读完你也别写)
    注意:同一行数据,我没写完你别读,我没读完你也别写。默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。
    实现:在读取数据的瞬间必须先对其加共享锁 ,但是直到事务结束才释放,可以防止不可重复读。从数据库的底层实现更深入的来理解,既数据库会对游标当前的数据上加共享锁,但是当游标离开当前行的时候,立即释放该行的共享锁。防止了“脏读”数据,但是不能避免丢失更新,不可重复读,幻读 。但由于读完数据后立即释放共享锁,所以它不能避免可重复读,同时它也不能避免丢失更新。
  4. serializable(串行化):事务序列化执行,即串行化规避并发,牺牲性能保证准确性。(行了,一个一个来吧,谁也别争,省的冲突。)
    注意:锁机制,实现串行化。
    实现:对事务中所读取或者更改的数据所在的表加表锁。
    在这里插入图片描述
什么是MVCC?
  • Multi-Version-Concurrent-Control(多行版本并发控制)

  • InnoDB数据库每行存储时保有3个隐藏字段:DB_TRX_ID字段,6字节,事务标识符,标记最后一个事务。DB_ROLL_PTR字段,7字节,叫做回滚指针(roll pointer),指向写入回滚段的撤消日志(Undo Log)。DB_ROW_ID字段,6字节,包含一个随着新行插入而单调增加的行ID,如果innodb自动生成聚集索引,则该索引包含行ID值。否则,DB_ROW_ID列不会出现在任何索引中。

  • 每次变更,类比头插的链表,将历史记录串串,用于后续回滚。具体过程为:

    1. 用排他锁锁定该行
    2. 把该行修改前的值拷贝到Undo Log中(类比新建链表结点)
    3. 修改当前行的值,填写事务编号,使回滚指针指向Undo Log中的修改前的行(头插链表、串串历史版本链)
    4. 记录Redo Log,包括Undo Log中的变化(前滚日志包含回滚日志)
    备注:根据经验值没提交的事务行数在1000~10000之间,InnoDB效率还是非常高的(唐成-数据库多版本实现内幕)。
    
什么是XID_EVENT?
  • binlog的日志写入会涉及信息描述,比如:binlog的版本、binlog的下一个文件指向、MYSQL服务停止等等。可以将其看作是一件一件的事情,所以binlog引入了event的概念。
  • XID_EVENT是event的一种,当事务提交时,无论是 statement格式 还是 row格式 的binlog都会添加一个 XID_EVENT 作为事务的结束。该事件记录了该事务的ID。在mysql进行崩溃恢复时根据binlog中提交的情况来决定是否提交存储引擎中prepared状态的事务。
什么是group commit?
  • 提高性能,将有关联的多个数据修改操作放到一个事务中。可以避免对每个修改操作都执行完整的持久化操作。

  • 更进一步的,记录binlog的操作也可以按组的思想进行优化,将多个事务涉及到的binlog一次性flush,而不是每次flush一个binlog。

  • 在MySQL5.6以前,事务提交(commit指令)后,MySql进入Prepare阶段,立即写内存中的二进制日志(binlog commit),然后写事务日志。Prepare阶段,为了保证二进制日志与事务日志的一致性,启用一个prepare_commit_mutex锁来保证顺序性与一致性。但其会导致开启二进制日志后group commmit失效,特别是在主从复制结构中,几乎都会开启二进制日志。

  • 在MySQL5.6中进行了改进,提交事务时,在存储引擎层的上一层结构中会将事务按序放入一个队列,队列中的第一个事务称为leader,其他事务称为follower,leader控制着follower的行为。虽然顺序还是一样先刷二进制,再刷事务日志,但是机制完全改变了:删除了原来的prepare_commit_mutex行为,也能保证即使开启了二进制日志,group commit也是有效的。

  • MySQL5.6中分为3个步骤:flush阶段、sync阶段、commit阶段。

如何理解关系型数据库的索引?

  • 目的:避免全表扫描,提高检索速度。保证唯一性。
  • InnoDB 引擎:B树索引。B+树索引。

如何理解关系型数据库的锁机制?

锁由来
  1. 数据库的访问,免不了并发,需要用锁机制保证数据一致性。
锁分类
  1. 按操作划分:DML锁、DDL锁。

    1.1 DML锁:用于保护数据的完整性,包括行级锁(Row Locks(TX锁))、表级锁(Table Locks(TM锁))。

    排他DDL锁(Exclusive DDL Lock)又称写锁,是写入数据时加的锁。
    SELECT ... FOR UPDATE;(事务可以显示加排他锁)
    在查询语句后面增加FOR UPDATE,独占锁。
    一旦对数据A加上独占锁,则其他事务不可以再对A加任何类型的锁。
    获取到排他锁的事务,既可以读数据,又可以写数据。
    如果结果集中有一行数据使用了独占锁,则申请共享锁会被阻塞。
    

    1.2 DDL锁:用于保护数据库对象结构,如表、索引等结构定义。DDL锁包括排他DDL锁(Exclusive DDL Lock)共享DDL锁(Share DDL Lock)、可中断解析锁(Breakable Parse Locks)

    共享DDL锁(Share DDL Lock)又称读锁,是读取数据时加的锁。
    SELECT ... LOCK IN SHARE MODE;(事务可以显示加共享锁)
    如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。
    
  2. 按粒度划分:行级锁、表级锁、页级锁。

  3. 按锁级别划分:共享锁\读锁(Share Lock \ S)、排他锁\写锁(Exclusive Lock \ X)。

    S锁:事务A 对 数据对象rowA 加 S锁,则事务A 可以 对数据对象rowA进行读操作,但是不能进行写操作。
    		则其他事物 可以继续 对数据对象rowA加上S锁,也就是 共享 读。
    		但其他事物 不能    对数据对象rowA加上X锁,以保证 “我没读完,你别写!”
    
    x锁:事务B 对 数据对象rowB 加 X锁,则事务B 可以 对数据对象rowB进行读操作 和 写操作。
    		其他事物 不能 对数据对象rowB加任何锁,直到事务B释放锁定。(排他性)
    		排他锁的语义是“我没有写完,你别写,也别读!”
    
    意向锁,InnoDB自动加的,不需要用户干预。
    对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X)。
    对于一般的Select语句,InnoDB不会加任何锁。
    
  4. 按加锁方式划分:自动锁、显示锁。

  5. 按使用方式划分:乐观锁、悲观锁。

    乐观锁,Optimistic Locking(OL):
    乐观的认为一般不会产生冲突。
    尽可能直接做下去,直到提交才去锁定,所以不会产生任何锁和死锁。
    一般实现乐观锁方式,是记录数据版本。
    乐观锁 实现 借助 版本号/时间戳 来实现。
    
    悲观锁,Pessimistic Concurrency Control(PCC):
    悲观的认为竞争总是存在的。
    先取锁再访问,保证数据安全。
    实现方式:
    1. 对记录修改前先尝试加排他锁。
    2. 如果无法加排他锁,则认为该记录正在被锁定,当前操作不被允许。
    3. 如果加排他锁成功,则进一步执行修改操作。
    4. 提交事务。
    Demo:
    	begin; // 开始事务
    		select quality from item_tb for update; // 获取排他锁
    		update item_tb set quality = 1 where id = 123; // 更新库存为1
    	commit; // 提交事务
    
常用的关系型数据库技巧性操作常识都有哪些?
truncate
  • truncate、delete和drop区别

    1. truncate 和 drop 是DDL语句,delete 是DML语句。
    2. truncate 仅作用于表,而 drop 和 delete 作用于视图。
    3. truncate 仅清空表所有行,而 drop会删除表结构及其所依赖的约束、索引。
    4. truncate 会重制表自增值。delete 不会。
    5. truncate 不会激活与表有关的删除触发器,delete 可以。
    6. truncate 会把表和索引所占用空间恢复到初始大小。delete 不会改变表和索引所占用空间。 drop 会把表和索引所占用空间释放掉。
    
  • truncate特点

    1. truncate 无法通过binlog回滚。
    2. truncate 会清空所有数据,执行速度快。
    3. truncate 不能对有外键约束引用的表使用。
    4. 执行truncate,需要drop权限。
    5. 执行truncate,最好提前备份数据。
    
  • truncate为什么快?

    1. 非事务
    	DML语句会把数据放到rollback中,事务提交后才生效,所以事务提交之前会锁表,从而占用一定磁盘空间。
    	truncate属于DDL语句,无法回滚,操作后即生效,无需锁定。
    2. 无日志
    	delete语句每次删除一行,并在事务日志中,为删除的每行记录一项。
    	truncate通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。delete日志比truncate更耗时。
    
join、in 和 exists 的效率问题
  • 假设,A表(大表,id=1,2,3,4,5,6),B表(小表,id=1,2):无论是join、in还是exists都是集合比对的过程。我们希望比对的成本尽量低,效能尽量高。

    in 语句:
    select * from A where id in (select id from B);
    in语句的执行过程是,先执行select id from B,对应id结果集是:1,2
    然后,借助A的索引查询B的id结果集1,2,分别看看是否在1,2,3,4,5,6中。
    此处,A表越大效能越好,因为子查询是遍历,主查询是索引查询,子查询结果集行数决定外表主查询索引查询次数。
    我们称A表为被驱动表,B表为驱动表。
    
    exists 语句:
    select * from A where exists(select id from B where id=A.id)
    exists,谓词,关联子查询,子查询不返回任何结果集,只返回true或者false。
    exists 语句执行过程是,先执行select * from A,对应id结果集是:1,2,3,4,5,6
    然后,借助B的索引查询A的id结果集1,2,3,4,5,6,分别看看是否在1,2中。
    此处,A表越小效能越好,因为A表行数决定子查询索引查询的遍历次数。
    
    in 与 exists 小结:
    简单来看,in 和 exists 实际上,是对于遍历与索引查询的权衡。
    in,是索引主查询(外表),遍历子查询(内表)。
    exists,是索引子查询(内表),遍历主查询(外表)。
    索引查询的效能远远优于遍历的效能。
    所以:
    对于in而言,外表越大内表越小越好,越有利于发挥索引查询的优势。in适合B表很少,A表很大且有索引的情况。并且,当B表的id集合达到一定的临界值,会导致全表扫描。
    对于exists而言,内表越大外表越小越好,越有利于发挥索引查询的优势。
    
    join查询如何理解?
    答:in子查询会被优化为join(5.7版本),所以,执行计划其实是一样的。mysql在join表连接时会选择一个结果集较小的表作为驱动表,再以此遍历被驱动表。如果对驱动表的字段order by没有问题,但是,表A和表B的数据集行数是会变化的,无法预测未来究竟哪个是驱动表,进而可能导致对被驱动表order by,会产生using temporary,以及filesort,性能较低。所以,有order by的情况下,建议使用exists或者in子查询代替。
    
bool类型是否需要索引?
	myIsam:bool 类型索引,更慢。
	Innodb:bool 类型索引,更快,因为非聚簇索引找主键,再主键聚簇索引找全文。
predicate使用?
  • 正则匹配

    数字正则匹配:SELECT * FROM `item_manage` where `emp_id` REGEXP '[1-9]';
    
  • 模糊查询

    select * from item_manage where emp_id like '李%'; 
    
  • 时间操作

    时间加减
    SELECT DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 1 DAY),'%Y-%m-%d %H:%m:%s');
    SELECT DATE_FORMAT(DATE_ADD(NOW(),INTERVAL 1 DAY),'%Y-%m-%d %H:%m:%s');
    

常见问题

  • You can’t specify target table ‘my_tb’ for update in FROM clause
// 不能对进行查询操作的表进行update操作
update my_tb set creator = '小天' where id in 
(
	SELECT distinct id FROM `my_tb` where creator = '小明'
);
// 重命名临时表解决
update my_tb set creator = '小天' where id in 
(
	select id from (SELECT distinct id FROM `my_tb` where creator = '小明')a
);

Reference

  • https://blog.csdn.net/q736317048/article/details/113838636(传统关系型数据库事务以及MVCC讲解)
  • https://www.mysql.com/(mysql官网)
  • https://zhuanlan.zhihu.com/p/90187667(MySQL是怎么保证数据一致性的)
  • https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html#auto_id_12(详细分析MySQL事务日志(redo log和undo log))
  • https://blog.csdn.net/u010002184/article/details/88526708(MySQL日志系统:redo log、binlog、undo log 区别与作用)
  • https://blog.csdn.net/huangzhilin2015/article/details/115396599(MySQL–buffer pool、redo log、undo log、binlog)
  • https://www.cnblogs.com/eternityz/p/12443016.html(mysql两个重要的日志redolog和binlog)
  • https://blog.csdn.net/GZHarryAnonymous/article/details/84931704(mysql 数据库 引擎:InnoDB 与 MyISAM)
  • https://www.cnblogs.com/danhuangpai/p/11484306.html(mysql原理binlog系列之event浅谈)
  • https://blog.csdn.net/qq_38807792/article/details/89467023(日志系统:redo log和binlog)
  • https://zhuanlan.zhihu.com/p/129860691(跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现)
  • https://www.cnblogs.com/mysqljs/p/12626232.html(Truncate用法详解)
  • http://www.360doc.com/content/20/1109/23/72011420_945053555.shtml(truncate为什么比delete快?)
  • https://blog.csdn.net/sbdx/article/details/76890174?utm_source=blogkpcl1(MySQL里bool类型字段是否需要添加索引测试)
  • https://www.cnblogs.com/williamjie/p/11081081.html(MySQL的InnoDB索引原理详解)
  • https://www.jianshu.com/p/f4b040507b31(in,exists,join效率分析)
  • https://blog.csdn.net/weixin_39606118/article/details/110586511(mysql isnull用法_mysql索引优化,无论你开发多久,这篇文章都要看一下)
  • https://blog.csdn.net/weixin_44294385/article/details/112528899(MySQL(predicate))
  • https://www.cnblogs.com/limuzi1994/p/9684083.html(mysql的事务四个特性以及事务的四个隔离级别)
  • https://blog.csdn.net/qq_36756682/article/details/107370119(脏读、不可重复读、幻读、两类丢失更新与四大隔离级别)
  • https://www.zhihu.com/question/434734816/answer/1625842017(2020-12-14:mysql中,可重复读是怎么实现的?)
  • https://zhuanlan.zhihu.com/p/161933980(高性能MySQL-如何实现可重复读?)
  • https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651961444&idx=1&sn=830a93eb74ca484cbcedb06e485f611e&chksm=bd2d0db88a5a84ae5865cd05f8c7899153d16ec7e7976f06033f4fbfbecc2fdee6e8b89bb17b&scene=21#wechat_redirect(InnoDB并发如此高,原因竟然在这?)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值