MYSQL进阶

之前写了篇文章, 记录了MySQL的一些常用命令, 现在看来仍然有遗漏的地方, 所以补充一下

组合查询#

UNION

使用UNION来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。
每个查询必须包含相同的列、表达式和聚集函数。
默认会去除相同行,如果需要保留相同行,使用UNION ALL
只能包含一个 ORDER BY 子句,并且必须位于语句的最后

SELECT col
FROM mytable
WHERE col = 1
UNION
SELECT col
FROM mytable
WHERE col =2;

内置函数#

内置函数很多, 见: MySQL 函数

事务#

我们一般使用START TRANSACTIONBEGIN开启事务, COMMIT提交事务中的命令, SAVEPOINT: 相当于设置一个还原点, ROLLBACK TO: 回滚到某个还原点下
一般的使用格式如下:

START TRANSACTION
-- ...
SAVEPOINT delete1
-- ...
ROLLBACK TO delete1
-- ...
COMMIT

#

开启事务时, 默认加锁
根据类型可分为共享锁(SHARED LOCK)和排他锁(EXCLUSIVE LOCK)或者叫读锁(READ LOCK)和写锁(WRITE LOCK)。

读锁(共享锁):针对同一块数据,多个读操作可以同时进行而不会互相影响。由读表操作加上的锁,加锁后其他用户只能获取该表或行的共享锁,不能获取排它锁,也就是说只能读不能写。
写锁(排它锁):当当前写操作没有完成之前,它会阻断其他写锁和读锁。由写表操作加上的锁,加锁后其他用户不能获取该表或行的任何锁。

根据粒度划分又分表锁和行锁。表锁由数据库服务器实现,行锁由存储引擎实现。

除此之外,我们可以显示加锁

SELECT .... LOCK IN SHARE MODE; -- 加共享锁
SELECT .....FOR UPDATE; -- 加排他锁

加锁时, 如果没有索引,会锁表,如果加了索引,就会锁行

死锁#

InnoDB默认支持行锁,获取锁是分步的,并不是一次性获取所有的锁,因此在锁竞争的时候就会出现死锁的情况
解决方法:

  • 首先要解决死锁问题,在程序的设计上,当发现程序有高并发的访问某一个表时,尽量对该表的执行操作串行化,或者锁升级,一次性获取所有的锁资源。
  • 然后也可以设置参数innodb_lock_wait_timeout,超时时间,并且将参数innodb_deadlock_detect打开,当发现死锁的时候,自动回滚其中的某一个事务。

事务的基本特性#

即ACID特性:

  1. 原子性: 一个事务中的操作要么全部成功, 要么全部失败
  2. 一致性: 数据库总是从一个一致性的状态转换到另一个一致性状态, 比如A转账个B100块, 假如转账期间系统崩溃, A也不会损失100块, 因为事务还未提交, 不会保存到数据库中
  3. 隔离性: 一个事务在提交前, 对其他事务是不可见的
  4. 持久性: 一旦事务提交, 所做的修改就会永久保存到数据库中

事务并发引发的问题#

  1. 丢失更新
    事务A提交或撤销时, 事务B把数据覆盖了

  2. 脏读
    事务A执行过程中, 修改了数据, 事务B读取了, 但事务A后来回滚了, 则事务B读取的数据为脏数据
    即: 读取了另一个事务未提交数据的现象为脏读
    脏读示意图

  3. 不可重复读
    事务B读取了两次数据, 但其间事务A修改了数据, 导致事务B两次读取数据不一致
    不可重复读示意图

  4. 幻读
    事务B两次读取同一范围的数据, 但其间事务A新增了数据, 导致事务B后面读取的数据与前面读取的数据行数不一致
    幻读强调集合增减, 不可重复读强调数据修改

    幻读示意图

隔离级别#

由于并发事务会引发上面这些问题, 我们可以设置事务的隔离级别解决上面的问题.
MySQL的默认隔离级别(可重复读)
隔离级别

查看当前会话隔离级别
方式1

SHOW VARIABLES LIKE 'transaction_isolation';
+-----------------------+--------------+
| Variable_name | Value |
+-----------------------+--------------+
| transaction_isolation | SERIALIZABLE |
+-----------------------+--------------+

方式2

SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE |
+-------------------------+

设置隔离级别

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;

GLOBAL时, 只对执行完该语句之后产生的会话起作用, 当前已经存在的会话无效
SESSION时, 对当前会话的所有后续的事务有效, 该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务 如果在事务之间执行,则对后续的事务有效。
不指定时, 只对当前会话中下一个即将开启的事务有效, 下一个事务执行完后,后续事务将恢复到之前的隔离级别, 该语句不能在已经开启的事务中间执行,会报错的
level的四个值: REPEATABLE READ READ COMMITTED READ UNCOMMITTED SERIALIZABLE

主从集群与读写分离#

主从集群的示意图如下:
mysql集群数据同步

主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。

  • binlog dump 线程 :负责将主服务器上的数据更改写入二进制日志(Binary log)中。
  • I/O 线程 :负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)。
  • SQL 线程 :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。

