TiDB 快速上手与 SQL 基础

一、搭建实验环境

学习 TiDB 最好的方式就是动手操作。本节介绍如何快速搭建一个本地实验环境。

方式一:TiUP Playground(推荐本地使用)

TiUP 是 TiDB 的包管理和集群管理工具,Playground 是其子命令,用于快速启动本地测试集群。

# 安装 TiUP
curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh

# 使 tiup 命令生效(如果 source 不生效,可手动设置 PATH)
source ~/.bashrc
export PATH="$HOME/.tiup/bin:$PATH"

# 启动一个包含基础组件的测试集群
# 注意:首次启动需下载组件,受网络影响可能需重试。默认包含 1 个 TiFlash 节点,可通过 --tiflash 0 关闭
tiup playground v8.5.0 --db 2 --pd 3 --kv 3

参数说明:

参数 含义
--db 2 启动 2 个 TiDB Server 节点
--pd 3 启动 3 个 PD 节点
--kv 3 启动 3 个 TiKV 节点

启动成功后,终端会输出连接信息:

🎉 TiDB Playground Cluster is started, enjoy!

Connect TiDB:    mysql --comments --host 127.0.0.1 --port 4000 -u root
Connect TiDB:    mysql --comments --host 127.0.0.1 --port 4001 -u root
TiDB Dashboard:  http://127.0.0.1:2379/dashboard
Grafana:         http://127.0.0.1:3000

实操经验

  • 网络不稳定:首次启动需从 https://tiup-mirrors.pingcap.com 下载组件(TiDB、TiKV、PD 等),如果遇到 connection reset by peer 错误,重试即可,通常 1-2 次后成功
  • TiFlash:默认会启动 1 个 TiFlash 节点,如果不需要列存功能或网络不佳,可添加 --tiflash 0 跳过
  • 端口变化:启动多个 TiDB 实例时,端口从 4000 开始递增(4000, 4001, …),连接任意一个即可

方式二:Docker Compose

如果更熟悉 Docker,也可以用 Docker Compose 部署:

# docker-compose.yml
version: '3.8'
services:
  pd:
    image: pingcap/pd:v8.5.0
    command: --name=pd --client-urls=http://0.0.0.0:2379 --peer-urls=http://0.0.0.0:2380 --initial-cluster=pd=http://pd:2380 --advertise-client-urls=http://pd:2379
    ports:
      - "2379:2379"
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:2379/pd/api/v1/health"]
      interval: 5s
      retries: 10
  tikv:
    image: pingcap/tikv:v8.5.0
    command: --addr=0.0.0.0:20160 --status-addr=0.0.0.0:20180 --pd=pd:2379
    depends_on:
      pd:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:20180/metrics"]
      interval: 5s
      retries: 10
  tidb:
    image: pingcap/tidb:v8.5.0
    command: --store=tikv --path=pd:2379
    ports:
      - "4000:4000"
    depends_on:
      tikv:
        condition: service_healthy
# 注意:Docker Compose v2 使用 docker compose(无连字符),旧版使用 docker-compose
docker compose up -d
# 等待 TiKV 就绪后(约 30 秒)再连接
mysql -h 127.0.0.1 -P 4000 -u root

注意:Docker Compose 方式启动较慢,需等待所有组件健康检查通过后再连接。仅推荐用于简单测试,生产环境请使用 TiUP 或 TiDB Operator。


二、连接 TiDB

使用 MySQL 客户端连接

TiDB 兼容 MySQL 协议,因此可以直接用 mysql 命令行工具连接。

安装 MySQL 客户端(如果尚未安装):

# Ubuntu/Debian
sudo apt-get install -y default-mysql-client

# CentOS/RHEL
sudo yum install -y mysql

# macOS
brew install mysql-client

连接命令:

mysql -h 127.0.0.1 -P 4000 -u root

