23MySQL

索引哪些情况会失效

InnoDB引擎: 主键索引、普通索引
InnoDB用了B+树的结构来存储索引数据。
索引失效口诀:
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
like 百分写最右, 覆盖索引不写 * ;
不等控制还有or,索引失效要少用;
var引号不能丢, sql 高级也不难

1)index_a_b_c 第一个没匹配上或者中间b断了,则索引失效。
2)函数计算
3)范围 between and 大于小于等于等情况
4)百分号在左边则失效,建议所有的模糊只做右模糊 -> 符合最左匹配
5)select *
6)is (not) null
7) 使用or连接查询的时候,or语句前后没有同时使用索引,那么索引会失效。只有or左右查询字段都是
索引列的时候,才会生效
7) 字符串 加上 ’ ’ 假设字段为varchar 存的也是数字 0 1 2 ,那么赋值数字也是没问题的,但是索引会失效

索引不适合的场景

1、 数据量少的不适合加索引
2、更新比较频繁的也不适合加索引
3、区分度低的字段不适合加索引(如性别)

索引的潜规则

1、覆盖索引
2、回表
3、索引数据结构(B+树)
4、最左前缀原则
5、索引下推

调优

explain命令来查看sql的执行计划,然后针对性的进行调优即可

在这里插入图片描述

id

代表sql中查询语句的序列号,序列号越大则执行的优先级越高,序号一样谁在前谁先执行。id为null则最后执行。

select_type

查询类型,表示当前被分析的sql语句的查询的复杂度。这个字段有多个值。
SIMPLE:表示简单查询。
PRIMARY:表示复杂查询中的最外层的select查询语句。
SUBQUERY:表是子查询语句 跟在select 关键字后面的select查询语句;
DERIVED: 派生查询,跟在一个select查询语句的from关键字后面的select查询语句

table

表示当前访问的表的名称。

当from中有子查询时,table字段显示的是 N为derived的id的值。

partitions

返回的是数据分区的信息(不经常用)

type

这个字段决定mysql如何查找表中的数据,查找数据记录的大概范围。这个字段的所有值表示的从最优到最差依次为:

system > const > eq_ref > ref > range > index > all;

一般来说我们优化到range就可以了 最好到ref。

1)null:type字段的值如果为null,那么表示当前的查询语句不需要访问表,只需要从索引树中就可以获取我们需要的数据;

一般如果是主键索引的话 ,查询主键字段或者唯一索引的话 查询主键字段 type字段的值就为null。

explain select id from table where id =1;

2)system/const
用户主键索引或者唯一索引查询时,只能匹配1条数据 一般可以对sql查询语句优化成一个常量,那么type一般就是system或者const,system是const的一个特例。

explain select * from (select * from table where id = 1) tmp;

3)eq_ref
在进行连接查询时,例如left join 时,如果是使用主键索引或者唯一索引连接查询 ,结果返回一条数据,则type的值为一般为eq_ref。

explain SELECT * from a left join b on b.id = a.bId;

on的连接的字段为主键/唯一键
4) ref
相比较eq_ref,不使用主键索引或者唯一索引,使用的是普通索引或者唯一索引的部分前缀,索引与一个值进行比较后可能获取到多个符合条件的行,不在是唯一的行了。
on的连接的字段为普通索引/唯一索引的部分前缀
5)range
通常使用范围查找,例如between,in,<,>,>=等使用索引进行范围检索。
6)index
扫描索引树就能获取到的数据,一般是扫描二级索引,并且不会从根节点扫描,一般直接扫描二级索引的叶子节点,速度比较慢。因为二级索引叶子节点不保存表中其他字段数据 只保存主键,所以二级索引还是比较小的,扫描速度相比All还是很快的。
这里用到了覆盖索引,什么是覆盖索引:可以直接遍历索引树就能获取数据叫做覆盖索引。这里遍历name索引树就可以获取到主键id的值就是覆盖索引

假设有个索引为index_name

select id, name from  table 

那么命中索引+主键索引
索引会扫描idx_name索引树的叶子节点,那么根据B+Tree树的结构,叶子节点保存的是name字段的索引值 和 data数据(主键id)。而正好我们只需要查询id和name两个字段,我们查询的字段被索引(二级索引)给覆盖了 这就是覆盖索引,因此 type的类型就是index。

7)ALL
这是一种效率最低的type,需要扫描主键索引树的叶子节点,获取数据是表中其他列的数据,即全表扫描。

select remark from table 

没索引,且没命中任何索引,则进行全表查询 select_type 为ALL

possible_keys

这个字段显示的是sql在查询时可能使用到的索引,但是不一定真的使用,只是一种可能。
如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查 where 子句看是否可以创造一个适当的索引来提
高查询性能,然后用 explain 查看效果。

key

sql执行中真正用到的索引字段。

key_len

用到的索引字段的长度,通过这个字段可以显示具体使用到了索引字段中的哪些列(主要针对联合索引):计算公式如下

字符串
char(n):n字节长度
varchar(n):如果是utf-8,则长度 3n + 2 字节,加的2字节用来存储字符串长度

数值类型
tinyint:1字节
smallint:2字节
int:4字节
bigint:8字节

时间类型
date:3字节
timestamp:4字节
datetime:8字节

如果字段允许为 NULL,需要1字节记录是否为 NULL
索引最大长度是768字节,当字符串过长时,mysql会做一个类似左前缀索引的处理,将前半部分的字符提取出来做索
引。

ref

表示那些列或常量被用于查找索引列上的值

rows

表示在查询过程中检索了多少列 但是并不一定就是返回这么多列数据。

filtered

Extra

展示一些额外信息。

Using filesort
这种情况是在使用 order by 关键字的时候,如果待排序的内容无法通过索引直接直接进行排序,mysql就有可能进行文件排序。

当然不是说出现了此情况就会对sql语句的效率造成影响。但是由于查询次数过多的话,对于排序的效率还是有一定的影响的。
可以通过设置 max_length_for_sort_data 来 提高 order by 的效率。如果操作的数据大小高于max_length_for_sort_data 的缓存大小时,mysql会产生临时表进行查询,一定程度上会印象效率。 max_length_for_sort_data的默认值是1024。
优化方案:

优化业务逻辑,不使用 order by ,而在业务代码中执行排序方法
设置索引,将带排序的内容放在索引中,直接利用索引进行排序
Using index

使用索引,表示索引能够覆盖所有的查询字段,无需进行回表查询所以效率会高。大部分情况代表最优

Using where 单独出现
表示当前查询的字段不能被索引覆盖,所以可能会产生回表,效率比前者低

