暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PolarDB-X 在可计算存储上部署测试体验

2605
本文主要分享 PolarDB-X 在可计算存储上的部署和测试体验,分享自己对部分架构原理和问题的理解。可能有些地方理解不够准确,欢迎公众号留言交流。

从没有接触过分布式数据库产品的朋友,可以从中了解这款使用最久最流行的分布式数据库中间件产品的成熟形态;接触过同类 DRDS 产品的朋友也可以对比看看不同 DRDS 在面临相同需求时的不同解决方案;接触过 TiDB 和 OceanBase 的朋友也可以看看 DRDS 架构在其特有场景下的独特优势。没有接触过可计算存储的,可以看看如何利用 PolarDB-X 和可计算存储构建跟阿里云线上PolarDB类似的降本增效方案。
注:文章里的代码里空行格式问题是微信公众号平台编辑器问题。



PolarDB-X 简介

了解PolarDB-X先要从阿里巴巴数据库架构历史说起。
早期阿里巴巴数据库去 O 时,在电商场景选择了 MySQL 方案。随着淘宝业务增长,MySQL 从单实例又演变为分布式中间件加MySQL。早期淘宝的数据库中间件内部叫TDDL,主要功能是分库分表和简单SQL路由,不支持分布式事务。TDDL 的形态是 jar 包,部署在应用服务器上(应用也主要是 java 应用)。其中分库分表规则保存在一个独立的 MySQL 中并由一个单独的应用提供接口去读取和修改规则。当数据库里表和库信息发生变化时(新增表和修改表结构、分表扩容、数据库和实例数量扩容),这个应用会负责将最新的分库分表规则推送到所有应用服务器节点。当然应用服务器规模也越来越大,这其中可能也有一些主动拉取和缓存的机制来减轻元数据应用的压力。不过随着分库和表的规模越来越大时,规则的推送和拉取终究会面临一个性能瓶颈(超时)。所以理论上分库分表的规模也不是无限大。在 2016 年最大的分库数量是 256 个,最大的分表数量是 4096 。这是历史上多次扩容的结果(扩容简单就是分表数量和分库数量分别独立的翻倍,这也是由 MySQL 单实例的能力决定的)。就这样一套架构,支撑了很多年的天猫双十一大促活动。所以它是经得起考验的。
当 TDDL 产品要对外输出时,这种部署在客户端的中间件就不适合外部客户,所以产品又推出了服务端的中间件(DRDS SERVER),即通过一组无状态的应用(部署在 Docker 或 ECS 里)进程提供分库分表和 SQL 路由功能,并在后期还实现了对分布式事务的支持。这就是阿里云数据库产品的DRDS。其他云厂商也陆续推出各自的 DRDS 产品。在云上的 DRDS 产品功能是最全面丰富的,在私有云推出的 DRDS 功能会显得相对不足。这其中的挑战就是 IAAS 层的变化给运维带来的困难。DRDS 架构是计算和存储分离。计算节点是一组无状态的应用进程( JAVA 开发),存储节点是一组 MySQL 实例。这里面涉及到应用的部署和启动、MySQL 的安装部署以及高可用等等,要求运维既要懂 Java 又要懂数据库,所以使用起来非常困难,后来 Docker 和 k8s 容器技术极大缓解了这个问题。只是客户端服务器要能保证一定的CPU和内存资源。
阿里云后来将 DRDS 品牌升级为 PolarDB-X,并进一步完善了其SQL和事务能力。如今的PolarDB-X 对用户确实实现了分库分表的透明,用户使用就像使用一个单实例数据库一样。不过 PolarDB-X 也保留了高级用户自定义分库分表规则的用法。PolarDB-X 分为两代。第一代就是DRDS,第二代加入了很多云原生特性。这里引用官方的介绍:PolarDB-X 是一款面向超高并发、海量存储、复杂查询场景设计的云原生分布式数据库系统。其采用 Shared-nothing 与存储计算分离架构,支持水平扩展、分布式事务、混合负载等能力,具备企业级、云原生、高可用、高度兼容 MySQL 系统及生态等特点。更详细的特性介绍可以查看官方文档:https://docs.polardbx.com/ 。本文后面主要是实操记录和分析。


可计算存储简介

PolarDB-X 的规模主要由其计算和存储的能力决定。其中存储就是 MySQL 集群。名为集群,实际实例之间并没有集群关系,只是有些实例是主备实例关系。MySQL集群的规模就由单实例的能力决定。通常提升单实例能力一是优化 MySQL 内核能力,大厂有数据库研发团队,做这个很擅长,普通用户就依赖开源 MySQL 的能力。二是提升服务器资源能力,即更多的CPU更大的内存和更快的存储(主要是SSD)。这个就要舍得投入。三就是优化应用SQL,这个DBA和应用开发一起分析优化即可,相对投入更低,收益更大。
可计算存储是在 SSD 里引入一定计算能力,从而实现将服务器CPU的一些计算工作(如加密和压缩)卸载(offload)到SSD中,也是一种提升单机MySQL能力的方案,同时可计算存储还能通过扩容降低数据存储的平均成本。本节属于自有产品宣传,并非 PolarDB-X 必须的。PolarDB 在云上有一种计算和存储分离形态,其存储是分布式存储,使用 PolarFS 文件系统,下面的存储磁盘使用的就是可计算存储技术。

PolarDB MySQL 使用可计算存储就是为了最大程度降低存储成本(TCO),目前这是相比其他云厂商的一个独特优势。这是云上的好处,在线下开源的 PolarDB-X 后端也是 MySQL 数据库,用户如果将普通的 SSD 替换为 ScaleFlux 产品 CSD3000 ,就可以享受跟阿里云线上类似的降低存储成本的效果( PolarDB-X 里没有 PolarFS 产品,使用的是本地文件系统。)
有关可计算存储原理和实践 感兴趣的可以搜索公众号相关文章分享。本文主要是部署记录和分析。后面会展示可计算存储的存储优势以及PolarDB-X 的性能(跟 Intel Gen4 SSD 对比)。



PolarDB-X Docker 部署简介

PolarDB-X 架构和部署类型

在部署PolarDB-X 之前,首先了解一下PolarDB-X的架构。

PolarDB-X 采用 Shared-nothing 与存储计算分离架构进行设计,系统由 4个核心组件组成。
  • 计算节点(CN, Compute Node)

计算节点是系统的入口,采用无状态设计,包括 SQL 解析器、优化器、执行器等模块。负责数据分布式路由、计算及动态调度,负责分布式事务 2PC 协调、全局二级索引维护等,同时提供 SQL 限流、三权分立等企业级特性。这里面比较特别的就是分布式事务和全局二级索引功能,这是早期DRDS产品的痛点。

  • 存储节点(DN, Data Node)

存储节点负责数据的持久化,基于多数派 Paxos 协议提供数据高可靠、强一致保障,同时通过 MVCC 维护分布式事务可见性。这里面的特别之处应该就是使用Paxos协议实现MySQL的高可用。

  • 元数据服务(GMS, Global Meta Service)

元数据服务负责维护全局强一致的 Table/Schema, Statistics 等系统 Meta 信息,维护账号、权限等安全信息,同时提供全局授时服务(即 TSO)。这个是 PolarDB-X 自己的元数据服务,包含分库分表规则和实例数据库信息。应用不需要再自己存储特殊的 jar 包了。

  • 日志节点(CDC, Change Data Capture)

日志节点提供完全兼容 MySQL Binlog 格式和协议的增量订阅能力,提供兼容 MySQL Replication 协议的主从复制能力。这个也是很实用的功能,方便用户订阅PolarDB-X的日志(BINLOG),它实现了将后端MySQL的Binlog 根据一些事务规则聚合。

PolarDB-X 提供通过 K8S Operator 方式管理以上4个组件,同时计算节点与存储节点之间可通过私有协议进行 RPC 通信。同时也提供相应的Docker 容器和自动化部署工具PXD。本文主要是通过PXD 部署PolarDB-X集群。




PolarDB-X Docker 部署总结

