华为云用户手册

  • 原因分析 查看 GaussDB (for MySQL)的错误日志,观察是否有如下信息:connection xxx is established slowly。示例: 有上述日志,说明存在某些连接超过一定时间仍未被MySQL处理,客户端的超时时间大于该时间,就会报错。 进一步查看线程池配置(默认开启),可以在控制台查看。 可以看出,threadpool_size为1,threadpool_stall_limit为500ms,threadpool_oversubscribe为3,线程池处理连接等待的时间主要与上述3个参数相关: 当线程池所有线程在忙碌,线程池中的调度线程会每隔500ms(threadpool_stall_limit)创建一个新线程,所以此时,每个线程组平均每500ms才能处理一个新的连接。如果队列太长,很可能导致客户端超时; 所有线程都在忙碌是指,工作线程达到线程池总线程数,在大量建立连接时,总线程数计算方法:threadpool_size*(threadpool_oversubscribe+1))
  • 解决方案 对于存在大量新建连接,建议调大threadpool_oversubscribe增加线程总数。 减少线程重复创建与销毁部分的开销,提高性能,同时它也限制了MySQL的runing线程数,关键时刻可以保护系统,防止雪崩。 正常情况下,线程池适用于大量短连接的场景,如果客户是长连接,并且连接数量不多(客户端使用了连接池等情况),线程池的作用不大,此时调整threadpool_size和threadpool_oversubscribe两个参数扩大线程总数,或者直接关闭线程池。
  • 解决方案 ERROR 2013是MySQL常见错误,一般为配置错误导致。 “wait_timeout”:服务器关闭非交互连接之前等待活动的秒数。 “interactive_timeout”:服务器关闭交互连接之前等待活动的秒数。 查看实例状态是否处于正常状态。 经查看实例状态正常,继续排查其他问题。 查看错误日志。 使用MySQL命令行客户端连接数据库,执行status命令,确认数据库实例是否频繁重启。 Uptime代表实例的运行时间,从排查结果可知,数据库并没有频繁重启,因而,客户端连接被断开,不是因数据库重启引起的。 查看“wait_timeout”和“interactive_timeout”参数设置,MySQL会自动断开超时的空连接。 您可根据实际应用需求量,修改“wait_timeout”和“interactive_timeout”参数值,无需重启实例。 恢复结果确认,等到10分钟左右,再次执行show databases命令,确认连接是否正常。 如图所示,说明连接正常。
  • mysqldump选项解析 表1 配置项说明 选项名称 说明 add-drop-table 每个数据表创建之前添加drop数据表语句。 events,E 导出事件。 routines,R 存储过程以及自定义函数。 flush-logs 开始导出之前刷新日志。 no-create-db,n 只导出数据,而不添加CREATE DATABASE语句。 add-drop-database 创建数据库之前添加drop数据库语句。 no-create-info,t 只导出数据,而不添加CREATE TABLE语句。 no-data,d 不导出任何数据,只导出数据库表结构。 set-gtid-purged=OFF 不导出gtid相关语句。 hex-blob 使用十六进制格式导出二进制字符串字段。
  • 场景描述 在搭建canal环境,使用指定用户从GaussDB(for MySQL)获取Binlog时,启动canal经常会报如下错误:'show master status' has an error! Access denied: you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation 完整报错信息如下: 2021-01-10 23:58:32.964 [destination = evoicedc , address = /dbus-mysql:3306 , EventParser] ERROR com.alibaba.ot ter.canal.common.alarm.LogAlarmHandler - destination:evoicedc[com.alibaba.otter.canal.parse.exception.CanalParseEx ception: command : 'show master status' has an error! Caused by: java.io.IOException: ErrorPacket [errorNumber=1227, fieldCount=-1, message=Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation, sqlState=42000, sqlStateMarker=#] with command: show master status at com.alibaba.otter.canal.parse.driver.mysql.MysqlQueryExecutor.query(MysqlQueryExecutor.java:61)
  • 原因分析 MySQL内部在执行复杂SQL时,会借助临时表进行分组(group by)、排序(order by)、去重(distinct)、Union等操作,当内存空间不够时,便会使用磁盘空间。 排查思路: 因为其他只读节点磁盘占用空间正常,且是偶尔出现,说明该实例磁盘占用高,与承载的业务相关。 获取该实例的慢日志,分析磁盘占用高期间,是否有对应的慢SQL。 如果有慢SQL,执行explain [慢SQL语句],分析相应慢SQL语句。 观察explain语句输出的extra列,是否有using temporary、using filesort,如果有,说明该语句用到了临时表或临时文件,数据量大的情况下,会导致磁盘占用高。
  • 原因分析 同一条SQL语句在数据库中执行第一次和第二次可能会性能差异巨大,这是由数据库的buffer_pool机制决定的: 第一次执行时,数据在磁盘上,称之为冷数据,读取需要一定的耗时。 读取完,数据会被存放于内存的buffer_pool中,称为热数据,读取迅速;对于热数据的访问速度极大的超过冷数据,所以当数据是热数据时,SQL语句的执行速度会远快于冷数据。 该场景中,源端数据库中常用的数据一般是热数据,所以访问时速度极快。当数据迁移到云上GaussDB(for MySQL)时,第一次执行同样的SQL语句,很可能是冷数据,就会访问较慢,但再次访问速度就会得到提升。
  • 场景描述 canal解析Binlog出现错误,导致拉取Binlog中断,错误信息如下: com.alibaba.otter.canal.parse.exception.CanalParseException: java.lang.NumberFormatException:- Caused by: java.lang.NumberFormatException: - at com.alibaba.fastsql.sql.parser.Lexer.integerValue(Lexer.java:2454)
  • 原因分析 检查GaussDB(for MySQL)的参数“binlog_rows_query_log_events”的值是否设置为1或ON。 目前canal只能支持ROW格式的Binlog增量订阅。 当GaussDB(for MySQL)的参数“binlog_rows_query_log_events”的值设置为1或ON时,会在Binlog中产生Rows_query类型的event,此类event非ROW格式,一些场景下,会导致canal出现blank topic问题,引发Binlog解析失败。
  • 原因分析 经过排查,是因为参数“sql_mode”设置了NO_FIELD_OPTIONS属性。 sql_mode相关属性介绍: NO_FIELD_OPTIONS:不要在SHOW CREATE TABLE的输出中打印MySQL专用列选项。 NO_KEY_OPTIONS:不要在SHOW CREATE TABLE的输出中打印MySQL专用索引选项。 NO_TABLE_OPTIONS:不要在SHOW CREATE TABLE的输出中打印MySQL专用表选项(例如ENGINE)。
  • 场景1 慢查询导致CPU升高 问题原因:大量慢SQL导致实例CPU升高,需要优化相应的慢SQL。 排查思路: 查看CPU使用率和慢日志个数统计监控指标。 如果慢日志个数很多,且与CPU曲线吻合,可以确定是慢SQL导致CPU升高。 如果慢日志个数不多,但与CPU使用率基本一致,进一步查看行读取速率指标是否与CPU曲线吻合。 如果吻合,说明是少量慢SQL访问大量行数据导致CPU升高:由于这些慢SQL查询执行效率低,为获得预期的结果需要访问大量的数据导致平均IO高,因此在QPS并不高的情况下(例如网站访问量不大),也会导致实例的CPU使用率偏高。 解决方案: 根据CPU使用率过高的时间点,查看对应时间段的慢日志信息。 重点关注扫描行数、返回结果行数超过百万级别的慢查询,以及锁等待时间长的慢查询。 慢查询用户可自行分析,或使用数据管理服务(DAS)的SQL诊断工具对慢查询语句进行诊断。 使用数据库代理+只读节点架构,实现读写分离。只读节点专门负责查询,减轻主库压力,提升数据库吞吐能力,详见读写分离简介。 通过分析数据库执行中的会话来定位执行效率低的SQL。 连接数据库。 执行show full processlist;。 分析执行时间长、运行状态为Sending data、Copying to tmp table、Copying to tmp table on disk、Sorting result、Using filesort的会话,均可能存在性能问题,通过会话来分析其正在执行的SQL。
  • 场景2 连接和QPS升高导致CPU上升 问题原因:业务请求增高导致实例CPU升高,需要从业务侧分析请求变化的原因。 排查思路: 查看QPS、当前活跃连接数、数据库总连接数、CPU使用率监控指标是否吻合。 QPS的含义是每秒查询数,QPS和当前活跃连接数同时上升,且QPS和CPU使用率曲线变化吻合,可以确定是业务请求增高导致CPU上升,如下图: 该场景下,SQL语句一般比较简单,执行效率也高,数据库侧优化余地小,需要从业务源头优化。 解决方案: 单纯的QPS高导致CPU使用率过高,往往出现在实例规格较小的情况下,建议升级实例CPU规格。 优化慢查询,优化方法参照场景1 慢查询导致CPU升高的解决方案。若优化慢查询后效果不明显,建议升级实例CPU规格。 对于数据量大的表,建议通过分库分表减小单次查询访问的数据量。 使用数据库代理+只读节点架构,实现读写分离。只读节点专门负责查询,减轻主库压力,提升数据库吞吐能力,详见读写分离简介。
  • 场景描述 14点~15点之间数据库出现大量行锁冲突,内核中大量update/insert会话在等待行锁释放,导致CPU使用率达到70%左右,数据库操作变慢。 查看 CES 指标行锁等待个数、MDL锁数量,下图仅供参考: 发生死锁的表: ********* 1. row *********Table: table_test Create Table: CREATE TABLE table_test(...CONSTRAINT act_fk_exe_parent FOREIGN KEY (parent_id_) REFERENCES act_ru_execution (id_) ON DELETE CASCADE,CONSTRAINT act_fk_exe_procdef FOREIGN KEY (proc_def_id_) REFERENCES act_re_procdef (id_),CONSTRAINT act_fk_exe_procinst FOREIGN KEY (proc_inst_id_) REFERENCES act_ru_execution (id_) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT act_fk_exe_super FOREIGN KEY (super_exec_) REFERENCES act_ru_execution (id_) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
  • 解决方案 如果数据变化较多,表中实际数据量远小于自增主键的容量,则可以考虑将该表的数据全量导入新表,删除原表,然后rename将新表名改回原表名。(使用数据导入导出的方法有多种实现方法,此处仅举其中一种例子) 创建表auto_test5_tmp。 create table auto_test5_tmp(id tinyint not null AUTO_INCREMENT, name varchar(8), PRIMARY KEY (`id`)); Query OK, 0 rows affected (0.07 sec) 插入数据。 insert into auto_test5_tmp select 0,name from auto_test5; Query OK, 6 rows affected (0.01 sec) Records: 6 Duplicates: 0 Warnings: 0 查询表数据。 select * from auto_test5_tmp; +----+------+ | id | name | +----+------+ | 1 | A | | 2 | B | | 3 | C | | 4 | X | | 5 | Y | | 6 | Z | +----+------+ 删除表。 drop table auto_test5; 重命名。 rename table auto_test5_tmp to auto_test5;Query OK, 0 rows affected (0.12 sec) 如果自增主键的取值范围不够,则修改自增主键的字段类型。 alter table auto_test6 modify column id int NOT NULL AUTO_INCREMENT; Query OK, 6 rows affected (0.15 sec) Records: 6 Duplicates: 0 Warnings: 0
  • 使用mysqlbinlog工具获取binlog 本文以从弹性 云服务器ECS 上拉取为例,其他环境下方法类似。 在E CS 上安装MySQL客户端,详情请参考安装MySQL客户端。 GaussDB(for MySQL)兼容社区MySQL 8.0及以上版本,请勿安装8.0以下版本的版本的客户端。 执行命令,下载binlog文件。 mysqlbinlog -hxxx -uxxx -Pxxx -pxxx binlog.xxxx --read-from-remote-server mysqlbinlog的常用参数: -h:数据库host。 -u:用户名。 -P:端口号。 -p:密码。 --start-position:表示从指定的起始位置开始解析。 --start-datetime:表示从指定的时间开始解析。 --stop-position:表示解析到指定的位置。 --stop-datetime:表示解析到指定的时间。 --skip-gtids:跳过打印gtid_log_event。 --short-form:表示只显示statements。 --result-file:将binlog解析生成sql文件。 --read-from-remote-server:远程下载binlog(用于mysqlbinlog与数据库服务端不再同一台机器的情况)。 父主题: 备份恢复
  • 场景案例 假定max_allowed_packet参数大小为1073741824。 创建表。 CREATE TABLE IF NOT EXISTS zstest1 ( id int PRIMARY KEY not null, c_longtext LONGTEXT ); 向表中插入数据。 insert into zstest1 values(1, repeat('a', 1073741800)); insert into zstest1 values(2, repeat('a', 1073741800)); insert into zstest1 values(3, repeat('a', 1073741800)); insert into zstest1 values(4, repeat('a', 1073741800)); insert into zstest1 values(5, repeat('a', 1073741800)); insert into zstest1 values(6, repeat('a', 1073741800)); insert into zstest1 values(7, repeat('a', 1073741800)); insert into zstest1 values(8, repeat('a', 1073741800)); insert into zstest1 values(9, repeat('a', 1073741800)); insert into zstest1 values(10, repeat('a', 1073741800)); 删除数据。 delete from zstest1; 执行查询语句。 select id from zstest1; //执行缓慢
  • Metadata说明 Metadata是Terraform支持的内置元参数,可以在 provider,resource,data块中使用。本章节主要介绍 resource块支持的元参数,主要包括: depends_on:用于指定资源的依赖项 count:用于创建多个相同配置的资源 for_each:用于根据映射、字符串集合创建多个资源 provider:用于选择非默认的 provider lifecycle:用于定制资源的生命周期 父主题: Metadata
  • lifecycle 每个资源实例都具有创建 、更新和销毁三个阶段,在一个资源实例的生命周期过程中都会经历其中的2至3个阶段。通过元参数 lifecycle 可以对资源实例的生命周期过程进行改变,lifecycle 支持以下参数: create_before_destroy 默认情况下,当我们需要改变资源中不支持更新的参数时,Terraform会先销毁已有实例,再使用新配置的参数创建新的对象进行替换。当我们将 create_before_destroy 参数设置为 true 时,Terraform将先创建新的实例,再销毁之前的实例。这个参数可以适用于保持业务连续的场景,由于新旧实例会同时存在,需要提前确认资源实例是否有唯一的名称要求或其他约束。 lifecycle { create_before_destroy = true} prevent_destroy 当我们将 prevent_destroy 参数设置为true时,Terraform将会阻止对此资源的删除操作并返回错误。这个元参数可以作为一种防止因意外操作而重新创建成本较高实例的安全措施,例如数据库实例。如果要删除此资源,需要将这个配置删除后再执行 destroy 操作。 lifecycle { prevent_destroy = true} ignore_changes 默认情况下,Terraform plan/apply 操作将检测云上资源的属性和本地资源块中的差异,如果不一致将会调用更新或者重建操作来匹配配置。我们可以用 ignore_changes 来忽略某些参数不进行更新或重新。ignore_changes 的值可以是属性的相对地址列表,对于 Map 和 List 类型,可以使用索引表示法引用,如 tags["Name"],list[0] 等。 resource "huaweicloud_rds_instance" "myinstance" { ... lifecycle { ignore_changes = [ name, ] }} 此时,Terraform 将会忽略对 name 参数的修改。除了列表之外,我们也可以使用关键字 all 忽略所有属性的更新。 resource "huaweicloud_rds_instance" "myinstance" { ... lifecycle { ignore_changes = all }} 父主题: Metadata
  • depends_on 在同一个 Terraform 配置文件中可以包含多个资源。通过在资源中引用其他资源的属性值,Terraform可以自动推断出资源的依赖关系。然而,某些资源的依赖关系对于Terraform是不可见的,这就需要使用 depends_on 来创建显式依赖。我们可以使用 depends_on 来更改资源的创建顺序或执行顺序,使其在所依赖资源之后处理。depends_on 的表达式是依赖资源的地址列表。例如我们在远程操作一台ECS服务器之前,需要为其绑定EIP或配置NAT规则。 resource "huaweicloud_compute_instance" "myinstance" { ...}resource "huaweicloud_vpc_eip" "myeip" { ...}resource "huaweicloud_compute_eip_associate" "associated" { public_ip = huaweicloud_vpc_eip.myeip.address instance_id = huaweicloud_compute_instance.myinstance.id}resource "null_resource" "provision" { depends_on = [huaweicloud_compute_eip_associate.associated] provisioner "remote-exec" { connection { # 通过公网地址访问 ECS host = huaweicloud_vpc_eip.myeip.address ... } inline = [ ... ] }} 父主题: Metadata
  • 样式约定 Terraform约定了一些惯用的风格样式,以确保不同团队编写的文件和模块的风格一致性。建议用户遵循这些约定,推荐的样式约定如下: 对于每个嵌套级别,缩进两个空格。 当多个单行的参数在同一嵌套级别连续出现时,建议将等号对齐。 name = "myinstance"security_groups = ["default", "internal"] 使用空行分隔块中的逻辑参数组。 当块主体同时包含参数和块时,建议将所有参数放在顶部,嵌套块放在参数的下方并使用空行隔开。 将元参数(meta-arguments) 放在块主体的顶部,并使用空行与其它参数隔开;将元参数块(meta-argument blocks) 放在块主体的最后,并用空行与其他块隔开。 resource "huaweicloud_obs_bucket" "demo" { count = 1 bucket = "bucket_demo" acl = "public-read" tags = { foo = "bar" env = "test" } lifecycle { create_before_destroy = true }} 顶层块之间使用空行将彼此隔开。 建议将相同类型的嵌套块放在一起,不同类型的嵌套块使用空行隔开。
  • terraform apply terraform apply 命令用于执行资源的创建或变更。在操作执行前会进行一次人机交互,用于对资源创建和变更的确认。我们也可以使用 "-auto-approve" 选项跳过人机交互直接执行。 $ terraform applyAn execution plan has been generated and is shown below.Resource actions are indicated with the following symbols:Terraform will perform the following action:...Plan: 1 to add, 0 to change, 1 to destroy.Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: terraform apply 的执行结果会保存在状态文件 (terraform.tfstate) 中,并且会显示定义的输出变量值。 Apply complete! Resources: 1 to add, 0 to change, 1 to destroy.Outputs:vpc_id = df507d37-bce2-4750-8873-f62abb3b085c
  • terraform plan terraform plan 命令用于创建执行前的计划,是 terraform apply 执行前的一个预览方式,可以检查当前的变更是否符合预期。terraform plan 命令将检测云上资源的属性和状态文件是否存在差异,如果不一致,Terraform 会将差异结果显示在命令下方: $ terraform planRefreshing Terraform state in-memory prior to plan...The refreshed state will be used to calculate this plan, but will not bepersisted to local or state storage....Plan: 1 to add, 0 to change, 1 to destroy.... 如果 Terraform 未检测到资源或根模块的更改,则 terraform plan 会输出如下提示: $ terraform plan...No changes. Infrastructure is up-to-date.This means that Terraform did not detect ant differences between yourconfiguration and real physical resources that exist. As a result, noactions need to be performed. 默认情况下,terraform plan 命令首先会从远端更新资源的属性。在管理资源较多的情况下,该操作会耗时较长,我们可以使用 "-refresh=false" 选项来禁止更新。
  • 变量定义优先级 我们可以自由组合使用上述设置变量的方式。对于复合类型的变量,为了提高可读性并避免转义带来的问题,建议使用变量定义文件来设置。如果我们为同一个变量分配了多个值,Terraform 将使用最后一个值进行覆盖。Terraform 根据以下顺序加载变量 (根据顺序,后面的源优于前面的源): 环境变量 terraform.tfvars 或 terraform.tfvars.json 文件 *.auto.tfvars 或 *.auto.tfvars.json 文件 命令行中的 -var 和 -var-file 选项
  • 声明输出变量 按照约定,输出变量通常在名为 variables.tf 的文件中定义。输出变量通过“output”关键字进行声明: output "ecs_address" { value = huaweicloud_compute_instance.myinstance.network[0].fixed_ip_v4 description = "The private IP address of my ECS"}
  • 文件操作函数 表7 文件操作函数 函数名称 函数描述 样例 运行结果 abspath 计算文件的绝对路径 abspath("./hello.txt") /home/demo/test/terraform/hello.txt dirname 计算字符串中包含的路径 dirname("foo/bar/baz.txt") foo/bar basename 计算字符串中的文件名 basename("foo/bar/baz.txt") baz.txt file 读取文件并返回文件内容 file("./hello.txt") Hello, cloud! filebase64 读取文件并返回文件内容的base64编码 filebase64("./hello.txt") SGVsbG8sIGNsb3VkIQ==
  • 哈希和加密函数 表6 哈希和加密函数 函数名称 函数描述 样例 运行结果 sha256 计算字符串的SHA256值(16进制) sha256("Hello, cloud!") 0ad167d1e3ac8e9f4e4f7ba83e92d0e3838177e959858631c770caaed8cc5e3a sha512 计算字符串的SHA512值(16进制) sha512("Hello, cloud!") 6eb6ed9fc4edffaf90e742e7697f6cc7d8548e98aa4d5aa74982e5cdf78359e84a3ae9f226313b2dec765bf1ea4c83922dbfe4a61636d585da44ffbd7e900f56 base64sha256 计算字符串的SHA256值,并转换为base64编码 base64sha256("Hello, cloud!") CtFn0eOsjp9OT3uoPpLQ44OBd+lZhYYxx3DKrtjMXjo= base64sha512 计算字符串的SHA512值,并转换为base64编码 base64sha512("Hello, cloud!") brbtn8Tt/6+Q50LnaX9sx9hUjpiqTVqnSYLlzfeDWehKOunyJjE7Lex2W/HqTIOSLb/kphY21YXaRP+9fpAPVg== md5 计算MD5值 md5("hello world") 5eb63bbbe01eeed093cb22bb8f5acdc3 base64sha512("Hello, cloud!")不等于base64encode(sha512("Hello, cloud!")),因为sha512计算的十六进制值结果在Terraform中是Unicode编码格式,并没指定UTF-8实现。
  • 编码函数 表5 编码函数 函数名称 函数描述 样例 运行结果 base64encode 将UTF-8字符串转换为base64编码 base64encode("Hello, cloud!") SGVsbG8sIGNsb3VkIQ== base64decode 将base64编码解码为UTF-8字符串(结果非UTF-8格式会报错) base64decode("SGVsbG8sIGNsb3VkIQ==") Hello, cloud! base64gzip 将UTF-8字符串压缩并转换为base64编码 base64gzip("Hello, cloud!") H4sIAAAAAAAA//JIzcnJ11FIzskvTVEEAAAA//8BAAD//wbrhYUNAAAA
  • 类型转化函数 表4 类型转化函数 函数名称 函数描述 样例 运行结果 toset 将列表类型转换为集合类型 toset(["One", "Two", "One"]) ["One", "Two"] tolist 将集合类型转换为列表类型 tolist(["One", "Two", "Three"]) ["One", "Two", "Three"] tonumber 将字符串类型转换为数字类型 tonumber("33") 33 tostring 将数字类型转换为字符串类型 tostring(33) "33"
  • 字符串函数 表1 字符串函数 函数名称 函数描述 样例 运行结果 format 字符串格式化 format("Hello, %s!", "cloud") Hello, cloud! lower 将字符串中的字母转换为小写 lower("HELLO") hello upper 将字符串中的字母转换为大写 upper("hello") HELLO join 使用自定义字符将列表拼接成字符串 join(", ", ["One", "Two", "Three"]) One, Two, Three split 根据分隔符拆分字符串 split(", ", "One, Two, Three") ["One", "Two", "Three"] substr 通过偏移量和长度从给定的字符串中提取一个子串 substr("hello world!", 1, 4) ello replace 把字符串中的str1替换成str2 replace("hello, cloud!", "h", "H") Hello, cloud!
  • 集合函数 表3 集合函数 函数名称 函数描述 样例 运行结果 element 通过下标从列表中检索对应元素值 element(["One", "Two", "Three"], 1) Two index 返回给定值在列表中的索引,如果该值不存在将报错。 index(["a", "b", "c"], "b") 1 lookup 使用给定的键从映射表中检索对应的值。如果给定的键不存在,则返回默认值。 lookup({IT="A", CT="B"}, "IT", "G") lookup({IT="A", CT="B"}, "IE", "G") A G flatten 展开列表中的嵌套元素 flatten([["a", "b"], [], ["c"]]) ["a", "b", "c"] keys 返回map中的所有key keys({a=1, b=2, c=3}) ["a", "b", "c"] length 获取列表、映射或是字符串的长度 length(["One", "Two", "Three"]) length({IT="A", CT="B"}) length("Hello, cloud!") 3 2 13
共100000条