展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > MySQL子查询原理是什么

MySQL子查询原理是什么

01前言子查询,通俗解释就是查询语句中嵌套着另一个查询语句。相信日常工作中接触到MySQL的同学都了解或使用过子查询,但是具体它是怎样实现的呢?查询效率如何?这些恐怕好多人就不太清楚了,下面咱们就围绕这两个问题共同探索一下。02准备内容这里我们需要用到3个表,这3个表都有一个主键索引id和一个索引a,字段b上无索引。存储过程idata()往表t1里插入的是100行数据,表t2、t3里插入了1000行数据。建表语句如下:CREATETABLE`t1`(`id`INT(11)NOTNULL,`t1_a`INT

本文目录

      01前言

      子查询,通俗解释就是查询语句中嵌套着另一个查询语句。相信日常工作中接触到 MySQL 的同学都了解或使用过子查询,但是具体它是怎样实现的呢? 查询效率如何? 这些恐怕好多人就不太清楚了,下面咱们就围绕这两个问题共同探索一下。

      02准备内容

      这里我们需要用到3个表,这3个表都有一个主键索引 id 和一个索引 a,字段 b 上无索引。存储过程 idata() 往表 t1 里插入的是 100 行数据,表 t2、t3 里插入了 1000 行数据。建表语句如下:

      CREATE TABLE `t1` (
          `id` INT ( 11 ) NOT NULL,
          `t1_a` INT ( 11 ) DEFAULT NULL,
          `t1_b` INT ( 11 ) DEFAULT NULL,
      PRIMARY KEY ( `id` ),
      KEY `idx_a` ( `t1_a` )) ENGINE = INNODB;
      
      CREATE TABLE `t2` (
          `id` INT ( 11 ) NOT NULL,
          `t2_a` INT ( 11 ) DEFAULT NULL,
          `t2_b` INT ( 11 ) DEFAULT NULL,
      PRIMARY KEY ( `id` ),
      KEY `idx_a` ( `t2_a` )) ENGINE = INNODB;
      
      CREATE TABLE `t3` (
          `id` INT ( 11 ) NOT NULL,
          `t3_a` INT ( 11 ) DEFAULT NULL,
          `t3_b` INT ( 11 ) DEFAULT NULL,
      PRIMARY KEY ( `id` ),
      KEY `idx_a` ( `t3_a` )) ENGINE = INNODB;
      
      -- 向t1添加100条数据
      -- drop procedure idata;
      delimiter ;;
      create procedure idata()
      begin
        declare i int;
        set i=1;
        while(i<=100)do
              insert into t1 values(i, i, i);
          set i=i+1;
        end while;
      end;;
      delimiter ;
      call idata();
      
      -- 向t2添加1000条数据
      drop procedure idata;
      delimiter ;;
      create procedure idata()
      begin
        declare i int;
        set i=101;
        while(i<=1100)do
              insert into t2 values(i, i, i);
          set i=i+1;
        end while;
      end;;
      delimiter ;
      call idata();
      
      -- 向t2添加1000条数据,且t3_a列的值为倒叙
      drop procedure idata;
      delimiter ;;
      create procedure idata()
      begin
        declare i int;
        set i=101;
        while(i<=1100)do
              insert into t3 values(i, 1101-i, i);
          set i=i+1;
        end while;
      end;;
      delimiter ;
      call idata();

      03子查询的语法形式和分类

      3.1 语法形式

      子查询的语法规定,子查询可以在一个外层查询的各种位置出现,这里我们只介绍常用的几个:

      3.1.1  FROM子句中

      如 SELECT m, n FROM (SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2) AS t;

      这个例子中的子查询是:(SELECT m2 + 1 AS m, n2 AS n FROM t2 WHERE m2 > 2),这个放在FROM子句中的子查询相当于一个表,但又和我们平常使用的表有点儿不一样,这种由子查询结果集组成的表称之为派生表。

      3.1.2 WHERE或IN子句中

      如:SELECT * FROM t1 WHERE m1 = (SELECT MIN(m2) FROM t2);

             SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2);

      其他的还有 SELECT 子句中,ORDER BY 子句中,GROUP BY 子句中,虽然语法支持,但没啥意义,就不唠叨这些情况了。

      3.2 分类

      3.2.1 按返回的结果集区分

      标量子查询,只返回一个单一值的子查询称之为标量子查询,比如:

      SELECT * FROM t1 WHERE m1 = (SELECT m1 FROM t1 LIMIT 1);

      行子查询,就是只返回一条记录的子查询,不过这条记录需要包含多个列(只包含一个列就成了标量子查询了)。比如:SELECT * FROM t1 WHERE (m1, n1) = (SELECT m2, n2 FROM t2 LIMIT 1);

      列子查询,就是只返回一个列的数据,不过这个列的数据需要包含多条记录(只包含一条记录就成了标量子查询了)。比如:SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2);

      表子查询,就是子查询的结果既包含很多条记录,又包含很多个列,比如:

      SELECT * FROM t1 WHERE (m1, n1) IN (SELECT m2, n2 FROM t2);

      其中的 (SELECT m2, n2 FROM t2) 就是一个表子查询,这里需要和行子查询对比一下,行子查询中我们用了 LIMIT 1 来保证子查询的结果只有一条记录。

      3.2.2 按与外层查询关系来区分

      不相关子查询,就是子查询可以单独运行出结果,而不依赖于外层查询的值,我们就可以把这个子查询称之为不相关子查询。
      相关子查询,就是需要依赖于外层查询的值的子查询称之为相关子查询。比如:SELECT * FROM t1 WHERE m1 IN (SELECT m2 FROM t2 WHERE n1 = n2);

      04子查询在MySQL中是怎么执行的

      4.1 标量子查询、行子查询的执行方式

      4.1.1 不相关子查询

      如下边这个查询语句:

      mysql root@localhost:test> explain select * from t1 where t1_a = (select t2_a from t2 limit 1);
      +----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+
      | id | select_type | table | type  | possible_keys | key   | key_len | ref    | rows | Extra       |
      +----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+
      | 1  | PRIMARY     | t1    | ref   | idx_a         | idx_a | 5       | const  | 1    | Using where |
      | 2  | SUBQUERY    | t2    | index |         | idx_a | 5       |  | 1000 | Using index |
      +----+-------------+-------+-------+---------------+-------+---------+--------+------+-------------+

      它的执行方式:

      先单独执行 (select t2_a from t2 limit 1) 这个子查询。

      然后在将上一步子查询得到的结果当作外层查询的参数再执行外层查询 select * from t1 where t1_a = ...。

      也就是说,对于包含不相关的标量子查询或者行子查询的查询语句来说,MySQL 会分别独立的执行外层查询和子查询,就当作两个单表查询就好了。

      4.1.2 相关的子查询

      比如下边这个查询:

      mysql root@localhost:test> explain select * from t1 where t1_a = (select t2_a from t2 where t1.t1_b=t2.t2_b  limit 1);
      +----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+
      | id | select_type        | table | type | possible_keys | key    | key_len | ref    | rows | Extra       |
      +----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+
      | 1  | PRIMARY            | t1    | ALL  |         |  |   |  | 100  | Using where |
      | 2  | DEPENDENT SUBQUERY | t2    | ALL  |         |  |   |  | 1000 | Using where |
      +----+--------------------+-------+------+---------------+--------+---------+--------+------+-------------+

      它的执行方式就是这样的:

      先从外层查询中获取一条记录,本例中也就是先从 t1 表中获取一条记录。

      然后从上一步骤中获取的那条记录中找出子查询中涉及到的值,就是 t1 表中找出 t1.t1_b 列的值,然后执行子查询。

      最后根据子查询的查询结果来检测外层查询 WHERE 子句的条件是否成立,如果成立,就把外层查询的那条记录加入到结果集,否则就丢弃。

      然后重复以上步骤,直到 t1 中的记录全部匹配完。

      4.2 IN子查询

      4.2.1 物化

      如果子查询的结果集中的记录条数很少,那么把子查询和外层查询分别看成两个单独的单表查询效率还是蛮高的,但是如果单独执行子查询后的结果集太多的话,就会导致这些问题:

      结果集太多,可能内存中都放不下~

      对于外层查询来说,如果子查询的结果集太多,那就意味着 IN 子句中的参数特别多,这就导致:

      1)无法有效的使用索引,只能对外层查询进行全表扫描。

      2)在对外层查询执行全表扫描时,由于 IN 子句中的参数太多,这会导致检测一条记录是否符合和 IN 子句中的参数匹配花费的时间太长。

      于是就有:不直接将不相关子查询的结果集当作外层查询的参数,而是将该结果集写入一个临时表里。写入临时表的过程是这样的:

      该临时表的列就是子查询结果集中的列。

      写入临时表的记录会被去重,让临时表变得更小,更省地方。

      一般情况下子查询结果集不大时,就会为它建立基于内存的使用 Memory 存储引擎的临时表,而且会为该表建立哈希索引。

      如果子查询的结果集非常大,超过了系统变量 tmp_table_size或者 max_heap_table_size,临时表会转而使用基于磁盘的存储引擎来保存结果集中的记录,索引类型也对应转变为 B+ 树索引。

      这个将子查询结果集中的记录保存到临时表的过程称之为物化(Materialize)。为了方便起见,我们就把那个存储子查询结果集的临时表称之为物化表。正因为物化表中的记录都建立了索引(基于内存的物化表有哈希索引,基于磁盘的有 B+ 树索引),通过索引执行IN语句判断某个操作数在不在子查询结果集中变得非常快,从而提升了子查询语句的性能。

      mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2);
      +----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+
      | id | select_type  | table       | type   | possible_keys | key        | key_len | ref          | rows | Extra       |
      +----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+
      | 1  | SIMPLE       | t3          | ALL    | idx_a         |      |   |        | 1000 | Using where |
      | 1  | SIMPLE       |  | eq_ref |     |  | 5       | test.t3.t3_a | 1    |       |
      | 2  | MATERIALIZED | t2          | index  | idx_a         | idx_a      | 5       |        | 1000 | Using index |
      +----+--------------+-------------+--------+---------------+------------+---------+--------------+------+-------------+

      其实上边的查询就相当于表 t3 和子查询物化表进行内连接:

      mysql root@localhost:test> explain select * from t3 left join t2 on t3.t3_a=t2.t2_a;
      +----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+
      | id | select_type | table | type | possible_keys | key    | key_len | ref          | rows | Extra  |
      +----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+
      | 1  | SIMPLE      | t3    | ALL  |         |  |   |        | 1000 |  |
      | 1  | SIMPLE      | t2    | ref  | idx_a         | idx_a  | 5       | test.t3.t3_a | 1    |  |
      +----+-------------+-------+------+---------------+--------+---------+--------------+------+--------+

      此时 MySQL 查询优化器会通过运算来选择成本更低的方案来执行查询。

      虽然,上面通过物化表的方式,将IN子查询转换成了联接查询,但还是会有建立临时表的成本,能不能不进行物化操作直接把子查询转换为连接呢?直接转换肯定不行。
      -- 这里我们先构造了3条记录,其实也是构造不唯一的普通索引

      +------+------+------+
      | id   | t2_a | t2_b |
      +------+------+------+
      | 1100 | 1000 | 1000 |
      | 1101 | 1000 | 1000 |
      | 1102 | 1000 | 1000 |
      +------+------+------+
      -- 加限制条件where t2.id>=1100是为了减少要显示的数据
      mysql root@localhost:test> select * from t3 where t3_a in (select t2_a from t2 where t2.id>=1100);
      +-----+------+------+
      | id  | t3_a | t3_b |
      +-----+------+------+
      | 101 | 1000 | 101  |
      +-----+------+------+
      1 row in set
      Time: 0.016s
      mysql root@localhost:test> select * from t3 left join t2 on t3.t3_a=t2.t2_a where t2.id>=1100;
      +-----+------+------+------+------+------+
      | id  | t3_a | t3_b | id   | t2_a | t2_b |
      +-----+------+------+------+------+------+
      | 101 | 1000 | 101  | 1100 | 1000 | 1000 |
      | 101 | 1000 | 101  | 1101 | 1000 | 1000 |
      | 101 | 1000 | 101  | 1102 | 1000 | 1000 |
      +-----+------+------+------+------+------+
      3 rows in set
      Time: 0.018s

      所以说 IN 子查询和表联接之间并不完全等价。而我们需要的是另一种叫做半联接 (semi-join) 的联接方式 :对于 t3 表的某条记录来说,我们只关心在 t2 表中是否存在与之匹配的记录,而不关心具体有多少条记录与之匹配,最终的结果集中也只保留 t3 表的记录。

      注意:semi-join 只是在 MySQL 内部采用的一种执行子查询的方式,MySQL 并没有提供面向用户的 semi-join 语法。

      4.2.2 半联接的实现:
      • Table pullout (子查询中的表上拉)

      当子查询的查询列表处只有主键或者唯一索引列时,可以直接把子查询中的表上拉到外层查询的 FROM 子句中,并把子查询中的搜索条件合并到外层查询的搜索条件中,比如这个:

      mysql root@localhost:test> select * from t3 where t3_a in (select t2_a from t2 where t2.id=999)
      +-----+------+------+
      | id  | t3_a | t3_b |
      +-----+------+------+
      | 102 | 999  | 102  |
      +-----+------+------+
      1 row in set
      Time: 0.024s
      mysql root@localhost:test> select * from t3 join t2 on t3.t3_a=t2.t2_a where t2.id=999;
      +-----+------+------+-----+------+------+
      | id  | t3_a | t3_b | id  | t2_a | t2_b |
      +-----+------+------+-----+------+------+
      | 102 | 999  | 102  | 999 | 999  | 999  |
      +-----+------+------+-----+------+------+
      1 row in set
      Time: 0.028s
      mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.id=999)
      +----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+
      | id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra  |
      +----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+
      | 1  | SIMPLE      | t2    | const | PRIMARY,idx_a | PRIMARY | 4       | const | 1    |  |
      | 1  | SIMPLE      | t3    | ref   | idx_a         | idx_a   | 5       | const | 1    |  |
      +----+-------------+-------+-------+---------------+---------+---------+-------+------+--------+
      • FirstMatch execution strategy (首次匹配)

      FirstMatch 是一种最原始的半连接执行方式,跟相关子查询的执行方式是一样的,就是说先取一条外层查询的中的记录,然后到子查询的表中寻找符合匹配条件的记录,如果能找到一条,则将该外层查询的记录放入最终的结果集并且停止查找更多匹配的记录,如果找不到则把该外层查询的记录丢弃掉。然后再开始取下一条外层查询中的记录,重复上边这个过程。

      mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.t2_a=1000)
      +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+
      | id | select_type | table | type | possible_keys | key   | key_len | ref   | rows | Extra                       |
      +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+
      | 1  | SIMPLE      | t3    | ref  | idx_a         | idx_a | 5       | const | 1    |                       |
      | 1  | SIMPLE      | t2    | ref  | idx_a         | idx_a | 5       | const | 4    | Using index; FirstMatch(t3) |
      +----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------+
      • DuplicateWeedout execution strategy (重复值消除)

      转换为半连接查询后,t3 表中的某条记录可能在 t2 表中有多条匹配的记录,所以该条记录可能多次被添加到最后的结果集中,为了消除重复,我们可以建立一个临时表,并设置主键id,每当某条 t3 表中的记录要加入结果集时,就首先把这条记录的id值加入到这个临时表里,如果添加成功,说明之前这条 t2 表中的记录并没有加入最终的结果集,是一条需要的结果;如果添加失败,说明之前这条 s1 表中的记录已经加入过最终的结果集,直接把它丢弃。

      • LooseScan execution strategy (松散扫描)

      这种虽然是扫描索引,但只取值相同的记录的第一条去做匹配操作的方式称之为松散扫描。

      4.2.3 半联接的适用条件

      当然,并不是所有包含IN子查询的查询语句都可以转换为 semi-join,只有形如这样的查询才可以被转换为 semi-join:

      SELECT ... FROM outer_tables 
          WHERE expr IN (SELECT ... FROM inner_tables ...) AND ...
      
      -- 或者这样的形式也可以:
      
      SELECT ... FROM outer_tables 
          WHERE (oe1, oe2, ...) IN (SELECT ie1, ie2, ... FROM inner_tables ...) AND ...

      用文字总结一下,只有符合下边这些条件的子查询才可以被转换为 semi-join:

      1. 该子查询必须是和IN语句组成的布尔表达式,并且在外层查询的 WHERE 或者 ON 子句中出现

      2. 外层查询也可以有其他的搜索条件,只不过和 IN 子查询的搜索条件必须使用AND 连接起来

      3. 该子查询必须是一个单一的查询,不能是由若干查询由 UNION 连接起来的形式

      4. 该子查询不能包含 GROUP BY 或者 HAVING 语句或者聚集函数

      4.2.4 转为 EXISTS 子查询

      不管子查询是相关的还是不相关的,都可以把 IN 子查询尝试转为 EXISTS子查询。其实对于任意一个 IN 子查询来说,都可以被转为 EXISTS 子查询,通用的例子如下:

      outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)
      -- 可以被转换为:
      EXISTS (SELECT inner_expr FROM ... WHERE subquery_where AND outer_expr=inner_expr)

      当然这个过程中有一些特殊情况,比如在 outer_expr 或者 inner_expr 值为 NULL 的情况下就比较特殊。因为有 NULL 值作为操作数的表达式结果往往是 NULL,比方说:

      mysql root@localhost:test> SELECT NULL IN (1, 2, 3);
      +-------------------+
      | NULL IN (1, 2, 3) |
      +-------------------+
      |             |
      +-------------------+
      1 row in set

      而 EXISTS 子查询的结果肯定是 TRUE 或者 FASLE 。但是现实中我们大部分使用 IN 子查询的场景是把它放在 WHERE 或者 ON 子句中,而 WHERE 或者 ON 子句是不区分 NULL 和 FALSE 的,比方说:

      mysql root@localhost:test> SELECT 1 FROM s1 WHERE NULL;
      +---+
      | 1 |
      +---+
      0 rows in set
      Time: 0.016s
      mysql root@localhost:test> SELECT 1 FROM s1 WHERE FALSE;
      +---+
      | 1 |
      +---+
      0 rows in set
      Time: 0.033s

      所以只要我们的IN子查询是放在 WHERE 或者 ON 子句中的,那么 IN ->  EXISTS 的转换就是没问题的。说了这么多,为啥要转换呢?这是因为不转换的话可能用不到索引,比方说下边这个查询:

      mysql root@localhost:test> explain select * from t3 where t3_a in (select t2_a from t2 where t2.t2_a>=999) or t3_b > 1000;
      +----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+
      | id | select_type | table | type  | possible_keys | key    | key_len | ref    | rows | Extra                    |
      +----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+
      | 1  | PRIMARY     | t3    | ALL   |         |  |   |  | 1000 | Using where              |
      | 2  | SUBQUERY    | t2    | range | idx_a         | idx_a  | 5       |  | 107  | Using where; Using index |
      +----+-------------+-------+-------+---------------+--------+---------+--------+------+--------------------------+

      但是将它转为 EXISTS 子查询后却可以使用到索引:

      mysql root@localhost:test> explain select * from t3 where exists (select 1 from t2 where t2.t2_a>=999 and t2.t2_a=t3.t3_a) or t3_b > 1000;
      +----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+
      | id | select_type        | table | type | possible_keys | key    | key_len | ref          | rows | Extra                    |
      +----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+
      | 1  | PRIMARY            | t3    | ALL  |         |  |   |        | 1000 | Using where              |
      | 2  | DEPENDENT SUBQUERY | t2    | ref  | idx_a         | idx_a  | 5       | test.t3.t3_a | 1    | Using where; Using index |
      +----+--------------------+-------+------+---------------+--------+---------+--------------+------+--------------------------+

      需要注意的是,如果 IN 子查询不满足转换为 semi-join 的条件,又不能转换为物化表或者转换为物化表的成本太大,那么它就会被转换为 EXISTS 查询。或者转换为物化表的成本太大,那么它就会被转换为 EXISTS 查询。

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 MySQL
      相关文章 更多
      精品专题 更多
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      macOS软件

      正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

      IOS软件

      正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

      AI

      正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

      Mac软件 更多
      Postman macOS版
      Postman macOS版

      Postman是一套面向API全生命周期的开发与测试平台,可在Mac上发送和调试多种协议请求,管理集合、环境及认证信息,并完成脚本测试、模拟服务、监控、文档发布和团队协作。它适合开发、测试及运维人员使用,提供个人免费方案。

      Docker Desktop macOS版
      Docker Desktop macOS版

      Docker Desktop 是 Docker 官方为 Mac 提供的容器开发环境,集成 Docker Engine、命令行工具、Compose、Build、Kubernetes 与图形化管理界面。

      Android Studio macOS版
      Android Studio macOS版

      Android Studio 是 Google 面向安卓应用开发提供的官方集成开发环境,基于 IntelliJ IDEA 构建。它整合智能代码编辑、Gradle 构建、界面预览、设备模拟、性能分析、调试测试及 APK 分析等工具。

      Xcode macOS版
      Xcode macOS版

      Xcode 是苹果面向 Mac 提供的官方集成开发环境,将代码编辑、项目构建、SwiftUI 实时预览、设备模拟、自动化测试、断点调试、性能分析和应用分发整合在同一套工作流中,适合开发与维护 iPhone、iPad、Mac、Apple Wa

      JetBrains WebStorm macOS版
      JetBrains WebStorm macOS版

      JetBrains WebStorm 是面向 JavaScript、TypeScript 和现代 Web 技术的专业集成开发环境,可智能理解项目结构,提供代码补全、重构、错误检查、运行调试、测试及 Git 工具。

      PhpStorm macOS版
      PhpStorm macOS版

      PhpStorm 是 JetBrains 推出的专业 PHP 集成开发环境,可在 Mac 上完成代码编写、智能检查、重构、调试、测试和数据库管理。它支持主流 PHP 框架、Composer、Git、Docker 与远程解释器。

      JetBrains PyCharm macOS版
      JetBrains PyCharm macOS版

      JetBrains PyCharm 是面向 Python 开发者的集成开发环境,提供智能补全、代码检查、重构、调试、测试、版本控制及虚拟环境管理功能。Mac 版兼容 Intel 与 Apple 芯片,核心功能可免费使用。

      JetBrains IntelliJ IDEA macOS版
      JetBrains IntelliJ IDEA macOS版

      IntelliJ IDEA 是 JetBrains 推出的专业集成开发环境,重点服务于 Java、Kotlin 与 JVM 项目开发。软件集成代码补全、静态检查、重构、调试、测试、版本控制及构建工具,并可借助插件扩展语言和框架支持。

      Visual Studio Code macOS版
      Visual Studio Code macOS版

      Visual Studio Code 是微软提供的免费代码编辑器,支持 macOS,并为 Intel 与 Apple 芯片 Mac 提供适配版本。软件集代码编辑、调试、Git 源代码管理、集成终端和扩展市场于一体。

      REAPER macOS版
      REAPER macOS版

      REAPER是Cockos开发的数字音频工作站,提供多轨音频与MIDI录制、剪辑、处理、混音和母带制作工具。Mac版兼容Intel与Apple芯片,支持AU、VST、VST3、CLAP等插件格式,并提供高度可定制的工作流程。

      Adobe Audition macOS版
      Adobe Audition macOS版

      Adobe Audition 是面向播客、影视后期、音乐制作及内容创作者的专业音频工作站,提供波形精修、多轨录制与混音、频谱编辑、降噪修复、响度匹配和效果处理工具,并可与 Adobe Premiere 协同完成视频声音制作。

      Pro Tools macOS版
      Pro Tools macOS版

      Pro Tools 是 Avid 推出的专业数字音频工作站,覆盖多轨录音、无损剪辑、MIDI 编曲、插件处理、自动化混音及声音与画面同步等工作。它拥有 Intro、Artist、Studio 和 Ultimate 等方案。

      WINDOWS 更多
      PixPix 网页版
      PixPix 网页版

      PixPix 是面向品牌、卖家和内容团队的在线 AI 图片视频创作平台,提供图视频 Agent、无限画布、图片生成、视频生成、自然语言编辑及商品套图、产品精修和带货视频等电商工具。

      Postman Windows版
      Postman Windows版

      Postman 是面向 API 全生命周期的开发与协作平台。Windows 桌面版可用于构建和发送 HTTP、GraphQL、gRPC、WebSocket、MQTT 等请求,并提供环境变量、集合、脚本测试、模拟服务、监控、文档及团队工作区等

      Docker Desktop Windows版
      Docker Desktop Windows版

      Docker Desktop 为 Windows 开发者提供集成式容器环境,内置 Docker Engine、命令行工具、Compose、Build 与 Kubernetes 等组件,可在本机完成镜像构建、容器运行、日志查看和资源管理。

      Android Studio Windows版
      Android Studio Windows版

      Android Studio 是 Google 面向 Android 应用开发提供的官方集成开发环境,以 IntelliJ IDEA 为基础,集成智能代码编辑、Gradle 构建、设备模拟、界面预览、调试测试及性能分析工具,

      JetBrains WebStorm Windows版
      JetBrains WebStorm Windows版

      WebStorm 是 JetBrains 面向 JavaScript、TypeScript 和现代 Web 技术打造的集成开发环境。它提供项目级代码分析、智能补全、安全重构、导航搜索,并集成调试、测试、终端、Git 与常用前端工具,

      JetBrains PhpStorm Windows版
      JetBrains PhpStorm Windows版

      PhpStorm 是 JetBrains 面向 PHP 与现代 Web 开发打造的专业集成开发环境,提供代码补全、静态检查、重构、调试、测试、数据库、版本控制及前端语言支持。Windows 用户可直接下载安装程序,

      JetBrains PyCharm Windows版
      JetBrains PyCharm Windows版

      PyCharm 是 JetBrains 推出的 Python 集成开发环境,提供智能代码补全、错误检查、重构、调试、测试、Git、终端及 Jupyter Notebook 支持。统一版安装后可体验 30 天 Pro 功能,

      JetBrains IntelliJ IDEA Windows版
      JetBrains IntelliJ IDEA Windows版

      JetBrains IntelliJ IDEA 是以 Java、Kotlin 和 JVM 项目为核心的专业集成开发环境,提供智能补全、代码检查、安全重构、调试、测试、版本控制及构建工具集成。统一发行版的核心功能可免费用于商业和非商业项目,

      Visual Studio Code Windows版
      Visual Studio Code Windows版

      Visual Studio Code 是微软推出的免费代码编辑器,Windows 版集成智能补全、代码导航、调试、终端和版本控制,并可通过扩展添加语言、格式化及远程开发能力。它启动较快、配置灵活,

      REAPER Windows版
      REAPER Windows版

      REAPER 是 Cockos 推出的数字音频工作站,适用于录音、MIDI 编曲、音频编辑、混音、母带制作及声音设计。软件安装包小巧,支持多轨录制、灵活路由、自动化、第三方插件和脚本扩展,并提供完整功能的 60 天免费评估期。

      Adobe Audition Windows版
      Adobe Audition Windows版

      Adobe Audition 是面向播客、影视后期、广播与音乐制作的专业音频工作站,提供波形编辑、多轨混音、频谱分析、录音、降噪修复和效果处理等功能。Windows 版通过 Adobe Creative Cloud 安装和管理,

      Pro Tools Windows版
      Pro Tools Windows版

      Pro Tools 是 Avid 推出的专业数字音频工作站,提供多轨录音、音频剪辑、MIDI编曲、插件处理、自动化混音及影视声音制作能力。它适合音乐人、录音师、制作人和音频后期团队使用,并提供免费入门版、订阅方案及永久授权选择。