高性能 MySQL 第四版(GPT 重译)(一)(4)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 高性能 MySQL 第四版(GPT 重译)(一)

高性能 MySQL 第四版(GPT 重译)(一)(3)https://developer.aliyun.com/article/1484263

你可能还想了解读取和写入的字节数和行数。为此,使用全局状态变量 Handler_*

mysql> WITH rows_read AS (SELECT SUM(VARIABLE_VALUE) AS rows_read
    -> FROM global_status
    -> WHERE VARIABLE_NAME IN ('Handler_read_first', 'Handler_read_key',
    -> 'Handler_read_next', 'Handler_read_last', 'Handler_read_prev',
    -> 'Handler_read_rnd', 'Handler_read_rnd_next')), 
    -> rows_written AS (SELECT SUM(VARIABLE_VALUE) AS rows_written
    -> FROM global_status
    -> WHERE VARIABLE_NAME IN ('Handler_write')) 
    -> SELECT * FROM rows_read, rows_written\G
*************************** 1\. row ***************************
rows_read: 169358114082
rows_written: 33038251685
1 row in set (0.00 sec)

检查元数据锁

元数据锁用于保护数据库对象定义免受修改。任�� SQL 语句都会设置共享元数据锁:SELECTUPDATE 等。它们不会影响其他需要共享元数据锁的语句。但是,它们会阻止那些改变数据库对象定义的语句(如 ALTER TABLECREATE INDEX)启动,直到锁被释放。虽然大多数由元数据锁冲突引起的问题影响表,但锁本身是为任何数据库对象设置的,如 SCHEMAEVENTTABLESPACE 等。

元数据锁会一直保持直到事务结束。如果使用多语句事务,这会使故障排除变得更加困难。哪个语句正在等待锁通常是明确的:DDL 语句会隐式提交事务,因此它们是新事务中唯一的语句,并且你会在进程列表中找到它们处于"等待元数据锁"状态。然而,持有锁的语句可能会在进程列表中消失,如果它是仍然打开的多语句事务的一部分。

performance_schema 中的 metadata_locks 表保存了不同线程当前设置的锁的信息,还保存了等待锁的锁请求信息。这样,你可以轻松地识别哪个线程不允许你的 DDL 请求启动,并决定是否要终止此语句或等待其执行完成。

要启用元数据锁仪表化,需要启用 wait/lock/metadata/sql/mdl 仪表。

以下示例显示了一个线程,在进程列表中以 ID 5 可见,持有了线程 processlist_id=4 正在等待的锁:

mysql> SELECT processlist_id, object_type, 
    -> lock_type, lock_status, source
    -> FROM metadata_locks JOIN threads ON (owner_thread_id=thread_id)
    -> WHERE object_schema='employees' AND object_name='titles'\G
*************************** 1\. row ***************************
 processlist_id: 4
 object_type: TABLE
 lock_type: EXCLUSIVE
 lock_status: PENDING -- waits
 source: mdl.cc:3263
*************************** 2\. row ***************************
 processlist_id: 5
 object_type: TABLE
 lock_type: SHARED_READ
 lock_status: GRANTED -- holds
 source: sql_parse.cc:5707

检查内存使用情况

要在 performance_schema 中启用内存仪表化,需要启用 memory 类的仪表。一旦启用,你可以找到有关 MySQL 内部结构如何使用内存的详细信息。

直接使用 performance_schema

Performance Schema 将内存使用统计信息存储在以memory_summary_前缀开头的摘要表中。内存使用聚合在 Table 3-8 中描述。

表 3-8. 内存使用的聚合参数

聚合参数 描述
global 每个事件名称的全局
thread 每个线程:包括后台线程和用户线程
account 用户账户
host 主机
user 用户名

例如,要找到使用大部分内存的 InnoDB 结构,请执行以下查询:

mysql> SELECT EVENT_NAME, 
    -> CURRENT_NUMBER_OF_BYTES_USED/1024/1024 AS CURRENT_MB, 
    -> HIGH_NUMBER_OF_BYTES_USED/1024/1024 AS HIGH_MB 
    -> FROM performance_schema.memory_summary_global_by_event_name 
    -> WHERE EVENT_NAME LIKE 'memory/innodb/%' 
    -> ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 10;