Using where;Using index
表示查询的列被索引覆盖,且where筛选条件是索引列前导列的一个范围,或者是索引列的非前导列。 效率也比较高

null
表示查询的列未被索引覆盖,且where筛选条件是索引的前导列,这意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表查询”来实现,因而性能也比前两者差。

Using index condition
表示查询条件中虽然出现了索引列,但是有部分条件无法使用索引,会根据能用索引的条件先搜索一遍再匹配无法使用索引的条件。

explain分析sql:https://blog.csdn.net/bugNoneNull/article/details/107489256
explain sql的 extra解释:https://blog.csdn.net/Harswlgb/article/details/121907305

解决死锁问题

死锁理论:

  1. 死锁,就是两个或者两个以上的线程在执行过程中,去争夺同一个共享资源导致互相等待的现象。
    在没有外部干预的情况下,线程会一直处于阻塞状态,无法往下执行。
  2. 产生死锁需要满足的条件
    互斥条件, 共享资源x和y只能被一个线程占用
    请求和保持条件, 线程t1已经获取共享资源x,在等待共享资源y的时候,不释放共享资源x
    不可抢占 条件, 其他线程不能强行抢占线程t1占有的资源
    循环等待条件, 线程t1等到线程t2占有的资源,线程t2等待线程t1占有的资源,形成循环等待
  3. 破环死锁
    线程在产生死锁以后,只能通过外部干预来解决,比如重启、或者kill线程等。
    所以我们在写代码的时候,就应该去刻意规避死锁的问题。
    也就是避免同时满足这四个条件。
    在这四个条件里面,互斥条件是锁本身的特性,无法被破坏,其他三个条件都可以被破坏
    对于请求和保持条件,我们可以在第一次执行的时候一次性申请所有的共享资源
    比如常量,第一次请求的时候后放入缓存
    对于不可抢占条件,占用部分资源的线程在进一步申请其他资源的时候,如果申请不到,就主动释放
    它占有的资源。
    循环等待条件,可以按照顺序来申请资源,相当于给资源编号,按照编号顺序申请就可以避免循
    环等待。
    当然,死锁问题不仅仅局限在多线程领域,单反涉及到互斥锁的地方都有可能出现,
  4. 具体的解决方法
    程序出现死锁,是因为在多线程环境里面两个或两个以上的线程同时满足
    互斥条件、请求保持条件、不可抢占条件、循环等待条件。
    出现死锁以后,可以通过jstack命令去导出线程的dump日志,
    然后从dump日志里面定位到具体死锁的程序代码。
    通过修改程序代码去破坏这四个条件里面的任意一个,就可以解决死锁问题。
    当然,因为互斥条件因为是锁本身的特性,所以不能被破坏。

数据库优化

mysql本身程序的优化

在my.cnf中修改。
binlog日志,默认是不开启
缓存池bufferpoll的默认大小配置等。
由于这些配置一般都和用户安装的硬件环境以及使用场景有关系,因此这些配置官方只会提供一个默
认值,具体情况还得由使用者来修改。

关于配置项的修改,需要关注两个方面。

  • 配置的作用域,分为会话级别和全局
  • 是否支持热加载
    因此,针对这两个点,我们需要注意的是:
  • 全局参数的设定对于已经存在的会话无法生效
  • 会话参数的设定随着会话的销毁而失效
  • 全局类的统一配置建议配置在默认配置文件中,否则重启服务会导致配置失效

搭建Mysql主从集群

单个Mysql服务容易单点故障,一旦服务器宕机,将会导致依赖Mysql数据库的应用全部无法响应。 主从集群或者主主集群可以保证服务的高可用性。
我的博客有配置主从的过程

读写分离设计

在读多写少的场景中,通过读写分离的方案,可以避免读写冲突导致的性能影响
springboot的使用场景:
引入
import com.baomidou.dynamic.datasource.annotation.DS;
使用注解
@DS(‘数据源’)
注意事项

  • 不能使用事务,否则数据源不会切换,使用的还是第一次加载的数据源;
  • 第一次加载数据源之后,第二次、第三次…操作其它数据源,如果数据源不存在,使用的还是第一次加载的数据源;
  • 数据源名称不要包含下划线,否则不能切换。

引入分库分表机制

通过分库可以降低单个服务器节点的IO压力,通过分表的方式可以降低单表数据量,从而提升sql查询的效率。

针对热点数据

可以引入更为高效的分布式数据库,比如Redis、MongoDB等,他们可以很好的缓解Mysql的访问压力,同时还能提升数据检索性能。

分库分表

  • 水平分库:以字段为依据,按照一定策略(hash、range 等),将一个库中的数
    据拆分到多个库中。
  • 水平分表:以字段为依据,按照一定策略(hash、range 等),将一个表中的数
    据拆分到多个表中。
  • 垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。
  • 垂直分表:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和
    扩展表)中。
    常用的分库分表中间件:
  • sharding-jdbc(当当)
  • Mycat  TDDL(淘宝)
  • Oceanus(58 同城数据库中间件)  vitess(谷歌开发的数据库中间件)
  • Atlas(Qihoo 360)

分库分表可能遇到的问题

  • 事务问题:需要用分布式事务啦
    微服务可利用seata,但是存在bug
    之前有大佬给的建议是自己做回滚操作,比如事务中存在新增的接口,则需要提供一个回滚的减接口
  • 跨节点 Join 的问题:解决这一问题可以分两次查询实现
  • 跨节点的 count,order by,group by 以及聚合函数问题:分别在各个节点上得到
    结果后在应用程序端进行合并
  • 数据迁移,容量规划,扩容等问题
  • ID 问题:数据库被切分后,不能再依赖数据库自身的主键生成机制啦,最简单
    可以考虑 UUID
  • 跨分片的排序分页问题(后台加大 pagesize 处理?)

三种应用场景

  • 只分库不分表(并发高)
    当数据库的读写访问量过高,还有可能会出现数据库连接不够用的情况。这个时候我们就需要
    考虑分库,通过增加数据库实例的方式来获得更多的数据库连接,从而提升系统的并发性能。
  • 只分表不分库(数据大)
    当单表存储的数据量非常大的情况下,并且并发量也不高,数据库的连接也还够用。但是数据
    写入和查询的性能出现了瓶颈,这个时候就需要考虑分表了。将数据拆分到多张表中来减少单表存储
    的数据量,从而提升读写的效率。
  • 既分库又分表
    结合前面的两种情况,如果同时满足前面的两个条件,也就是数据连接也不够用,并且单表的
    数据量也很大,从而导致数据库读写速度变慢的情况,这个时候就要考虑既分库又分表。

