分布式的共识算法
分布式系统的共识算法是指在分布式环境中多个节点就某些信息达成一致的算法。这些算法在分布式系统中至关重要,尤其是在容错和保证一致性方面。
Paxos 算法
Paxos 算法是一种经典的分布式共识算法,由 Leslie Lamport 在 1990 年代提出。它旨在解决分布式系统中多个节点就某些信息达成一致的问题,尤其是在存在网络分区或节点故障的情况下。Paxos 算法特别强调强一致性,即所有参与的节点最终都会同意同一个值。
Paxos 算法包括两个主要的角色:Proposer 和 Acceptor 。此外,还有一种角色叫做 Learner,用于监听并记录共识的结果。以下是 Paxos 算法的基本步骤:
- Prepare 阶段 :
- Proposer 选择一个提案编号 n,并向一组 Acceptor 发送一个 Prepare(n) 消息。
- Acceptor 收到 Prepare(n) 消息后,如果它尚未对编号大于等于 n 的提案做出承诺,它会回复一个 Promise 消息,其中包含它先前承诺过的最大编号的提案值(如果有的话)。
- Proposer 收集大多数 Acceptor 的 Promise 消息。
- Accept 阶段 :
- Proposer 选择一个值 v(通常是它收到的 Promise 消息中最大的提案值,如果没有则可以自由选择),并发送一个 Accept(n, v) 消息给所有 Acceptor。
- Acceptor 收到 Accept(n, v) 消息后,如果它已经回复了 Prepare(n) 的 Promise 消息,它就会接受该提案并记录提案值 v。
- Proposer 收集大多数 Acceptor 的 Accept 消息。
- Learn 阶段 :
- Learner 收集大多数 Acceptor 的 Accept 消息后,它会学习到提案值 v,并将其传播给所有参与者。
Raft 算法是一种分布式共识算法,由 Diego Ongaro 和 John Ousterhout 在 2014 年提出。它旨在简化分布式系统中多个节点就某些信息达成一致的过程,并且相比 Paxos 算法更加直观易懂。Raft 算法特别强调通过选举领导者来简化共识过程,并且易于理解和实现。
Raft 算法的基本原理
Raft 算法的核心在于通过选举领导者来简化共识过程。它包括三种节点状态:Follower 、Candidate 和 Leader。以下是 Raft 算法的基本步骤:
- Nacos中选举领导者 :
- 当前领导者挂掉了 某个Follower 由于长时间没有收到当前领导者的心跳 所以将自己的状态改为Candidata 然后更换自己的任期编号后 向集群中的所有节点发送投票请求 并给自己投一票。
- 集群中的Follower 节点在收到投票请求后,会判断自己的任期编号和发起请求的Candidata 节点的任期编号,如果比发起请求的节点的任期编号小并且自己在当前任期并未给其他节点投票的话,那么当前节点会投票给发起请求的节点,并返回给发起请求的Candidata 节点
- 如果 Candidate 收集到大多数节点的投票,它将成为 Leader。
- Nacos中日志复制:
- 当 Leader 接收到服务的注册或发现请求时,它会将此操作作为一条日志条目写入自己的日志。
- Leader 然后向集群中的所有 Follower 节点发送这条日志条目的复制请求。
- Follower 节点接收到日志条目后,会检查其任期编号是否与自己的任期编号匹配。如果匹配,则将日志条目写入自己的日志,并回复确认消息。
- 当 Leader 收到多数 Follower 返回的确认消息后,它会认为这条日志条目已经被复制成功。
- Leader 这时才会应用这条日志条目,执行相应的服务注册或发现操作,并更新自己的状态机。
- Leader 会继续向集群中的所有节点发送心跳消息,以维持自己的领导地位。
- Nacos中容错机制 :
- 当 Leader 长时间没有收到某个 Follower 的心跳消息时,它会认为该 Follower 已经失效。
- Leader 会尝试联系该 Follower,看看能否恢复它的连接。如果恢复成功, Leader 节点会将最新的日志同步给恢复成功的节点,使其能够重新加入集群 。
- 如果多次尝试都无法恢复与该 Follower 的连接,那么 Leader 会认为该 Follower 长久失效,并将其标记为不可用。
- 在某些情况下,如集群规模较小或配置文件允许的情况下,Leader 可能会尝试自动替换掉失效的 Follower,以便保持集群的可用性。
- 如果集群中有足够多的健康节点,那么系统仍然可以正常运行;否则,可能会出现性能下降或功能受限的情况。
Nacos中使用了Raft算法来保证服务注册与发现的一致性。
ZAB 算法
ZAB 算法是 专为 ZooKeeper 设计的一种分布式共识协议
ZAB的选举机制的大致过程:
- 初始化: 当 ZooKeeper 集群启动时,每个节点都会初始化自己的轮次号为 0。
- 选举开始 :
- 当前领导者失败或无法响应客户端请求。
- 所有节点检测到领导者失败,并进入
LOOKING
状态。并将自己的轮次号增加1。
- 发送选举请求 :
- 每个处于
LOOKING
状态的节点都会向集群中的所有节点发送一个选举请求消息。 - 消息包含自己的节点ID和轮次号。
- 节点在发送选举请求时,会将自己的轮次号加 1,例如从 0 加到 1。
- 每个处于
- 投票 :
- 每个节点收到选举请求后,会检查自己的投票记录。
- 如果还没有投票或者认为当前的候选者更好,则投票给这个候选者,并将投票信息发送回候选者。
- 节点在投票时,会比较候选者的轮次号和自己记录的轮次号。如果候选者的轮次号更高,节点会投票给这个候选者;如果轮次号相同,则根据节点ID来决定。(会投票给节点ID更低的节点,节点ID一般是按照节点的加入顺序分配的,越早加入的节点会获得更低的节点ID)
- 成为领导者 :
- 当一个候选者获得集群中超过半数节点的投票时,它成为新的领导者。
- 成为领导者后,该节点会将自己的状态设置为
LEADING
,并且将自己的轮次号作为当前的领导者轮次号。
- 通知其他节点 :
- 领导者向集群中的所有其他节点发送消息,通知它们自己已成为领导者。
- 其他节点收到消息后,将自己的状态设置为
FOLLOWING
。 - 其他节点也会更新自己的轮次号为领导者轮次号。
redission做分布式锁:
redission的看门狗机制
简化来说就是后台有一个线程定期的去检测锁的过期时间,去持续的续费锁的过期时间。这个机制使得Redission实现的分布式锁是可以自动续期的。
看门狗机制的默认是在锁的有效时间剩余1/3的时候触发。
两种获取锁的方式,第一种是通过trylock()方法,此方法是非阻塞的,如果在指定时间内没有获取到锁,则立即返回。 该方法会返回一个布尔值,指示是否成功获取锁。
trylock代码如下:
java
public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException {
return tryLock(waitTime, -1, unit);
}
1. waitTime:获取锁的最大等待时间(没有传默认为-1)
2. leaseTime:锁自动释放的时间(没有传的话默认-1)
3. unit:时间的单位(等待时间和锁自动释放的时间单位)
一般加锁和释放锁的操作包含在try{}finally{}代码块中执行,防止以为异常退出导致锁没有正常释放的情况。在finally代码块中释放锁之前,还需要判断一下锁是否是被当前线程锁持有,以避免释放不属于当前线程的锁。
另外一种是lock()
lock.lock()
方法试图获取锁,如果没有获取到锁,它将阻塞当前线程,直到成功获取锁为止。这意味着如果锁被其他客户端持有,调用 lock.lock()
的线程将等待锁被释放。
trylock示例代码如下:
java
lock.lock();
try {
// 执行关键操作
System.out.println("Processing with lock");
// 模拟一些业务逻辑
Thread.sleep(10000); // 模拟业务逻辑执行耗时
} finally {
// 释放锁
lock.unlock();
}
mysql的锁有几种
1. 行级锁 (Row-Level Locks)
行级锁是最细粒度的锁,它只锁定被操作的具体行。这种锁可以进一步分为共享锁(S锁)和排他锁(X锁)。
- 共享锁 (S Lock) :
- 共享锁允许多个事务读取同一行数据,但不允许写入。
- 当一个事务请求对某一行数据加共享锁时,其他事务可以同时对该行数据加共享锁,但不能加排他锁。
- 适用于 SELECT 语句。
- 排他锁 (X Lock) :
- 排他锁阻止其他事务对加锁的行进行任何读写操作。
- 当一个事务请求对某一行数据加排他锁时,其他事务既不能对该行数据加共享锁也不能加排他锁。
- 适用于 INSERT、UPDATE、DELETE 语句。
2. 表级锁 (Table-Level Locks)
表级锁是一种较粗粒度的锁,它锁定整个表。这种锁在早期的 MySQL 存储引擎中较为常见,如 MyISAM。
- 读锁 (Read Lock) :
- 读锁允许其他事务读取表中的数据,但不允许写入。
- 适用于 SELECT 语句。
- 写锁 (Write Lock) :
- 写锁阻止其他事务对表进行任何读写操作。
- 适用于 INSERT、UPDATE、DELETE 语句。
3. 页级锁 (Page-Level Locks)
页级锁是一种介于行级锁和表级锁之间的锁,它锁定的是表的一部分,而不是整个表。InnoDB 存储引擎使用页级锁。
4. 意向锁 (Intention Locks)
意向锁不是直接由用户设置的,而是由 InnoDB 存储引擎在需要时自动添加的。意向锁分为意向共享锁(IS)和意向排他锁(IX)。
- 意向共享锁 (IS Lock) :
- 表示事务想要获得表上所有行的共享锁。
- 意向排他锁 (IX Lock) :
- 表示事务想要获得表上所有行的排他锁。
5. 其他锁类型
除了上述常见的锁类型外,还有一些特殊的锁:
- 间隙锁 (Gap Lock) :
- 间隙锁锁定的是索引项之间的"间隙",它是为了防止其他事务插入行到锁定行之间。
- 仅在 InnoDB 存储引擎中使用。
- 记录锁 (Record Lock) :
- 记录锁是对索引项上的记录进行锁定。
- 插入意向锁 (Insert Intention Lock) :
- 插入意向锁是一种特殊的排他锁,用于在插入新行前锁定相邻记录的间隙。
Mysql的事务隔离级别
-
读未提交(RU):
可以读取到其他事物未提交的数据,可能会出现幻读,不可重复读,脏读的情况
-
读已提交(RC):
读取其他事物已经提交的数据。可能会出现幻读和不可重复读的情况。(在每次读取同一行数据的时候都会生成新的快照读)
-
可重复读(RR):
读取的数据都是已经提交的。解决了不可重复读的问题(在一个事务中 出现多次读取一行数据的情况,只会在第一次生成快照,意思是在这个事务中,不管读取几次这行数据 读取的都是undo log中的同一个版本的数据),但是会出现幻读
-
串行化(S):
事务串行执行,不会出现幻读、脏读、不可重复读,是最高的隔离级别,但同时也是并发性能最低的一个隔离级别。
MVCC是什么?
MVCC 是一种用于支持读取操作与事务并发的技术,它通过保留多个版本的数据来避免读取操作与未提交的更新操作之间产生冲突。这种机制使得读取操作不会阻塞写入操作,从而提高了系统的并发性。
MVCC在MySQL中的实现
MySQL 的 InnoDB 存储引擎实现了 MVCC,它利用了行记录中的隐藏字段来管理不同版本的数据。
主要实现是依靠mysql的undo log 日志,他会在undo log 中保存一些旧版本的数据,通过mysql的隐藏列的指针 ,形成一个数据链,其中事务未提交的数据也会保存在其中。当有事务执行读取操作的时候,首先会生成一个快照,快照里面有三个数据 一个是当前我这个执行查询操作的事务ID 另外一个是当前已经开始但是未提交的最小事务ID 还有一个是未开始的事务ID也就是最大事务ID 然后通过这个快照信息去判断 到底应该选择的是那个版本的数据,(简而言之就是选择最近一次提交的数据 )
读已提交和可重复读中使用MVCC是不同的
读已提交
在一个事务中 如果我连续两次 读取同一行的操作 他每次都会去生成一个快照
可重复读
只有第一次会生成快照 第二次直接使用第一次生成的快照
MVCC不能解决幻读的问题
mysql的索引类型
-
单列索引 (Single Column Index)
-
定义:
- 单列索引是针对表中单个列建立的索引。
- 这是最常见的索引类型。
-
示例:
sqlCREATE INDEX idx_column_name ON table_name(column_name);
2**. 复合索引 (Composite Index / Multiple Column Index)**
-
定义:
- 复合索引是在表的多个列上建立的索引。
- 可以提高涉及多个列的查询的性能。
-
示例:
sqlCREATE INDEX idx_columns ON table_name(column1, column2, column3);
3. 唯一索引 (Unique Index)
-
定义:
- 唯一索引用于确保表中某列或某几列的值是唯一的。
- 可以用作主键索引的替代品。
-
示例:
sqlCREATE UNIQUE INDEX idx_unique ON table_name(column_name);
- 主键索引 (Primary Key Index)
-
定义:
- 主键索引是一种唯一索引,用于标识表中的每一行。
- 每个表只能有一个主键。
-
示例:
sqlALTER TABLE table_name ADD PRIMARY KEY(column_name);
5. 全文索引 (Full-text Index)
-
定义:
- 全文索引用于全文搜索,可以快速查找包含特定单词或短语的行。
- 适用于包含大量文本的列。
-
示例:
sqlCREATE FULLTEXT INDEX idx_fulltext ON table_name(column_name);
6. 空间索引 (Spatial Index / R-tree Index)
-
定义:
- 空间索引用于地理空间数据,可以快速查找位于特定地理区域内的对象。
- 使用 R-tree 索引来组织空间数据。
-
示例:
sqlCREATE SPATIAL INDEX idx_spatial ON table_name(column_name);
7. 聚簇索引 (Clustered Index)
-
定义:
- 聚簇索引决定了表中行的物理存储顺序。
- InnoDB 存储引擎使用主键作为聚簇索引,如果没有定义主键,则使用一个隐藏的聚簇索引来存储行。
-
示例:
sqlCREATE TABLE table_name ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) ) ENGINE=InnoDB;
8. 函数索引 (Function-based Index)
-
定义:
- 函数索引是在表达式或函数计算结果的基础上建立的索引。
- 可以用于优化涉及表达式的查询。
-
示例:
sqlCREATE INDEX idx_function ON table_name(LENGTH(column_name));
- 虚拟列索引 (Virtual Column Index)
-
定义:
- 虚拟列索引是在虚拟列上建立的索引。
- 虚拟列是基于其他列计算得出的列,不会实际存储在表中。
-
示例:
sqlCREATE TABLE table_name ( col1 INT, col2 INT AS (col1 * 2), INDEX idx_virtual(col2) ) ENGINE=InnoDB;
10. 隐式索引 (Implicit Index)
- 定义
- 隐式索引是 MySQL 自动为某些情况创建的索引,如主键索引。
- 无需手动创建。
11. 显式索引 (Explicit Index)
- 定义
- 显式索引是用户手动创建的索引。
- 用于提高查询性能。
-
mysql的索引结构
InnoDB引擎中,使用的B+树,在myISAM引擎中使用的是B树。
1. InnoDB 存储引擎的索引结构
InnoDB 存储引擎使用 B+ 树作为其索引结构的基础。B+ 树是一种自平衡树,它能够保证在索引中查找数据的时间复杂度为 O(log n)。以下是 InnoDB 中索引的一些关键特性:
B+ 树索引
- 叶子节点 :
- 包含实际的数据行。
- 叶子节点之间通过双向链表相互连接。
- 叶子节点按关键字排序。
- 非叶子节点 :
- 存储索引关键字和指向子节点的指针。
- 非叶子节点用于加速查找过程。
- 聚簇索引 (Clustered Index) :
- InnoDB 使用主键作为聚簇索引。
- 如果没有定义主键,则使用一个隐藏的聚簇索引来存储行。
- 聚簇索引决定了表中行的物理存储顺序。
- 二级索引 (Secondary Index) :
- 除了聚簇索引之外的所有索引都是二级索引。
- 二级索引包含一个索引键和一个指向前述聚簇索引中的行的指针。
- 二级索引可以是单列索引或多列索引。
2. MyISAM 存储引擎的索引结构
MyISAM 存储引擎也使用 B+ 树作为索引结构的基础。MyISAM 的索引结构与 InnoDB 类似,但有一些不同之处:
B+ 树索引
- 叶子节点 :
- 包含实际的数据行。
- 叶子节点之间通过双向链表相互连接。
- 叶子节点按关键字排序。
- 非叶子节点 :
- 存储索引关键字和指向子节点的指针。
- 非叶子节点用于加速查找过程。
- 非聚簇索引 :
- MyISAM 使用非聚簇索引。
- 索引文件与数据文件分开存储。
- 索引包含一个索引键和一个指向数据文件中行的偏移量。
索引结构的示例
InnoDB 示例
假设我们有一个名为 employees
的表,包含以下字段:
- id: 主键,整数类型。
- first_name: 名字,字符串类型。
- last_name: 姓氏,字符串类型。
- email: 邮箱,字符串类型。
聚簇索引
- 聚簇索引:
- 使用
id
作为聚簇索引。 - 行数据按照
id
的值进行排序。
- 使用
二级索引
- 二级索引:
- 创建一个基于
first_name
的二级索引。 - 二级索引包含
first_name
的值和指向聚簇索引中相应行的指针。
- 创建一个基于
MyISAM 示例
假设我们有同样的 employees
表。
索引
- 索引:
- 创建一个基于
first_name
的索引。 - 索引包含
first_name
的值和指向数据文件中行的偏移量。
- 创建一个基于
总结
- InnoDB :
- 使用聚簇索引和二级索引。
- 聚簇索引决定了行的物理存储顺序。
- 二级索引包含指向聚簇索引中行的指针。
- MyISAM :
- 使用非聚簇索引。
- 索引文件与数据文件分开存储。
- 索引包含指向数据文件中行的偏移量。
mysql索引失效的场景
MySQL 中索引失效的场景是指在查询过程中,MySQL 优化器决定不使用索引,转而使用全表扫描或其他方式来执行查询的情况。了解索引失效的原因可以帮助你更好地设计和优化查询。下面列举了一些常见的索引失效场景:
1. 使用非等值比较操作符
-
原因:
- 当使用
>
、<
、>=
、<=
、!=
、<>
等非等值比较操作符时,索引可能无法被充分利用。 - 这些操作符通常会导致全表扫描。
- 当使用
-
示例:
sqlSELECT * FROM table_name WHERE column_name > 10;
2. 使用 LIKE
操作符匹配前缀模糊查询
-
原因:
- 当使用
LIKE
操作符进行前缀模糊查询(如%abc%
或abc%
)时,索引可能无法被充分利用。 - 前缀模糊查询会导致全表扫描。
- 当使用
-
示例:
sqlSELECT * FROM table_name WHERE column_name LIKE '%abc%';
3. 使用函数或表达式
-
原因:
- 当在 WHERE 子句中使用函数或表达式时,索引可能无法被充分利用。
- MySQL 优化器可能无法有效地使用索引。
-
示例:
sqlSELECT * FROM table_name WHERE LENGTH(column_name) > 5;
4. 使用 OR
操作符连接非等值条件
-
原因:
- 当使用
OR
操作符连接非等值条件时,索引可能无法被充分利用。 OR
操作符可能会导致全表扫描。
- 当使用
-
示例:
sqlSELECT * FROM table_name WHERE column_name1 = 'value1' OR column_name2 > 10;
5. 使用 NOT IN
或 NOT EXISTS
-
原因:
- 使用
NOT IN
或NOT EXISTS
时,索引可能无法被充分利用。 - 这些操作符可能会导致性能下降。
- 使用
-
示例:
sqlSELECT * FROM table_name WHERE column_name NOT IN (select column_name from other_table);
6. 使用 IN
操作符与非常量列表
-
原因:
- 当使用
IN
操作符与非常量列表(如子查询结果)时,索引可能无法被充分利用。 - 这种情况下,MySQL 可能会选择全表扫描。
- 当使用
-
示例:
sqlSELECT * FROM table_name WHERE column_name IN (select column_name from other_table);
7. 使用 ORDER BY
或 GROUP BY
与非索引列
-
原因:
- 当使用
ORDER BY
或GROUP BY
与非索引列时,索引可能无法被充分利用。 - 这种情况下,MySQL 可能会选择全表扫描。
- 当使用
-
示例:
sqlSELECT * FROM table_name ORDER BY non_index_column;
8. 使用 LIMIT
与非索引列
-
原因:
- 当使用
LIMIT
与非索引列时,索引可能无法被充分利用。 - 这种情况下,MySQL 可能会选择全表扫描。
- 当使用
-
示例:
sqlSELECT * FROM table_name WHERE non_index_column = 'value' LIMIT 10;
9. 使用 JOIN
操作符与非等值条件
-
原因:
- 当使用
JOIN
操作符与非等值条件时,索引可能无法被充分利用。 - 这种情况下,MySQL 可能会选择全表扫描。
- 当使用
-
示例:
sqlSELECT * FROM table1 JOIN table2 ON table1.column_name > table2.column_name;
10. 使用 BETWEEN
操作符与非等值条件
-
原因:
- 当使用
BETWEEN
操作符与非等值条件时,索引可能无法被充分利用。 - 这种情况下,MySQL 可能会选择全表扫描。
- 当使用
-
示例:
sqlSELECT * FROM table_name WHERE column_name BETWEEN 1 AND 100;
慢查询的使用
慢查询日志是什么?
MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阈值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。
long_query_time的默认值为10,意思是运行10秒以上的语句
由慢查询日志来查看哪些SQL超出了我们的最大忍耐时间值,比如一条SQL执行超过5秒钟,我们就算慢SQL,希望能收集超过5秒钟的SQL
查看慢查询日志是否开以及如何开启
- **查看慢查询日志是否开启:**SHOW VARIABLES LIKE '%slow_query_log%';。
- **开启慢查询日志:**SET GLOBAL slow_query_log = 1;。使用该方法开启MySQL的慢查询日志只对当前数据库生效,如果MySQL重启后会失效。
shell
-- 指定数据库
mysql> use advanced_mysql_learning;
Database changed
-- 查看慢查询日志是否开启
mysql> SHOW VARIABLES LIKE '%slow_query_log%';
+---------------------+---------------------------------------------------------------------------+
| Variable_name | Value |
+---------------------+---------------------------------------------------------------------------+
| slow_query_log | OFF |
| slow_query_log_file | D:\Development\Sql\Mysql\mysql8\exe\mysql-8.0.27-winx64\data\dam-slow.log |
+---------------------+---------------------------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)
-- 开启慢查询日志
mysql> SET GLOBAL slow_query_log = 1;
Query OK, 0 rows affected (0.01 sec)
------------------------------------------------
设置慢SQL的时间阈值
- 查看阈值: SHOW VARIABLES LIKE 'long_query_time%';。
时间阈值是由参数long_query_time控制的,默认情况下long_query_time的值为10秒。 - 设置阈值: set global long_query_time=3;
MySQL中查看long_query_time的时间:
shell
mysql> SHOW VARIABLES LIKE 'long_query_time%';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set, 1 warning (0.00 sec)
-- 设置阈值
mysql> set global long_query_time=3;
Query OK, 0 rows affected (0.00 sec)
-- 可以发现设置没有成功
mysql> SHOW VARIABLES LIKE 'long_query_time%';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set, 1 warning (0.00 sec)
注意:是超过阈值才会被记录,等于不会被记录
查询慢查询日志文件中的总记录条数
shell
mysql> SHOW GLOBAL STATUS LIKE '%Slow_queries%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries | 0 |
+---------------+-------+
1 row in set (0.00 sec)
慢查询日志展示
shell
# Time: 2024-08-19T05:29:50.247911Z
# User@Host: skip-grants user[root] @ localhost [127.0.0.1] Id: 20
# Query_time: 5.000377 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0
use Test;
SET timestamp=1724045390;
select sleep(5);
- **use Test:**使用的数据库
- **Query_time:**实际查询时间,单位是秒
- **Lock_time:**锁时间
- **select sleep(4):**超时的语句
如此可以定位到具体执行慢的SQL语句。
可以通过Linux版本的Mysql中自带的日志分析工具mysqldumpslow去分析慢查询日志
例如:
sql
# 得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/slow.log
# 得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/slow.log
# 得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/slow.log
# 另外建议使用这些命令时结合|和more使用,否则出现爆屏的情况
mysqldumpslow -s r -t 10 /var/lib/mysql/slow.log | more
EXPLAIN 的使用
EXPLAIN
是 MySQL 中的一个非常有用的命令,用于分析查询的执行计划。它可以帮助你理解 MySQL 如何执行查询,包括使用了哪些索引、表的扫描方式等。通过 EXPLAIN
,你可以诊断查询性能问题并优化查询。
EXPLAIN 的语法
EXPLAIN
命令的基本语法如下:
sql
EXPLAIN [extended] [verbose] SELECT 查询语句;
extended
:- 使用扩展输出格式显示更多的信息。
- 例如,可以显示索引是否被使用。
verbose
:- 显示更详细的输出,包括分区信息等。
EXPLAIN 输出的列
EXPLAIN
命令的输出通常包含以下列:
id
:- 查询块的标识符。
- 数字越小优先级越高,数字相同则顺序执行。
select_type
:- 查询块的类型。
- 例如,
SIMPLE
(简单查询)、PRIMARY
(主查询)、DEPENDENT SUBQUERY
(依赖子查询)等。
table
:- 表名。
type
:- 表的访问类型。
- 例如,
ALL
(全表扫描)、index
(索引扫描)、range
(范围扫描)、ref
(使用索引列)、eq_ref
(使用索引列并且是等值比较)等。
possible_keys
:- 可能使用的索引列表。
key
:- 实际使用的索引名称。
key_len
:- 使用的索引的长度。
ref
:- 使用的索引列或常量。
rows
:- MySQL 估计需要检查的行数。
Extra
:- 额外的信息。
- 例如,
Using where
(使用了WHERE条件)、Using index
(使用了索引)、Using temporary
(使用了临时表)等。
示例
假设我们有一个名为 employees
的表,包含以下字段:
- id: 主键,整数类型。
- first_name: 名字,字符串类型。
- last_name: 姓氏,字符串类型。
- email: 邮箱,字符串类型。
我们想查看以下查询的执行计划:
sql
1SELECT * FROM employees WHERE first_name = 'John';
使用 EXPLAIN
sql
EXPLAIN SELECT * FROM employees WHERE first_name = 'John';
输出解释
以下是可能的 EXPLAIN
输出:
shell
+----+-------------+----------+--------+-------------------+---------+---------+-------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+--------+-------------------+---------+---------+-------+------+---------------------------------+
| 1 | SIMPLE | employees| range | first_name | first_name| 100 | const | 1 | Using where; Using index |
+----+-------------+----------+--------+-------------------+---------+---------+-------+------+---------------------------------+
输出解释
id
: 1,表示这是一个单一的查询块。select_type
:SIMPLE
,表示这是一个简单的查询。table
:employees
,表示查询的表。type
:range
,表示使用了范围扫描。possible_keys
:first_name
,表示可能使用的索引。key
:first_name
,表示实际使用的索引。key_len
: 100,表示使用的索引长度(这里假设first_name
的索引长度为 100 字节)。ref
:const
,表示使用了常量作为索引查找的条件。rows
: 1,表示 MySQL 估计需要检查的行数为 1。Extra
:Using where
(使用了WHERE条件);Using index
(使用了索引)。
总结
通过使用 EXPLAIN
,你可以了解 MySQL 如何执行查询,并据此优化查询性能。如果你还有其他问题或需要进一步的解释,请随时告诉我!
Sentinel 的流控、熔断、降级
1. 流控 (Rate Limiting)
作用:
- 限制进入服务的请求流量,防止服务过载。
- 可以按资源、客户端、接口等维度进行限流。
- 有助于保证服务的稳定性和响应时间。
2. 熔断 (Circuit Breaker)
作用:
- 当依赖的服务出现异常或响应时间过长时,暂时停止调用,以避免雪崩效应。
- 通过监控依赖服务的异常比例、异常数量、响应时间等指标来触发熔断。
- 有助于保护上游服务不受影响。
3. 降级 (Degrading)
作用:
- 当系统整体负载较高时,主动降级某些非核心功能,以释放资源。
- 通过设置阈值来触发降级,例如CPU使用率、内存使用率等。
- 有助于保证核心服务的可用性。
在使用Spring Cloud Alibaba Sentinel时,可以分别为流控、熔断和降级配置不同的回调函数。这些回调函数会在相应的场景下被调用。下面是如何为这些场景配置回调函数的详细步骤和示例代码。
示例代码
假设我们有一个名为ExampleController
的控制器类,其中定义了一个名为exampleMethod
的方法,该方法可能会被Sentinel进行流控、熔断或降级。我们将通过以下步骤来配置不同的回调函数:
- 定义回调方法 :
- 定义用于处理流控、熔断和降级的回调方法。
- 配置回调方法 :
- 使用
@SentinelResource
注解来配置不同的回调方法。
- 使用
下面是具体的实现代码:
java
@RestController
public class ExampleController {
private final RestTemplate restTemplate;
public ExampleController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/example")
@SentinelResource(
value = "exampleMethod",
// 流控回调
blockHandler = "handleFlowControl",
// 熔断回调
fallback = "handleCircuitBreaker",
// 降级回调
degradeHandler = "handleDegrade"
)
public String exampleMethod() {
// 这里是实际的业务逻辑
return "Success";
}
public String handleFlowControl(BlockException exception) {
// 处理被Sentinel流控的情况
return "请求被Sentinel流控";
}
public String handleCircuitBreaker(Throwable ex) {
// 处理被Sentinel熔断的情况
return "请求被Sentinel熔断";
}
public String handleDegrade() {
// 处理被Sentinel降级的情况
return "请求被Sentinel降级";
}
}
代码解释
@SentinelResource
注解 :- 指定名为
exampleMethod
的服务调用,并配置了三个回调方法:blockHandler参数 用于处理流控,fallback参数 用于处理熔断,degradeHandler参数用于处理降级。
- 指定名为
handleFlowControl
方法 :- 当请求被Sentinel流控时,就会调用这个方法来处理。
handleCircuitBreaker
方法 :- 当请求被Sentinel熔断时,就会调用这个方法来处理。
handleDegrade
方法 :- 当请求被Sentinel降级时,就会调用这个方法来处理。
当线上某个微服务的CPU飙升,你怎么办?
- 定位具体Java代码
-
首先,使用top命令查询到CPU占用最高的一个进程,获取到它的PID
-
然后通过top -Hp PID 显示进程的线程运行的信息列表
-
通过cpu列查看CPU占用最高的线程获取到该线程的PID
-
最后通过 jstack 工具查看该线程的堆栈信息 jstack -F (进程PID) | grep '(线程PID的十六进制数)' -C5 --color
- 具体问题具体分析