Java学习笔记(十二)
Mysql explain Extra
MySQL的EXPLAIN语句是优化数据库查询的重要手段,其中的Extra列包含了不适合在其他列中显示但十分重要的额外信息。以下是对Extra列的详细介绍及举例:
一、Using filesort
-
解释:表示MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”。
-
示例:
- 假设有一个表test,其中建立了(col1, col2, col3)三个字段的复合索引。如果查询时排序没有按照(col1, col2, col3)的顺序进行,就会产生外部的索引排序,即Using filesort,此时查询效率较低。
- 反之,如果排序时按照(col1, col2, col3)的顺序进行,则不会使用外部的索引排序,查询效率较高。
二、Using temporary
-
解释:表示MySQL在对查询结果排序或分组查询时使用了临时表保存中间结果。
-
示例:
- 假设有一个表test,其中建立了(col1, col2)两个字段的复合索引。如果查询时分组没有按照(col1, col2)的顺序进行,就会产生临时表和外部的索引排序,即Using temporary和Using filesort。
- 反之,如果分组时按照(col1, col2)的顺序进行,则不会产生临时表,也不会使用外部的索引排序,查询效率较高。
三、Using index
-
解释:表示相应的SELECT操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率较高。覆盖索引是指SELECT的数据列只用从索引中就能够取得,不必读取数据行。
-
示例:
- 假设有一个表test_users,其中建立了(user_name, age)的复合索引。如果查询时SELECT的字段是user_name和age,且WHERE子句中的条件能够使用该复合索引,则Extra列会显示Using index,表示查询只使用了索引而没有访问表的数据行。
四、Using where
-
解释:表示查询中使用了WHERE子句进行过滤。
-
示例:
- 假设有一个表test,查询语句为SELECT * FROM test WHERE age > 18。此时Extra列会显示Using where,表示查询中使用了WHERE子句对age字段进行过滤。
五、Using join buffer
-
解释:表示使用了连接缓存。这通常发生在连接操作(如JOIN)中,当驱动表(被连接的表,如LEFT JOIN左边的表或INNER JOIN中数据少的表)没有索引时,MySQL可能会使用连接缓存来提高查询效率。
-
示例:
- 假设有两个表table1和table2,其中table1是驱动表且没有索引。查询语句为SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id。此时如果Extra列显示Using join buffer,表示MySQL使用了连接缓存来提高查询效率。
六、Impossible where
-
解释:表示WHERE子句的值总是false,不能用来获取任何元组。
-
示例:
- 假设有一个表test,查询语句为SELECT * FROM test WHERE 1=0。此时Extra列会显示Impossible where,表示WHERE子句的条件永远不满足,无法获取任何数据。
七、Select tables optimized away
-
解释:表示在没有GROUP BY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作。这些优化可以在查询执行计划生成的阶段即完成,而不必等到执行阶段再进行计算。
-
示例:
- 假设有一个使用MyISAM存储引擎的表test,查询语句为SELECT COUNT() FROM test WHERE age > 18。如果age字段上有索引,且查询执行计划显示Select tables optimized away,则表示MySQL在查询执行计划生成的阶段就完成了COUNT()操作的优化。
八、Distinct
-
解释:表示优化DISTINCT操作。在找到第一匹配的元组后即停止找同样值的动作,以提高查询效率。
-
示例:
- 假设有一个表test,查询语句为SELECT DISTINCT age FROM test。如果Extra列显示Distinct,则表示MySQL在查询时优化了DISTINCT操作,只返回不重复的age值。
综上所述,MySQL EXPLAIN语句中的Extra列提供了关于查询执行计划的额外信息,有助于优化数据库查询。在实际应用中,可以根据Extra列的信息调整索引、查询语句等以提高查询效率。
Mysql explain key_len
在MySQL中,EXPLAIN
语句是用来分析SQL查询的性能的。当你对一个查询使用EXPLAIN
时,它会返回查询的执行计划,其中包含了诸如表的访问方式、使用的索引、扫描的行数等详细信息。在EXPLAIN
的输出中,key_len
是一个关键的列,它提供了关于MySQL在查询中使用的索引键长度的信息。
key_len 列的详细说明
key_len
表示MySQL在查询中使用的索引键的总长度(以字节为单位)。这个长度是根据索引中的列的数据类型和长度来计算的。
如何计算 key_len
-
单列索引:
- 对于字符串类型(如
CHAR
,VARCHAR
),key_len
等于字符集的最大字节长度乘以列定义的字符数。例如,VARCHAR(255) CHARACTER SET utf8
的key_len
将是255 * 3 = 765
字节(因为utf8
字符集最多使用3个字节)。 - 对于数值类型(如
INT
,BIGINT
),key_len
等于数据类型的大小。例如,INT
类型的key_len
是4字节,BIGINT
是8字节。
- 对于字符串类型(如
-
多列索引:
- 当使用多列索引时,
key_len
是所有索引列长度的总和。
- 当使用多列索引时,
示例
假设有一个表users
,定义如下:
CREATE TABLE users (id INT NOT NULL AUTO_INCREMENT,username VARCHAR(50) NOT NULL,email VARCHAR(100) NOT NULL,PRIMARY KEY (id),INDEX idx_username_email (username, email)
);
- 如果查询是
SELECT * FROM users WHERE username = 'john';
,并且MySQL决定使用idx_username_email
索引,key_len
将是username
列的长度。假设username
列使用的是utf8
字符集,那么key_len
将是50 * 3 = 150
字节。 - 如果查询是
SELECT * FROM users WHERE username = 'john' AND email = 'john@example.com';
,并且MySQL决定使用idx_username_email
索引,key_len
将是username
和email
两列的长度之和。假设email
列也使用utf8
字符集,那么key_len
将是50 * 3 + 100 * 3 = 450
字节。
使用 key_len 的注意事项
key_len
可以帮助你理解MySQL是如何使用索引的,以及索引是否有效地减少了需要扫描的数据量。- 如果
key_len
比预期的要小,可能是因为查询条件中只使用了索引的一部分列,或者是因为索引列有前缀索引(即只索引了列的前缀部分)。 - 在设计索引和查询优化时,
key_len
是一个重要的参考指标,但也要结合其他EXPLAIN
输出信息(如type
,rows
,Extra
等)来综合分析。
通过理解key_len
的含义和计算方法,你可以更有效地使用MySQL的索引功能,从而优化查询性能。
为了统计一个List<String>
中每种字符串的数量,并且将这些数量按顺序存储到一个List<Integer>
中,你可以采用以下步骤:
- 遍历输入的字符串列表。
- 使用一个
HashMap
来记录每种字符串的出现次数。 - 遍历完列表后,根据输入列表中的字符串顺序,从
HashMap
中提取对应的数量。 - 将这些数量存储到一个新的
List<Integer>
中。
以下是Java代码示例:
import java.util.*;public class StringCounter {public static void main(String[] args) {List<String> input = Arrays.asList("a", "a", "b", "ck", "ck");List<Integer> counts = countStrings(input);System.out.println(counts); // 输出: [2, 1, 2]}public static List<Integer> countStrings(List<String> input) {Map<String, Integer> countMap = new HashMap<>();List<Integer> result = new ArrayList<>();// 统计每种字符串的数量for (String str : input) {countMap.put(str, countMap.getOrDefault(str, 0) + 1);}// 按照输入列表的顺序获取数量for (String str : input) {result.add(countMap.get(str));}return result;}
}
在这段代码中:
countStrings
方法接收一个List<String>
作为输入,并返回一个List<Integer>
。- 使用
HashMap<String, Integer>
来存储每种字符串及其对应的数量。 getOrDefault
方法用于在HashMap
中查找字符串的数量,如果字符串不存在,则返回默认值0,并随后加1。- 第二个for循环遍历输入列表,并使用
countMap
来获取每种字符串的数量,然后将这些数量添加到结果列表中。
这种方法的时间复杂度是O(n),其中n是输入列表的长度,因为我们需要遍历列表两次:一次用于统计数量,一次用于构建结果列表。空间复杂度也是O(n),因为在最坏的情况下,HashMap
中可能需要存储n个不同的字符串及其数量。然而,在实际应用中,由于可能存在重复的字符串,所以HashMap
的大小通常远小于n。
Java 一个线程对象调用start方法实现多线程执行的原理
在 Java 中,当一个线程对象调用 start()
方法时,会触发一系列的操作来实现多线程的执行。以下是这一过程的详细原理:
-
创建线程对象:
首先,你通过创建一个Thread
类的实例(或者一个继承自Thread
类的子类实例)来定义一个线程对象。在这个线程对象中,你需要重写run()
方法,该方法包含了线程要执行的任务代码。 -
调用
start()
方法:
当你调用线程对象的start()
方法时,这个方法并不会直接执行run()
方法中的代码。相反,它会做几件事情:- 检查线程状态:首先,
start()
方法会检查线程的状态。如果线程已经启动过了(即它的状态不是NEW
),那么start()
方法会抛出一个IllegalThreadStateException
异常。 - 设置线程状态:如果线程状态是
NEW
,start()
方法会将线程的状态设置为RUNNABLE
(或更准确地说是READY
,因为线程可能还需要等待 CPU 时间片)。 - 调用本地方法:
start()
方法最终会调用一个本地方法(native method),这个本地方法会请求操作系统的线程调度器创建一个新的本地线程(native thread)。这个本地线程会被分配给一定的 CPU 时间片,以便执行。
- 检查线程状态:首先,
-
线程调度和执行:
一旦操作系统创建了本地线程,这个线程就会开始执行。但是,它并不会直接执行 Java 线程对象的run()
方法。相反,它会执行 JVM 提供的一段启动代码(这段代码是 JVM 的一部分,而不是 Java 代码)。这段启动代码会负责:- 在 JVM 内部维护一个对 Java 线程对象的引用。
- 调用 Java 线程对象的
run()
方法。
因此,当你看到 Java 线程对象的
run()
方法被执行时,实际上是在一个新的本地线程中,由 JVM 的启动代码间接调用的。 -
线程终止:
当run()
方法执行完毕,或者线程因为某种原因(如抛出未捕获的异常)而终止时,本地线程也会相应地终止。JVM 会清理与这个线程相关的资源,并将其状态设置为TERMINATED
。
需要注意的是,直接调用 Java 线程对象的 run()
方法并不会启动一个新的线程。相反,它只会在当前线程中同步地执行 run()
方法中的代码。要启动一个新的线程,必须调用 start()
方法。
总结来说,Java 中一个线程对象调用 start()
方法实现多线程执行的原理涉及到线程状态的改变、本地线程的创建、JVM 启动代码的调用以及线程调度和执行等多个方面。