InnoDB 与MyISAM 的区别

  • 第一个,数据存储的方式不同,
    MyISAM中的数据和索引是分开存储的
    InnoDB是把索引和数据存储在同一个文件里面。
  • 第二个,事务的支持不同
    MyISAM不支持事务,
    InnoDB支持ACID特性的事务处理
  • 第三个 锁的支持不同
    MyISAM只支持表锁
    InnoDB可以根据不同的情况,支持行锁,表
    锁,间隙锁,临键锁
  • 第四个
    MyISAM不支持外键
    InnoDB支持外键

因此基于这些特性我们在实际应用中,可以根据不同的场景来选择合适的存储引擎。
比如如果需要支持事务,那必须要选择InnoDB。如果大部分的表操作都是查询,可以选择MyISAM。

MyISAM和InnoDB到底有什么区别

MySQL 5.5以后的版本开始将InnoDB作为默认的存储引擎,之前的版本都是MyISAM。关于
MyISAM和InnoDB的区别,我总结为以下5个方面,希望能帮助到大家。
1). 数据的存储结构不同
MyISAM,每个MyISAM在磁盘上存储成三个文件,它们以表的名字开头来命名。.frm文件存储表定义。.MYD(MYD)存储数据文件。.MYI(MYIndex)存储索引文件。
InnoDB在磁盘上保存为两个文件。.frm文件同样存储为表结构文件,.ibd文件存储的是
数据和索引文件。
MyISAM的索引和数据是分开存储的,因此索引查找的时候,MyISAM的叶子节点存
储的是数据所在的地址,而不是数据。
InnoDB叶子节点存储的是整个数据行所有的数据。
2). 存储空间的消耗不同
MyISAM可被压缩,存储空间较小。支持三种不同的存储格式:静态表(默认,但是注
意数据末尾不能有空格,会被去掉)、动态表、压缩表。
InnoDB需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数
据和索引。InnoDB所在的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表
空间),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB。
3). 对事务的支持不同
MyISAM强调的是性能,每次查询具有原子性,其执行速度比Innodb类型更快,但是不提
供事务支持。
InnoDB除了提供事务支持和外部键等高级数据库功能。还具有事务提交(commit)、回
滚(rollback)和崩溃修复能力(crach recovery capabilities)等这些事务安全
(transaction-safe ACID compliant)型表。
4). 对锁的支持不同
如果只是执行大量的查询, MyISAM是更好的选择。MyISAM在增删的时候需要锁定整个表
格,效率会低一些。
而innoDB支持行级锁,删除插入的时候只需要锁定操作行就行。如果有大量的插入、修改
删除操作,使用InnoDB性能能会更高。
5). 对外键的支持不同
MyISAM不支持外键,而InoDB支持外键。当然,各种不同MySQL版本对两者的支持都有
所改进

总结

1). 数据的存储结构不同
2). 存储空间的消耗不同
3). 对事务的支持不同
4). 对锁的支持不同
5). 对外键的支持不同

1、如果需要支持事务,选择InnoDB,不需要事务则选择MyISAM。
2、如果大部分表操作都是查询,选择MyISAM,有写又有读选InnoDB。
3、如果系统崩溃导致数据难以恢复,且成本高,不要选择MyISAM。

数据库索引的原理,为什么要用 B+树,为什么不用二叉树?

B树(英语:B-tree)是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。B树,概括来说是一个一般化的二叉查找树(binary search tree),可以拥有多于2个子节点。与自平衡二叉查找树不同,B树为系统大块数据的读写操作做了优化。B树减少定位记录时所经历的中间过程,从而加快存取速度。B树这种数据结构可以用来描述外部存储。这种数据结构常被应用在数据库和文件系统的实现上。

常规回答

B+树非叶子节点上是不存储数据的,仅存储键值
B 树节点中不仅存储键值,也会存储数据。
innodb 中页的默认大小是 16KB,如果不存储数据,那么就会存储更多的键值,相应的树的阶数
(节点的子节点树)就会更大,树就会更矮更胖,如此一来我们查找数据进行磁盘的 IO 次数有会再
次减少,数据查询的效率也会更快。
B+树索引的所有数据均存储在叶子节点,而且数据是按照顺序排列的,链表连着的。那么 B+树使得
范围查找,排序查找,分组查找以及去重查找变得异常简单。

原理

为什么采用B树

B树

  • 常规的数据库存储引擎,一般都是采用B树或者B+树来实现索引的存储。
  • B树是一种多路平衡树,用这种存储结构来存储大量数据,它的整个高度会相比二叉树来说,会
    矮很多。
  • 对于数据库来说,所有的数据必然都是存储在磁盘上的,而磁盘IO的效率实际上是很低的,特别
    是在随机磁盘IO的情况下效率更低。
  • 树的高度能够决定磁盘IO的次数,磁盘IO次数越少,对于性能的提升就越大
MySQL的innodb

B+树

  • B+树的所有数据都存储在叶子节点,非叶子节点只存储索引。
  • 叶子节点中的数据使用双向链表的方式进行关联。

B+树来实现索引的原因

1、从磁盘I/O效率方面来看:B+树的非叶子节点不存储数据,所以树的每一层就能够存储
更多的索引数量,也就是说,B+树在层高相同的情况下,比B树的存储数据量更多,间接会减少
磁盘I/O的次数。
2、从范围查询效率方面来看:在MySQL中,范围查询是一个比较常用的操作,而B+树的
所有存储在叶子节点的数据使用了双向链表来关联,所以B+树在查询的时候只需查两个节点进
行遍历就行,而B树需要获取所有节点,因此,B+树在范围查询上效率更高。
3、从全表扫描方面来看:因为,B+树的叶子节点存储所有数据,所以B+树的全局扫描能
力更强一些,因为它只需要扫描叶子节点。而B树需要遍历整个树。
4、从自增ID方面来看:基于B+树的这样一种数据结构,如果采用自增的整型数据作为主键,
还能更好的避免增加数据的时候,带来叶子节点分裂导致的大量运算的问题。

聚集索引与非聚集索引的区别

简短回答聚集索引与非聚集索引的区别

  • 聚集索引就是通过主键来构建的索引结构。
  • 非聚集索引就是除了主键以外的其他索引。

原理