实操经验

  • 必须用 127.0.0.1 而非 localhostlocalhost 会通过 Unix socket 连接,而 TiDB 只监听 TCP,必须指定 -h 127.0.0.1
  • 默认无密码:root 用户默认空密码,直接回车即可
  • 无 sudo 权限时:如果当前用户没有 sudo 权限且系统未预装 mysql 客户端,可考虑通过 Docker 运行:docker run --rm -it mysql:8 mysql -h 127.0.0.1 -P 4000 -u root

连接成功后验证:

-- 查看版本信息
SELECT tidb_version()\G

-- 查看数据库列表
SHOW DATABASES;

-- 当前连接信息
SELECT current_user(), @@version;

输出示例:

+---------------+--------------------+
| current_user()| @@version          |
+---------------+--------------------+
| root@%        | 8.0.11-TiDB-v8.5.0 |
+---------------+--------------------+

注意@@version 返回的是 TiDB 兼容的 MySQL 版本号(8.0.11)加上 TiDB 自身的版本号(v8.5.0),并非 TiDB 版本本身。

使用可视化工具连接

常用工具:

  • DBeaver:免费开源,支持 MySQL 连接即可连接 TiDB
  • Navicat:商业工具,功能完善
  • DataGrip:JetBrains 出品

连接参数:

参数
Host 127.0.0.1
Port 4000
User root
Password 空(默认)
Database 任意

三、SQL 基础操作

3.1 数据库管理

-- 创建数据库
CREATE DATABASE bookshop CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- 查看建库语句
SHOW CREATE DATABASE bookshop;

-- 切换数据库
USE bookshop;

-- 删除数据库
DROP DATABASE IF EXISTS bookshop;

TiDB 支持的字符集和排序规则:

-- 查看所有支持的字符集
SHOW CHARACTER SET;

-- 查看所有支持的排序规则
SHOW COLLATION;

注意SHOW CHARACTER SETSHOW COLLATION 不支持 LIMIT 子句。如需过滤,可在客户端用 grep 或用 information_schema 查询。

3.2 数据类型

TiDB 支持的数据类型与 MySQL 基本一致,以下是常用类型:

数值类型

类型 大小 范围(有符号) 用途
TINYINT 1 字节 -128 ~ 127 布尔值、状态标志
SMALLINT 2 字节 -32768 ~ 32767 小整数
INT 4 字节 -21 亿 ~ 21 亿 一般整数
BIGINT 8 字节 很大 主键 ID
DECIMAL (M, D) 变长 精确小数 金额
FLOAT/DOUBLE 4/8 字节 浮点数 科学计算

字符串类型

类型 说明 用途
VARCHAR (N) 可变长度字符串 姓名、标题等
CHAR (N) 定长字符串 固定长度编码
TEXT 长文本 文章内容
BLOB 二进制大对象 图片、文件

日期时间类型

类型 格式 范围
DATE YYYY-MM-DD 1000-01-01 ~ 9999-12-31
DATETIME YYYY-MM-DD HH:MM: SS 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59
TIMESTAMP YYYY-MM-DD HH:MM: SS 1970-01-01 ~ 2038-01-19
TIME HH:MM: SS -838:59:59 ~ 838:59:59

JSON 类型

TiDB 原生支持 JSON 类型,可以直接存储和查询 JSON 数据:

CREATE TABLE products (
    id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    attributes JSON
);

INSERT INTO products VALUES (1, '笔记本电脑',
    '{"brand": "ThinkPad", "cpu": "i7-13700H", "memory": "32GB", "price": 8999}'
);

-- 查询 JSON 中的特定字段
SELECT id, name, attributes->'$.cpu' AS cpu FROM products;

-- 按 JSON 字段筛选
SELECT * FROM products WHERE attributes->'$.price' > 5000;

3.3 创建表

CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(64) NOT NULL,
    email VARCHAR(128) UNIQUE,
    age INT DEFAULT 0,
    status TINYINT DEFAULT 1 COMMENT '1=正常 0=禁用',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