+----------------------------+--------------+--------------+
| EVENT_NAME                 | CURRENT_MB   | HIGH_MB      |
+----------------------------+--------------+--------------+
| memory/innodb/buf_buf_pool | 130.68750000 | 130.68750000 |
| memory/innodb/ut0link_buf  |  24.00006104 |  24.00006104 |
| memory/innodb/buf0dblwr    |  17.07897949 |  24.96951294 |
| memory/innodb/ut0new       |  16.07891273 |  16.07891273 |
| memory/innodb/sync0arr     |   6.25006866 |   6.25006866 |
| memory/innodb/lock0lock    |   4.85086060 |   4.85086060 |
| memory/innodb/ut0pool      |   4.00003052 |   4.00003052 |
| memory/innodb/hash0hash    |   3.69776917 |   3.69776917 |
| memory/innodb/os0file      |   2.60422516 |   3.61988068 |
| memory/innodb/memory       |   1.23812866 |   1.42373657 |
+----------------------------+--------------+--------------+
10 rows in set (0,00 sec)

使用 sys 模式

sys模式具有视图,允许您以更好的方式获取内存统计信息。它们还支持按hostuserthreadglobal进行聚合。视图memory_global_total包含一个单一值,显示了被检测内存的总量:

mysql> SELECT * FROM sys.memory_global_total;
+-----------------+
| total_allocated |
+-----------------+
| 441.84 MiB      |
+-----------------+
1 row in set (0,09 sec)

聚合视图将字节转换为需要的千字节、兆字节和千兆字节。视图memory_by_thread_by_current_bytes有一个user列,可能取以下值之一:

NAME@HOST

常规用户账户,比如sveta@oreilly.com