聚集索引就是基于主键创建的索引,除了主键索引以外的其他索引,称为非聚集索引,也
叫做二级索引。
聚集索引

  • InnoDB引擎里面,一张表的数据对应的物理文件本身就是按照B+树来组织的一种索引结构,
    而聚集索引就是按照每张表的主键来构建一颗B+树,然后叶子节点里面存储了这个表的每一行数据
    记录。

  • 基于InnoDB这样的特性,聚集索引并不仅仅是一种索引类型,还代表着一种数据的存储方式。
    同时也意味着每个表里面必须要有一个主键,如果没有主键,InnoDB会默认选择或者添加一个隐藏
    列作为主键索引来存储这个表的数据行。一般情况是建议使用自增id作为主键,这样的话id本身具有
    连续性使得对应的数据也会按照顺序存储在磁盘上,写入性能和检索性能都很高。否则,如果使用
    uuid这种随机id,那么在频繁插入数据的时候,就会导致随机磁盘IO,从而导致性能较低。

  • InnoDB里面只能存在一个聚集索引,原因很简单,如果存在多个聚集索引,那么意
    味着这个表里面的数据存在多个副本,造成磁盘空间的浪费,以及数据维护的困难。

  • 主键索引表示的是一种数据存储结构,所以如果是基于非聚集索引来查询一条
    完整的记录,最终还是需要访问主键索引来检索。

limit100000

  • 方案一:如果 id 是连续的,可以这样,返回上次查询的最大记录(偏移量),再往下 limit
    select id,name from employee where id>1000000 limit 10.
  • 方案二:在业务允许的情况下限制页数:
    建议跟业务讨论,有没有必要查这么后的分页啦。因为绝大多数用户都不会往后翻太多页。
  • 方案三:order by + 索引(id 为索引)
    select id,name from employee order by id limit 1000000,10
  • 方案四:利用延迟关联或者子查询优化超多分页场景。(先快速定位需要获
    取的 id 段,然后再关联)

合适的分布式主键方案

数据库自增长序列或字段。

  • UUID。
  • Redis 生成 ID
  • Twitter 的snowflake 算法
  • 利用 zookeeper 生成唯一 ID
  • MongoDB 的 ObjectId

事务的隔离级别有哪些?

读未提交(Read Uncommitted)
读已提交(Read Committed)
可重复读(Repeatable Read)
串行化(Serializable)
Mysql 默认的事务隔离级别是可重复读(Repeatable Read)

什么是幻读,脏读,不可重复读呢?

  • 脏读
    假设有两个事务T1/T2同时在执行,T1事务有可能会读取到T2事务未提交的数据,但是未提交的事
    务T2可能会回滚,也就导致了T1事务读取到最终不一定存在的数据
  • 不可重复读
    假设有两个事务T1/T2同时执行,事务T1在不同的时刻读取同一行数据的时候结果可能不一样
  • 幻读
    假设有两个事务T1/T2同时执行,事务T1执行范围查询或者范围修改的过程中,事务T2插入了一条
    属于事务T1范围内的数据并且提交了,这时候在事务T1查询发现多出来了一条数据,或者在T1事务
    发现这条数据没有被修改,看起来像是产生了幻觉

读未提交,在这种隔离级别下,可能会产生脏读、不可重复读、幻读。
读已提交(RC),在这种隔离级别下,可能会产生不可重复读和幻读。
可重复读(RR),在这种隔离级别下,可能会产生幻读
串行化,在这种隔离级别下,多个并行事务串行化执行,不会产生安全性问题。
这四种隔离级别里面,只有串行化解决了全部的问题,但也意味着这种隔离级别的性能是最低的。
在Mysql里面,InnoDB引擎默认的隔离级别是RR(可重复读),因为它需要保证事务ACID特性中
的隔离性特征

数据库的乐观锁和悲观锁

悲观锁

悲观锁她专一且缺乏安全感了,她的心只属于当前事务,每时每刻都担心着它心爱的数据可能被别的
事务修改,所以一个事务拥有(获得)悲观锁后,其他任何事务都不能对数据进行修改啦,只能等待
锁被释放才可以执行。

乐观锁

乐观锁的“乐观情绪”体现在,它认为数据的变动不会太频繁。因此,它允许多 个事务同时对数据
进行变动。实现方式:乐观锁一般会使用版本号机制或 CAS 算法实现。//todo 后续拓展 版本号机制和CAS算法

在高并发情况下,如何做到安全的修改同一行数据?

要安全的修改同一行数据,就要保证一个线程在修改时其它线程无法更新这行记录。一般有悲观锁和
乐观锁两种方案~

悲观锁

悲观锁思想就是,当前线程要进来修改数据时,别的线程都得拒之门外~ 比如, 可以使用 select…
for update ~
select * from talbe where name=‘czmczm’ for update
以上这条 sql 语句会锁定了 User 表中所有符合检索条件(name=‘jay’)的记录。本次事务提交
之前,别的线程都无法修改这些记录。 使用乐观锁

for update

select 查询语句是不会加锁的,但是 select for update 除了有查询的作用外, 还会加锁呢,而且它
是悲观锁哦。至于加了是行锁还是表锁,这就要看是不是 用了索引/主键啦。
没用索引/主键的话就是表锁,否则就是是行锁。

乐观锁

思想就是,有线程过来,先放过去修改,如果看到别的线程没修改过, 就可以修改成功,如
果别的线程修改过,就修改失败或者重试。实现方式:乐观锁一般会使用版本号机制或 CAS 算法实
现。

MySQL 事务的四大特性以及实现原理

  • 原子性: 事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都
    不执行。
  • 一致性: 指在事务开始之前和事务结束以后,数据不会被破坏,假如 A 账户给 B 账户转 10 块钱,不管成功与否,A 和B 的总金额是不变的。
  • 隔离性: 多个事务并发访问时,事务之间是相互隔离的,即一个事务不影响其它事务运行效
    果。简言之,就是事务之间是进水不犯河水的。
  • 持久性: 表示事务完成以后,该事务对数据库所作的操作更改,将持久地保存在数据库之中。

如果某个表有近千万数据,CRUD比较慢,如何优化

  • 分表方案(水平分表,垂直分表,切分规则hash等)
  • 分库分表中间件(Mycat,sharding-jdbc等)
  • 分库分表一些问题(事务问题?跨节点Join的问题)
  • 解决方案(分布式事务等)
  • 索引优化

mysql中in和exists的区别

select * from A where id in (select id from B)
select * from A where exists(select id from Bwhere A.id = B.aId)

mysql优化原则,就是小表驱动大表,小的数据集驱动大的数据集,从而让性能更优。