TiDB 特有的主键类型:

-- AUTO_RANDOM:自动生成不连续的主键(推荐)
-- 比 AUTO_INCREMENT 更适合高并发场景,避免写入热点
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_RANDOM,
    user_id BIGINT NOT NULL,
    amount DECIMAL(10, 2),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 普通 AUTO_INCREMENT
CREATE TABLE logs (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    message TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

AUTO_RANDOM vs AUTO_INCREMENT 的选择

特性 AUTO_INCREMENT AUTO_RANDOM
值的连续性 连续递增 不连续(随机分布)
写入热点 可能产生(数据集中在一个 Region) 避免(数据分散到多个 Region)
适用场景 日志表、顺序写入表 高并发订单表、用户行为表

实操经验:使用 AUTO_RANDOM 时,主键 ID 是大整数(如 3746994889972252673),无法预测具体值。在编写多表关联的测试数据时,不能硬编码子表的父表 ID,而应使用子查询获取实际 ID。详见本文第六节博客练习示例。

3.4 索引创建

-- 主键索引(自动创建)
-- UNIQUE 约束已在建表时自动创建索引
-- ALTER TABLE users ADD UNIQUE INDEX uk_email (email); -- 无需重复添加

-- 普通二级索引
CREATE INDEX idx_user_name ON users(name);

-- 联合索引
CREATE INDEX idx_status_age ON users(status, age);

-- 查看索引
SHOW INDEX FROM users;

注意:建表语句中 email VARCHAR(128) UNIQUE 已自动创建了唯一索引,无需再用 ALTER TABLE ... ADD UNIQUE INDEX 重复创建。

3.5 数据插入

-- 单条插入
INSERT INTO users (id, name, email, age) VALUES (1, '张三', 'zhangsan@example.com', 28);

-- 批量插入(推荐,性能更高)
INSERT INTO users (id, name, email, age) VALUES
    (2, '李四', 'lisi@example.com', 32),
    (3, '王五', 'wangwu@example.com', 25),
    (4, '赵六', 'zhaoliu@example.com', 30);

-- 插入时忽略冲突(遇到唯一键冲突不报错,跳过)
INSERT IGNORE INTO users (id, name, email) VALUES
    (1, '张三(重复)', 'duplicate@example.com');

-- 插入或更新(遇到唯一键冲突则执行 UPDATE)
INSERT INTO users (id, name, email, age) VALUES (1, '张三(更新)', 'zhangsan@example.com', 29)
    ON DUPLICATE KEY UPDATE name = VALUES(name), age = VALUES(age);

批量插入性能对比

-- 方式一:逐条插入(慢)
INSERT INTO users VALUES (1, ...);
INSERT INTO users VALUES (2, ...);
INSERT INTO users VALUES (3, ...);

-- 方式二:批量插入(快,推荐)
INSERT INTO users VALUES (1, ...), (2, ...), (3, ...);

在分布式数据库中,批量插入能显著减少网络往返次数,提升写入吞吐量。

3.6 数据查询

-- 基础查询
SELECT * FROM users WHERE age > 25;

-- 条件查询
SELECT name, email FROM users
    WHERE status = 1 AND age BETWEEN 25 AND 35
    ORDER BY age DESC
    LIMIT 10;

-- 聚合查询
SELECT
    status,
    COUNT(*) AS user_count,
    AVG(age) AS avg_age,
    MIN(age) AS min_age,
    MAX(age) AS max_age
FROM users
GROUP BY status;

-- 分页查询
SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 0;  -- 第1页
SELECT * FROM users ORDER BY id LIMIT 20 OFFSET 20; -- 第2页

3.7 多表 JOIN

-- 创建订单表
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_RANDOM,
    user_id BIGINT NOT NULL,
    product_name VARCHAR(128),
    amount DECIMAL(10, 2),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user_id (user_id)
);