系统用户,比如sql/maininnodb/*

此类“用户名”的数据来自threads表,当您需要了解特定线程在做什么时非常方便。

视图memory_by_thread_by_current_bytes中的行按照当前分配的内存量降序排序,因此您将轻松找到占用大部分内存的线程:

mysql> SELECT thread_id tid, user, 
    -> current_allocated ca, total_allocated 
    -> FROM sys.memory_by_thread_by_current_bytes LIMIT 9;
+-----+----------------------------+------------+-----------------+
| tid | user                       | ca         | total_allocated |
+-----+----------------------------+------------+-----------------+
|  52 | sveta@localhost            | 1.36 MiB   | 10.18 MiB       |
|   1 | sql/main                   | 1.02 MiB   | 4.95 MiB        |
|  33 | innodb/clone_gtid_thread   | 525.36 KiB | 24.04 MiB       |
|  44 | sql/event_scheduler        | 145.72 KiB | 4.23 MiB        |
|  43 | sql/slave_sql              | 48.74 KiB  | 142.46 KiB      |
|  42 | sql/slave_io               | 20.03 KiB  | 232.23 KiB      |
|  48 | sql/compress_gtid_table    | 13.91 KiB  | 17.06 KiB       |
|  25 | innodb/fts_optimize_thread | 1.92 KiB   | 2.00 KiB        |
|  34 | innodb/srv_purge_thread    | 1.56 KiB   | 1.64 KiB        |
+-----+----------------------------+------------+-----------------+
9 rows in set (0,03 sec)

上面的示例是在笔记本电脑上进行的;因此,数字并不描述生产服务器的情况。仍然清楚的是,本地连接使用了大部分内存,其次是主服务器进程。

当你需要找到占用最多内存的用户线程时,内存工具非常方便。在下面的示例中,一个用户连接分配了 36 GB 的 RAM,即使在现代高内存系统中也相当巨大:

mysql> SELECT * FROM sys.memory_by_thread_by_current_bytes
    -> ORDER BY current_allocated desc\G
*************************** 1\. row ***************************
 thread_id: 152
 user: lj@127.0.0.1
 current_count_used: 325
 current_allocated: 36.00 GiB
 current_avg_alloc: 113.43 MiB
 current_max_alloc: 36.00 GiB
 total_allocated: 37.95 GiB
...

检查变量

Performance Schema 将变量检测提升到一个新水平。它为以下内容提供了检测:

  • 服务器变量
  • 全局
  • 会话,适用于所有当前打开的会话
  • 来源,所有当前变量值的来源
  • 状态变量
  • 全局
  • 会话,适用于所有当前打开的会话
  • 按照聚合
  • 主机
  • 用户
  • 账户
  • 线程
  • 用户变量
警告

在 5.7 版本之前,服务器和状态变量在information_schema中被检测。这种检测是有限的:它只允许跟踪全局和当前会话值。其他会话中的变量和状态信息,以及用户变量的信息是不可访问的。然而,出于向后兼容性的原因,MySQL 5.7 使用information_schema来跟踪变量。要启用对变量的performance_schema支持,您需要将配置变量show_compatibility_56设置为0。这个要求,以及information_schema中的变量表,在 8.0 版本中不再存在。

全局变量值存储在表global_variables中。当前会话的会话变量存储在表session_variables中。这两个表只有两列,列名自明:VARIABLE_NAMEVARIABLE_VALUE

variables_by_thread表有一个额外的列,THREAD_ID,指示变量所属的线程。这使您可以找到将会话变量值设置为与默认配置不同的线程。

在下面的示例中,具有THREAD_ID=84的线程将变量tx_isolation设置为SERIALIZABLE,这可能导致事务获取的锁比使用默认级别时更多:

mysql> SELECT * FROM variables_by_thread 
    -> WHERE VARIABLE_NAME='tx_isolation';
+-----------+---------------+-----------------+
| THREAD_ID | VARIABLE_NAME |  VARIABLE_VALUE |
+-----------+---------------+-----------------+
|        71 |  tx_isolation | REPEATABLE-READ |
|        83 |  tx_isolation | REPEATABLE-READ |
|        84 |  tx_isolation | SERIALIZABLE    |
+-----------+---------------+-----------------+
3 rows in set, 3 warnings (0.00 sec)

下面的示例找到所有具有与当前活动会话不同的会话变量值的线程:

mysql> SELECT vt2.THREAD_ID AS TID, vt2.VARIABLE_NAME, 
    -> vt1.VARIABLE_VALUE AS MY_VALUE, 
    -> vt2.VARIABLE_VALUE AS OTHER_VALUE 
    -> FROM performance_schema.variables_by_thread vt1 
    -> JOIN performance_schema.threads t USING(THREAD_ID) 
    -> JOIN performance_schema.variables_by_thread vt2 
    -> USING(VARIABLE_NAME) 
    -> WHERE vt1.VARIABLE_VALUE != vt2.VARIABLE_VALUE 
    -> AND t.PROCESSLIST_ID=@@pseudo_thread_id;
+-----+--------------------+-------------------+--------------------+
| TID | VARIABLE_NAME      | MY_VALUE          | OTHER_VALUE        |
+-----+--------------------+-------------------+--------------------+
|  42 | max_allowed_packet | 67108864          | 1073741824         |
|  42 | pseudo_thread_id   | 22715             | 5                  |
|  42 | timestamp          | 1626650242.678049 | 1626567255.695062  |
|  43 | gtid_next          | AUTOMATIC         | NOT_YET_DETERMINED |
|  43 | pseudo_thread_id   | 22715             | 6                  |
|  43 | timestamp          | 1626650242.678049 | 1626567255.707031  |
+-----+--------------------+-------------------+--------------------+
6 rows in set (0,01 sec)

全局和当前会话状态值分别存储在表global_statussession_status中。它们也只有两列:VARIABLE_NAMEVARIABLE_VALUE

状态变量可以按用户帐户、主机、用户和线程进行聚合。在我看来,最有趣的聚合是按线程进行的,因为它可以快速识别哪个连接在服务器上造成了大部分资源压力。例如,以下代码片段清楚地显示了THREAD_ID=83的连接正在进行大部分写操作:

mysql> SELECT * FROM status_by_thread 
    -> WHERE VARIABLE_NAME='Handler_write';
+-----------+---------------+----------------+
| THREAD_ID | VARIABLE_NAME | VARIABLE_VALUE |
+-----------+---------------+----------------+
|        71 | Handler_write | 94             |
|        83 | Handler_write | 4777777777     | -- Most writes
|        84 | Handler_write | 101            |
+-----------+---------------+----------------+
3 rows in set (0.00 sec)

用户定义变量是通过SET @my_var = 'foo'创建的,并在表user_variables_by_thread中进行跟踪:

mysql> SELECT * FROM user_variables_by_thread;
+-----------+---------------+----------------+
| THREAD_ID | VARIABLE_NAME | VARIABLE_VALUE |
+-----------+---------------+----------------+
|        71 | baz           | boo            |
|        84 | foo           | bar            |
+-----------+---------------+----------------+
2 rows in set (0.00 sec)

当您需要找出内存消耗的来源时,此工具非常有用,因为每个变量都需要字节来保存其值。您还可以使用此信息解决与持久��接、使用用户定义变量相关的棘手问题。最后但同样重要的是,此表是唯一的方法来查找您在自己会话中定义的变量。

variables_info不包含任何变量值。相反,它包含有关服务器变量来源以及其他文档的信息,例如变量的默认最小值和最大值。SET_TIME列包含最新变量更改的时间戳。SET_HOSTSET_USER列标识设置变量的用户帐户。例如,要查找自服务器启动以来动态更改的所有变量,请运行:

mysql> SELECT * FROM performance_schema.variables_info 
    -> WHERE VARIABLE_SOURCE = 'DYNAMIC'\G
*************************** 1\. row ***************************
 VARIABLE_NAME: foreign_key_checks
 VARIABLE_SOURCE: DYNAMIC
 VARIABLE_PATH:
    MIN_VALUE: 0
    MAX_VALUE: 0
    SET_TIME: 2021-07-18 03:14:15.560745
    SET_USER: NULL
    SET_HOST: NULL
*************************** 2\. row ***************************
 VARIABLE_NAME: sort_buffer_size
 VARIABLE_SOURCE: DYNAMIC
 VARIABLE_PATH:
    MIN_VALUE: 32768
    MAX_VALUE: 18446744073709551615
    SET_TIME: 2021-07-19 02:37:11.948190
    SET_USER: sveta
    SET_HOST: localhost
2 rows in set (0,00 sec)

可能的VARIABLE_SOURCE值包括:

COMMAND_LINE

在命令行上设置的变量

COMPILED

编译默认值

PERSISTED

从特定服务器的mysqld-auto.cnf选项文件设置

也有许多变量选项,设置在不同的选项文件中。我不会讨论它们全部:它们要么是自描述的,要么可以在用户参考手册中轻松查找。细节的数量也随着版本的增加而增加。

检查最频繁的错误

除了特定的错误信息,performance_schema还提供摘要表,通过用户、主机、帐户、线程以及全局按错误编号聚合错误。所有聚合表的结构与events_errors_summary_global_by_error表中使用的结构类似:

mysql> USE performance_schema;
mysql> SHOW CREATE TABLE events_errors_summary_global_by_error\G
*************************** 1\. row ***************************
    Table: events_errors_summary_global_by_error
Create Table: CREATE TABLE `events_errors_summary_global_by_error` (
 `ERROR_NUMBER` int DEFAULT NULL,
 `ERROR_NAME` varchar(64) DEFAULT NULL,
 `SQL_STATE` varchar(5) DEFAULT NULL,
 `SUM_ERROR_RAISED` bigint unsigned NOT NULL,
 `SUM_ERROR_HANDLED` bigint unsigned NOT NULL,
 `FIRST_SEEN` timestamp NULL DEFAULT '0000-00-00 00:00:00',
 `LAST_SEEN` timestamp NULL DEFAULT '0000-00-00 00:00:00',
 UNIQUE KEY `ERROR_NUMBER` (`ERROR_NUMBER`)
) ENGINE=PERFORMANCE_SCHEMA DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0,00 sec)

ERROR_NUMBERERROR_NAMESQL_STATE标识错误。SUM_ERROR_RAISED是错误被引发的次数。SUM_ERROR_HANDLED是错误被处理的次数。FIRST_SEENLAST_SEEN是错误首次和最后出现的时间戳。

特定的聚合表具有额外的列。因此,表events_errors_summary_by_thread_by_error有一个名为THREAD_ID的列,用于标识引发错误的线程,表events_errors_summary_by_host_by_error有一个名为HOST的列,依此类推。

例如,要查找所有运行导致错误超过 10 次的语句的帐户,请运行:

mysql> SELECT * FROM 
    -> performance_schema.events_errors_summary_by_account_by_error 
    -> WHERE SUM_ERROR_RAISED > 10 AND USER IS NOT NULL 
    -> ORDER BY SUM_ERROR_RAISED DESC\G
*************************** 1\. row ***************************
    USER: sveta
    HOST: localhost
    ERROR_NUMBER: 3554
    ERROR_NAME: ER_NO_SYSTEM_TABLE_ACCESS
    SQL_STATE: HY000
    SUM_ERROR_RAISED: 60
    SUM_ERROR_HANDLED: 0
    FIRST_SEEN: 2021-07-18 03:14:59
    LAST_SEEN: 2021-07-19 02:50:13
1 row in set (0,01 sec)

错误摘要表对于查找哪些用户帐户、主机、用户或线程发送了最多错误查询并执行操作可能很有用。它们还可以帮助处理像ER_DEPRECATED_UTF8_ALIAS这样的错误,这可能表明一些经常使用的查询是为以前的 MySQL 版本编写的,需要更新。

检查性能模式本身

您可以使用与自己模式相同的工具和消费者检查性能模式本身。只需注意,默认情况下,如果将performance_schema设置为默认数据库,则不会跟踪对其的查询。如果需要检查对performance_schema的查询,首先需要更新setup_actors表。

一旦更新了setup_actors表,所有工具都可以使用。例如,要查找在performance_schema中分配了大部分内存的前 10 个消费者,请运行:

mysql> SELECT SUBSTRING_INDEX(EVENT_NAME, '/', -1) AS EVENT,
    -> CURRENT_NUMBER_OF_BYTES_USED/1024/1024 AS CURRENT_MB, 
    -> HIGH_NUMBER_OF_BYTES_USED/1024/1024 AS HIGH_MB 
    -> FROM performance_schema.memory_summary_global_by_event_name 
    -> WHERE EVENT_NAME LIKE 'memory/performance_schema/%'
    -> ORDER BY CURRENT_NUMBER_OF_BYTES_USED DESC LIMIT 10;
+----------------------------------------+-------------+-------------+
| EVENT                                  | CURRENT_MB  | HIGH_MB     |
+----------------------------------------+-------------+-------------+
| events_statements_summary_by_digest    | 39.67285156 | 39.67285156 |
| events_statements_history_long         | 13.88549805 | 13.88549805 |
| events_errors_summary_by_thread_by_... | 11.81640625 | 11.81640625 |
| events_statements_summary_by_thread... |  9.79296875 |  9.79296875 |
| events_statements_history_long.dige... |  9.76562500 |  9.76562500 |
| events_statements_summary_by_digest... |  9.76562500 |  9.76562500 |
| events_statements_history_long.sql_... |  9.76562500 |  9.76562500 |
| memory_summary_by_thread_by_event_name |  7.91015625 |  7.91015625 |
| events_errors_summary_by_host_by_error |  5.90820313 |  5.90820313 |
| events_errors_summary_by_account_by... |  5.90820313 |  5.90820313 |
+----------------------------------------+-------------+-------------+
10 rows in set (0,00 sec)

或使用sys模式:

mysql> SELECT SUBSTRING_INDEX(event_name, '/', -1), current_alloc 
    -> FROM sys.memory_global_by_current_bytes 
    -> WHERE event_name LIKE 'memory/performance_schema/%' LIMIT 10;
+---------------------------------------------------+---------------+
| SUBSTRING_INDEX(event_name, '/', -1)              | current_alloc |
+---------------------------------------------------+---------------+
| events_statements_summary_by_digest               | 39.67 MiB     |
| events_statements_history_long                    | 13.89 MiB     |
| events_errors_summary_by_thread_by_error          | 11.82 MiB     |
| events_statements_summary_by_thread_by_event_name | 9.79 MiB      |
| events_statements_history_long.digest_text        | 9.77 MiB      |
| events_statements_summary_by_digest.digest_text   | 9.77 MiB      |
| events_statements_history_long.sql_text           | 9.77 MiB      |
| memory_summary_by_thread_by_event_name            | 7.91 MiB      |
| events_errors_summary_by_host_by_error            | 5.91 MiB      |
| events_errors_summary_by_account_by_error         | 5.91 MiB      |
+---------------------------------------------------+---------------+
10 rows in set (0,00 sec)

performance_schema还支持SHOW ENGINE PERFORMANCE_SCHEMA STATUS语句:

mysql> SHOW ENGINE PERFORMANCE_SCHEMA STATUS\G
*************************** 1\. row ***************************
 Type: performance_schema
 Name: events_waits_current.size
 Status: 176
*************************** 2\. row ***************************
 Type: performance_schema
 Name: events_waits_current.count
 Status: 1536
*************************** 3\. row ***************************
 Type: performance_schema
 Name: events_waits_history.size
 Status: 176
*************************** 4\. row ***************************
 Type: performance_schema
 Name: events_waits_history.count
 Status: 2560
*************************** 244\. row ***************************
 Type: performance_schema
 Name: (pfs_buffer_scalable_container).count
 Status: 17
*************************** 245\. row ***************************
 Type: performance_schema
 Name: (pfs_buffer_scalable_container).memory
 Status: 1904
*************************** 246\. row ***************************
 Type: performance_schema
 Name: (max_global_server_errors).count
 Status: 4890
*************************** 247\. row ***************************
 Type: performance_schema
 Name: (max_session_server_errors).count
 Status: 1512
*************************** 248\. row ***************************
 Type: performance_schema
 Name: performance_schema.memory
 Status: 218456400
248 rows in set (0,00 sec)

在其输出中,您将找到诸如存储在消费者中的特定事件数量或特定指标的最大值等细节。最后一行包含性能模式当前占用的字节数。

总结

性能模式是一个经常受到批评的功能。MySQL 的早期版本实现不够优化,导致资源消耗较高。通常建议只需关闭它。

它也被认为难以理解。启用一个仪器只是在服务器中启用一个额外的代码片段,记录数据并将其提交给消费者。消费者只是存储在内存中的表,您需要使用标准 SQL 向表提出正确的问题,以找到您要查找的内容。了解性能模式如何管理自己的内存将帮助您意识到 MySQL 并非内存泄漏;它只是将消费者数据保留在内存中,并且只在重新启动时释放该内存。

我在这里的建议很简单:您应该保持性能模式启用,动态启用仪器和消费者,以帮助您解决可能存在的任何问题——查询性能、锁定、磁盘 I/O、错误等。您还应该利用sys模式作为解决最常见问题的捷径。这样做将为您提供一种直接从 MySQL 内部测量性能的可访问方式。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6天前
|
前端开发 JavaScript 安全
JavaScript 权威指南第七版(GPT 重译)(七)(4)
JavaScript 权威指南第七版(GPT 重译)(七)
29 0
|
6天前
|
前端开发 JavaScript 算法
JavaScript 权威指南第七版(GPT 重译)(七)(3)
JavaScript 权威指南第七版(GPT 重译)(七)
39 0
|
6天前
|
前端开发 JavaScript Unix
JavaScript 权威指南第七版(GPT 重译)(七)(2)
JavaScript 权威指南第七版(GPT 重译)(七)
43 0
|
6天前
|
前端开发 JavaScript 算法
JavaScript 权威指南第七版(GPT 重译)(七)(1)
JavaScript 权威指南第七版(GPT 重译)(七)
69 0
|
6天前
|
存储 前端开发 JavaScript
JavaScript 权威指南第七版(GPT 重译)(六)(4)
JavaScript 权威指南第七版(GPT 重译)(六)
126 3
JavaScript 权威指南第七版(GPT 重译)(六)(4)
|
6天前
|
前端开发 JavaScript API
JavaScript 权威指南第七版(GPT 重译)(六)(3)
JavaScript 权威指南第七版(GPT 重译)(六)
68 4
|
6天前
|
XML 前端开发 JavaScript
JavaScript 权威指南第七版(GPT 重译)(六)(2)
JavaScript 权威指南第七版(GPT 重译)(六)
69 4
JavaScript 权威指南第七版(GPT 重译)(六)(2)
|
6天前
|
前端开发 JavaScript 安全
JavaScript 权威指南第七版(GPT 重译)(六)(1)
JavaScript 权威指南第七版(GPT 重译)(六)
34 3
JavaScript 权威指南第七版(GPT 重译)(六)(1)
|
6天前
|
存储 前端开发 JavaScript
JavaScript 权威指南第七版(GPT 重译)(五)(4)
JavaScript 权威指南第七版(GPT 重译)(五)
43 9
|
6天前
|
前端开发 JavaScript 程序员
JavaScript 权威指南第七版(GPT 重译)(五)(3)
JavaScript 权威指南第七版(GPT 重译)(五)
40 8