TiDB 部署及数据同步

Easter79
• 阅读 581

简介

TiDB 是 PingCAP 公司受 Google Spanner / F1 论文启发而设计的开源分布式 HTAP (Hybrid Transactional and Analytical Processing) 数据库, 结合了传统的 RDBMS 和 NoSQL 的最佳特性。TiDB 兼容 MySQL,支持无限的水平扩展,具备强一致性和高可用性。 TiDB 的目标是为 OLTP (Online Transactional Processing) 和 OLAP (Online Analytical Processing) 场景提供一站式的解决方案。

TiDB整体架构

TiDB 部署及数据同步

TiDB 集群主要分为三个组件:

  • TiDB Server

    TiDB Server 负责接收 SQL 请求,处理 SQL 相关的逻辑,并通过 PD 找到存储计算所需数据的 TiKV 地址, 与 TiKV 交互获取数据,最终返回结果。 TiDB Server 是无状态的,其本身并不存储数据,只负责计算,可以无限水平扩展, 可以通过负载均衡组件(如LVS、HAProxy 或 F5)对外提供统一的接入地址。

  • PD Server

    Placement Driver (简称 PD) 是整个集群的管理模块,其主要工作有三个: 一是存储集群的元信息(某个 Key 存储在哪个 TiKV 节点); 二是对 TiKV 集群进行调度和负载均衡(如数据的迁移、Raft group leader 的迁移等);三是分配全局唯一且递增的事务 ID。

    PD 是一个集群,需要部署奇数个节点,一般线上推荐至少部署 3 个节点。

  • TiKV Server

    TiKV Server 负责存储数据,从外部看 TiKV 是一个分布式的提供事务的 Key-Value 存储引擎。存储数据的基本单位是 Region, 每个 Region 负责存储一个 Key Range (从 StartKey 到 EndKey 的左闭右开区间)的数据, 每个 TiKV 节点会负责多个 Region 。TiKV 使用 Raft 协议做复制,保持数据的一致性和容灾。 副本以 Region 为单位进行管理,不同节点上的多个 Region 构成一个 Raft Group,互为副本。 数据在多个 TiKV 之间的负载均衡由 PD 调度,这里也是以 Region 为单位进行调度。

安装与部署

单节点方式快速部署(适用于功能测试,预览)

执行步骤:

# 下载压缩包

wget http://download.pingcap.org/tidb-latest-linux-amd64.tar.gz
wget http://download.pingcap.org/tidb-latest-linux-amd64.sha256

# 检查文件完整性,返回 ok 则正确
sha256sum -c tidb-latest-linux-amd64.sha256

# 解开压缩包
tar -xzf tidb-latest-linux-amd64.tar.gz
cd tidb-latest-linux-amd64

在获取 TiDB 二进制文件包后,我们可以在单机上面,运行和测试 TiDB 集群,请按如下步骤依次启动 PD,TiKV,TiDB。

注意:以下启动各个应用程序组件实例的时候,请选择后台启动,避免前台失效后程序自动退出。

  • 步骤一,启动 PD

    ./bin/pd-server --data-dir=pd
    --log-file=pd.log

  • 步骤二,启动 TiKV

    ./bin/tikv-server --pd="127.0.0.1:2379"
    --data-dir=tikv
    --log-file=tikv.log

  • 步骤三,启动 TiDB

    ./bin/tidb-server --store=tikv
    --path="127.0.0.1:2379"
    --log-file=tidb.log

  • 步骤四,使用 MySQL 客户端连接 TiDB:

    mysql -h 127.0.0.1 -P 4000 -u root -D test

Ansible 部署集群(强烈推荐)

准备机器