exists查询的理解就是,先执行主查询,获得数据后,再放到子查询中做条件验证,根据验证结果(true或者false),来决定主查询的数据结果是否得意保留。
B的数据量小于A,适合使用in,如果B的数据量大于A,即适合选择exists,这就是in和exists的区别。

数据库自增主键可能遇到什么问题

  • 使用自增主键对数据库做分库分表,可能出现诸如主键重复等的问题。
    解决方案话,简单点的话可以考虑使用 UUID 哈
  • 自增主键会产生表锁,从而引发问题
  • 自增主键可能用完问题。

MVCC原理:

MVCC原理解释

  • 事务版本号
  • 表的隐藏列
  • undo log
  • read view

数据库中间件

  • sharding-jdbc 目前是基于 jdbc 驱动,无需额外的 proxy,因此也无需关注proxy 本身的高
    可用。
  • Mycat 是基于 Proxy,它复写了 MySQL 协议,将 Mycat Server 伪装成一个MySQL 数据
    库,而 Sharding-JDBC 是基于 JDBC 接口的扩展,是以 jar 包的形式提供轻量级服务的。

MYSQL 的主从延迟

主从复制过程

分了五个步骤进行:

  • 步骤一:主库的更新事件(update、insert、delete)被写到 binlog
  • 步骤二:从库发起连接,连接到主库。
  • 步骤三:此时主库创建一个 binlog dump thread,把binlog 的内容发送到从库。
  • 步骤四:从库启动之后,创建一个 I/O 线程,读取主库传过来的 binlog 内容并写入到 relay
    log
  • 步骤五:还会创建一个 SQL 线程,从 relay log 里面读取内容,从Exec_Master_Log_Pos
    位置开始执行读取到的更新事件,将更新内容写入到slave 的db

主从同步延迟的原因

一个服务器开放N个链接给客户端来连接的,这样有会有大并发的更新操作, 但是从服务器的里面读
取 binlog 的线程仅有一个,当某个 SQL 在从服务器上执行的时间稍长 或者由于某个 SQL 要进行锁
表就会导致,主服务器的 SQL 大量积压,未被同步到从服务器里。这就导致了主从不一致, 也就是
主从延迟。(当有一个修改语句太慢且锁表,导致数据积压,读取不准确,从而使主从数据不一致)

主从同步延迟的解决办法

  • 主服务器要负责更新操作,对安全性的要求比从服务器要高,所以有些设置参数可以修改,
    比如 sync_binlog=1,innodb_flush_log_at_trx_commit = 1 之类的设置等。
  • 选择更好的硬件设备作为 slave。
  • 把一台从服务器当度作为备份使用, 而不提供查询, 那边他的负载下来了, 执行relay log 里面的 SQL 效率自然就高了。
  • 增加从服务器喽,这个目的还是分散读的压力,从而降低服务器负载。

大表查询的优化方案

  • 优化 shema、sql 语句+索引;
  • 可以考虑加缓存,memcached, redis,或者 JVM 本地缓存;
  • 主从复制,读写分离;
  • 分库分表。

MySql逻辑图

在这里插入图片描述

逻辑架构图三层结构

  • 第一层负责连接处理,授权认证,安全等等
  • 第二层负责编译并优化 SQL
  • 第三层是存储引擎。

第二层

  • 先检查该语句是否有权限
  • 如果没有权限,直接返回错误信息
  • 如果有权限,在 MySQL8.0 版本以前,会先查询缓存。
  • 如果没有缓存,分析器进行词法分析,提取 sql 语句 select 等的关键元素。然后
    判断sql 语句是否有语法错误,比如关键词是否正确等等。
  • 优化器进行确定执行方案
  • 进行权限校验,如果没有权限就直接返回错误信息,如果有权限就会调用数据库引
    擎接口,返回执行结果

InnoDB引擎中的索引策略

  • 覆盖索引
  • 最左前缀原则
  • 索引下推
  • 索引下推优化是 MySQL 5.6 引入的, 可以在索引遍历过程中,对索引中包含的字
    段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
  • 最左前缀原则:MySQl 建立联合索引时,会遵循最左前缀匹配的原则,
    即最左优先。如果你建立一个(a,b,c)的联合索引,相当于建立了
    (a)、(a,b)、(a,b,c)三个索引。
  • 覆盖索引:只需要在一棵索引树上就能获取 SQL 所需的所有列数据,无
    需回表,速度更快

MYSQL数据库服务器性能分析的方法命令有哪些

Show status, 一些值得监控的变量值

Bytes_received 和 Bytes_sent 和服务器之间来往的流量。

  • Com_*服务器正在执行的命令。
  • Created_*在查询执行期限间创建的临时表和文件。
  • Handler_*存储引擎操作。
  • Select_*不同类型的联接执行计划。
  • Sort_*几种排序信息
    在这里插入图片描述

Show profiles 是MySql 用来分析当前会话 SQL 语句执行的资源消耗情况

在这里插入图片描述

Blob 和text 有什么区别

  • Blob 用于存储二进制数据,而 Text 用于存储大字符串。
  • Blob 值被视为二进制字符串(字节字符串),它们没有字符集,并且排序和比较基
    于列值中的字节的数值。
  • text 值被视为非二进制字符串(字符字符串)。它们有一个字符集,并根据字符
    集的排序规则对值进行排序和比较

Mysql中几种锁

  • 表锁: 开销小,加锁快;锁定力度大,发生锁冲突概率高,并发度最低;不会出现
    死锁。
  • 行锁: 开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发
    度高。
  • 页锁: 开销和加锁速度介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和
    行锁之间,并发度一般

mysql的内连接、左连接、右连接

  • Inner join 内连接,在两张表进行连接查询时,只保留两张表中完全匹配的结果集
  • left join 在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配
    的记录。
  • right join 在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有
    匹配的记录。

内连接、外连接、交叉连接、笛卡尔积

内连接(inner join):取得两张表中满足存在连接匹配关系的记录。

外连接(outer join):取得两张表中满足存在连接匹配关系的记录,以及某张表

(或两张表)中不满足匹配关系的记录。

左外连接

左外连接 = 内连接 + 左边表中失配的元组
其中,缺少的右边表中的属性值用NULL表示

右外连接

右外连接 = 内连接 + 右边表中失配的元组
其中,缺少的左边表中的属性值用NULL表示

全外连接

全外连接 = 内连接 + 左边表中失配的元组 + 右边表中失配的元组。
其中,缺少的左边表或者右边表中的属性值用NULL表示

交叉连接(cross join)

显示两张表所有记录一一对应,没有匹配关系进行筛选,、也被称为:笛卡尔积。