官方的部署文档在:https://docs.polardbx.com/quickstart/topics/quickstart-pxd-cluster.html 。这里简单说一些要点。
首先部署的服务器 CPU、内存和空间要满足部署要求。如果关注性能,实际的 CPU、内存和存储要尽可能的高。
PXD 工具是 python3 开发。所以还需要安装python3. 不过用户服务器可能已有 python,并且可能还有相关的依赖。这里推荐先安装 pyvenv 这个包,它可以允许你开辟一个独立版本的python 环境,这样安装部署时就避免了很多python 依赖包和被依赖包的冲突问题。

    [root@sfx110008 ~]# source polardb/bin/activate
    (polardb) [root@sfx110008 ~]# python -V
    Python 3.7.6
    (polardb) [root@sfx110008 ~]#

    PXD 部署时通常需要一个配置文件。第一次用需要从文档里复制配置文件。不过也可以使用 pxd tryout 命令先体验一个最小规模的 polardb-x 集群,它会根据服务器信息自动生成配置文件并部署相关容器。

      (polardb) [root@sfx110008 ~]# pxd tryout
      /root/polardb/lib/python3.7/site-packages/deployer
      Start creating PolarDB-X cluster pxc-tryout
      on your local machine
      PolarDB-X Cluster params:
      * cn
      count: 1, version: latest
      * dn
      count: 1, version: latest
      *
      cdc count: 1, version: latest
      *
      gms count: 1, version: latest
      *
      leader_only: True
      Processing[------------------------------------] 0% pre check
      Processing[##----------------------------------] 7% generate topology
      Processing[#####-------------------------------] 15% check docker engine version
      Processing[########----------------------------] 23% pull images
      Pull image: polardbx/galaxysql:latest at
      127.0.0.1
      latest:Pulling from polardbx/galaxysql
      Digest:
      sha256:dc48b544ccb12e14b8d40b5a5aa7445045ec5753809235a4320dde382106ac5b
      Status: Image is up to date for
      polardbx/galaxysql:latest
      Pull image: polardbx/galaxyengine:latest at
      127.0.0.1
      latest:Pulling from polardbx/galaxyengine
      Digest:
      sha256:32fac0ec5bd03997e2fffc8829208e21f33e31124c7eecf7120f1eb491846d6c
      Status: Image is up to date for
      polardbx/galaxyengine:latest
      Pull image: polardbx/polardbx-init:latest
      at 127.0.0.1
      latest:Pulling from polardbx/polardbx-init
      Digest:
      sha256:e2d7984f8a01845708eec86ef7d0168aa3b7cb8082040eb13e1c3160e0f799e3
      Status: Image is up to date for
      polardbx/polardbx-init:latest
      Pull image: polardbx/xstore-tools:latest at
      127.0.0.1
      latest:Pulling from polardbx/xstore-tools
      Digest:
      sha256:914f44294bdcd5b141dbbdf2ce98dbeca42ed615fb9642dd1daebc3f98970620
      Status: Image is up to date for
      polardbx/xstore-tools:latest
      Pull image: polardbx/galaxycdc:latest at
      127.0.0.1
      latest:Pulling from polardbx/galaxycdc
      Digest:
      sha256:0ffe8a3ac06be9faed3cb27dbd0a736295292360817cc3c2610245b053a636a7
      Status: Image is up to date for
      polardbx/galaxycdc:latest
      Processing[###########-------------------------] 30% create gms node
      Processing[#############-----------------------] 38% create gms db and tables
      Processing[################--------------------] 46% create PolarDB-X root
      account
      Processing[###################-----------------] 53% create dn
      Processing[######################--------------] 61% register dn to gms
      Processing[########################------------] 69% create cn
      Processing[###########################---------] 76% wait cn ready
      Processing[##############################------] 84% create cdc containers
      Processing[#################################---] 92% wait PolarDB-X ready
      Processing[####################################] 100%
      PolarDB-X cluster create successfully, you
      can try it out now.
      Connect PolarDB-X using the following
      command:
      mysql -h127.0.0.1 -P59533 -upolardbx_root -plQgzQpWE

      部署最后一行是数据库连接信息,需要记录下来。这个密码不允许修改,所以要尽快创建其他管理员账号。

        (polardb) [root@sfx110008 ~]# mysql
        -h127.0.0.1 -P59533 -upolardbx_root -plQgzQpWE
        mysql: [Warning] Using a password on the
        command line interface can be insecure.
        Welcome to the MySQL monitor. Commands end with ; or \g.
        Your MySQL connection id is 11
        Server version: 5.6.29 Tddl Server
        (ALIBABA)
        Copyright (c) 2000, 2021, Oracle and/or its
        affiliates.
        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> select user,host from mysql.user;
        +---------------+------+
        | user | host |
        +---------------+------+
        | polardbx_root | % |
        +---------------+------+
        1 row in set (0.05 sec)
        mysql> set PASSWORD FOR
        'polardbx_root'@'%' = PASSWORD('scaleFLUX2023') ;
        ERROR 5107 (HY000):
        [1597fedb36001000][172.17.0.4:59533][polardbx]ERR-CODE:
        [PXC-5107][ERR_OPERATION_NOT_ALLOWED] Can not modify polardbx_root since it is
        reserved for system
        mysql>

        命令 pxd list 可以创建新建的集群。

          (polardb) [root@sfx110008 ~]# pxd list
          /root/polardb/lib/python3.7/site-packages/deployer
          NAME CN DN CDC STATUS
          pxc-tryout 1 1 1 running
          (polardb) [root@sfx110008 ~]#

          使用Docker命令可以查看这些容器的信息,这里就不重复了。
          下面演示使用配置文件部署自己的 PolarDB-X 集群。我这里服务器只有一台,CPU96,内存768G。所以我打算部署1个 CN 节点,2个 DN 节点。配置文件供参考。

            (polardb) [root@sfx110008 polardb-test]#cat polardb-x-sfx.yamlversion: v1type: polardbxcluster:name: pxcsfxgms:image: polardbx/galaxyengine:latesthost_group: [192.168.110.8]resources:cpu_limit: 4mem_limit: 4G cn:image: polardbx/galaxysql:latestreplica: 1nodes:- host: 192.168.110.8resources:cpu_limit: 36mem_limit: 96G dn:image: polardbx/galaxyengine:latestreplica: 2nodes:- host_group: [192.168.110.8]- host_group: [192.168.110.8]resources:cpu_limit: 24mem_limit: 48Gcdc:image: polardbx/galaxycdc:latestreplica: 1nodes:- host: 192.168.110.8resources:cpu_limit: 6mem_limit: 32G(polardb) [root@sfx110008 polardb-test]#

            这里我部署了2个同等规格的 PolarDB-X 集群:pxcintel 和 pxcsfx ,主要是为了对比两块 SSD上的 PolarDB-X 性能。

              (polardb) [root@sfx110008 polardb-test]#
              pxd list
              /root/polardb/lib/python3.7/site-packages/deployer
              NAME CN DN CDC STATUS
              pxcintel 1 2 1 running
              pxcsfx 1 2 1 running
              pxc-tryout 1 1 1 running
              (polardb) [root@sfx110008 polardb-test]#

              注意,配置文件中各个容器需要的内存之和要小于服务器可用的内存。DN 节点里的 MySQL 使用的是文件系统,里面 MySQL 的日志会使用到 PageCache。所以后面会观察到内存有一部分在 PageCache 里。

              默认 DN 和 GMS 节点的 MySQL 都是部署在当前用户 home 目录下隐藏文件夹  ~/.pxd/data 下,这个位置需要调整。由于没有找到在配置文件中指定目录的方式,我这里将 mysql 和 mysql_log 下面实例目录移动到 SSD 盘里,并使用软链接技术。

                (polardb) [root@sfx110008 ~]# cd~/.pxd/data/(polardb) [root@sfx110008 data]# lltotal 16drwxr-xr-x3 root root   27 Jan 19 08:29cachedrwxr-xr-x4 root root 4096 Jan 28 10:26 mysqldrwxr-xr-x4 root root 4096 Jan 28 10:26 mysql_logdrwxr-xr-x 13 root root 4096 Jan 28 10:26podinfodrwxr-xr-x 10 root root 4096 Jan 28 10:26shareddrwxr-xr-x2 root root   28 Jan 19 08:35template(polardb) [root@sfx110008 data]#(polardb) [root@sfx110008 data]# ll mysql/total 0drwxr-xr-x 6 1000 1000 101 Jan 28 10:26127_0_0_1_14765drwxr-xr-x 6 1000 1000 101 Jan 28 10:25127_0_0_1_15598lrwxrwxrwx 1 root root 39 Jan 19 08:48 192_168_110_8_15285 ->/data/nvme0n1/mysql/192_168_110_8_15285lrwxrwxrwx 1 root root 39 Jan 19 08:45 192_168_110_8_15898 ->/data/nvme1n1/mysql/192_168_110_8_15898lrwxrwxrwx 1 root root 39 Jan 19 08:45 192_168_110_8_16176 ->/data/nvme1n1/mysql/192_168_110_8_16176lrwxrwxrwx 1 root root 39 Jan 19 08:48 192_168_110_8_16525 ->/data/nvme0n1/mysql/192_168_110_8_16525lrwxrwxrwx 1 root root 39 Jan 19 08:48 192_168_110_8_16890 ->/data/nvme0n1/mysql/192_168_110_8_16890lrwxrwxrwx 1 root root 39 Jan 19 08:45 192_168_110_8_17868 ->/data/nvme1n1/mysql/192_168_110_8_17868(polardb) [root@sfx110008 data]# llmysql_log/total 0drwxr-xr-x 3 root root 16 Jan 28 10:26127_0_0_1_14765drwxr-xr-x 3 root root 16 Jan 28 10:25127_0_0_1_15598lrwxrwxrwx 1 root root 43 Jan 19 08:47192_168_110_8_15285 -> data/nvme0n1/mysql_log/192_168_110_8_15285lrwxrwxrwx 1 root root 43 Jan 19 08:47192_168_110_8_15898 -> data/nvme1n1/mysql_log/192_168_110_8_15898lrwxrwxrwx 1 root root 43 Jan 19 08:47192_168_110_8_16176 -> data/nvme1n1/mysql_log/192_168_110_8_16176lrwxrwxrwx 1 root root 43 Jan 19 08:47192_168_110_8_16525 -> data/nvme0n1/mysql_log/192_168_110_8_16525lrwxrwxrwx 1 root root 43 Jan 19 08:47192_168_110_8_16890 -> data/nvme0n1/mysql_log/192_168_110_8_16890lrwxrwxrwx 1 root root 43 Jan 19 08:46192_168_110_8_17868 -> data/nvme1n1/mysql_log/192_168_110_8_17868(polardb) [root@sfx110008 data]#

                其中 nvme0n1 是可计算存储。

                  (polardb) [root@sfx110008 data]# lsblk |grep
                  nvme
                  nvme0n1 259:1 0 3.5T 0 disk data/nvme0n1
                  nvme1n1 259:0 0 3.5T 0 disk data/nvme1n1
                  (polardb) [root@sfx110008 data]# nvme list
                  Node SN Model Namespace
                  Usage Format FW Rev
                  ---------------- --------------------
                  ---------------------------------------- --------- --------------------------
                  ---------------- --------
                  /dev/nvme0n1 UE2237C1501M CSD-3310 1 1.32 TB 3.84 TB 4 KiB + 0 B U3219118
                  /dev/nvme1n1 BTAC14930A0A3P8AGN INTEL SSDPF2KX038TZ 1 3.84 TB 3.84 TB 4 KiB + 0 B JCV10100
                  (polardb) [root@sfx110008 data]#

                  使用命令 docker stats 可以实时观察 docker容器的性能。这里主要是看看容器的内存资源规格。

                    (polardb) [root@sfx110008 data]# docker
                    stats
                    CONTAINER ID NAME CPU % MEM USAGE LIMIT MEM % NET I/O BLOCK I/O PIDS
                    7a134a7f8819 pxc-tryout-cdc-YzFi 4.68% 1.418GiB 2GiB 70.90% 43.8MB 13MB 598kB
                    227MB 448
                    192d639dab9c pxc-tryout-cn-DRhz-59533 1.42% 1.734GiB 2GiB 86.72% 47.5MB 30.7MB 16.4kB 0B 23
                    47b819ededc1 pxc-tryout-dn-0-Cand-14765 3.22% 918.1MiB 2GiB 44.83% 21.5MB 43.6MB 79MB
                    6.08GB 191
                    8910af8257e7 pxc-tryout-gms-Cand-15598 2.34% 900.2MiB 2GiB 43.96% 14.8MB 40.3MB 78.1MB 4.93GB 196
                    528d02263414 pxcsfx-cdc-OuBg 5.81% 7.499GiB 32GiB 23.44% 0B 0B 0B 594kB 546
                    d67c933141db pxcsfx-cn-rSAu-57751 2.85% 71.84GiB 96GiB 74.84% 0B 0B 0B 213kB 87
                    eb75fd10e0c0 pxcsfx-dn-1-Cand-16890 7.14% 9.429GiB 48GiB 19.64% 0B 0B 1.28GB 95.9GB 193
                    eaccd28a089a pxcsfx-dn-0-Cand-16525 7.56% 14.34GiB 48GiB 29.87% 0B 0B 1.25GB 157GB 193
                    60f0ea52966d pxcsfx-gms-Cand-15285 2.67% 4GiB 4GiB 99.99% 0B 0B 5.68GB 50.8GB 197
                    ae4fd818ab70 pxcintel-cdc-tzKD 0.30% 3.489GiB 32GiB 10.90% 0B 0B 164kB
                    58.8MB 134
                    1137dcdd007b pxcintel-cn-POfb-52289 1.48% 73.41GiB 96GiB 76.47% 0B 0B 49.2kB 123kB 87
                    2007f8324d48 pxcintel-dn-1-Cand-17868 0.88% 36.38GiB 48GiB 75.79% 0B 0B 16.9GB 112GB 191
                    8c020d4b6a25 pxcintel-dn-0-Cand-15898 6.39% 42.45GiB 48GiB 88.44% 0B 0B 24.3GB 131GB 192
                    9955e8db3b7c pxcintel-gms-Cand-16176 7.27% 3.845GiB 4GiB 96.13% 0B 0B 1.12GB 22.9GB 197
                    ^C



                    PolarDB-X OLTP 测试简介

                    分库分表介绍

                    分库类型

                    PolarDB-X 目前是2.0,保留了1.0的DRDS功能。PolarDB-X的目标是让用户像单实例一样使用数据库。首先进入 PolarDB-X 默认只有 information_schema 数据库。用户需要创建一个数据库。

                    命令:create database yourdb [ mode = ‘drds | auto’ ] ; 

                    完整的语法见帮助文档。
                     

                    增加了 mode 选项,可以不提供,默认值看起来是 drds 。二者区别看后面分析。

                      mysql> create database testdb_default;Query OK, 1 row affected (0.47 sec)mysql> show databases;+--------------------+| DATABASE           |+--------------------+| information_schema || testdb_default     |+--------------------+2 rows in set (0.00 sec)

                      目前建好的数据库看起来跟单实例一样,我们倒后端 DN 节点的 MySQL 中看看数据库列表。

                      DN 节点容器名中的数字就是MySQL的监听端口,默认用户 root,密码空。只能本地登录。

                        [root@sfx110008 polardb-test]# docker exec -it pxc-tryout-dn-0-Cand-14765 bash
                        [root@47b819ededc1 /]# mysql -h 127.1 -u root -P 14765 -p
                        Enter password:
                        Welcome to the MySQL monitor. Commands end with ; or \g.
                        <…>
                        mysql> show databases;
                        +-----------------------+
                        | Database |
                        +-----------------------+
                        | __cdc___000000 |
                        | __cdc___single |
                        | __recycle_bin__ |
                        | information_schema |
                        | mysql |
                        | performance_schema |
                        | sys |
                        | testdb_default_000000 |
                        | testdb_default_000001 |
                        | testdb_default_000002 |
                        | testdb_default_000003 |
                        | testdb_default_000004 |
                        | testdb_default_000005 |
                        | testdb_default_000006 |
                        | testdb_default_000007 |
                        | testdb_default_single |
                        +-----------------------+
                        16 rows in set (0.00 sec)

                        我们可以看到后端 DN 节点里跟 testdb_default 有关的数据库有9个,其中8个数据库名以数字结尾,从000000 到000007 ,我们简称为 testdb_default 的分库,最后一个以 single 结尾,简称为 testdb_default 的单库。目前数据库下都没有业务表。

                        这里有8个分库,应该是一个参数控制的,默认值是8 。深入使用 PolarDB-X 的时候,这个分库数量是有讲究的,这里初次使用就不展开了。8这个值挺好的。

                        我们回到 PolarDB-X 的会话中,继续建表。

                          mysql> use testdb_default;
                          Database changed
                          mysql> CREATE TABLE single_tbl(
                          -> id bigint not null auto_increment,
                          -> name varchar(30),
                          -> primary key(id)
                          -> );
                          Query OK, 0 rows affected (0.85 sec)


                          mysql> show tables;
                          +--------------------------+
                          | TABLES_IN_TESTDB_DEFAULT |
                          +--------------------------+
                          | single_tbl |
                          +--------------------------+
                          1 row in set (0.00 sec)


                          mysql> show create table single_tbl \G
                          *************************** 1. row ***************************
                          Table: single_tbl
                          Create Table: CREATE TABLE `single_tbl` (
                          `id` bigint(20) NOT NULL AUTO_INCREMENT,
                          `name` varchar(30) DEFAULT NULL,
                          PRIMARY KEY (`id`)
                          ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 DEFAULT COLLATE = utf8mb4_0900_ai_ci
                          1 row in set (0.02 sec)

                          跟单实例建表看起来是一样的。

                          我们再看看后端DN节点中 MySQL 实例下的表。

                            mysql> show tables from testdb_default_000000;
                            +---------------------------------+
                            | Tables_in_testdb_default_000000 |
                            +---------------------------------+
                            | __drds_global_tx_log |
                            +---------------------------------+
                            1 row in set (0.00 sec)


                            mysql> show tables from testdb_default_000007;
                            +---------------------------------+
                            | Tables_in_testdb_default_000007 |
                            +---------------------------------+
                            | __drds_global_tx_log |
                            +---------------------------------+
                            1 row in set (0.00 sec)


                            mysql> show tables from testdb_default_single ;
                            +---------------------------------+
                            | Tables_in_testdb_default_single |
                            +---------------------------------+
                            | __drds_global_tx_log |
                            | single_tbl_gb4f |
                            +---------------------------------+
                            2 rows in set (0.00 sec)


                            mysql> use testdb_default_single;


                            Database changed
                            mysql> show create table single_tbl_gb4f \G
                            *************************** 1. row ***************************
                            Table: single_tbl_gb4f
                            Create Table: CREATE TABLE `single_tbl_gb4f` (
                            `id` bigint(20) NOT NULL AUTO_INCREMENT,
                            `name` varchar(30) DEFAULT NULL,
                            PRIMARY KEY (`id`)
                            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
                            1 row in set (0.00 sec)

                            从上看出只在单库下看到表 single_tbl,但是名字后面被自动追加了一个随机字符串后缀。这个估计是为了避免重名。表结构跟前面建表完全一样。

                            上面的表我建表时用自增列做了主键。如果建表不带主键,PolarDB-X 会在后端 MySQL 实例里自动给表加一个自增列作为主键。所以业务尽量创建主键,否则就不要在没有主键的时候创建自增列,在这里会报错。

                              mysql> CREATE TABLE single_tbl_no_pk( id bigint not null auto_increment, name varchar(30) );
                              ERROR 4998 (HY000): [1598296a64c01000][172.17.0.4:59533][testdb_default]ERR-CODE: [PXC-4518][ERR_VALIDATE] ERR-CODE: [PXC-4998][ERR_NOT_SUPPORT] Incorrect table definition; there can be only one auto column and it must be defined as a key not support yet!
                              mysql> CREATE TABLE single_tbl_no_pk( id bigint not null , name varchar(30) );
                              Query OK, 0 rows affected (0.52 sec)


                              mysql>

                              看一下后端表,验证一下这个结论。

                                mysql> show tables;
                                +---------------------------------+
                                | Tables_in_testdb_default_single |
                                +---------------------------------+
                                | __drds_global_tx_log |
                                | single_tbl_gb4f |
                                | single_tbl_no_pk_xrcv |
                                +---------------------------------+
                                3 rows in set (0.00 sec)


                                mysql> show create table single_tbl_no_pk_xrcv\G
                                *************************** 1. row ***************************
                                Table: single_tbl_no_pk_xrcv
                                Create Table: CREATE TABLE `single_tbl_no_pk_xrcv` (
                                `id` bigint(20) NOT NULL,
                                `name` varchar(30) DEFAULT NULL,
                                `_drds_implicit_id_` bigint(20) NOT NULL AUTO_INCREMENT,
                                PRIMARY KEY (`_drds_implicit_id_`)
                                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
                                1 row in set (0.00 sec)


                                mysql>

                                PolarDB-X 添加的自增列的名字叫 _drds_implicit_id_ ,所以业务列不要用这个名字。

                                截止目前这个表还是个普通表,我们简称为单表。单表通常是在单库(以 _single)结尾里。我们也可以在 PolarDB-X 的会话里通过命令 show topology 查看表的拓扑结构,通过命令 show rule 查看表的分库分表信息。

                                  mysql> show topology from single_tbl;
                                  +------+-----------------------------+-----------------+----------------+
                                  | ID | GROUP_NAME | TABLE_NAME | PARTITION_NAME |
                                  +------+-----------------------------+-----------------+----------------+
                                  | 0 | TESTDB_DEFAULT_SINGLE_GROUP | single_tbl_GB4F | - |
                                  +------+-----------------------------+-----------------+----------------+
                                  1 row in set (0.00 sec)


                                  mysql> show rule;
                                  +------+------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                  | ID | TABLE_NAME | BROADCAST | DB_PARTITION_KEY | DB_PARTITION_POLICY | DB_PARTITION_COUNT | TB_PARTITION_KEY | TB_PARTITION_POLICY | TB_PARTITION_COUNT |
                                  +------+------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                  | 0 | single_tbl | 0 | | NULL | 1 | | NULL | 1 |
                                  | 1 | single_tbl_no_pk | 0 | | NULL | 1 | | NULL | 1 |
                                  +------+------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                  2 rows in set (0.01 sec)

                                  上面拓扑里的 GROUP_NAME 通常就对应后端的数据库(分库或单库),名字会在数据库名上加个后缀“_GROUP”。

                                  从命令 show rule 可以看出,单表实际上就是单库单表,就是分库数量为1,分表数量也为1 。下面我们看看分表用法。

                                  分表类型

                                  分表指分库分表。一个逻辑表拆分为 M 个分库,每个分库下可以再拆分为 N 个分表。M,N >= 1,可以独立指定。M*N 就是这个逻辑表被拆分的分片数。前面介绍历史的时候说,淘宝交易表历史上最大拆分分片是4096,仅供参考。

                                  分表语法就是普通建表语句后增加2个语法:dbpartition by 分库拆分策略(拆分字段) 、tbpartition by 分表拆分策略(拆分字段) 。

                                  我们先从简单的情形开始,分库不分表,M=8, N=1 。

                                    mysql> CREATE TABLE multi_db_single_tbl(
                                    -> id bigint not null auto_increment,
                                    -> name varchar(30),
                                    -> primary key(id)
                                    -> ) dbpartition by hash(id);
                                    Query OK, 0 rows affected (0.54 sec)


                                    mysql> show topology from multi_db_single_tbl ;
                                    +------+-----------------------------+--------------------------+----------------+
                                    | ID | GROUP_NAME | TABLE_NAME | PARTITION_NAME |
                                    +------+-----------------------------+--------------------------+----------------+
                                    | 0 | TESTDB_DEFAULT_000000_GROUP | multi_db_single_tbl_G7Uh | - |
                                    | 1 | TESTDB_DEFAULT_000001_GROUP | multi_db_single_tbl_G7Uh | - |
                                    | 2 | TESTDB_DEFAULT_000002_GROUP | multi_db_single_tbl_G7Uh | - |
                                    | 3 | TESTDB_DEFAULT_000003_GROUP | multi_db_single_tbl_G7Uh | - |
                                    | 4 | TESTDB_DEFAULT_000004_GROUP | multi_db_single_tbl_G7Uh | - |
                                    | 5 | TESTDB_DEFAULT_000005_GROUP | multi_db_single_tbl_G7Uh | - |
                                    | 6 | TESTDB_DEFAULT_000006_GROUP | multi_db_single_tbl_G7Uh | - |
                                    | 7 | TESTDB_DEFAULT_000007_GROUP | multi_db_single_tbl_G7Uh | - |
                                    +------+-----------------------------+--------------------------+----------------+
                                    8 rows in set (0.00 sec)


                                    mysql> show rule from multi_db_single_tbl;
                                    +------+---------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                    | ID | TABLE_NAME | BROADCAST | DB_PARTITION_KEY | DB_PARTITION_POLICY | DB_PARTITION_COUNT | TB_PARTITION_KEY | TB_PARTITION_POLICY | TB_PARTITION_COUNT |
                                    +------+---------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                    | 0 | multi_db_single_tbl | 0 | id | hash | 8 | | NULL | 1 |
                                    +------+---------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                    1 row in set (0.00 sec)

                                    建表时不需要指定分多少个库,实际会根据单个DN节点内部MySQL实例里的分库数定。从拓扑里看一个分成了8个分表,每个分库里有一个。再到后端验证一下。

                                      mysql> show tables from testdb_default_000000;
                                      +---------------------------------+
                                      | Tables_in_testdb_default_000000 |
                                      +---------------------------------+
                                      | __drds_global_tx_log |
                                      | multi_db_single_tbl_g7uh |
                                      +---------------------------------+
                                      2 rows in set (0.00 sec)


                                      mysql> show tables from testdb_default_000007;
                                      +---------------------------------+
                                      | Tables_in_testdb_default_000007 |
                                      +---------------------------------+
                                      | __drds_global_tx_log |
                                      | multi_db_single_tbl_g7uh |
                                      +---------------------------------+
                                      2 rows in set (0.00 sec)


                                      mysql> show create table testdb_default_000005.multi_db_single_tbl_g7uh\G
                                      *************************** 1. row ***************************
                                      Table: multi_db_single_tbl_g7uh
                                      Create Table: CREATE TABLE `multi_db_single_tbl_g7uh` (
                                      `id` bigint(20) NOT NULL AUTO_INCREMENT,
                                      `name` varchar(30) DEFAULT NULL,
                                      PRIMARY KEY (`id`)
                                      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
                                      1 row in set (0.00 sec)


                                      mysql> show tables from testdb_default_single ;
                                      +---------------------------------+
                                      | Tables_in_testdb_default_single |
                                      +---------------------------------+
                                      | __drds_global_tx_log |
                                      | single_tbl_gb4f |
                                      | single_tbl_no_pk_xrcv |
                                      +---------------------------------+
                                      3 rows in set (0.01 sec)

                                      果然,分表只在分库里存在,且表结构一样。同样,创建分表如果不带主键,后端库里的分表会自动创建自增列做主键。这里不再验证。

                                      接下来看分库分表,M=8, N>1 。

                                        mysql> show topology from multi_db_multi_tbl ;
                                        +------+-----------------------------+---------------------------+----------------+
                                        | ID | GROUP_NAME | TABLE_NAME | PARTITION_NAME |
                                        +------+-----------------------------+---------------------------+----------------+
                                        | 0 | TESTDB_DEFAULT_000000_GROUP | multi_db_multi_tbl_buSd_0 | - |
                                        | 1 | TESTDB_DEFAULT_000000_GROUP | multi_db_multi_tbl_buSd_1 | - |
                                        | 2 | TESTDB_DEFAULT_000000_GROUP | multi_db_multi_tbl_buSd_2 | - |
                                        | 3 | TESTDB_DEFAULT_000001_GROUP | multi_db_multi_tbl_buSd_0 | - |
                                        | 4 | TESTDB_DEFAULT_000001_GROUP | multi_db_multi_tbl_buSd_1 | - |
                                        | 5 | TESTDB_DEFAULT_000001_GROUP | multi_db_multi_tbl_buSd_2 | - |
                                        | 6 | TESTDB_DEFAULT_000002_GROUP | multi_db_multi_tbl_buSd_0 | - |
                                        | 7 | TESTDB_DEFAULT_000002_GROUP | multi_db_multi_tbl_buSd_1 | - |
                                        | 8 | TESTDB_DEFAULT_000002_GROUP | multi_db_multi_tbl_buSd_2 | - |
                                        | 9 | TESTDB_DEFAULT_000003_GROUP | multi_db_multi_tbl_buSd_0 | - |
                                        | 10 | TESTDB_DEFAULT_000003_GROUP | multi_db_multi_tbl_buSd_1 | - |
                                        | 11 | TESTDB_DEFAULT_000003_GROUP | multi_db_multi_tbl_buSd_2 | - |
                                        | 12 | TESTDB_DEFAULT_000004_GROUP | multi_db_multi_tbl_buSd_0 | - |
                                        | 13 | TESTDB_DEFAULT_000004_GROUP | multi_db_multi_tbl_buSd_1 | - |
                                        | 14 | TESTDB_DEFAULT_000004_GROUP | multi_db_multi_tbl_buSd_2 | - |
                                        | 15 | TESTDB_DEFAULT_000005_GROUP | multi_db_multi_tbl_buSd_0 | - |
                                        | 16 | TESTDB_DEFAULT_000005_GROUP | multi_db_multi_tbl_buSd_1 | - |
                                        | 17 | TESTDB_DEFAULT_000005_GROUP | multi_db_multi_tbl_buSd_2 | - |
                                        | 18 | TESTDB_DEFAULT_000006_GROUP | multi_db_multi_tbl_buSd_0 | - |
                                        | 19 | TESTDB_DEFAULT_000006_GROUP | multi_db_multi_tbl_buSd_1 | - |
                                        | 20 | TESTDB_DEFAULT_000006_GROUP | multi_db_multi_tbl_buSd_2 | - |
                                        | 21 | TESTDB_DEFAULT_000007_GROUP | multi_db_multi_tbl_buSd_0 | - |
                                        | 22 | TESTDB_DEFAULT_000007_GROUP | multi_db_multi_tbl_buSd_1 | - |
                                        | 23 | TESTDB_DEFAULT_000007_GROUP | multi_db_multi_tbl_buSd_2 | - |
                                        +------+-----------------------------+---------------------------+----------------+
                                        24 rows in set (0.01 sec)


                                        mysql> show rule from multi_db_multi_tbl ;
                                        +------+--------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                        | ID | TABLE_NAME | BROADCAST | DB_PARTITION_KEY | DB_PARTITION_POLICY | DB_PARTITION_COUNT | TB_PARTITION_KEY | TB_PARTITION_POLICY | TB_PARTITION_COUNT |
                                        +------+--------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                        | 0 | multi_db_multi_tbl | 0 | id | hash | 8 | bid | hash | 3 |
                                        +------+--------------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                        1 row in set (0.01 sec)

                                        最后我们来试试能不能不分库只分表,即M=1, N>1 。

                                          mysql> CREATE TABLE single_db_multi_tbl( id bigint not null auto_increment, bid int, name varchar(30), primary key(id) ) tbpartition by hash(bid) tbpartitions 3;
                                          ERROR 4601 (HY000): [159830f976401000][172.17.0.4:59533][testdb_default]ERR-CODE: [PXC-4601][ERR_EXECUTOR] A single database shard with multiple table shards is not allowed in PolarDB-X
                                          mysql>

                                          看来是限制了不行,估计是为了管理方便。可以改为只分库不分表,也可以接受。

                                          不过甲方用户也是能够决定产品的用法的。为了让用户用起来更方便,PolarDB-X推出自动分库分表功能。

                                          自动分表

                                          在创建数据库时,如果mode选项指定为 auto ,那又是另外一种形态。

                                            mysql> create database testdb_auto mode='auto';
                                            Query OK, 1 row affected (0.07 sec)


                                            mysql> use testdb_auto;
                                            Database changed


                                            mysql> CREATE TABLE single_tbl( id bigint not null auto_increment,name varchar(30),primary key(id));
                                            Query OK, 0 rows affected (0.67 sec)


                                            mysql> show create table single_tbl \G
                                            *************************** 1. row ***************************
                                            TABLE: single_tbl
                                            CREATE TABLE: CREATE TABLE `single_tbl` (
                                            `id` bigint(20) NOT NULL AUTO_INCREMENT,
                                            `name` varchar(30) DEFAULT NULL,
                                            PRIMARY KEY (`id`)
                                            ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 DEFAULT COLLATE = utf8mb4_0900_ai_ci
                                            1 row in set (0.01 sec)


                                            mysql> show topology from single_tbl ;
                                            +------+--------------------------+-----------------------+----------------+--------------------+-----------------+
                                            | ID | GROUP_NAME | TABLE_NAME | PARTITION_NAME | PHY_DB_NAME | DN_ID |
                                            +------+--------------------------+-----------------------+----------------+--------------------+-----------------+
                                            | 0 | TESTDB_AUTO_P00000_GROUP | single_tbl_BaPx_00000 | p1 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                            | 1 | TESTDB_AUTO_P00000_GROUP | single_tbl_BaPx_00001 | p2 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                            | 2 | TESTDB_AUTO_P00000_GROUP | single_tbl_BaPx_00002 | p3 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                            | 3 | TESTDB_AUTO_P00000_GROUP | single_tbl_BaPx_00003 | p4 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                            | 4 | TESTDB_AUTO_P00000_GROUP | single_tbl_BaPx_00004 | p5 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                            | 5 | TESTDB_AUTO_P00000_GROUP | single_tbl_BaPx_00005 | p6 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                            | 6 | TESTDB_AUTO_P00000_GROUP | single_tbl_BaPx_00006 | p7 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                            | 7 | TESTDB_AUTO_P00000_GROUP | single_tbl_BaPx_00007 | p8 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                            +------+--------------------------+-----------------------+----------------+--------------------+-----------------+
                                            8 rows in set (0.00 sec)




                                            mysql> show rule from single_tbl ;
                                            +------+------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                            | ID | TABLE_NAME | BROADCAST | DB_PARTITION_KEY | DB_PARTITION_POLICY | DB_PARTITION_COUNT | TB_PARTITION_KEY | TB_PARTITION_POLICY | TB_PARTITION_COUNT |
                                            +------+------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                            | 0 | single_tbl | 0 | | | | id | KEY | 8 |
                                            +------+------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                            1 row in set (0.00 sec)

                                            从拓扑看,虽然建表没有指定分库分表语法,PolarDB-X 还是自动分了8个分表在一个分库里。从分库名字和分表名字推断,应该还有参数指定自动分库时默认分库数量和分表数量。

                                            我们再看看后端 MySQL。

                                              mysql> show databases;
                                              +-----------------------+
                                              | Database |
                                              +-----------------------+
                                              | __cdc___000000 |
                                              | __cdc___single |
                                              | __recycle_bin__ |
                                              | information_schema |
                                              | mysql |
                                              | performance_schema |
                                              | sys |
                                              | testdb_auto_p00000 |
                                              | testdb_default_000000 |
                                              | testdb_default_000001 |
                                              | testdb_default_000002 |
                                              | testdb_default_000003 |
                                              | testdb_default_000004 |
                                              | testdb_default_000005 |
                                              | testdb_default_000006 |
                                              | testdb_default_000007 |
                                              | testdb_default_single |
                                              +-----------------------+
                                              17 rows in set (0.00 sec)


                                              mysql> show tables from testdb_auto_p00000 ;
                                              +------------------------------+
                                              | Tables_in_testdb_auto_p00000 |
                                              +------------------------------+
                                              | __drds_global_tx_log |
                                              | single_tbl_bapx_00000 |
                                              | single_tbl_bapx_00001 |
                                              | single_tbl_bapx_00002 |
                                              | single_tbl_bapx_00003 |
                                              | single_tbl_bapx_00004 |
                                              | single_tbl_bapx_00005 |
                                              | single_tbl_bapx_00006 |
                                              | single_tbl_bapx_00007 |
                                              +------------------------------+
                                              9 rows in set (0.00 sec)


                                              mysql> use testdb_auto_p00000;
                                              Database changed
                                              mysql> show create table single_tbl_bapx_00000 \G
                                              *************************** 1. row ***************************
                                              Table: single_tbl_bapx_00000
                                              Create Table: CREATE TABLE `single_tbl_bapx_00000` (
                                              `id` bigint(20) NOT NULL AUTO_INCREMENT,
                                              `name` varchar(30) DEFAULT NULL,
                                              PRIMARY KEY (`id`)
                                              ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
                                              1 row in set (0.00 sec)

                                              索引表

                                              分库分表架构早期有个痛点,就是不支持全局的二级索引。那个时候创建的二级索引都是每个分表上独立索引,没有办法形成一个整体索引。早期 DRDS 在支持这个功能的时候,先是让业务创建一个索引表充当这个作用,由业务去维护这个“索引表”和业务表的数据一致性。后来 DRDS 改为自己做索引表的数据同步(支持分布式事务)。目前看PolarDB-X ,这个索引的功能已经很完备和成熟了。用户在创建索引的时候感知不到后端有个索引表。

                                                mysql> create index idx_name on single_tbl(name);
                                                Query OK, 0 rows affected (1.61 sec)


                                                mysql> show create table single_tbl \G
                                                *************************** 1. row ***************************
                                                TABLE: single_tbl
                                                CREATE TABLE: CREATE TABLE `single_tbl` (
                                                `id` bigint(20) NOT NULL AUTO_INCREMENT,
                                                `name` varchar(30) DEFAULT NULL,
                                                PRIMARY KEY (`id`),
                                                INDEX `idx_name` (`name`)
                                                ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 DEFAULT COLLATE = utf8mb4_0900_ai_ci
                                                1 row in set (0.01 sec)


                                                mysql> show tables;
                                                +-----------------------+
                                                | TABLES_IN_TESTDB_AUTO |
                                                +-----------------------+
                                                | single_tbl |
                                                +-----------------------+
                                                1 row in set (0.01 sec)

                                                不过我们使用 show topology 和 show rule 命令,还是能发现这个隐藏的细节。

                                                  mysql> show rule;
                                                  +------+----------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                                  | ID | TABLE_NAME | BROADCAST | DB_PARTITION_KEY | DB_PARTITION_POLICY | DB_PARTITION_COUNT | TB_PARTITION_KEY | TB_PARTITION_POLICY | TB_PARTITION_COUNT |
                                                  +------+----------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                                  | 0 | idx_name_$b02c | 0 | | | | name,id | KEY | 8 |
                                                  | 1 | single_tbl | 0 | | | | id | KEY | 8 |
                                                  +------+----------------+-----------+------------------+---------------------+--------------------+------------------+---------------------+--------------------+
                                                  2 rows in set (0.00 sec)


                                                  mysql> show topology from idx_name_$b02c ;
                                                  +------+--------------------------+---------------------------+----------------+--------------------+-----------------+
                                                  | ID | GROUP_NAME | TABLE_NAME | PARTITION_NAME | PHY_DB_NAME | DN_ID |
                                                  +------+--------------------------+---------------------------+----------------+--------------------+-----------------+
                                                  | 0 | TESTDB_AUTO_P00000_GROUP | idx_name_$b02c_7gwU_00000 | p1 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                                  | 1 | TESTDB_AUTO_P00000_GROUP | idx_name_$b02c_7gwU_00001 | p2 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                                  | 2 | TESTDB_AUTO_P00000_GROUP | idx_name_$b02c_7gwU_00002 | p3 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                                  | 3 | TESTDB_AUTO_P00000_GROUP | idx_name_$b02c_7gwU_00003 | p4 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                                  | 4 | TESTDB_AUTO_P00000_GROUP | idx_name_$b02c_7gwU_00004 | p5 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                                  | 5 | TESTDB_AUTO_P00000_GROUP | idx_name_$b02c_7gwU_00005 | p6 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                                  | 6 | TESTDB_AUTO_P00000_GROUP | idx_name_$b02c_7gwU_00006 | p7 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                                  | 7 | TESTDB_AUTO_P00000_GROUP | idx_name_$b02c_7gwU_00007 | p8 | testdb_auto_p00000 | pxc-tryout-dn-0 |
                                                  +------+--------------------------+---------------------------+----------------+--------------------+-----------------+
                                                  8 rows in set (0.00 sec)

                                                  再查看一下后端表结构,就是包含主键和索引的字段。

                                                    mysql> use testdb_auto_p00000;


                                                    Database changed
                                                    mysql> show tables;
                                                    +------------------------------+
                                                    | Tables_in_testdb_auto_p00000 |
                                                    +------------------------------+
                                                    | __drds_global_tx_log |
                                                    | idx_name_$b02c_7gwu_00000 |
                                                    | idx_name_$b02c_7gwu_00001 |
                                                    | idx_name_$b02c_7gwu_00002 |
                                                    | idx_name_$b02c_7gwu_00003 |
                                                    | idx_name_$b02c_7gwu_00004 |
                                                    | idx_name_$b02c_7gwu_00005 |
                                                    | idx_name_$b02c_7gwu_00006 |
                                                    | idx_name_$b02c_7gwu_00007 |
                                                    | single_tbl_bapx_00000 |
                                                    | single_tbl_bapx_00001 |
                                                    | single_tbl_bapx_00002 |
                                                    | single_tbl_bapx_00003 |
                                                    | single_tbl_bapx_00004 |
                                                    | single_tbl_bapx_00005 |
                                                    | single_tbl_bapx_00006 |
                                                    | single_tbl_bapx_00007 |
                                                    +------------------------------+
                                                    17 rows in set (0.00 sec)


                                                    mysql> show create table idx_name_$b02c_7gwu_00005 \G
                                                    *************************** 1. row ***************************
                                                    Table: idx_name_$b02c_7gwu_00005
                                                    Create Table: CREATE TABLE `idx_name_$b02c_7gwu_00005` (
                                                    `id` bigint(20) NOT NULL,
                                                    `name` varchar(30) DEFAULT NULL,
                                                    PRIMARY KEY (`id`),
                                                    KEY `auto_shard_key_name` (`name`) USING BTREE
                                                    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
                                                    1 row in set (0.00 sec)

                                                    索引表只会在自动分库类型的数据库中存在,如果是DRDS那种手动分库类型里,通常不会有索引表。但是如果使用分区表且有索引时,也会自动创建索引表。

                                                    分区表

                                                    看到 PolarDB-X 支持分区表我还是有点意外。本来最早分库分表架构诞生的一个原因就是MySQL自身的分区表功能太弱性能太差。

                                                    不过 PolarDB-X 的分区表简单试了一下,跟 MySQL 分区表没有关系,其原理依然是分表技术。初步使用时还不明白为什么要有这个语法,可能也是被客户需求教育的。这里针对分区表就不深入探索了,感兴趣的直接查看帮助文档:https://docs.polardbx.com/dev-guide/topics/partition-syntax.html


                                                    分库分表如何决策

                                                    到目前我们已经看到 PolarDB-X 的分库分表的功能特点。分库分表技术诞生最早,也最容易被人理解接受。虽然自从有了 TiDB 不用分库分表的技术方案后,分库分表备受争议,不过这并不能抹灭这个架构的价值和存在的意义。从 PolarDB-X 的历史可知分库分表架构支撑了淘宝天猫多年的双十一大促,在大数据高并发情况下,加上3个以上的异地站点多活和单元化的苛刻需求,分库分表仍然是唯一可行的解决方案。原因可以参考《数据库的异地多活分析和方案》。目前市面上大部分分布式数据库的架构里还都有分库分表的影子,这也是一个证明。从 PolarDB-X 的产品文档里的SQL手册也可以看出推出了很多非标准SQL,但是都是用户真实的需求带来的 SQL,所以这个架构依然有着很强的生命力。

                                                    对于普通的用户来说,自动拆分、分库分表拆分和分区表拆分等技术都是工具。各个产品都在朝着让用户使用简单的方向发展。比如说对用户隐藏内部拆分细节、隐藏分布式事务细节。PolarDB-X 2.0 相比早期 DRDS,已经做到了这点,尽管跟其他竞品相比看起来并不“高端”。这里要说的是对用户简单,并不意味着事情就很简单,很多时候产品只是将复杂隐藏在内部(留给自己),把简单留给用户。在普通的业务场景下,简单总是更好一些。但是当业务要求变得不简单时,如大数据量、或高并发、或读写混合时,深入使用分布式数据库,想要保持高性能、高可靠时,DBA还是需要深入了解产品的内在原理,还是要去了解那些被隐藏的复杂细节。然后根据这个复杂的特点,引导业务做相应的优化。对于分布式拆分这个工具而言,不同工具特性原理有相同之处,也有不同之处。要最大化发挥分布式数据库的性能需要将数据的访问请求特点、SQL的执行计划和分布特点跟数据库存储节点数据存储特点三者保持一致或尽可能契合。阿里巴巴的异地多活单元化就是一个典范。有这样特点的业务并不仅此一家。还有火车票购票系统、证券交易所的交易、结算系统、券商的交易系统等等,都非常的有互联网特质(大数据,高并发,可拆分),并且特点还不一样,复杂不可怕,可怕的是不能掌控。到底哪个产品更适合自己的业务,还需要深入测试和分析。

                                                    单说 PolarDB-X,业务分库和分表又如何定呢。实际业务特点决定会存在一批表不能拆分,这些表就放到单库里以单表形式存在,最好是单独一个实例。其他可拆分的业务,根据业务模块再划分为多个实例然后单独拆分。不同业务的拆分的实例数、分库数不一定相同。同一个业务模块不同表的拆分字段尽可能相同。如果分库拆分字段不同,尽可能用不同实例的不同分库(建表可以指定使用哪些 DB GROUP或DN节点)。分表字段也可以不同,挑战是表关联。在PolarDB-X 里有一个特殊的表叫广播表,适合不能拆分的表,表数据量有一定规模,还会有变化但不是很频繁。这个表会被多个可拆分业务模块高度依赖,经常关联查询。那么建成广播表就可以将这个表的内容复制到多个分库中。这个数据同步是有PolarDB-X自动维护的。广播表的设计在OceanBase 里对应的方案是复制表,目的都是一样的,让关联查询尽可能在单个节点内部发生,减少跨节点的数据传输。从而提升数据库总体吞吐能力。这里就不深入展开了。


                                                    sysbench 使用介绍

                                                    观察一个分布式数据库除了看功能和跑简单的SQL外,还可以用 sysbench 做 OLTP 场景测试。sysbench 是开源项目,支持 MySQL、PG、ORACLE 等常用传统数据库连接。PolarDB-X兼容MySQL数据库,理论上也可以直接跑 sysbench。这里推荐 PolarDB 团队提供的 sysbench ,里面增加了很多场景测试语句。阿里云上官方给出 PolarDB-X的sysbench 测试实践,具体请参考 https://help.aliyun.com/document_detail/405017.html ,这里也不一一展示了。

                                                    我测试了DRDS那种分库分表的用法,也测试了自动分库的用法。下面是sysbench建表,使用DRDS 那种分库类型。

                                                      sysbench --db-driver=mysql --mysql-host=192.168.110.8 --mysql-port=61074 --mysql-user=sbtest --mysql-password='scaleflux' --mysql-db=sysbenchdb --table-size=100000000 --tables=20 --threads=20 --time=120 --events=0 --report-interval=60 --percentile=99 --create_table_options='dbpartition by hash(`id`) tbpartition by hash(id) tbpartitions 4' --create_secondary=true oltp_read_only.lua run

                                                      在实际sysbench 初始化和测试之前,先设置一组PolarDB-X 参数,这都是为了避免后面各种报错或者提升性能的。这些参数就不一一解释了。具体参数可以查看:https://docs.polardbx.com/maintance/topics/cn-variables.html 。

                                                      下面参数在PolarDB-X 实例中设置:

                                                        set global CONN_POOL_MIN_POOL_SIZE=50;
                                                        set global CONN_POOL_XPROTO_MAX_POOLED_SESSION_PER_INST=1000;
                                                        set global XPROTO_MAX_DN_CONCURRENT=4000;
                                                        set global XPROTO_MAX_DN_WAIT_CONNECTION=4000;
                                                        set global RECORD_SQL = false;
                                                        set global ENABLE_CPU_PROFILE = false;
                                                        set global GROUP_PARALLELISM = 1;
                                                        set global SHARE_READ_VIEW = false;
                                                        set global CONN_POOL_XPROTO_XPLAN = false;
                                                        set global NEW_SEQ_GROUPING_TIMEOUT=30000;
                                                        set global MAX_SESSION_PREPARED_STMT_COUNT=1000;
                                                        set global sync_binlog=0;
                                                        set global innodb_flush_log_at_trx_commit=0;

                                                        下面参数在后端DN节点的MySQL实例中设置:

                                                          set global binlog_expire_logs_seconds=10800;


                                                          set global sync_binlog=0;
                                                          set global innodb_flush_log_at_trx_commit=0;

                                                          注意,上面为了提升数据初始化速度,将 MySQL 的Redo 和 binlog 日志都设置为不实时落盘。当数据初始化完成后 做性能测试的时候,为了对比 PolarDB-X 在 CSD 和普通 SSD 上性能差异,还是会将两个落盘参数改回1 。

                                                          此外,sysbench 默认是初始完数据后再创建索引,虽然指定了20个并行,后来发现到索引创建这个环节,创建索引是串行执行的。这导致后面的很多索引创建任务最终都超时退出了。也许有个超时参数可以规避这个问题,不过我选择修改 oltp_common.lua 脚本,把索引创建语句移到数据初始化之前了(空表的时候建索引)。这样规避了报错,只是数据初始化时间变长了一倍多。这是一个经验,不一定非要这样。


                                                          sysbench 数据存储成本分析

                                                          这一节主要分析 sysbench 数据在可计算存储上的成本。

                                                          sysbench 提供了列的字符串的生成参数,在 oltp_common.lua 文件中,默认 sysbench 数据都是随机字符串。通常随机字符串也可以压缩,使用gzip算法时,压缩比大概在1.4 左右(这是不严谨的估算)。阿里云 PolarDB MySQL 业务的数据放在可计算存储 CSD2000 产品上压缩比大概在 2.4~4.0 ,不同业务数据压缩比会略有不同。为了让 sysbench 测试尽可能接近实际业务,我对  oltp_common.lua  脚本做了一些修改,使得生成的数据不是那么的完全随机,从而让整个数据压缩比提升到合理水平。

                                                            vim /usr/local/share/sysbench/oltp_common.lua
                                                            :set nu


                                                            176 -- Template strings of random digits with 11-digit groups separated by dashes
                                                            177
                                                            178 -- 10 groups, 119 characters
                                                            179 local c_value_template = "0000000000#-0000000000#-0000000000#-" ..
                                                            180 "0000000000#-0000000000#-012345#####-" ..
                                                            181 "###########-###########-###########-" ..
                                                            182 "###########"
                                                            183
                                                            184 -- 5 groups, 59 characters
                                                            185 local pad_value_template = "0000000000#-0000000000#-0000000000#-" ..
                                                            186 "012########-###########"
                                                            187

                                                            50表总共50亿记录,数据目录大概 1.4 TiB左右。

                                                              [root@sfx110008 polardb-test]# df -h |grep nvme
                                                              /dev/nvme1n1 3.5T 1.4T 1.9T 43% /data/nvme1n1
                                                              /dev/nvme0n1 3.5T 1.4T 2.0T 41% /data/nvme0n1

                                                              CSD3000 实际NAND物理容量大概使用 578GB 左右。

                                                                [root@sfx110008 polardb-test]# nvme list
                                                                Node SN Model Namespace Usage Format FW Rev
                                                                ---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
                                                                /dev/nvme0n1 UE2237C1501M CSD-3310 1 547.06 GB / 3.84 TB 4 KiB + 0 B U3219118
                                                                /dev/nvme1n1 BTAC14930A0A3P8AGN INTEL SSDPF2KX038TZ 1 3.84 TB / 3.84 TB 4 KiB + 0 B JCV10100

                                                                sysbench数据在CSD3000上数据压缩比大概在2.75 左右。

                                                                  [root@sfx110008 polardb-test]# sfx-status


                                                                  SFX card: /dev/nvme0n1
                                                                  PCIe Vendor ID: 0xcc53
                                                                  PCIe Subsystem Vendor ID: 0xcc53
                                                                  Manufacturer: ScaleFlux
                                                                  Model: CSD-3310
                                                                  Serial Number: UE2237C1501M
                                                                  OPN: CSDU5SPC38M1
                                                                  Drive Type: U.2-V
                                                                  Firmware Revision: U3219118
                                                                  Temperature: 36 C
                                                                  Power Consumption: 10956 mW
                                                                  Atomic Write mode: ON
                                                                  Percentage Used: 1%
                                                                  Data Read: 133657 GiB
                                                                  Data Written: 324914 GiB
                                                                  Correctable Error Cnt: 0
                                                                  Uncorrectable Error Cnt: 0
                                                                  PCIe Link Status: Speed 16GT/s, Width x4
                                                                  PCIe Device Status: Good
                                                                  Formatted Capacity: 3840 GB
                                                                  Provisioned Capacity: 3840 GB
                                                                  Compression Ratio: 275%
                                                                  Physical Used Ratio: 14%
                                                                  Free Physical Space: 3292 GB
                                                                  RSA Verify: OFF
                                                                  AES Encryption: OFF
                                                                  IO Speed: Normal
                                                                  Critical Warning: 0
                                                                  [root@sfx110008 polardb-test]#

                                                                  当反复初始化和压测数据后,将 CSD 和 SSD 的逻辑容量所有地址(LBA)都写过一轮后,CSD3000 的 NAND 物理使用容量维持在 1.32 TB 左右,压缩比在 2.28 左右。

                                                                    [root@sfx110008 polardb-test]# nvme list
                                                                    Node SN Model Namespace Usage Format FW Rev
                                                                    ---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- --------
                                                                    /dev/nvme0n1 UE2237C1501M CSD-3310 1 1.32 TB / 3.84 TB 4 KiB + 0 B U3219118
                                                                    /dev/nvme1n1 BTAC14930A0A3P8AGN INTEL SSDPF2KX038TZ 1 3.84 TB / 3.84 TB 4 KiB + 0 B JCV10100

                                                                    这里对 SSD 挂载文件系统时没有指定 discard 选项,所以删除文件时SSD里NAND使用空间不会释放。严格来说有点浪费SSD物理空间,不过大部分用户都是这么简单使用SSD的。

                                                                      [root@sfx110008 polardb-test]# cat /etc/fstab |grep nvme
                                                                      /dev/nvme0n1 /data/nvme0n1 ext4 nodelalloc,defaults,noatime 0 0
                                                                      /dev/nvme1n1 /data/nvme1n1 ext4 nodelalloc,defaults,noatime 0 0

                                                                      此时测试 sysbench OLTP 场景比较接近生产环境(SSD 写过一段时间后接近稳态),其结果可以作为 CSD3000 替换普通SSD的参考。


                                                                      sysbench 测试问题

                                                                      基本上每个国产数据库在初次使用的时候,面对sysbench 大数据量高并发压测的时候,都会问题频发。首要原因就是产品的测试文档对于测试资源的要求不明确或者解释的不明确,对于参数的调优说的太少或者太简单。当你熬过这个阶段后,对这个数据库的理解肯定会有显著的进步。虽然我对 DRDS 产品有一些了解,在面临 PolarDB-X 时,测试还是会碰到连接问题、SQL 超时问题、死锁等等问题。能通过搜索文档解决一部分,还有一部分是要咨询原厂研发才能解决。

                                                                      首先是连接问题。

                                                                      连接问题

                                                                      问题1:连接 prepare 问题。

                                                                        FATAL: mysql_stmt_prepare() failed
                                                                        FATAL: MySQL error: 0 "[15839d19e4401002][192.168.110.8:61074][sysbenchdb]Can't create more than max_prepared_stmt_count statements in one session (current value: 100)"
                                                                        FATAL: `thread_init' function failed: ./oltp_common.lua:298: SQL API error
                                                                        FATAL: mysql_stmt_prepare() failed

                                                                        sysbench 默认运行测试的时候,会自动发起 prepare 语句。并发高的时候 prepare 会话可能超出一个 PolarDB-X 会话(应用到CN节点的会话)允许的限制。

                                                                        解决方法一是修改参数 MAX_SESSION_PREPARED_STMT_COUNT 值。这里麻烦的是看不到这个参数的默认值是多少,即使自己设置了,也查不到它的值(也可能是我没找到方法)

                                                                          mysql> set global MAX_SESSION_PREPARED_STMT_COUNT=512;
                                                                          Query OK, 0 rows affected (0.01 sec)

                                                                          方法二就是 sysbench 带上参数 --db-ps-mode=disable  直接关闭 prepare 省事。

                                                                          问题2:不能创建连接。

                                                                          sysbench 的threads 指定的过高时可能会碰到无法创建连接的报错。这个报错原因可能是来自 CN 节点的设置,也可能是来自后端 DN 节点(MySQL)的设置。由于一个逻辑库后面对应16个分库(我这里有2个DN节点),所以一个客户端连接有可能会导致后端增加16个连接。

                                                                          sysbench 跟 PolarDB-X 的CN节点之间的连接是短连接,CN 响应客户端请求时内部有维护一个连接池,并通过参数
                                                                          CONN_POOL_MIN_POOL_SIZE、XPROTO_MAX_DN_CONCURRENT、 XPROTO_MAX_DN_WAIT_CONNECTION、CONN_POOL_XPROTO_MAX_POOLED_SESSION_PER_INST 等参数控制这个连接池的行为。CN节点跟DN节点的连接也受 DN 节点MySQL自身的连接参数限制。不过由于这个后端连接是PolarDB-X 自定义协议(RPC协议)的连接,看起来这个连接数是受 MySQL 的参数 mysqlx_max_connections 限制。这些参数都需要相应的调大。

                                                                          此外,当sysbench的连接执行的是写操作时,后端 DN 节点上还会出现相应的由CDC发起的会话(可能是在获取DN节点 MySQL 的 binlog 增量),这部分会话数量也随着压测会话数增长而增长。所以我也没有知道精确计算这些参数的方法。只要把CN节点并发连接数设置的够大(比如说 2000),后端DN节点 mysql 的 mysqlx_max_connections 设置到 8000 以上。那么理论上 sysbench 压测 1000个并发至少不会碰到连接报错问题。

                                                                          PolarDB-X的连接和会话是分离的,客户端发起多少个连接,并不意味着就会有多少个会话去执行请求,而是会从一个池子里取获取可用的会话去相应请求。如果没有就会排队等待,直到等待超时。所以连接报错也不一定是立即就出现的。

                                                                          以上的分析是根据仅有的文档描述猜测的,对于连接和会话的概念并没有严格区分,不排除有可能理解错了。但解决方法大体不差。

                                                                          PolarDB-X 也提供了几个有用的命令诊断连接问题。

                                                                          命令 show ds 用于查看各个分库简单信息。

                                                                            mysql> show databases;
                                                                            +--------------------+
                                                                            | DATABASE |
                                                                            +--------------------+
                                                                            | information_schema |
                                                                            | sysbenchdb |
                                                                            | sysbenchdb_auto |
                                                                            +--------------------+
                                                                            3 rows in set (0.00 sec)


                                                                            mysql> show ds;
                                                                            +------+-----------------+--------------------+---------------------------------+------------------------+---------+
                                                                            | ID | STORAGE_INST_ID | DB | GROUP | PHY_DB | MOVABLE |
                                                                            +------+-----------------+--------------------+---------------------------------+------------------------+---------+
                                                                            | 0 | pxcintel-gms | information_schema | INFORMATION_SCHEMA_SINGLE_GROUP | polardbx_info_schema | 0 |
                                                                            | 1 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_000000_GROUP | sysbenchdb_000000 | 1 |
                                                                            | 2 | pxcintel-dn-1 | sysbenchdb | SYSBENCHDB_000001_GROUP | sysbenchdb_000001 | 1 |
                                                                            | 3 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_000002_GROUP | sysbenchdb_000002 | 1 |
                                                                            | 4 | pxcintel-dn-1 | sysbenchdb | SYSBENCHDB_000003_GROUP | sysbenchdb_000003 | 1 |
                                                                            | 5 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_000004_GROUP | sysbenchdb_000004 | 1 |
                                                                            | 6 | pxcintel-dn-1 | sysbenchdb | SYSBENCHDB_000005_GROUP | sysbenchdb_000005 | 1 |
                                                                            | 7 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_000006_GROUP | sysbenchdb_000006 | 1 |
                                                                            | 8 | pxcintel-dn-1 | sysbenchdb | SYSBENCHDB_000007_GROUP | sysbenchdb_000007 | 1 |
                                                                            | 9 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_000008_GROUP | sysbenchdb_000008 | 1 |
                                                                            | 10 | pxcintel-dn-1 | sysbenchdb | SYSBENCHDB_000009_GROUP | sysbenchdb_000009 | 1 |
                                                                            | 11 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_000010_GROUP | sysbenchdb_000010 | 1 |
                                                                            | 12 | pxcintel-dn-1 | sysbenchdb | SYSBENCHDB_000011_GROUP | sysbenchdb_000011 | 1 |
                                                                            | 13 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_000012_GROUP | sysbenchdb_000012 | 1 |
                                                                            | 14 | pxcintel-dn-1 | sysbenchdb | SYSBENCHDB_000013_GROUP | sysbenchdb_000013 | 1 |
                                                                            | 15 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_000014_GROUP | sysbenchdb_000014 | 1 |
                                                                            | 16 | pxcintel-dn-1 | sysbenchdb | SYSBENCHDB_000015_GROUP | sysbenchdb_000015 | 1 |
                                                                            | 17 | pxcintel-dn-0 | sysbenchdb | SYSBENCHDB_SINGLE_GROUP | sysbenchdb_single | 0 |
                                                                            | 18 | pxcintel-dn-0 | sysbenchdb_auto | SYSBENCHDB_AUTO_P00000_GROUP | sysbenchdb_auto_p00000 | 1 |
                                                                            | 19 | pxcintel-dn-1 | sysbenchdb_auto | SYSBENCHDB_AUTO_P00001_GROUP | sysbenchdb_auto_p00001 | 1 |
                                                                            +------+-----------------+--------------------+---------------------------------+------------------------+---------+
                                                                            20 rows in set (0.00 sec)

                                                                                    要看具体的连接池信息,还要切换到对应的数据库下。

                                                                              mysql> use sysbenchdb_auto;
                                                                              Database changed


                                                                              mysql> show datasources \G
                                                                              *************************** 1. row ***************************
                                                                              ID: 0
                                                                              SCHEMA: sysbenchdb_auto@pxcintel
                                                                              NAME: dskey_sysbenchdb_auto_p00000_group#pxcintel-dn-0#192.168.110.8-15898#sysbenchdb_auto_p00000_48
                                                                              GROUP: SYSBENCHDB_AUTO_P00000_GROUP
                                                                              URL: X://admin@192.168.110.8:43898/sysbenchdb_auto_p00000?connectTimeout=5000&characterEncoding=utf8mb4&socketTimeout=900000
                                                                              USER: admin
                                                                              TYPE: xdb
                                                                              INIT: 0
                                                                              MIN: 32
                                                                              MAX: 32768
                                                                              IDLE_TIMEOUT: 60
                                                                              MAX_WAIT: 5000
                                                                              ON_FATAL_ERROR_MAX_ACTIVE: 4000
                                                                              MAX_WAIT_THREAD_COUNT: 8000
                                                                              ACTIVE_COUNT: 0
                                                                              POOLING_COUNT: 41
                                                                              ATOM: dskey_sysbenchdb_auto_p00000_group#pxcintel-dn-0#192.168.110.8-15898#sysbenchdb_auto_p00000
                                                                              READ_WEIGHT: 10
                                                                              WRITE_WEIGHT: 10
                                                                              STORAGE_INST_ID: pxcintel-dn-0

                                                                              如果分库类型是 DRDS 模式,每个数据库后端会对应16个分库(这里是2个 DN 节点),这个会输出很多记录。每个分库的连接池信息在这里面都有体现,只是修改这个连接池就要靠CN节点提供的参数。具体哪个参数的修改会影响到这里面的各个字段,还需要深入测试分析。这里也不展开了。

                                                                              关于连接数还有个规律。如果要并发连接数尽可能的高,那么单个连接的 SQL 执行时间就要尽可能的短。对于PK查询和更新,1000 个并发更新引发的后端会话数可能也就 1000出头(还有部分CDC会话),但是如果跑的是 oltp_read_only 场景,涉及到复杂的SQL时,则每个DN节点上后端连接数可能就到了 8000 左右了。所以连接报错问题也看当前连接数规模以及SQL平均执行时间。如果本身连接数规模已经很大了,那优化方向首先应该是SQL性能优化。

                                                                              DDL问题

                                                                              sysbench 初始化数据集默认是表数据加载完毕再创建二级索引。当分库类型是自动分库时,PolarDB-X 会创建独立的索引表去模拟“索引”的功能,所以这个会分批次抽取所有分表的数据,在 CN 节点内部重新排序,然后再写入索引表。这个过程也比较久。同时看起来好像每一时刻,数据库下只能有一个DDL会话(这点文档里没有说明,是我猜测的。如果不对,欢迎指出),导致很多索引创建会话排队等待超时最终报错。说起分布式数据库的DDL串行执行,在其他分布式数据库里我也看到过。推测DDL涉及到元数据的变更,可能会导致数据库内部相关 cache 失效,可能对分布式数据库性能稳定性影响比较大,所以分布式数据库先采用串行执行。

                                                                              这个问题通过修改 oltp_common.lua 脚本,将索引放到建表语句中绕过。代价就是数据初始化变慢了。

                                                                              PolarDB-X 比较友好的地方是提供了命令取查看 DDL 的状态和结果。

                                                                              命令:show ddl result 用于查看当前数据库下的 DDL 线程结果。

                                                                              命令 show ddl 可以查看正在运行的 DDL。如果SQL执行比较快,这里可能看不到。正常情况下 PolarDB-X 一个DDL 执行时间在毫秒级别。如果是建空表或删除表长期不结束,那可能有别的原因。

                                                                              如果 DDL 出现报错,会在这里显示。此时可以选择用命令是继续 RECOVER  DDL 还是回滚 ROLLBACK  DDL。没有这个功能的时候,DDL 也是分库分表的一个痛点,尤其是部分分库执行成功部分失败时,需要 DBA 挨个去检查 并修补。

                                                                              死锁问题

                                                                              首先要理解,死锁问题通常应用的事务设计问题。两个不同业务事务,按相反的顺序锁定了相关记录,导致数据库发生死锁。MySQL 在 RR 隔离级别下单表 DML 也可能会发生死锁。这种就按 MySQL 的解决方法去排查。在分布式数据库里,死锁涉及到多个 DN 节点上的事务,情形更复杂。

                                                                              PolarDB-X 提供了命令 show deadlock 查看死锁。并且区分是 DN 节点本地死锁还是所有 DN 节点间的全局死锁。

                                                                              分布式死锁的探测技术门槛比较高,支持这个的分布式数据库并不多。不过我这里没有展开测试分布式死锁案例。

                                                                              SQL 性能问题

                                                                              SQL 执行慢时,CN 节点和 DN 节点的连接数都可能会上涨,服务器压力也增加。此时首先要排查慢 SQL。单实例MySQL 里可以看慢查日志,在分布式MySQL里,后端MySQL 太多,挨个排查效率不高。PolarDB-X 提供命令 show slow 用于查看慢SQL。

                                                                                mysql> show slow;
                                                                                +------------------+--------+---------------+------------+---------------------+--------------+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
                                                                                | TRACE_ID | USER | HOST | DB | START_TIME | EXECUTE_TIME | AFFECT_ROW | SQL |
                                                                                +------------------+--------+---------------+------------+---------------------+--------------+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
                                                                                | 15986b09ab001002 | sbtest | 192.168.110.8 | sysbenchdb | 2023-01-28 18:25:23 | 2875 | 0 | CREATE TABLE sbtest20( id INTEGER NOT NULL, k INTEGER DEFAULT '0' NOT NULL, c CHAR(120) DEFAULT '' NOT NULL, pad CHAR(60) DEFAULT '' NOT NULL, PRIMARY KEY (id) ) /*! ENGINE = innodb */ |
                                                                                | 15986b0a2c801000 | sbtest | 192.168.110.8 | sysbenchdb | 2023-01-28 18:25:23 | 2710 | 0 | CREATE INDEX k_16 ON sbtest16(k) |
                                                                                | 15986b0b47001000 | sbtest | 192.168.110.8 | sysbenchdb | 2023-01-28 18:25:24 | 2612 | 0 | CREATE INDEX k_11 ON sbtest11(k)
                                                                                <…>




                                                                                | 15986d2852801000 | sbtest | 192.168.110.8 | sysbenchdb | 2023-01-28 18:34:37 | 1007 | 0 | CREATE TABLE sbtest26( id INTEGER NOT NULL, k INTEGER DEFAULT '0' NOT NULL, c CHAR(120) DEFAULT '' NOT NULL, pad CHAR(60) DEFAULT '' NOT NULL, PRIMARY KEY (id) ) /*! ENGINE = innodb */ |
                                                                                +------------------+--------+---------------+------------+---------------------+--------------+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
                                                                                63 rows in set (0.03 sec)

                                                                                命令 clear slow 清除上次慢SQL结果。

                                                                                命令 trace SQL 可以跟踪特定SQL。命令 show trace 可以显示最近一次SQL跟踪结果。

                                                                                命令show [full] trans 可以查看当前事务。目前信息还比较少,期待以后能完善。

                                                                                命令 explain [detail | execute ] SQL 可以查看SQL执行计划,或者查看后端 DN 节点上该 SQL 的执行计划。不过后端DN节点上的 SQL 不一定跟实际SQL完全一样。CN 节点在SQL 解析时可能会做对SQL条件进行拆分、裁剪或聚合等,这考验的是 PolarDB-X SQL引擎的能力。而这个一直都是产品后期发展的主要方向(要支持HTAP复杂场景)。


                                                                                sysbench在可计算存储上测试结论

                                                                                最后简单说明一下对比测试的信息。sysbench 50 表,总共 50 亿数据,两个 PolarDB-X 集群,DN 节点数据分别存储在 CSD3000 和 P5510 上。CSD3000 上的 PolarDB-X  集群先后重建两次,区别在于第二次重建的时候,将文件系统格式化重新挂载,启用 ext4 bigalloc特性,支持16KB IO,同时关闭DN节点的MySQL的原子写(参数 innodb_doublewrite)。CSD3000 默认开启了原子写功能,支持 4KB~128KB IO 的原子写。

                                                                                观察 sysbench 在不同并发下的吞吐QPS和相应的平均延时RT。

                                                                                QPS 对比如下:

                                                                                RT对比如下:

                                                                                从这个测试结果上可以看,在只读场景里,CSD3000 相比 P5510 并没有太大优势,但是在写场景里,CSD3000 上 PolarDB-X 性能会有提升。尤其是在延时方面会更低一些。关闭 MySQL 双写启用 CSD3000 原子写的性能有提升,但幅度看起来没有 MySQL 单实例测试那么大。

                                                                                总体来说 PolarDB-X 部署在 CSD3000 在读写方面有一些性能提升,幅度不是特别大。不过部署在 CSD3000上实际并发数还可以再高一些。

                                                                                数据库性能测试有很大的不确定性。数据量、并发数、内存大小等,以及服务器负载等等。这次测试并没有把服务器 CPU 利用率压测到 80%以上,主要是 PolarDB-X 实例参数方面可能还不是最优的,有些稳定性问题还不知道原因导致压测并发上不去。此外就是计算和存储节点没有分离部署。

                                                                                不过数据压缩比倒是接近线上业务数据压缩比,在这样的压缩比下,同等物理容量的 CSD3000 相比普通的 SSD 能节省近 57% 的闪存容量。反过来对 CSD3000 进行 2倍 扩容使用(3.84 T 盘当 7.68 T盘用)时,跟普通 SSD 相比,依然能节省很多闪存容量,同时在部分场景依然有不同程度的性能提升。这就是可计算存储降本增效的原理。




                                                                                更多阅读





                                                                                文章转载自数据库技术闲谈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                                                评论