-- 插入订单数据
INSERT INTO orders (user_id, product_name, amount) VALUES
    (1, '机械键盘', 599.00),
    (1, '显示器', 1299.00),
    (2, '无线鼠标', 129.00),
    (3, '键盘手托', 79.00);

-- INNER JOIN:查询用户及其订单
SELECT
    u.name,
    u.email,
    o.product_name,
    o.amount
FROM users u
    INNER JOIN orders o ON u.id = o.user_id
ORDER BY u.id, o.amount DESC;

-- LEFT JOIN:查询所有用户(包括没有订单的)
SELECT
    u.name,
    o.product_name,
    o.amount
FROM users u
    LEFT JOIN orders o ON u.id = o.user_id;

-- 聚合统计:每个用户的订单数量和总金额
SELECT
    u.name,
    COUNT(o.id) AS order_count,
    SUM(o.amount) AS total_amount
FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY total_amount DESC;

3.8 数据更新

-- 更新单条
UPDATE users SET age = 29 WHERE id = 1;

-- 批量更新
UPDATE users SET status = 0 WHERE age < 18;

-- 条件更新
UPDATE orders SET amount = amount * 0.9
    WHERE created_at < '2024-01-01' AND amount > 1000;

3.9 数据删除

-- 删除特定记录
DELETE FROM users WHERE id = 4;

-- 条件删除
DELETE FROM orders WHERE amount < 10;

-- 清空全表(更快,但不能回滚)
TRUNCATE TABLE orders;

四、实用查询技巧

4.1 查看表结构

-- 查看表定义
DESCRIBE users;

-- 查看详细建表语句
SHOW CREATE TABLE users\G