数据库的三大范式

  • 第一范式:数据表中的每一列(每个字段)都不可以再拆分。
  • 第二范式:在第一范式的基础上,分主键列完全依赖于主键,而不能是依赖于主键
    的一部分。
  • 第三范式:在满足第二范式的基础上,表中的非主键只依赖于主键,而不依赖于其
    他非主键。

Mysql 的binlog 有几种录入格式

  • statement,记录的是 SQL 的原文。不需要记录每一行的变化,减少了binlog 日志量,节约了 IO,提高性能。由于 sql 的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制。
  • row,不记录 sql 语句上下文相关信息,仅保存哪条记录被修改。记录单元为
    每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改
    动(比如 alter table),因此这种模式的文件保存的信息太多,日志量太大。
  • mixed,一种折中的方案,普通操作使用 statement 记录,当无法使用 statement 的时候使用 row。

InnoDB引擎的 4 大特性

  • 插入缓冲(insert buffer)
  • 二次写(double write)
  • 自适应哈希索引(ahi)
  • 预读(read ahead)

后面拓展这些的概念

索引有哪些优缺点

优点

  • 索引,是一种能够帮助Mysql高效从磁盘上检索数据的一种数据结构。

  • 在Mysql中的InnoDB引擎中,采用了B+树的结构来实现索引和数据的存储

  • 通过B+树的结构来存储数据,可以大大减少数据检索时的磁盘IO次数,从而提升数据查询的性能
    B+树索引在进行范围查找的时候,只需要找到起始节点,然后基于叶子节点的链表结构往下读取即
    可,查询效率较高。

  • 通过唯一索引约束,可以保证数据表中每一行数据的唯一性

缺点

  • 数据的增加、修改、删除,需要涉及到索引的维护,当数据量较大的情况下,索引的维护会带来较大
    的性能开销。
  • 一个表中允许存在一个聚簇索引和多个非聚簇索引,但是索引数不能创建太多,否则造成的索引维护
    成本过高。
  • 创建索引的时候,需要考虑到索引字段值的分散性,如果字段的重复数据过多,创建索引反而会带来
    性能降低。

索引类型

  • 主键索引: 数据列不允许重复,不允许为 NULL,一个表只能有一个主键。
  • 唯一索引: 数据列不允许重复,允许为 NULL 值,一个表允许多个列创建唯一索引。
  • 普通索引: 基本的索引类型,没有唯一性的限制,允许为 NULL 值。
  • 全文索引:是目前搜索引擎使用的一种关键技术,对文本的内容进行分词、搜索。
  • 覆盖索引:查询列要被所建的索引覆盖,不必读取数据行
  • 组合索引:多列值组成一个索引,用于组合搜索,效率大于索引合并

创建索引原则

-最左前缀匹配原则

  • 频繁作为查询条件的字段才去创建索引
  • 频繁更新的字段不适合创建索引
  • 索引列不能参与计算,不能有函数操作
  • 优先考虑扩展索引,而不是新建索引,避免不必要的索引
  • 在 order by 或者group by 子句中,创建索引需要注意顺序
  • 区分度低的数据列不适合做索引列(如性别)
  • 定义有外键的数据列一定要建立索引。
  • 对于定义为 text、image 数据类型的列不要建立索引。
  • 删除不再使用或者很少使用的索引

创建索引

  • 在执行 CREATE TABLE 时创建索引
  • 使用 ALTER TABLE 命令添加索引
    ALTER TABLE table_name ADD INDEX index_name (column);
  • 使用 CREATE INDEX 命令创建所以
    CREATE INDEX index_name ON table_name (column);

百万级别或以上的数据,你是如何删除的?

  • 我们想要删除百万数据的时候可以先删除索引
  • 然后批量删除其中无用数据
  • 删除完成后重新创建索引

覆盖索引、回表

  • 覆盖索引: 查询列要被所建的索引覆盖,不必从数据表中读取,换句话说查询列
    要被所使用的索引覆盖。
  • 回表:二级索引无法直接查询所有列的数据,所以通过二级索引查询到聚簇索引后,
    再查询到想要的数据,这种通过二级索引查询出来的过程,就叫做回表。
    比如:
    有一个索引 index_age
    select age from student where age<20
    列包含在索引中,则不需要回表。
    反而则需要回表。

联合索引是什么

联合索引,用户可以在多个列上建立索引,这种索引叫做联合索引。
联合索引遵守最左原则

数据库隔离级别与锁的关系(这个知识点我感觉有点绕)

可以先阐述四种隔离级别,再阐述它们的实现原理。隔离级别就是依赖锁和MVCC实现的。

InnoDb的锁

1、共享/排他锁(行级)
**行级锁**
共享锁(简称 S 锁):简称为 S 锁,**在事务要读取一条记录时,需要先获取该记录的 S 锁**
排他锁(简称 X锁):简称 X 锁,**在事务需要改动一条记录时,需要先获取该记录的 X 锁**

事务T1 拥有R的S锁,T2请求访问这记录
- T2请求S锁 ,结果T1T2共同拥有R行的S锁
- T2请求X锁,此操作被堵塞
事务T1 持有R的X锁, T2请求R的X、S锁都不被允许,T2必须等待T1释放X锁。因为X锁和任何的锁都不兼容

X 锁和S锁

X锁和S锁的定义, 既可以在行锁,又可以在表锁。
表锁
如果一个事务给表已经加了 S 锁,则:

  • 别的事务可以继续获得该表的 S 锁,也可以获得该表中某些记录的 S 锁。
  • 别的事务不可以继续获得该表的 X 锁,也不可以获得该表中某些记录的 X 锁。
2、意向锁(表/行级)
  • 意向锁是表级锁,是一种不与行级锁冲突的表级锁。
  • 在某个时刻,事务可能要加共享或者排它锁。
  • 加共享锁或排他锁时的 时候,需要提前声明个意向锁
为什么要意向锁

因为InnoDB 是支持表锁和行锁共存的,如果一个事务 A 获取到某一行的排他
锁,并未提交,这时候事务 B 请求获取同一个表的表共享锁。因为共享锁和排
他锁是互斥
的,因此事务 B 想对这个表加共享锁时,需要保证没有其他事务持
有这个表的表排他锁,同时还要保证没有其他事务持有表中任意一行的排他锁
然后问题来了,你要保证没有其他事务持有表中任意一行的排他锁的话,去遍
历每一行?这样显然是一个效率很差的做法。为了解决这个问题,InnoDb 的
设计大叔提出了意向锁。