测试部署,下列所有机器均由 Virtual Box 生成的虚拟环境,生产环境中请参考官方文档配置。 注意开启虚拟机的 specific IP,eg: config.vm.network "private_network", ip: "192.168.12.10"

  • 部署目标机器若干

    • 建议 4 台及以上,TiKV 至少 3 实例(本教程 3 实例),且与 TiDB、PD 模块不位于同一主机,详见部署建议

    • 推荐安装 CentOS 7.3 及以上版本 Linux 操作系统(本教程使用 ubuntu 16.04),x86_64 架构(amd64), 数据盘请使用 ext4 文件系统,挂载 ext4 文件系统时请添加 nodelalloc 挂载参数

      编辑 /etc/fstab 文件,添加 nodelalloc 挂载参数:

      # vi /etc/fstab
      /dev/nvme0n1 /data1 ext4 defaults,nodelalloc,noatime 0 2
      

      使用以下命令 umount 挂载目录并重新挂载:

      # umount /data1
      # mount -a
      

      通过以下命令确认是否生效:

      # mount -t ext4
      /dev/nvme0n1 on /data1 type ext4 (rw,noatime,nodelalloc,data=ordered)
      
    • 机器之间内网互通,防火墙如 iptables 等请在部署时关闭

    • 机器的时间、时区设置一致,开启 NTP 服务且在正常同步时间

      注: Ubuntu 系统请安装 ntpstat 软件包

      $ ntpstat
      unsynchronised # NTP 服务未正常同步
          
      $ ntpstat
      Unable to talk to NTP daemon. Is it running? # NTP 服务未正常运行
          
      sudo systemctl status ntp.service # 查看 ntp 服务运行状态
      
    • 创建 tidb 普通用户 (本教程使用 vagrant 用户)作为程序运行用户,tidb 用户可以免密码 sudo 到 root 用户

      # useradd tidb
      # passwd tidb
      # visudo
      tidb ALL=(ALL) NOPASSWD: ALL 
      

      本教程中 vagrant用户默认免密 sudo 到 root 用户

  • 部署中控机器一台

    • 中控机可以是部署目标机器中的某一台。

    • 推荐安装 CentOS 7.3 及以上版本 Linux 操作系统(默认包含 Python 2.7,本教程使用 ubuntu 16.04)。

    • 该机器需开放外网访问,用于下载 TiDB 及相关软件安装包。

    • 配置 ssh authorized_key 互信,在中控机上可以使用 tidb 用户(本教程使用 vagrant 用户)免密码 ssh 登录到部署目标机器

      # vagrant 用户
      $ ssh-keygen -t rsa
      Generating public/private rsa key pair.
      Enter file in which to save the key (/home/tidb/.ssh/id_rsa): Created directory '/home/tidb/.ssh'. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/tidb/.ssh/id_rsa. Your public key has been saved in /home/tidb/.ssh/id_rsa.pub. The key fingerprint is: SHA256:eIBykszR1KyECA/h0d7PRKz4fhAeli7IrVphhte7/So tidb@172.16.10.49 The key's randomart image is: +---[RSA 2048]----+ |=+o+.o. | |o=o+o.oo | | .O.=.= | | . B.B + | |o B * B S | | * + * + | | o + . | | o E+ . | |o ..+o. | +----[SHA256]-----+ sudo cat ~/.ssh/id_rsa.pub authorized_keys # 依次执行以下命令,将 192.168.12.10 替换成目标机器的ip,按提示输入部署目标机器 vagrant 用户密码, # 执行成功后即创建好 ssh 互信,其他机器同理。 ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.12.10 # 验证 ssh 互信 ssh 192.168.12.10 # 不需要输入密码并登录成功
      

在中控机上下载 TiDB-Ansible

以 vagrant 用户登录中控机并进入 /home/vagrant 目录,使用以下命令从 Github TiDB-Ansible 项目 上下载 TiDB-Ansible 相应版本, 默认的文件夹名称为 tidb-ansible,以下为各版本下载示例,版本选择可以咨询官方。

# 下载 2.0 GA 版本:
git clone -b release-2.0 https://github.com/pingcap/tidb-ansible.git

# 下载 master 版本: git clone https://github.com/pingcap/tidb-ansible.git 

本教程为master版本

在中控机上安装 Ansible 及其依赖

$ sudo apt-get install python-pip curl
$ cd tidb-ansible $ sudo pip install -r ./requirements.txt 

pip install 时可能会报Python locale error: unsupported locale setting,执行下列命令:

export LC_ALL="en_US.UTF-8"
export LC_CTYPE="en_US.UTF-8"
sudo dpkg-reconfigure locales

分配机器资源,编辑 inventory.ini 文件