-- 查看表大小信息
SELECT
    table_name,
    table_rows,
    ROUND(data_length / 1024 / 1024, 2) AS data_mb,
    ROUND(index_length / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = 'bookshop';

4.2 查看执行计划

-- EXPLAIN 只显示执行计划(不实际执行)
EXPLAIN SELECT * FROM users WHERE name = '张三';

-- EXPLAIN ANALYZE 显示执行计划和实际执行情况
EXPLAIN ANALYZE SELECT * FROM users WHERE name = '张三';

EXPLAIN ANALYZE 的输出包含实际执行信息,对排查性能问题非常有帮助:

+----------------------+---------+---------+------+----------------------------------+
| id                   | estRows | actRows | task | operator info                    |
+----------------------+---------+---------+------+----------------------------------+
| TableReader_7        | 1.00    | 1       | root | data:TableRangeScan_6            |
| └─TableRangeScan_6   | 1.00    | 1       | cop  | table:users, range:[1,1], ...    |
+----------------------+---------+---------+------+----------------------------------+
  • estRows:优化器估算的行数
  • actRows:实际返回的行数

4.3 查看集群信息

-- 查看集群组件信息
SELECT * FROM information_schema.cluster_info;

-- 查看集群负载
SELECT * FROM information_schema.cluster_load;

-- 查看所有 TiKV 节点
SELECT * FROM information_schema.tikv_store_status;

五、TiDB 特有功能初体验

5.1 全局自增 ID

TiDB 的自增 ID 不保证连续(分布式环境下这很难做到),但保证唯一和递增:

CREATE TABLE seq_test (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    data VARCHAR(50)
);

INSERT INTO seq_test (data) VALUES ('a'), ('b'), ('c');
SELECT * FROM seq_test;
-- id 值可能为 1, 2, 3 也可能为 1, 3001, 6001(段分配机制)

5.2 查看当前时间戳

TiDB 的全局时间戳由 PD 分配,可以在 SQL 中查看:

SELECT @@tidb_current_ts;

5.3 查看系统变量

-- 查看所有 TiDB 特有的系统变量
SHOW VARIABLES LIKE 'tidb_%';

-- 查看事务模式(乐观/悲观)
SHOW VARIABLES LIKE 'tidb_txn_mode';

-- 临时修改(仅当前会话)
SET SESSION tidb_txn_mode = 'pessimistic';

六、实践练习

练习:搭建一个简易博客系统数据库

-- 1. 创建数据库
CREATE DATABASE blog CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
USE blog;

-- 2. 创建用户表
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_RANDOM,
    username VARCHAR(32) NOT NULL UNIQUE,
    email VARCHAR(128) NOT NULL UNIQUE,
    avatar VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 3. 创建文章表
CREATE TABLE articles (
    id BIGINT PRIMARY KEY AUTO_RANDOM,
    user_id BIGINT NOT NULL,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    status TINYINT DEFAULT 0 COMMENT '0=草稿 1=已发布',
    view_count INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_user_status (user_id, status),
    INDEX idx_created (created_at)
);

-- 4. 创建评论表
CREATE TABLE comments (
    id BIGINT PRIMARY KEY AUTO_RANDOM,
    article_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_article (article_id)
);

-- 5. 插入测试数据
INSERT INTO users (username, email) VALUES
    ('alice', 'alice@example.com'),
    ('bob', 'bob@example.com');

-- 注意:articles 表使用 AUTO_RANDOM,主键 ID 由系统自动生成(大整数),
-- 不能硬编码为 1, 2, 3。插入后需查询实际 ID 再插入 comments。
INSERT INTO articles (user_id, title, content, status, view_count) VALUES
    (1, 'TiDB 入门指南', 'TiDB 是一款分布式数据库...', 1, 1024),
    (1, 'SQL 优化技巧', '优化 SQL 的第一步...', 1, 512),
    (2, '我的第一篇博客', 'Hello World!', 0, 0);

-- 查询实际生成的 article ID
SELECT id, user_id, title FROM articles;
-- 输出示例:
-- +---------------------+---------+--------------+
-- | id                  | user_id | title        |
-- +---------------------+---------+--------------+
-- | 3746994889972252673 |       1 | TiDB 入门指南 |
-- | 3746994889972252674 |       1 | SQL 优化技巧  |
-- | 3746994889972252675 |       2 | 我的第一篇博客 |
-- +---------------------+---------+--------------+

-- 使用子查询根据 title 获取实际 ID 来插入评论
INSERT INTO comments (article_id, user_id, content) VALUES
    ((SELECT id FROM articles WHERE title = 'TiDB 入门指南'), 2, '写得很清楚!'),
    ((SELECT id FROM articles WHERE title = 'TiDB 入门指南'), 1, '谢谢支持'),
    ((SELECT id FROM articles WHERE title = 'SQL 优化技巧'), 2, '学到了');

-- 6. 综合查询:获取每篇文章及其作者和评论数
SELECT
    a.title,
    u.username AS author,
    a.view_count,
    COUNT(c.id) AS comment_count
FROM articles a
    JOIN users u ON a.user_id = u.id
    LEFT JOIN comments c ON c.article_id = a.id
WHERE a.status = 1
GROUP BY a.id, a.title, u.username, a.view_count
ORDER BY a.view_count DESC;

-- 输出:
-- +--------------+--------+------------+---------------+
-- | title        | author | view_count | comment_count |
-- +--------------+--------+------------+---------------+
-- | TiDB 入门指南 | alice  | 1024       | 2             |
-- | SQL 优化技巧  | alice  | 512        | 1             |
-- +--------------+--------+------------+---------------+

七、常见错误与解决方案

错误 1:9006 - PD 集群未就绪

ERROR 9006 (HY000): PD cluster failed to respond

原因:PD 节点未正常启动或网络不通。

解决:检查 PD 节点状态,确认网络连接。

错误 2:1062 - 主键冲突

ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

解决:检查插入数据的主键是否已存在,或使用 AUTO_RANDOM 自动生成。

错误 3:写入延迟较高

原因:本地 playground 环境的磁盘 I/O 和网络延迟。

解决:生产环境中确保使用 SSD 和低延迟网络。

实操踩坑记录

以下是在实际搭建环境中遇到的问题及解决方法,供参考。

踩坑 1:source ~/.bashrctiup 命令仍找不到

现象

curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
source ~/.bashrc
tiup --version
# 输出:bash: tiup: 未找到命令

原因:当前 shell 会话可能在 source ~/.bashrc 之前已经加载了 PATH,或者使用了非交互式 shell,~/.bashrc 中的 PATH 变更未生效。

解决:手动 export PATH:

export PATH="$HOME/.tiup/bin:$PATH"
tiup --version
# 1.16.5 v1.16.5

踩坑 2:TiUP 下载组件时网络被重置

现象

Error: Playground bootstrapping failed: fetch /timestamp.json from mirror
failed: download from https://tiup-mirrors.pingcap.com/timestamp.json failed:
read tcp 172.31.2.251:35618->221.15.67.96:443: read: connection reset by peer

原因:TiUP 镜像服务器网络不稳定,偶尔出现连接重置。

解决:直接重试即可。可以先测试网络连通性:

# 测试网络
curl -sL --connect-timeout 15 https://tiup-mirrors.pingcap.com/timestamp.json

# 如果返回 JSON,说明网络正常,重试 playground
tiup playground v8.5.0 --db 2 --pd 3 --kv 3

提示:首次启动需下载 TiDB、TiKV、PD、Prometheus、Grafana 等组件,总下载量约 500 MB,请耐心等待。

踩坑 3:TiFlash 启动失败但集群仍可用

现象

TiFlash 127.0.0.1:3930 failed to start: fetch /6685.tiflash.json ...
connection reset by peer

🎉 TiDB Playground Cluster is started, enjoy!

原因:TiFlash 组件下载失败(同样因网络问题),但不影响核心组件。

解决:TiDB、PD、TiKV 已正常启动,可以正常进行 SQL 实验。如果确实需要 TiFlash(用于列存 / HTAP 场景),可以重试:

# 关闭旧集群(Ctrl+C),重新启动
tiup playground v8.5.0 --db 2 --pd 3 --kv 3 --tiflash 1

踩坑 4:apt-get install 权限不够

现象

apt-get install -y default-mysql-client
# E: 无法打开锁文件 /var/lib/apt/lists/lock - open (13: 权限不够)

解决:需要加 sudo

sudo apt-get install -y default-mysql-client

踩坑 5:SHOW CHARACTER SET 不支持 LIMIT

现象

SHOW CHARACTER SET LIMIT 5;
-- ERROR 1064 (42000): You have an error in your SQL syntax ... near "LIMIT 5"

解决SHOW CHARACTER SETSHOW COLLATION 不支持 LIMIT,直接执行即可:

SHOW CHARACTER SET;
SHOW COLLATION;

踩坑 6:@@version 输出与预期不符

现象

SELECT @@version;
-- 输出:8.0.11-TiDB-v8.5.0(不是 8.5.0)

原因@@version 返回的是 TiDB 兼容的 MySQL 协议版本(固定为 8.0.11)加上 TiDB 自身的版本号。8.0.11 不代表 TiDB 版本是 8.0.11。

正确查看 TiDB 版本的方式

SELECT tidb_version()\G

1 个赞

很不错的指导手册,保存一下

搭建,入门,常用的sql 操作,以及维护,基本上都全了!

分享的很棒,已经收藏

已收藏,写的很详细,非常实用

已收藏,谢谢 分享

已经收藏,感谢分享

写的很好,感谢

写得非常详细,值得学习。

今天又复习了一遍,新技能

感谢分享:+1::+1::+1:

分享的很棒,已经收藏

这个分享比较不错,适合初学者

感谢分享