意向锁分两类
  • 意向共享锁:简称 IS锁,当事务准备在某些记录上加 S 锁时,需要现在表级别加
    一个 IS锁。
  • 意向排他锁:简称 IX锁,当事务准备在某条记录上加上 X 锁时,需要现在表级别
    加一个 IX锁。

如果一个事务 A 获取到某一行的排他锁,并未提交,这时候表上就有意向排他
锁 和这一行的排他锁。这时候事务 B 想要获取这个表的共享锁,此时因为检
测到事务 A 持有了表的意向排他锁,因此事务 A 必然持有某些行的排他锁,
也就是说事务 B 对表的加锁请求需要阻塞等待,不再需要去检测表的每一行数
据是否存在排他锁啦。

3、记录所(Record Lock)
SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE

记录锁是最简单的行锁,仅仅锁住一行
如果 C1 字段是主键或者是唯一索引的话,这个 SQL 会加一个记录锁(Record Lock)

记录锁永远都是加在索引上的,即使一个表没有索引,InnoDB 也会隐式的创建一个索引,并使用这个索引实施记录锁。
它会阻塞其他事务对这行记录的插入、更新、删除。

一般我们看死锁日志时,都是找关键词,比如 lock_mode X locks rec but not
gap),就表示一个 X 型的记录锁。记录锁的关键词就是 rec but not gap。
以下就是一个记录锁的日志
在这里插入图片描述

4、间隙锁(Gap Lock)

为了解决幻读问题,InnoDB 引入了间隙锁(Gap Lock)。间隙锁是一种加在两
个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙
。它
锁住的是一个区间,而不仅仅是这个区间中的每一条数据
在这里插入图片描述

5、临键锁(Next-Key Lock)

Next-key 锁是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录
前面间隙上的锁。说得更具体一点就是:临键锁会封锁索引记录本身,以及索引
记录之前的区间,即它的锁区间是前开后闭,比如(5,10]。

如果一个会话占有了索引记录 R 的共享/排他锁,其他会话不能立刻在 R 之前的
区间插入新的索引记录。

6、插入意向锁

插入意向锁,是插入一行记录操作之前设置的一种间隙锁,这个锁释放了一种插
入方式的信号。 它解决的问题:多个事务,在同一个索引,同一个范围区间插
入记录时,如果插入的位置不冲突,不会阻塞彼此。
假设有索引值 4、7,几个不同的事务准备插入 5、6,每个锁都在获得插入
行的独占锁之前用插入意向锁各自锁住了 4、7 之间的间隙,但是不阻塞对方
因为插入行不冲突。
插入意向锁日志
锁模式兼容矩阵(横向是已持有锁,纵向是正在请求的锁):

7、自增锁(表级)

自增锁是一种特殊的表级别锁。它是专门针对 AUTO_INCREMENT类型的列,对
于这种列,如果表中新增数据时就会去持有自增锁。简言之,如果一个事务正
在往表中插入记录,所有其他事务的插入必须等待,以便第一个事务插入的行,
是连续的主键值

eg: 假设有一张表设置了自增主键
在这里插入图片描述
在这里插入图片描述
可以看出b还没提交事务,a提交事务,但是出现了3.

自增锁是一个表级别锁,那为什么会话 A 事务还没结束,事务 B 可以执行插入
成功呢?不是应该锁表嘛?这是因为在参数 innodb_autoinc_lock_mode上,这个
参数设置为 1的时候,相当于将这种 auto_inc lock弱化为了一个更轻量级的互斥
自增长机制去实现,官方称之为 mutex。
innodb_autoinc_lock_mode 还可以设置为 0 或者 2。

  • 0:表示传统锁模式,使用表级 AUTO_INC锁。一个事务✁ INSERT-LIKE语句在语
    句执行结束后释放 AUTO_INC 表级锁,而不是在事务结束后释放。

  • 1: 连续锁模式,连续锁模式对于 Simple inserts不会使用表级锁,而是使用一个轻
    量级锁来生成自增值,因为 InnoDB 可以提前直到插入多少行数据。自增值生成
    阶段使用轻量级互斥锁来生成所有的值,而不是一直加锁直到插入完成。对于bulk
    inserts
    类语句使用 AUTO_INC 表级锁直到语句完成。

  • 2:交错锁模式,所有的INSERT-LIKE语句都不使用表级锁,而是使用轻量级互
    斥锁(我的数据库是2)

  • INSERT-LIKE:指所有的插入语句,包括: INSERT、REPLACE、INSERT…
    SELECT、REPLACE…SELECT,LOAD DATA 等。

  • Simple inserts:指在插入前就能确定插入行数的语句,包括:
    INSERT、REPLACE,不包含 INSERT…ON DUPLICATE KEY UPDATE 这类
    语句。

  • Bulk inserts: 指在插入钱不能确定行数的语句,包括:INSERT …
    SELECT/REPLACE … SELECT/LOAD DATA。

count(1)、count(*) 与 count(列名)

  • count()包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL
    count(
    ) ,也就是整条数据,由于整条数据肯定不会为null,所以,专门做了优化,
    不会去取全部字段。直接扫描多少数据统计数据即可。

  • count(1)包括了忽略所有列,用 1 代表代码行,在统计结果的时候,不会忽略列
    值为NULL

    count(1) 扫描到一条数据,直接返回一个数字1,不会取扫描的字段,也不会去判断是否为null。
    这2个也是我们用得最多的,性能最好的。

  • count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空
    不是只空字符串或者 0,而是表示 null)的计数,即某个字段值为 NULL 时,不
    统计。
    1.首先去看字段是否能走索引,如果没索引,那么扫描数据不会走索引树,比有索引的字段慢。
    2.字段是否可为null,如果可为null,那么必须要去判断扫描出来的数据是否为null,为null不统计,所以不可为null的比可为null的要快。 如果是必填的会比不必填快

varchar(50)中 50 的涵义

  • 字段最多存放 50 个字符
  • 如 varchar(50) 和 varchar(200) 存储 “jay” 字符串所占空间是一样的,后者在
    排序时会消耗更多内存

UNION 与UNION ALL 的区别?

  • Union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;
  • Union All:对两个结果集进行并集操作,包括重复行,不进行排序;
  • UNION 的效率高于 UNION ALL

SQL 的生命周期?

  • 服务器与数据库的立连接
  • 数据库进程拿到请求 sql
  • 解析并生成执行计划,执行
  • 读取数据到内存,并进行逻辑处理
  • 通过步骤一的连接,发送结果到客户端
  • 关掉连接,释放资源