sudo vim ~/tidb-ansible/inventory.ini

配置如下:(更换ip为你的目标机器ip,192.168.12.22为中控机器ip,其他为目标机器ip)

## TiDB Cluster Part
[tidb_servers]
192.168.12.22

[tikv_servers]
192.168.12.10
192.168.12.2
192.168.12.3
[pd_servers]
192.168.12.22

[spark_master]

[spark_slaves]

## Monitoring Part
# prometheus and pushgateway servers
[monitoring_servers]
192.168.12.22

[grafana_servers]
192.168.12.22

# node_exporter and blackbox_exporter servers
[monitored_servers]
192.168.12.22
192.168.12.10
192.168.12.2
192.168.12.3

[alertmanager_servers]

## Binlog Part [pump_servers:children] tidb_servers ## Group variables [pd_servers:vars] # location_labels = ["zone","rack","host"] ## Global variables [all:vars] deploy_dir = /home/vagrant/deploy ## Connection # ssh via normal user ansible_user = vagrant cluster_name = test-cluster tidb_version = latest # process supervision, [systemd, supervise] process_supervision = systemd # timezone of deployment region timezone = Asia/Shanghai set_timezone = True enable_firewalld = False # check NTP service enable_ntpd = True set_hostname = False ## binlog trigger enable_binlog = False # zookeeper address of kafka cluster, example: # zookeeper_addrs = "192.168.0.11:2181,192.168.0.12:2181,192.168.0.13:2181" zookeeper_addrs = "" # store slow query log into seperate file enable_slow_query_log = False # enable TLS authentication in the TiDB cluster enable_tls = False # KV mode deploy_without_tidb = False # Optional: Set if you already have a alertmanager server. # Format: alertmanager_host:alertmanager_port alertmanager_target = "" grafana_admin_user = "admin" grafana_admin_password = "admin" 

部署任务

  1. 确认tidb-ansible/inventory.ini文件中ansible_user = vagrant,本例使用vagrant用户作为服务运行用户,配置如下:

    ## Connection
    # ssh via normal user ansible_user = vagrant 
    

    执行以下命令如果所有 server 返回 vagrant 表示 ssh 互信配置成功。

    ansible -i inventory.ini all -m shell -a 'whoami'
    

    执行以下命令如果所有 server 返回 root 表示 vagrant 用户 sudo 免密码配置成功。

    ansible -i inventory.ini all -m shell -a 'whoami' -b
    
  2. 执行local_prepare.yml playbook,联网下载 TiDB binary 到中控机:

    ansible-playbook local_prepare.yml
    
  3. 初始化系统环境,修改内核参数

    ansible-playbook bootstrap.yml
    
  4. 部署 TiDB 集群软件

    ansible-playbook deploy.yml
    
  5. 启动 TiDB 集群

    ansible-playbook start.yml
    
    
    # 返回下列字段时,表示启动成功
    Congrats! All goes well. :-)
    

测试集群

测试连接 TiDB 集群,推荐在 TiDB 前配置负载均衡来对外统一提供 SQL 接口。

  • 使用 MySQL 客户端连接测试,TCP 4000 端口是 TiDB 服务默认端口。

    mysql -u root -h 192.168.12.22 -P 4000 
    
    # eg
    vagrant@vagrant [09:48:48 PM] [~/tidb-ansible] [master *]
    -> % mysql -h192.168.12.22 -uroot -P 4000 Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 1 Server version: 5.7.10-TiDB-v2.0.0-rc.4-147-g00d4831 MySQL Community Server (Apache License 2.0) Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> 
    
  • 通过浏览器访问监控平台。

    地址:http://192.168.12.22:3000 默认帐号密码是:admin/admin

    TiDB 部署及数据同步

Docker Compose 部署方案

待补充…

数据同步

官方说明

由于 TiDB 本身兼容绝大多数的 MySQL 语法,所以对于绝大多数业务来说,最安全的切换数据库方式就是将 TiDB 作为现有数据库的从库接在主 MySQL 库的后方, 这样对业务方实现完全没有侵入性下使用 TiDB 对现有的业务进行备份,应对未来数据量或者并发量增长带来的单点故障风险,如需上线 TiDB, 也只需要简单的将业务的主 MySQL 地址指向 TiDB 即可。

