目录
MySQL中进行数据操作的时候为什么要尽量用内连接而不是外连接
MySQL 删除自增 id,随后重启 MySQL 服务,再插入数据,自增 id 会从几开始?
当前读和快照读
当前读(Current Read):
- 当前读是指在读取数据时获取最新的数据值,并且读取的数据结果会受到其他并发事务所做修改的影响。
- 当前读使用的是一致性读(Consistent Read)方式,即保证读取的数据是一个一致的时间点上的数据。
- 当前读可以保证读取到最新的数据,但也可能读取到未提交的数据或其他事务已经提交但未生效的数据。
- 当前读常用的场景是在需要读取最新数据的情况下,例如查询操作。
- 当前读读取的是数据的最新版本,并且当前读返回的记录都会上锁,保证其他事务不会并发修改这条记录。如update、insert、delete、select for update(排他锁)、select lockin share mode(共享锁) 都是当前读
快照读(Snapshot Read):
- 快照读是指在读取数据时获取事务开始时的数据快照,并且读取的数据结果不会受到其他并发事务所做修改的影响。
- 快照读使用的是一致性非锁定读(Consistent Non-locking Read)方式,即读取的数据是基于某个时间点的一致性数据快照,不会加锁。
- 快照读可以保证读取过程不会被其他事务的并发修改所干扰,但不能获得最新的已提交数据。
- 快照读常用的场景是在需要读取数据的过程中,不希望对数据加锁或受到其他并发事务干扰的情况。
- 快照读读取的是当前数据的可见版本,可能是会过期数据,不加锁的select就是快照读
通俗讲!
-
当前读:假设你正在网上购物,在确认购买前,你想要查看最新的商品信息和价格。你会刷新页面以获取最新的数据,这就是当前读。它可以确保你看到的数据是最新的,但也可能会受到其他人修改数据的影响。所以,如果你在下单之前等待太久,商品信息可能会发生改变。
-
快照读:现在假设你已经决定要购买某个商品,并放入购物车中。此时,你不再关心商品信息是否发生了变化,而是看中了你添加到购物车时的那个价格。尽管在你确认订单之前价格可能已经发生了变化,但你仍然希望看到的是添加到购物车时的价格,这就是快照读。它可以确保你查看的数据不会被其他人的修改所干扰。
MVCC是什么?
MVCC指的是多版本并发控制。它用来保证多个事务在同时读取和修改数据库时的一致性和隔离性——通过在数据库中保存多个版本的数据实现
简单来说——MVCC允许多个事务同时读取数据库中的数据,并且每个事务都可以看到自己开始执行时的那个版本(数据快照)。当事务做出修改操作时,MVCC会创建一个新的数据版本,而不是直接修改原始数据。这样做的好处是,其他事务可以继续读取原始数据版本,而不会受到正在修改的事务的影响。
MVCC的工作原理
- 每个数据行都有一个唯一的时间戳,表示该数据行的版本
- 事务开始时,会获取一个事务ID和一个读取的时间戳
- 事务在读取数据时,只能看到哪些事务开始之前已经存在的数据版本,不会看到其他人正在修改的事务所创建的版本
- 事务在修改数据时,会创建一个新的数据版本,将该版本和事务的时间戳相关联,并将其写入数据库
- 其他事务可以继续读取旧版本的数据,而不受新版本的影响
MVCC的工作原理由四个东西保证
undo log日志:记录了数据历史版本,可以方便回滚。
readview:事务进行快照读是动态产生的视图,决定了返回数据的哪一个版本
隐藏字段DB_TRX_ID:记录最近修改的事务ID
隐藏字段DB_ROLL_PTR:回滚指针,配合undo log指向数据的上一个版本
MySQL有哪些索引
主键索引:一张表只能有一个主键索引,主键索引列不能有控制和重复值
唯一索引:唯一索引不能有重复值,但允许为空
普通索引:允许出现重复值
组合索引:对多个字段建立一个联合索引,遵循最左匹配原则(例如,如果有一个组合索引 (A, B, C),那么当查询条件包含 A,或者同时包含 A 和 B,或者同时包含 A、B 和 C 时,数据库都可以利用该索引进行高效查询。但如果只包含 B 或者只包含 C,那么组合索引将无法有效利用。)
聚簇索引和非聚簇索引
聚簇索引:聚簇索引的叶子节点存放的是主键和数据行;辅助索引(在聚簇索引上创建的其他索引)的叶子节点存放的是主键值或指向数据行的指针
优点:根据索引可以直接获取值,所以它获取数据更快;对于主键的排序查找和范围查找效率更高
缺点:如果主键值很大的话,辅助索引也会变得很大;如果用uuid作为主键,数据存储会很稀疏;修改主键或乱序插入会让数据行移动导致页分裂;所以一般我们定义主键时尽量让主键值小,并且定义为自增和不可修改。
非聚簇索引(辅助索引):叶子节点存放的是数据行地址,先根据索引找到数据地址,再根据地址去找数据
他们都是B+树结构
上图为聚簇索引的呈现形式,在叶子节点可以直接返回我们想要找到的数据
上图是非聚簇索引的呈现形式,在非聚簇索引的叶子节点上存储的并不是真正的行数据,而是主键ID,所以当我们使用非聚簇索引进行查询的时候,首先得到的结果是一个ID,然后拿着ID去主键的聚簇索引上查询真正的行数据,这个过程也叫回表
Mysql如何慢SQL进行优化
常见的慢查询优化有:
(1)尽量减少select的数据列,尽量使用覆盖索引(相当于精简版的索引,包括了需要查询的列)
(3)groupby查询时,同样要用索引,避免使用到临时表
(4)分页查询时,如果limit 后面的数字太大,可以使用子查询查出主键,再limit主键后n条数据就能走覆盖索引
(5) 使用复杂查询时,使用关联查询来代替子查询,并且最好使用内连接
(6)使用count函数时直接使用count的话count(*)的效率最高
count(*)或count(唯一索引)或count(数字):表中总记录数,count(字段)不会统计null
(7) 在写update语句时,where条件要使用索引,否则会锁会从行锁升级为表锁
(8)表中数据是否太大,是不是要分库分表
MySQL中进行数据操作的时候为什么要尽量用内连接而不是外连接
用外连接的话连接顺序是固定的,例如left join,他必须先对左表进行全表扫描,然后再去跟右表当中做匹配。
而使用内连接的话,MySQL会自己根据查询优化器去判断用哪个表做驱动。
子查询的话同样会对驱动表做全表扫描,所以尽量用小表做驱动表
MySQL中整个查询的过程
(1)首先客户端向服务端去发送一条查询请求
(2)服务器会先检查缓存,如果名字缓存,则返回存储在缓存中的结果。否则进入下一阶段
(3)服务器进行SQL解析、预处理、再由优化器生成对应的执行计划
(4)MySQL执行计划,调用存储引擎的API来执行查询
(5)将结果返回给客户端,通俗缓存查询结果
注意:只有在8.0之前才有查询缓存,8.0之后查询缓存被去掉了
SQL的执行计划包含哪些字段
我们想看一个sql的执行计划使用的语句是explain+SQL(查看SQL详细信息)
表中的字段包括:
- type:扫描类型,效率从底到高为ALL(全表扫描)>index(全索引扫描,我们的需要的数据在索引中可以获取)>range(使用索引进行范围查找)>ref(使用非唯一索引列进行了关联查询)> eq_ref (使用唯一索引进行关联查询)>const(使用唯一索引查询一行数据)>system(表中只有一行数据)
- extra(额外的):mysql如何查询额外信息,常见的有:
- filesort:在排序缓冲区中进行排序,需要回表查询数据
- index:表示使用覆盖索引
- index scan:排序时使用了索引排序,但如果是按照降序排序的话就会使用反向扫描索引
- temporary:查询时要建立一个临时表存放数据
- rows:找到了多少行数据
- key:实际使用到的索引
- id:select查询的优先级,id越大优先级越高,子查询的id一般会更大
- select_type:查询的类型,是普通查询还是联合查询还是子查询,常见类型有simple(不包含子查询),primary(标记复杂查询中最外层的查询),union(标记primart只后子查询)
- table:者一行的数据是数哪张表的
- possible_keys(可能的):当前查询语句可能用到的索引,可能为null(如果用了索引但是为null有可能是表数据太少innodb认为全表扫描更快)
- ref(编号):显示索引的哪一行被使用了
MySQL中索引失效的情况!!
(1)where条件中有or,除非所有查询条件都建立了索引,否则失效
SELECT * FROM products WHERE name = 'A' OR category = 'X';
(2)like查询用%开头,索引失效
SELECT * FROM customers WHERE name LIKE '%B';
(3)索引列参与计算,索引失效
SELECT * FROM orders WHERE total_amount * 1.1 > 100;
(4)索引字段发生类型转换,索引失效(age定义的数据类型是整型,传入的为字符串类型,此时先进行隐式类型的转换,将'25'转为整数型,但这样就会导致索引失效降低效率)
SELECT * FROM employees WHERE age = '25';
(5)MySQL判断为全表扫描更快时(数据少),会导致索引失效
(6)使用联合索引的时候,违反了最左前缀原则,如果建立了联合索引(多个列组成的索引),那么在查询条件中必须遵循最左前缀原则,即从左到右依次使用联合索引中的列,不能跳过任何一列。否则,也会导致索引失效。例如,如果建立了(name, age, gender)的联合索引,那么在查询条件中可以使用name,或者name和age,或者name,age和gender,但是不能只使用age或gender,也不能只使用age和gender。解决方法是尽量按照最左前缀原则来使用联合索引,并且将区分度高的列放在前面。
MySQL有哪些锁
基于粒度:
- 表级锁:对整张表加锁,粒度大并发小
- 行级锁:对行加锁,粒度小并发大
- 间隙锁:锁住表的一个区间,间隙锁之间不会发生冲突,只在可重复读下才生效,避免了幻读
间隙锁——假设有一个名为"products"的表,包含以下列:id(主键)、name、price。
事务A执行如下语句:
BEGIN;
SELECT * FROM products WHERE price BETWEEN 10 AND 20;
此时事务A会对价格在10到20之间的记录进行查询,并且会对该范围的间隙加上间隙锁。
在事务A执行期间,事务B尝试插入一个新的价格为15的产品:
sql
BEGIN;
INSERT INTO products (name, price) VALUES ('New Product', 15);
由于事务A已经对价格在10到20之间的范围加上了间隙锁,所以事务B的插入操作会被阻塞。事务B必须等待事务A完成或回滚后才能继续执行。
如果事务A提交了或回滚了,事务B再次执行插入操作时,它会发现被阻塞的间隙锁已经释放,就可以成功地插入新的产品记录。
通过使用间隙锁,事务A可以确保在查询期间其他事务无法插入新的符合条件的记录,从而避免了幻读问题。
基于属性:
共享锁:又称读锁,一个事务为表加了读锁,其他事务只能加读锁不能加写锁
排他锁:又称写锁,一个事务加了写锁之后,其他事务就不能加任何锁,避免脏读问题。
Mysql内连接、左连接、右连接的区别
内连接取量表交集部分,左连接取左表全部右表匹配部分,右连接取右表全部坐表匹配部分
where和having的区别?
where是约束声明,having是过滤声明,where早于having执行,并且where不可以使用聚合函数,having可以
三大范式
第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
InnoDB 什么情况下会产生死锁
事务1已经获取数据A的写锁,想要去获取数据B的写锁,然后事务2获取了B的写锁,想要去获取A的写锁,相互等待形成死锁。
mysql解决死锁的机制有两个:
1.等待, 直到超时
2.发起死锁检测,主动回滚一条事务
死锁检测的原理是构建一个以事务为顶点、 锁为边的有向图, 判断有向图是否存在环, 存在即有死锁。
平时尽量减少事务操作的资源和隔离级别
MySQL 删除自增 id,随后重启 MySQL 服务,再插入数据,自增 id 会从几开始?
innodb 引擎:
MySQL8.0前,下次自增会取表中最大 id + 1。原理是最大id会记录在内存中,重启之后会重新读取表中最大的id
MySQL8.0后,仍从删除数据 id 后算起。原理是它将最大id记录在redolog里了
myisam:
自增的 id 都从删除数据 id 后算起。原理是它将最大id记录到数据文件里了
MySQL 删除自增 id,随后重启 MySQL 服务,再插入数据,自增 id 会从几开始?_mysql自增删除了最后一个新增从哪个开始_血莲丹的博客-CSDN博客
MySQL插入百万级的数据如何优化?
(1)一次SQL插入多条数据,可以减少写redolog和binlog的io次数(SQL长度是有限制的,但可以调整)
(2)保证数据按照索引进行有序插入
(3)可以分表后多线程插入