SQL的执行顺序

1.from (左表的名字)
2.on ( join 的条件)
3.(join的类型) (右表的名字)
4.where(where的条件)
5.group by (group by 的字段)
6.Having( having 的条件)
7.select
8.distinct(要查询的字段)
9.order by desc/asc
10.limit

自增id / uuid

如果是单机的话,选择自增 ID;如果是分布式系统,优先考虑 UUID 吧,但
还是最好自己公司有一套分布式唯一 ID 生产方案吧。

  • 自增 ID:数据存储空间小,查询效率高。但是如果数据量过大,会超出自增长的值
    范围,多库合并,也有可能有问题。
  • uuid:适合大量数据的插入和更新操作,但是它无序的,插入数据效率慢,占用
    空间大。
  • 雪花id:雪花算法生成ID是Long类型,长度64位。
    第 1 位: 符号位,暂时不用。
    第 2~42 位: 共41位,时间戳,单位是毫秒,可以支撑大约69年
    第 43~52 位: 共10位,机器ID,最多可容纳1024台机器
    第 53~64 位: 共12位,序列号,是自增值,表示同一毫秒内产生的ID,单台机器每毫秒最多可生成4096个订单ID
    – 接入非常简单,不需要搭建服务集群,。代码逻辑非常简单,,同一毫秒内,订单ID的序列号自增。同步锁只作用于本机,机器之间互不影响,每毫秒可以生成四百万个订单ID,非常强悍。
    – 生成规则不是固定的,可以根据自身的业务需求调整。如果你不需要那么大的并发量,可以把机器标识位拆出一部分,当作业务标识位,标识是哪个业务线生成的订单ID。### mysql 自增主键用完了怎么办?
    – 雪花算法严重依赖系统时钟。如果时钟回拨,就会生成重复ID
    自增主键一般用 int 类型,一般达不到最大值,可以考虑提前分库分表

读写分离常见方案

  • 应用程序根据业务逻辑来判断,增删改等写操作命令发给主库,查询命令发给备库。
    从库-> 查询 主库增删改
  • 利用中间件来做代理,负责对数据库的请求识别出读还是写,并分发到不同的数据
    库中。(如:amoeba,mysql-proxy)

MySQL的复制原理以及流程

  • 主数据库有个 bin-log 二进制文件,纪录了所有增删改 Sql 语句。(binlog 线程)
  • 从数据库把主数据库的 bin-log 文件的 sql 语句复制过来。(io 线程)
  • 从数据库的relay-log 重做日志文件中再执行一次这些 sql 语句。(Sql 执行
    线程)
    在这里插入图片描述

上图主从复制分了五个步骤进行:
步骤一:主库的更新事件(update、insert、delete)被写到 binlog
步骤二:从库发起连接,连接到主库。
步骤三:此时主库创建一个 binlog dump thread,把 binlog 的内容发送到从库。
步骤四:从库启动之后,创建一个 I/O 线程,读取主库传过来的binlog 内容并写入到 relay log
步骤五:还会创建一个 SQL 线程,从 relay log 里面读取内容,从Exec_Master_Log_Pos 位置开始
执行读取到的更新事件,将更新内容写入到slave 的 db

Innodb 的事务实现原理(ACID)

  • 原子性:是使用 undo log 来实现的,如果事务执行过程中出错或者用户执行
    了rollback,系统通过 undo log 日志返回事务开始的状态。
  • 持久性:使用 redo log 来实现,只要 redo log 日志持久化了,当系统崩溃,即可
    通过redo log 把数据恢复。
  • 隔离性:通过锁以及 MVCC,使事务相互隔离开。
  • 一致性:通过回滚、恢复,以及并发情况下的隔离性,从而实现一致性。

Innodb 的事务与日志的实现方式

innodb 两种日志 redo 和 undo

redo

在页修改的时候,先写到 redo log buffer 里面, 然后写到 redo log 的
文件系统缓存里面(fwrite),然后再同步到磁盘文件( fsync)。

undo

在 MySQL5.5 之前, undo 只能存放在 ibdata 文件里面, 5.6 之后,
可以通过设置 innodb_undo_tablespaces 参数把 undo log 存放在 ibdata 之外。

事务是如何通过日志来实现的
  • 因为事务在修改页时,要先记 undo,在记 undo 之前要记 undo的 redo, 然
    后修改数据页,再记数据页修改的redo。 Redo(里面包括 undo ✁修改) 一
    定要比数据页先持久化到磁盘。
  • 当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的状态,崩溃恢复
    时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo 把该事
    务修改回滚到事务开始之前。
  • 如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。

一个 6 亿的表 a,一个 3 亿的表 b,通过外间 tid 关联,你如何最快地查询出满足条件的第 50000 到第 50200 中的这200 条数据记录。

1、如果 A 表 TID 是自增长,并且是连续的,B 表的 ID 为索引 select * from a,bwhere
a.tid = b.id and a.tid>500000 limit 200;
2、如果 A 表的 TID 不是连续的,那么就需要使用覆盖索引.TID 要么是主键,要
么是辅助索引,B 表 ID 也需要有索引。 select * from b , (select tid from alimit
50000,200) a where b.id = a .tid;

Mysql 一条 SQL 加锁分析

一条 SQL 加锁,可以分 9 种情况进行

  • 组合一:id 列是主键,RC 隔离级别
  • 组合二:id 列是二级唯一索引,RC 隔离级别
  • 组合三:id 列是二级非唯一索引,RC 隔离级别
  • 组合四:id 列上没有索引,RC 隔离级别
  • 组合五:id 列是主键,RR 隔离级别
  • 组合六:id 列是二级唯一索引,RR 隔离级别
  • 组合七:id 列是二级非唯一索引,RR 隔离级别
  • 组合八:id 列上没有索引,RR 隔离级别
  • 组合九:Serializable 隔离级别

Mysql如何解决幻读问题

在RR(也就是可重复读)的事务隔离级别下,InnoDB采用了MVCC机制来解决幻读问题。
MVCC就是一种乐观锁的机制,它通过对不同事务生成不同的快照版本,通过UNDO版本链进行管
理并且在MVCC里面,规定了高版本能够看到低版本的事务变更,低版本看不到高版本的事务变更从
而实现了不同事务之间的数据隔离,解决了幻读的问题。
但是在当前读的情况下,是直接读取内存的数据,跳过了快照度,所以还是会出现幻读问题。
我认为可以通过两个方式来解决。
第一种是尽量避免当前读的情况
第二种是引入LBCC的方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值