下面我们详细介绍了如何将 MySQL 的数据迁移到 TiDB,并将 TiDB 作为 MySQL 的 Slave 进行数据同步。

这里我们假定 MySQL 以及 TiDB 服务信息如下:

+------------------+-----------------+----------------------------------------+
| Name             | Address         | Port   |   User      | Password        |
+------------------+-----------------+----------------------------------------+
| MySQL            |    127.0.0.1 | 3306 | root | | | TiDB | 192.168.12.22 | 4000 | vagrant | | +------------------+-------------+--------+-----------+-----------------------+ 

下载 TiDB 企业版工具集 (Linux)

注:普通工具集中,无syncermydumper等工具

# 下载 tool 压缩包
wget http://download.pingcap.org/tidb-enterprise-tools-latest-linux-amd64.tar.gz
wget http://download.pingcap.org/tidb-enterprise-tools-latest-linux-amd64.sha256

# 检查文件完整性,返回 ok 则正确
sha256sum -c tidb-enterprise-tools-latest-linux-amd64.sha256

# 解开压缩包
tar -xzf tidb-enterprise-tools-latest-linux-amd64.tar.gz
cd tidb-enterprise-tools-latest-linux-amd64

使用 checker 进行 Schema 检查

在迁移之前,我们可以使用 TiDB 的 checker 工具,checker 是我们开发的一个小工具,用于检测目标 MySQL 库中的表的表结构是否支持无缝的迁移到 TiDB, TiDB 支持绝大多数的 MySQL 常用的原生数据类型,所以大多数情况 checker 的返回应该是 ok。如果 check 某个 table schema 失败,表明 TiDB 当前并不支持, 我们不能对该 table 里面的数据进行迁移。checker 包含在 TiDB 工具集里面。

./bin/checker -host 127.0.0.1 -port 3306 -user root -password password db_name
2016/10/27 13:11:49 checker.go:48: [info] Checking database db_name
2016/10/27 13:11:49 main.go:37: [info] Database DSN: root:@tcp(127.0.0.1:3306)/db_name?charset=utf8
2016/10/27 13:11:49 checker.go:63: [info] Checking table t1
2016/10/27 13:11:49 checker.go:69: [info] Check table t1 succ
2016/10/27 13:11:49 checker.go:63: [info] Checking table t2 2016/10/27 13:11:49 checker.go:69: [info] Check table t2 succ 

使用 mydumper/myloader 全量导入数据

我们使用 mydumper 从 MySQL 导出数据,然后用 myloader 将其导入到 TiDB 里面。

注意,虽然我们也支持使用 MySQL 官方的 mysqldump 工具来进行数据的迁移工作,但相比于 mydumper/myloader,性能会慢很多, 对于大量数据的迁移会花费很多时间,这里我们并不推荐。

mydumper/myloader是一个更强大的数据迁移工具,具体可以参考https://github.com/maxbube/mydumper

# 下载 mydumper 压缩包
wget http://download.pingcap.org/mydumper-linux-amd64.tar.gz
wget http://download.pingcap.org/mydumper-linux-amd64.sha256

# 检查文件完整性,返回 ok 则正确
sha256sum -c mydumper-linux-amd64.sha256

# 解开压缩包
tar -xzf mydumper-linux-amd64.tar.gz
cd mydumper-linux-amd64

从 MySQL 导出数据

我们使用 mydumper 从 MySQL 导出数据,如下:

./bin/mydumper -h 127.0.0.1 -P 3306 -u root -t 16 -F 128 -B test -T t1,t2 -o ./guopisql

上面,我们使用-B test表明是对test这个database操作,然后用-T t1,t2表明只导出t1,t2两张表。 -t 16表明使用16个线程去导出数据。-F 128是将实际的table切分成多大的chunk,这里就是128MB一个chunk

注意:在阿里云一些需要super privilege的云上面,mydumper需要加上--no-locks参数,否则会提示没有权限操作。

给 TiDB 导入数据

我们使用 myloader 将之前导出的数据导入到 TiDB。