同步流程:

  1. MySQL变更数据时, 会记录到Binlog
  2. 主节点binlog dump线程监听, Binlog是否改变, 假如改变MySQL将主节点的Binlog同步给从节点
  3. 从节点的I/O 线程接收Binlog数据, 并写入到relay log文件
  4. 从节点的SQL 线程读取replay log文件, 并更新数据, 完成主从之间数据同步

由于MySQL主从集群只会从主节点同步到从节点, 不会反过来同步, 所以需要读写分离

读写分离需要在业务层面实现, 写数据只能在主节点上完成, 而读数据可以在主节点或从节点上完成

除此之外, 还可以将Binlog数据同步给Redis Kafaka等中间件, 同步数据

索引#

索引是帮助MySQL高效获取数据的排好序的数据结构
MySQL的索引有

  1. 主键索引
    建立在PRIMARY KEY上字段的索引称为主键索引, 值非空且唯一
  2. 唯一索引
    建立有UNIQUE的字段上的索引, 可以有多个唯一索引, 值可以为空且多个空值不会冲突
  3. 普通索引
    我们一般建立的INDEX
  4. 前缀索引
    对于一些字符或字节类型的数据, 可以取前几个字符或字节建立索引, 但是不能ORDER BYGROUP BY

B树与B+树#

推荐两个在线工具:

  1. B树可视化工具: B-Trees
  2. B+树可视化工具: B+ Trees

简单来说, B树是在红黑树(一个平衡二叉树)的基础上将一个节点存放多个值, 实现的, 降低了树的高度, 每个节点都存放索引及对应数据指针, 同一层的节点是递增的
而B+树在B树的基础上进行优化, 非叶子节点存放 子节点的开始的索引, 叶子节点存放索引和数据的指针, 且叶子节点之间有双向的指针
如下示意图:

B+树

不同的引擎, 主键索引存放的数据也不一样, 比如常见的MyISAMInnoDB
MyISAM的B+树叶子节点存放表数据的指针, InnoDB的B+树叶子节点存放处主键外的数据

其他的:

  1. InnoDB支持事务, MyISAM不支持事务
  2. InnoDB支持外键, MyISAM不支持
  3. InnoDB支持聚集索引, MyISAM, (即上面说的)
  4. InnoDB不保存表的行数, select count(*) ...时, 全表扫描, 而MyISAM保存了整个表的行数, 可以直接读取

联合索引及最左前缀原则#

即多个列组成一个索引, 语法:

create index indexName on tableName(column1,column2,...,columnN)

由于联合索引的B+树的结构, 根据列建立, 所以我们的查找条件也要根据索引列的顺序(where column1=x, column2=y,columnN...), 否则会全表扫描

索引失效的情况及原因#

  1. 使用!=或者<>导致索引失效

    SELECT * FROM `user` WHERE `name` != 'lczmx';

    我们给name字段建立了索引,但是如果!= 或者<>这种都会导致索引失效,进行全表扫描,所以如果数据量大的话,谨慎使用

  2. 类型不一致导致的索引失效
    这涉及MySQL的类型转换,

    • 索引为整形, 而比较值为字符串时, 会将字符串转化为数字, 除了是数字字符串外, 其他的都为0 ("1"=>1, "a"=>0)
    • 索引为字符串, 比较值为整形时, 会将索引装换为数字, 会破坏二叉树且不一定合法, 所以会走全表扫描
  3. 函数导致的索引失效

    SELECT * FROM `user` WHERE DATE(create_time) = '2022-03-03';

    如果你的索引字段使用了索引,不走索引

    实际上, 一般只要是对索引操作的, 都不会走索引

  4. 运算符导致的索引失效

    SELECT * FROM `user` WHERE age - 1 = 20;

    如果你对列进行了(+,-,*,/,!), 那么都将不会走索引。

    实际上, 一般只要是对索引操作的, 都不会走索引

  5. OR引起的索引失效

    SELECT * FROM `user` WHERE `name` = '张三' OR height = '175';

    OR导致索引是在特定情况下的,并不是所有的OR都是使索引失效,如果OR连接的是一个字段,那么索引不会失效反之索引失效

  6. 模糊搜索导致的索引失效

    SELECT * FROM `user` WHERE `name` LIKE '%mx';

    这个我相信大家都明白,模糊搜索如果你前缀也进行模糊搜索,那么不会走索引。

  7. NOT IN NOT EXISTS导致索引失效

    SELECT s.* FROM `user` s WHERE NOT EXISTS (SELECT * FROM `user` u WHERE u.name = s.`name` AND u.`name` = 'lczmx')
    SELECT * FROM `user` WHERE `name` NOT IN ('lczmx');

    这两种用法,也将使索引失效。另IN会走索引,但是当IN的取值范围较大时会导致索引失效,走全表扫描, 见: MySQL中使用IN会不会走索引

  8. IS NULL不走索引,IS NOT NULL走索引

    SELECT * FROM `user` WHERE address IS NULL

    不走索引。

    SELECT * FROM `user` WHERE address IS NOT NULL;

    走索引。

    所以设计表的时候, 建议不可为空, 而是将默认值设置为""( NOT NULL DEFAULT "")

posted @   403·Forbidden  阅读(368)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
主题色彩