./bin/myloader -h 192.168.12.22 -P 4000 -u vagrant -p password -t 16 -q 100 -d ./guopisql

这里-q 100表明每个事务包含多少个query,默认是1000,我们这里使用100就可以了(数据多的话,可以加大)。

导入成功之后,我们可以用 MySQL 官方客户端进入 TiDB,查看:

vagrant@vagrant [09:48:48 PM] [~/tidb-ansible] [master *]
-> % mysql -h192.168.12.22 -uroot -P 4000

mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| t1             |
| t2             |
+----------------+

使用 syncer 增量导入数据实现数据和 MySQL 实时同步

上面我们介绍了如何使用mydumper/myloader将 MySQL 的数据全量导入到 TiDB,但如果后续 MySQL 的数据有更新,我们仍然希望快速导入, 使用全量的方式就不合适了。

TiDB 提供syncer工具能方便的将 MySQL 的数据增量的导入到 TiDB 里面。

syncer也属于 TiDB 企业工具集。

假设我们之前已经使用mydumper/myloader导入了t1t2两张表的一些数据,现在我们希望这两张表的任何更新, 都是实时的同步到 TiDB 上面。

MySQL 开启 binlog

在使用 syncer 之前,我们必须保证:

  • MySQL 开启 binlog 功能,参考Setting the Replication Master Configuration

  • Binlog 格式必须使用row format,这也是 MySQL 5.7 之后推荐的binlog格式

    sudo vi /etc/mysql/my.cnf

    [mysqld] log-bin=mysql-bin server-id=1 binlog-format=ROW

重启mysql服务

sudo service mysql restart

获取同步 position

我们通过show master status得到当前binlogpositionsyncer的初始同步位置就是从这个地方开始。

show master status;
+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000001 | 154 | | | | +------------------+----------+--------------+------------------+-------------------+ 

我们将position相关的信息保存到一个syncer.meta文件里面,用于syncer的同步:

# vim syncer.meta
binlog-name = "mysql-bin.000001"
binlog-pos = 154 

启动 syncer

syncer的配置文件config.toml:

log-level = "info"

server-id = 1

# meta 文件地址 meta = "./syncer.meta" worker-count = 1 batch = 1 pprof-addr = ":10081"  [from] host = "127.0.0.1" user = "root" password = "your password" port = 3306  [to] host = "192.168.12.22" user = "root" password = "" port = 4000 

启动syncer

# 后台运行
vagrant@vagrant [10:10:07 PM] [~/tidb-enterprise-tools-latest-linux-amd64]
-> % nohup ./bin/syncer -config config.toml &
[1] 4193

syncer每隔30s会输出当前的同步统计,如下:

# cat nohub.out

2018/05/08 15:04:25 syncer.go:821: [info] [syncer]total events = 2, total tps = 0, recent tps = 0, master-binlog = (mysql-bin.000001, 574), master-binlog-gtid=, syncer-binlog = (mysql-bin.000001, 574), syncer-binlog-gtid =
2018/05/08 15:04:55 syncer.go:821: [info] [syncer]total events = 2, total tps = 0, recent tps = 0, master-binlog = (mysql-bin.000001, 574), master-binlog-gtid=, syncer-binlog = (mysql-bin.000001, 574), syncer-binlog-gtid =
2018/05/08 15:05:25 syncer.go:821: [info] [syncer]total events = 2, total tps = 0, recent tps = 0, master-binlog = (mysql-bin.000001, 574), master-binlog-gtid=, syncer-binlog = (mysql-bin.000001, 574), syncer-binlog-gtid =
2018/05/08 15:05:55 syncer.go:821: [info] [syncer]total events = 2, total tps = 0, recent tps = 0, master-binlog = (mysql-bin.000001, 574), master-binlog-gtid=, syncer-binlog = (mysql-bin.000001, 574), syncer-binlog-gtid =

在 MySQL 插入新的数据

INSERT INTO t1 VALUES (4, 4), (5, 5); 

登录到 TiDB 查看:

mysql -h127.0.0.1 -P4000 -uroot -p
mysql> select * from t1;
+----+------+
| id | age | +----+------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | | 4 | 4 | | 5 | 5 | +----+------+
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k