<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">



<title type="text">Sherry Yin</title>
<generator uri="https://github.com/mojombo/jekyll">Jekyll</generator>
<link rel="self" type="application/atom+xml" href="http://www.solidstack.io/feed.xml" />
<link rel="alternate" type="text/html" href="http://www.solidstack.io/" />
<updated>2026-11-14T00:12:48+08:00</updated>
<id>http://www.solidstack.io/</id>
<author>
  <name>Sherry Yin</name>
  <uri>http://www.solidstack.io/</uri>
  <email>yinxi@ymail.com</email>
</author>


<entry>
  <title type="html"><![CDATA[用 MySQL 的经验理解 MongoDB 鉴权：账号、角色与集群内部认证]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2026/10/mongodb-vs-mysql-access-control/"/>
  <id>http://www.solidstack.io/posts/2026/10/mongodb-vs-mysql-access-control</id>
  <published>2026-10-11T00:00:00+08:00</published>
  <updated>2026-10-11T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#MongoDB" term="MongoDB" /><category scheme="http://www.solidstack.io/tags/#MySQL" term="MySQL" /><category scheme="http://www.solidstack.io/tags/#%E9%89%B4%E6%9D%83" term="鉴权" /><category scheme="http://www.solidstack.io/tags/#%E8%AE%BF%E9%97%AE%E6%8E%A7%E5%88%B6" term="访问控制" /><category scheme="http://www.solidstack.io/tags/#%E5%89%AF%E6%9C%AC%E9%9B%86" term="副本集" /><category scheme="http://www.solidstack.io/tags/#%E5%88%86%E7%89%87%E9%9B%86%E7%BE%A4" term="分片集群" /><category scheme="http://www.solidstack.io/tags/#%E5%AE%89%E5%85%A8" term="安全" />
  <content type="html">
  
    &lt;p&gt;从 MySQL 转到 MongoDB 的人，很少会在 CRUD 上卡住，卡住的往往是鉴权：用户明明建了，连接却报 Authentication failed；角色授了，权限却不是想象中的那样；副本集开了认证以后，节点之间互相连不上。原因多半是带着 MySQL 的心智模型去理解 MongoDB，而两者在几个关键地方并不一样。&lt;/p&gt;

&lt;p&gt;这篇文章按 MySQL 的概念顺序，逐个对照 MongoDB 的做法，重点放在差异和容易踩的坑上。文中所有命令和输出都来自一套本地实验环境：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;MySQL 8.4&lt;/li&gt;
  &lt;li&gt;MongoDB 8.0.4，一个 3 节点副本集 rs0，另有一个最小的分片集群（1 个 config server、1 个分片、1 个 mongos）&lt;/li&gt;
  &lt;li&gt;两台客户端容器 client-a（172.30.0.21）和 client-b（172.30.0.22），用来演示按来源 IP 限制登录&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MySQL 用的是 8.4。还在用 8.0 的话，和认证相关的主要差别有几处：8.0 默认仍启用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql_native_password&lt;/code&gt;，8.4 默认禁用，用它创建账号会报 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Plugin &apos;mysql_native_password&apos; is not loaded&lt;/code&gt;；8.4 去掉了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default_authentication_plugin&lt;/code&gt; 变量，改用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authentication_policy&lt;/code&gt;；8.4 还去掉了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW SLAVE STATUS&lt;/code&gt; 这类旧的复制语法。依赖 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql_native_password&lt;/code&gt; 的老驱动连 8.4 会失败，需要用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--mysql-native-password=ON&lt;/code&gt; 显式打开，而到了 9.0 这个插件已经被移除。&lt;/p&gt;

&lt;p&gt;实验环境的代码在 GitHub 上：&lt;a href=&quot;https://github.com/yinx0004/mongo-mysql-auth-lab&quot;&gt;mongo-mysql-auth-lab&lt;/a&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./lab.sh up&lt;/code&gt; 启动环境，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./lab.sh run 00&lt;/code&gt; 到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;11&lt;/code&gt; 分别对应文中各节，其中 10 是 MySQL 8.0 和 8.4 的对比，11 是 MySQL 复制账号需要的权限。命令都是完整的；输出里过长的部分，比如报错信息里的 lsid 和 $clusterTime，我用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;...&lt;/code&gt; 省略了。&lt;/p&gt;

&lt;h2 id=&quot;先分清认证和授权&quot;&gt;先分清认证和授权&lt;/h2&gt;

&lt;p&gt;认证（authentication）回答”你是谁”，授权（authorization）回答”你能做什么”。这两步合起来，中文里常被统称为鉴权，本文标题用的就是这个意思；下文提到具体某一步时，只用认证和授权。MySQL 和 MongoDB 都有这两步，但组合方式不一样，后面很多差异都可以追溯到这里。&lt;/p&gt;

&lt;p&gt;MySQL 把两者绑在一起。建立连接时必须先完成认证：用户名、来源主机和密码要匹配上 mysql.user 里的某个账号，才会有会话；之后每执行一条语句，再按授权表检查权限。不存在”先连上、还没认证”的状态：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -e &apos;SELECT 1&apos;
ERROR 1045 (28000): Access denied for user &apos;root&apos;@&apos;172.30.0.21&apos; (using password: NO)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;唯一能把认证和授权一起关掉的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--skip-grant-tables&lt;/code&gt;，一般只在找回 root 密码这类紧急情况下使用。&lt;/p&gt;

&lt;p&gt;MongoDB 把两者拆开了。开启访问控制以后，没认证的连接照样能建立，只是除了 hello、ping 这类命令以外什么都做不了：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://mongo1:27017/?directConnection=true&quot; \
    --eval &apos;print(&quot;ping:&quot;, db.adminCommand({ping: 1}).ok);
            print(&quot;hello.me:&quot;, db.hello().me);
            db.adminCommand({listDatabases: 1})&apos;
ping: 1
hello.me: mongo1:27017
MongoServerError: Command listDatabases requires authentication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更关键的是，授权是一个单独的开关，也就是配置里的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;security.authorization&lt;/code&gt;，默认关闭，开启集群内部认证（keyfile 或 x509）时会自动打开。授权关闭时，连接如果带了用户名和密码，密码照样会被验证，错了一样报 Authentication failed；可一旦验证通过，权限就不再检查了。下面是一个没开访问控制的单机 mongod，用户 reader 只有 shop 库的 read 角色：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://reader:wrong-pw@noauth:27017/shop&quot; --eval &apos;db.orders.countDocuments()&apos;
MongoServerError: Authentication failed.

[client-a]$ mongosh &quot;mongodb://reader:reader-pw@noauth:27017/shop&quot; \
    --eval &apos;print(JSON.stringify(db.runCommand({connectionStatus: 1}).authInfo.authenticatedUsers));
            db.orders.insertOne({item: &quot;pen&quot;}).acknowledged&apos;
[{&quot;user&quot;:&quot;reader&quot;,&quot;db&quot;:&quot;shop&quot;}]
true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;reader 认证成功了，只读角色却写入成功。所以”用户都建好了，密码也验证通过了”并不代表集群受到了保护，要确认授权真的开着。可以看启动参数，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parsed&lt;/code&gt; 里没有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;security&lt;/code&gt; 一项就说明没开：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://reader:reader-pw@noauth:27017/shop&quot; \
    --eval &apos;JSON.stringify(db.adminCommand({getCmdLineOpts: 1}).parsed)&apos;
{&quot;net&quot;:{&quot;bindIp&quot;:&quot;*&quot;}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;分清这两步，报错也就好理解了：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;报错&lt;/th&gt;
      &lt;th&gt;哪一步出的问题&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MySQL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR 1045 Access denied for user&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;认证&lt;/td&gt;
      &lt;td&gt;账号不存在、主机不匹配或密码错误&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MySQL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR 1142&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1143 command denied&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;授权&lt;/td&gt;
      &lt;td&gt;已经登录，但没有这张表或这一列的权限&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authentication failed&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;认证&lt;/td&gt;
      &lt;td&gt;认证库里没有这个用户、密码错误，或不满足来源限制&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Command &amp;lt;命令名&amp;gt; requires authentication&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;还没认证&lt;/td&gt;
      &lt;td&gt;访问控制已开启，这个连接没有认证&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;not authorized on &amp;lt;库名&amp;gt; to execute command&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;授权&lt;/td&gt;
      &lt;td&gt;已经认证，但角色里没有这个操作&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;账号是什么userhost-和认证库&quot;&gt;账号是什么：user@host 和认证库&lt;/h2&gt;

&lt;p&gt;在 MySQL 里，账号由用户名和主机两部分组成，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;alice&apos;@&apos;%&apos;&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;alice&apos;@&apos;172.30.0.21&apos;&lt;/code&gt; 是两个完全独立的账号，可以有不同的密码和权限：&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;alice&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;IDENTIFIED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;alice-any-host&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;alice&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;172.30.0.21&apos;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDENTIFIED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;alice-client-a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从 client-a 登录时，MySQL 会优先匹配更具体的主机，所以只有 client-a 那个账号的密码能用：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u alice -p -e &quot;SELECT USER(), CURRENT_USER()&quot;
+-------------------+-------------------+
| USER()            | CURRENT_USER()    |
+-------------------+-------------------+
| alice@172.30.0.21 | alice@172.30.0.21 |
+-------------------+-------------------+

[client-a]$ mysql -h mysql -u alice -p     # 用 &apos;alice&apos;@&apos;%&apos; 的密码
ERROR 1045 (28000): Access denied for user &apos;alice&apos;@&apos;172.30.0.21&apos; (using password: YES)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从 client-b 登录，只能匹配到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;alice&apos;@&apos;%&apos;&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CURRENT_USER()&lt;/code&gt; 也随之变成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alice@%&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;MongoDB 的账号里没有主机，取而代之的是认证库（authentication database）：用户在哪个库里创建，就属于哪个库。shop 库里的 alice 和 admin 库里的 alice 是两个不同的用户：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alice&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alice-shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;readWrite&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]})&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alice&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alice-admin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reports&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;不管在哪个库创建，用户文档其实都存在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin.system.users&lt;/code&gt; 里，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_id&lt;/code&gt; 是”认证库.用户名”：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&quot;_id&quot;:&quot;admin.alice&quot;,&quot;user&quot;:&quot;alice&quot;,&quot;db&quot;:&quot;admin&quot;,&quot;roles&quot;:[{&quot;role&quot;:&quot;read&quot;,&quot;db&quot;:&quot;reports&quot;}]}
{&quot;_id&quot;:&quot;shop.alice&quot;,&quot;user&quot;:&quot;alice&quot;,&quot;db&quot;:&quot;shop&quot;,&quot;roles&quot;:[{&quot;role&quot;:&quot;readWrite&quot;,&quot;db&quot;:&quot;shop&quot;}]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;认证库只决定这个用户在哪里验证身份，不限制它能访问哪些库。admin 库的 alice 拥有的是 reports 库的读权限。&lt;/p&gt;

&lt;p&gt;因为身份里没有主机，同一个用户从 client-a 和 client-b 登录没有任何区别。想按来源限制，要用后面讲的 authenticationRestrictions。&lt;/p&gt;

&lt;h2 id=&quot;authsource最常见的-authentication-failed&quot;&gt;authSource：最常见的 Authentication failed&lt;/h2&gt;

&lt;p&gt;先看 MongoDB 连接串的格式。驱动、mongosh 和各种工具用的都是同一种 URI：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mongodb://用户名:密码@主机1:端口1,主机2:端口2/默认库?选项1=值1&amp;amp;选项2=值2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以本文用到的一个连接串为例：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mongodb://alice:alice-shop@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&amp;amp;authSource=shop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;部分&lt;/th&gt;
      &lt;th&gt;本例&lt;/th&gt;
      &lt;th&gt;说明&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;协议&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongodb://&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;另一种是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongodb+srv://&lt;/code&gt;，主机列表从 DNS 的 SRV 记录里取，Atlas 用的就是这种&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;用户名和密码&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alice:alice-shop@&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;可以省略，省略就是不认证。密码里有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%&lt;/code&gt; 这类字符时，要做百分号编码，比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@&lt;/code&gt; 写成 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%40&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;主机列表&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongo1:27017,mongo2:27017,mongo3:27017&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;种子列表。驱动连上其中任意一个，就能从它那里拿到副本集的全部成员，所以不必写全，但最好多写几个，以防其中一个正好不可用&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;默认库&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/shop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;不指定库时操作的库，同时也是 authSource 的默认值，这一点下面细说&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;replicaSet&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rs0&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;按副本集方式连接，驱动会自动找到主节点，主节点切换后也会跟着切&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;authSource&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;去哪个库验证用户名和密码&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;其他常用的选项还有 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;directConnection=true&lt;/code&gt;（只连这一个节点，不做副本集发现，本文直连某个成员或分片时用的就是它）、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls=true&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readPreference&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt;，以及 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appName&lt;/code&gt;。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appName&lt;/code&gt; 会出现在服务端日志和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currentOp&lt;/code&gt; 里，排查时很容易认出是哪个应用的连接。它是客户端自己上报的，驱动默认不设置，服务端也不校验，所以要在每个服务的连接串里显式写上，并且不能拿它做访问控制。&lt;/p&gt;

&lt;p&gt;要注意，mongosh 连单个主机且没写 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replicaSet&lt;/code&gt; 时，会自动加上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;directConnection=true&lt;/code&gt;。如果这个主机恰好是从节点，写入会直接报错：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://alice:alice-shop@mongo1:27017/shop&quot; \
    --eval &apos;print(db.getMongo()._uri); db.orders.insertOne({x: 1})&apos;
mongodb://alice:alice-shop@mongo1:27017/shop?directConnection=true&amp;amp;appName=mongosh+2.3.8
MongoServerError: not primary
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对比 MySQL：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql -h mysql -u alice -p shop&lt;/code&gt;，或者 JDBC 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jdbc:mysql://mysql:3306/shop&lt;/code&gt;，里面的库名只是连上以后默认使用的 schema，和身份验证没有关系，因为 MySQL 没有认证库这个概念。MongoDB 连接串里的库名却会影响认证。&lt;/p&gt;

&lt;p&gt;连接时，客户端必须告诉服务端去哪个库验证身份，这就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authSource&lt;/code&gt;。坑在于它的默认值：如果连接串的路径里写了库名，这个库名会被当作默认的 authSource；路径里没写，默认是 admin。&lt;/p&gt;

&lt;p&gt;路径里写的正好是认证库，一切正常：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://alice:alice-shop@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.orders.findOne()&apos;
{ _id: 1, item: &apos;book&apos; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;路径里不写库名，authSource 回落到 admin，而 admin 库里的 alice 是另一个密码：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://alice:alice-shop@mongo1:27017,mongo2:27017,mongo3:27017/?replicaSet=rs0&quot; \
    --eval &apos;db.getSiblingDB(&quot;shop&quot;).orders.findOne()&apos;
MongoServerError: Authentication failed.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;反过来的情况更隐蔽。路径里写的是想要使用的库，它同时也成了 authSource：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://alice:alice-admin@mongo1:27017,mongo2:27017,mongo3:27017/reports?replicaSet=rs0&quot; \
    --eval &apos;db.getName()&apos;
MongoServerError: Authentication failed.

[client-a]$ mongosh &quot;mongodb://alice:alice-admin@mongo1:27017,mongo2:27017,mongo3:27017/reports?replicaSet=rs0&amp;amp;authSource=admin&quot; \
    --eval &apos;db.getName()&apos;
reports
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;客户端拿到的永远是同一句 Authentication failed。要知道具体原因，得看服务端日志里 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;msg&quot;:&quot;Failed to authenticate&quot;&lt;/code&gt; 那一行的 error 字段，上面两次失败分别是：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;user=alice db=admin    error: AuthenticationFailed: SCRAM authentication failed, storedKey mismatch
user=alice db=reports  error: UserNotFound: Could not find user &quot;alice&quot; for db &quot;reports&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;UserNotFound 说明 authSource 不对，storedKey mismatch 说明这个库里确实有这个用户，但密码不对。我的建议是，生产环境的连接串一律显式写上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authSource&lt;/code&gt;，不依赖默认值。&lt;/p&gt;

&lt;h2 id=&quot;限制登录来源&quot;&gt;限制登录来源&lt;/h2&gt;

&lt;p&gt;MySQL 把允许的主机直接写在账号里：&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;report&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;172.30.0.21&apos;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDENTIFIED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;report-pw&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-b]$ mysql -h mysql -u report -p -e &quot;SELECT CURRENT_USER()&quot;
ERROR 1045 (28000): Access denied for user &apos;report&apos;@&apos;172.30.0.22&apos; (using password: YES)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MongoDB 对应的是用户上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authenticationRestrictions&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clientSource&lt;/code&gt; 填允许的客户端 IP 或网段，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serverAddress&lt;/code&gt; 可以限制客户端连的是哪个服务端地址：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;report-pw&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reports&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;authenticationRestrictions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clientSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;172.30.0.21&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从 client-a 登录成功，从 client-b 登录失败。客户端看到的仍然只是 Authentication failed，连 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Failed to authenticate&lt;/code&gt; 那行日志里的 error 也只写了 Authentication failed。真正的原因在它前面的另一行日志里：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Failed to acquire user because of unmet authentication restrictions
reason: Evaluation of direct authentication restrictions failed:
        Restriction &apos;{anyOf: [{allOf: [{&quot;clientSource&quot;: [&quot;172.30.0.21/32&quot;]}]}]}&apos; ... unmet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;authenticationRestrictions 也可以设置在角色上，这样拥有该角色的用户都会继承这个限制。很多从 MySQL 过来的人不知道 MongoDB 有这个功能，以为只能靠防火墙和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bindIp&lt;/code&gt; 控制来源。&lt;/p&gt;

&lt;h2 id=&quot;授权grant-与角色&quot;&gt;授权：GRANT 与角色&lt;/h2&gt;

&lt;p&gt;MySQL 8.0 引入了角色，但角色授予用户以后，默认并不生效：&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROLE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;app_read&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;app_read&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bob&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDENTIFIED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bob-pw&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;app_read&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bob&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u bob -p -e &quot;SHOW GRANTS; SELECT CURRENT_ROLE(); SELECT * FROM shop.orders&quot;
+-----------------------------------+
| Grants for bob@%                  |
+-----------------------------------+
| GRANT USAGE ON *.* TO `bob`@`%`   |
| GRANT `app_read`@`%` TO `bob`@`%` |
+-----------------------------------+
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE           |
+----------------+
ERROR 1142 (42000) at line 1: SELECT command denied to user &apos;bob&apos;@&apos;172.30.0.21&apos; for table &apos;orders&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;角色已经授予，但当前会话没有激活任何角色。激活角色分三个层面。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET ROLE&lt;/code&gt; 只作用于当前会话，新开的会话又回到没有角色的状态：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u bob -p -e &quot;SET ROLE &apos;app_read&apos;; SELECT CURRENT_ROLE(); SELECT * FROM shop.orders&quot;
+----------------+
| CURRENT_ROLE() |
+----------------+
| `app_read`@`%` |
+----------------+
+----+------+
| id | item |
+----+------+
|  1 | book |
+----+------+
[client-a]$ mysql -h mysql -u bob -p -e &quot;SELECT CURRENT_ROLE()&quot;
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE           |
+----------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET DEFAULT ROLE &apos;app_read&apos; TO &apos;bob&apos;@&apos;%&apos;&lt;/code&gt; 作用于这个账号以后的每次登录，对已经打开的会话不起作用。全局参数 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activate_all_roles_on_login&lt;/code&gt; 则作用于整个实例，打开后所有账号登录时都会激活自己被授予的全部角色。另外，MySQL 一直都可以绕过角色，直接把权限授予用户，比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT INSERT ON shop.orders TO &apos;bob&apos;@&apos;%&apos;&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;MongoDB 正好相反：权限只能放在角色里，用户只能绑定角色。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createUser&lt;/code&gt; 根本没有接收权限的字段：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; db.getSiblingDB(&quot;shop&quot;).createUser({user: &quot;bob&quot;, pwd: &quot;bob-pw&quot;, roles: [],
    privileges: [{resource: {db: &quot;shop&quot;, collection: &quot;orders&quot;}, actions: [&quot;find&quot;]}]})
MongoServerError: BSON field &apos;createUser.privileges&apos; is an unknown field.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以要先建一个自定义角色，权限由资源（库、集合，或者整个集群）加一组操作（find、insert、createIndex 等）组成：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;orderReader&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;privileges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;orders&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}]})&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;bob&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;bob-pw&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;orderReader&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;角色授予后立即生效，不需要激活。bob 能读 orders，读 customers 会被拒绝：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://bob:bob-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.customers.findOne()&apos;
MongoServerError: not authorized on shop to execute command { find: &quot;customers&quot;, filter: {}, limit: 1, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MongoDB 也没有默认角色。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; 是 createUser 的必填字段，省略会直接报错：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; db.getSiblingDB(&quot;shop&quot;).runCommand({createUser: &quot;nobody&quot;, pwd: &quot;nobody-pw&quot;})
MongoServerError: BSON field &apos;createUser.roles&apos; is missing but a required field
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以显式写 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles: []&lt;/code&gt;。这样的用户能通过认证，但 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authenticatedUserRoles&lt;/code&gt; 是空的，读数据、列集合，甚至改自己的密码都不行，差不多相当于 MySQL 里只有 USAGE 的新账号：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://nobody:nobody-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.runCommand({connectionStatus: 1}).authInfo&apos;
{
  authenticatedUsers: [ { user: &apos;nobody&apos;, db: &apos;shop&apos; } ],
  authenticatedUserRoles: []
}
[client-a]$ mongosh &quot;mongodb://nobody:nobody-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.changeUserPassword(&quot;nobody&quot;, &quot;new-pw&quot;)&apos;
MongoServerError: not authorized on shop to execute command { updateUser: &quot;nobody&quot;, pwd: &quot;xxx&quot;, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;还有一个规则：建在普通库里的角色，权限只能作用于这个库；只有建在 admin 库里的角色，才能包含其他库或集群级别的权限。&lt;/p&gt;

&lt;p&gt;MongoDB 的内置角色可以大致这样对应到 MySQL：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;MongoDB 内置角色&lt;/th&gt;
      &lt;th&gt;大致相当于 MySQL&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;read&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT SELECT ON db.*&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;readWrite&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT SELECT, INSERT, UPDATE, DELETE ON db.*&lt;/code&gt;，外加建集合、建索引&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;dbAdmin&lt;/td&gt;
      &lt;td&gt;库级的 DDL、统计和 profiler，但不能读业务数据&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;userAdmin&lt;/td&gt;
      &lt;td&gt;在这个库里建用户、建角色、授权&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;dbOwner&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT ALL ON db.*&lt;/code&gt;，包含上面三个&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;readAnyDatabase、readWriteAnyDatabase 等&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ON *.*&lt;/code&gt; 的版本&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;root&lt;/td&gt;
      &lt;td&gt;接近 MySQL 的 root&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;在把管理权限拆细这件事上，MySQL 8.0 向 MongoDB 靠近了一步。以前的管理类权限集中在一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUPER&lt;/code&gt; 上，8.0 把它拆成了一组按功能划分的动态权限，比如修改系统变量的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SYSTEM_VARIABLES_ADMIN&lt;/code&gt;、杀别人连接的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONNECTION_ADMIN&lt;/code&gt;、管理 binlog 的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BINLOG_ADMIN&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUPER&lt;/code&gt; 本身被标记为废弃。动态权限只能授予在全局级别（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ON *.*&lt;/code&gt;），存在 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql.global_grants&lt;/code&gt; 表里，插件和组件也可以注册自己的动态权限：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u root -p -e &quot;GRANT SUPER ON *.* TO &apos;ops&apos;@&apos;%&apos;; SHOW WARNINGS&quot;
+---------+------+----------------------------------------------+
| Level   | Code | Message                                      |
+---------+------+----------------------------------------------+
| Warning | 1287 | The SUPER privilege identifier is deprecated |
+---------+------+----------------------------------------------+
[client-a]$ mysql -h mysql -u root -p -e &quot;GRANT BINLOG_ADMIN ON shop.* TO &apos;ops&apos;@&apos;%&apos;&quot;
ERROR 3619 (HY000) at line 1: Illegal privilege level specified for BINLOG_ADMIN
[client-a]$ mysql -h mysql -u root -p -e &quot;REVOKE SUPER ON *.* FROM &apos;ops&apos;@&apos;%&apos;; GRANT SYSTEM_VARIABLES_ADMIN, CONNECTION_ADMIN ON *.* TO &apos;ops&apos;@&apos;%&apos;; SELECT * FROM mysql.global_grants WHERE USER = &apos;ops&apos;&quot;
+------+------+------------------------+-------------------+
| USER | HOST | PRIV                   | WITH_GRANT_OPTION |
+------+------+------------------------+-------------------+
| ops  | %    | CONNECTION_ADMIN       | N                 |
| ops  | %    | SYSTEM_VARIABLES_ADMIN | N                 |
+------+------+------------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对应到 MongoDB，就是集群资源上的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setParameter&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;killop&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replSetConfigure&lt;/code&gt; 这类操作。动态和静态权限在生效时机上也不一样，下文”权限改了什么时候生效”一节有实测。&lt;/p&gt;

&lt;p&gt;内置角色里，userAdmin 值得单独说一句。在 MySQL 里，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH GRANT OPTION&lt;/code&gt; 只能把自己已有的权限转授给别人。MongoDB 的 userAdmin 可以把这个库的任意角色授予任何用户，包括它自己：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://ua:ua-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.orders.findOne()&apos;
MongoServerError: not authorized on shop to execute command { find: &quot;orders&quot;, ... }

[client-a]$ mongosh &quot;mongodb://ua:ua-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.grantRolesToUser(&quot;ua&quot;, [&quot;dbOwner&quot;]); db.orders.findOne()&apos;
{ _id: 1, item: &apos;book&apos; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以拥有某个库的 userAdmin，实际上就等于拥有这个库的全部权限；admin 库上的 userAdminAnyDatabase 则相当于整个集群的 root。分配这两个角色时要按最高权限来对待。&lt;/p&gt;

&lt;h2 id=&quot;列级权限和视图&quot;&gt;列级权限和视图&lt;/h2&gt;

&lt;p&gt;MySQL 的权限可以细到列：&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;carol&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u carol -p -e &quot;SELECT id, name FROM shop.customers&quot;
+----+------+
| id | name |
+----+------+
|  1 | Ann  |
+----+------+
[client-a]$ mysql -h mysql -u carol -p -e &quot;SELECT * FROM shop.customers&quot;
ERROR 1142 (42000) at line 1: SELECT command denied to user &apos;carol&apos;@&apos;172.30.0.21&apos; for table &apos;customers&apos;
[client-a]$ mysql -h mysql -u carol -p -e &quot;SELECT name, phone FROM shop.customers&quot;
ERROR 1143 (42000) at line 1: SELECT command denied to user &apos;carol&apos;@&apos;172.30.0.21&apos; for column &apos;phone&apos; in table &apos;customers&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;顺带一提，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; 碰到没有权限的列，报的是表级的 1142，明确选了没有权限的列才是列级的 1143，排查时不要被误导。&lt;/p&gt;

&lt;p&gt;MongoDB 的权限只到集合，没有字段级权限。要对某些用户隐藏字段，常见做法是建一个视图，只把视图授权出去：&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;customers_public&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;customers&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;$project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;phone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}])&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;publicReader&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;privileges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;customers_public&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;用户只需要视图的 find 权限，不需要底层集合的权限：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://carol:carol-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.customers_public.find().toArray()&apos;
[ { _id: 1, name: &apos;Ann&apos; } ]
[client-a]$ mongosh &quot;mongodb://carol:carol-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.customers.find().toArray()&apos;
MongoServerError: not authorized on shop to execute command { find: &quot;customers&quot;, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;视图是只读的，需要按字段控制写入的场景，只能在应用层处理。如果目的是防止 DBA 或者能接触到存储的人看到敏感字段，那是另一类问题，要用客户端字段级加密（CSFLE）或 Queryable Encryption，而不是权限。&lt;/p&gt;

&lt;h2 id=&quot;权限改了什么时候生效flush-privileges&quot;&gt;权限改了什么时候生效：FLUSH PRIVILEGES&lt;/h2&gt;

&lt;p&gt;很多 MySQL 教程会在 GRANT 后面跟一句 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLUSH PRIVILEGES&lt;/code&gt;，其实这是多余的。GRANT、REVOKE、CREATE USER、ALTER USER 这些账号管理语句，会同时更新授权表和内存里的权限缓存，执行完立即生效：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u dave -p -e &quot;SELECT * FROM shop.orders&quot;
ERROR 1142 (42000) at line 1: SELECT command denied to user &apos;dave&apos;@&apos;172.30.0.21&apos; for table &apos;orders&apos;

mysql&amp;gt; GRANT SELECT ON shop.orders TO &apos;dave&apos;@&apos;%&apos;;

[client-a]$ mysql -h mysql -u dave -p -e &quot;SELECT * FROM shop.orders&quot;
+----+------+
| id | item |
+----+------+
|  1 | book |
+----+------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLUSH PRIVILEGES&lt;/code&gt; 的作用是让 MySQL 从授权表重新加载权限缓存。只有绕过账号管理语句，直接用 INSERT、UPDATE、DELETE 改了 mysql.user 这类表时才需要它。比如直接把账号的锁定标记改成 Y，在 FLUSH 之前完全不生效：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mysql&amp;gt; UPDATE mysql.user SET account_locked = &apos;Y&apos; WHERE user = &apos;dave&apos;;

[client-a]$ mysql -h mysql -u dave -p -e &quot;SELECT CURRENT_USER()&quot;
+----------------+
| CURRENT_USER() |
+----------------+
| dave@%         |
+----------------+

mysql&amp;gt; FLUSH PRIVILEGES;

[client-a]$ mysql -h mysql -u dave -p -e &quot;SELECT CURRENT_USER()&quot;
ERROR 3118 (HY000): Access denied for user &apos;dave&apos;@&apos;172.30.0.21&apos;. Account is locked.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另一个容易忽略的是已经打开的会话。官方文档把规则分成几类：表级和列级权限的变更，从会话的下一条语句开始生效；库级权限的变更，在下一次 USE 时生效；静态全局权限和密码的变更，对已经连上的会话不生效，要等重新连接；动态全局权限的变更则立即生效。静态全局权限是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON *.*&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUPER&lt;/code&gt; 这类传统权限，动态全局权限是 8.0 以后按功能拆出来的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SESSION_VARIABLES_ADMIN&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BACKUP_ADMIN&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONNECTION_ADMIN&lt;/code&gt; 等，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW PRIVILEGES&lt;/code&gt; 可以列出全部。&lt;/p&gt;

&lt;p&gt;我按这几类各准备了一个用户。每人打开一个会话，执行一条语句，等 4 秒再执行一次，在第 2 秒时收回各自的权限：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;用户&lt;/th&gt;
      &lt;th&gt;被收回的权限&lt;/th&gt;
      &lt;th&gt;同一会话里的第二次执行&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;dave&lt;/td&gt;
      &lt;td&gt;表级 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON shop.orders&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;被拒绝（1142）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;hank&lt;/td&gt;
      &lt;td&gt;库级 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON shop.*&lt;/code&gt;，会话已经 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;USE shop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;仍然成功，到下一次 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;USE shop&lt;/code&gt; 才被拒绝（1044）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ivy&lt;/td&gt;
      &lt;td&gt;库级 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON shop.*&lt;/code&gt;，会话没有 USE 过&lt;/td&gt;
      &lt;td&gt;被拒绝（1142）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;erin&lt;/td&gt;
      &lt;td&gt;静态全局 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON *.*&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;仍然成功，新开的会话才被拒绝&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;gina&lt;/td&gt;
      &lt;td&gt;动态全局 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SESSION_VARIABLES_ADMIN&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;被拒绝（1227）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;jack&lt;/td&gt;
      &lt;td&gt;已激活的角色（角色里是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON shop.*&lt;/code&gt;）&lt;/td&gt;
      &lt;td&gt;被拒绝（1142）&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;hank 和 ivy 的对比说明，”库级权限在下次 USE 时生效”指的是会话当前所在的库：这个库的库级权限缓存在会话里，直到切换数据库。没有切到这个库的会话，下一条语句就会按新权限检查。hank 的会话是这样的：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u hank -p -N shop -e &quot;SELECT &apos;before&apos;, COUNT(*) FROM orders; SELECT SLEEP(4) INTO @x; SELECT &apos;after&apos;, COUNT(*) FROM orders; USE shop; SELECT &apos;after USE&apos;, COUNT(*) FROM orders&quot;
before  1
after   1
ERROR 1044 (42000) at line 1: Access denied for user &apos;hank&apos;@&apos;%&apos; to database &apos;shop&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;erin 和 gina 的对比则是静态和动态全局权限的区别：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u erin -p -N -e &quot;SELECT &apos;before&apos;, COUNT(*) FROM shop.orders; SELECT SLEEP(4) INTO @x; SELECT &apos;after&apos;, COUNT(*) FROM shop.orders&quot;
before  1
after   1

[client-a]$ mysql -h mysql -u gina -p -N -e &quot;SET SESSION sql_log_bin = 1; SELECT &apos;before: SET ok&apos;; SELECT SLEEP(4) INTO @x; SET SESSION sql_log_bin = 1; SELECT &apos;after: SET ok&apos;&quot;
before: SET ok
ERROR 1227 (42000) at line 1: Access denied; you need (at least one of) the SUPER, SYSTEM_VARIABLES_ADMIN or SESSION_VARIABLES_ADMIN privilege(s) for this operation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以收回静态全局权限，或者收回某个库的库级权限时，如果要求立即生效，还要把这个用户已有的连接 KILL 掉。&lt;/p&gt;

&lt;p&gt;MongoDB 没有 FLUSH PRIVILEGES，用户和角色只能通过 createUser、grantRolesToUser 这类命令修改，修改后各成员的用户缓存随之失效。在副本集里，收回角色对已经打开的连接也立即生效。frank 用同一个连接读一次，等 4 秒再读，中间收回了他的 read 角色：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://frank:frank-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;print(&quot;before:&quot;, db.orders.countDocuments());
            sleep(4000);
            print(&quot;after:&quot;, db.orders.countDocuments())&apos;
before: 1
MongoServerError: not authorized on shop to execute command { aggregate: &quot;orders&quot;, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MongoDB 也没有类似 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET ROLE&lt;/code&gt; 的会话级激活，授予用户的角色始终全部生效，所以不存在”会话级”和”全局”之分。连登录来源限制都会作用到已经打开的连接上。kim 从 client-b 连上并读了一次，这时给 kim 加上只允许 client-a 登录的限制，同一个连接的下一次读取直接变成了未认证：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-b]$ mongosh &quot;mongodb://kim:kim-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;print(&quot;before:&quot;, db.orders.countDocuments());
            sleep(4000);
            print(&quot;after:&quot;, db.orders.countDocuments())&apos;
before: 1
MongoServerError: Command aggregate requires authentication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;第 2 秒执行的是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.getSiblingDB(&quot;shop&quot;).updateUser(&quot;kim&quot;, {authenticationRestrictions: [{clientSource: [&quot;172.30.0.21&quot;]}]})&lt;/code&gt;。服务端日志里对应的一行是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Removed user with unmet authentication restrictions from session cache of user information&lt;/code&gt;，也就是把这个用户从连接上移除了。MySQL 改登录主机要用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RENAME USER&lt;/code&gt;，主机只在建立连接时检查，已经打开的会话不会被断开。我在一个从 client-b 打开的会话期间执行了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RENAME USER &apos;lee&apos;@&apos;%&apos; TO &apos;lee&apos;@&apos;172.30.0.21&apos;&lt;/code&gt;，这个会话仍然连着，但表级权限跟着账号一起改了名，下一条读取报 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR 1142&lt;/code&gt;；从 client-b 新开会话则直接报 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR 1045&lt;/code&gt;。两边都让这个用户读不了数据，区别在于 MongoDB 把连接变成了未认证，MySQL 的会话还在，只是没了权限。&lt;/p&gt;

&lt;p&gt;分片集群是个例外。mongos 也会缓存用户信息，按参数 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userCacheInvalidationIntervalSecs&lt;/code&gt; 的设置（默认 30 秒）定期检查缓存是否过期，所以通过一个 mongos 修改的权限，在其他 mongos 上最多可能晚 30 秒生效。需要立即生效时，可以在每个 mongos 上执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.adminCommand({invalidateUserCache: 1})&lt;/code&gt;，这大概是 MongoDB 里最接近 FLUSH PRIVILEGES 的东西。这一点我没有在 lab 里复现，依据的是官方文档。&lt;/p&gt;

&lt;h2 id=&quot;第一个用户localhost-exception&quot;&gt;第一个用户：localhost exception&lt;/h2&gt;

&lt;p&gt;MySQL 在初始化时就会处理 root 密码：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqld --initialize&lt;/code&gt; 生成一个临时密码，Docker 镜像要求传入 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MYSQL_ROOT_PASSWORD&lt;/code&gt; 或者显式选择随机、空密码。除非用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--initialize-insecure&lt;/code&gt; 故意建一个空密码的 root，MySQL 从第一天起就是开启认证的。&lt;/p&gt;

&lt;p&gt;MongoDB 的做法是 localhost exception：开启了访问控制，但系统里一个用户都没有时，来自本机（localhost）的连接可以创建第一个用户。在一个开着 keyfile 认证、还没初始化的副本集上：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://mongo1:27017/?directConnection=true&quot; --eval &apos;rs.initiate()&apos;
MongoServerError: Command replSetInitiate requires authentication

[mongo1]$ mongosh --eval &apos;rs.initiate({_id: &quot;rs0&quot;, members: [
    {_id: 0, host: &quot;mongo1:27017&quot;}, {_id: 1, host: &quot;mongo2:27017&quot;}, {_id: 2, host: &quot;mongo3:27017&quot;}]}).ok&apos;
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;远程连接什么都做不了，本机连接可以初始化副本集，但仍然不能读写数据：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[mongo1]$ mongosh --eval &apos;db.getSiblingDB(&quot;shop&quot;).orders.insertOne({item: &quot;book&quot;})&apos;
MongoServerError: not authorized on shop to execute command { insert: &quot;orders&quot;, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;它能做的就是创建第一个用户。管理员密码事先放在环境变量 PW 里，避免出现在命令行和 shell 历史中。创建完成后，例外立即关闭，本机连接也必须认证：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[mongo1]$ mongosh admin --eval &apos;db.createUser({user: &quot;admin&quot;, pwd: process.env.PW, roles: [&quot;root&quot;]}).ok&apos;
1
[mongo1]$ mongosh --eval &apos;db.getSiblingDB(&quot;admin&quot;).createUser({user: &quot;second&quot;, pwd: &quot;second-pw&quot;, roles: [&quot;root&quot;]})&apos;
MongoServerError: Command createUser requires authentication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;实验中我还发现一个细节：把最后一个用户删掉以后，localhost exception 并不会马上重新打开，要等 mongod 重启。看起来 mongod 会记住”曾经有过用户”这件事，重启后才重新检查。如果不需要这个例外，可以用参数 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enableLocalhostAuthBypass=false&lt;/code&gt; 把它关掉。&lt;/p&gt;

&lt;h2 id=&quot;集群内部认证keyfile&quot;&gt;集群内部认证：keyfile&lt;/h2&gt;

&lt;p&gt;MySQL 的主从复制用的是一个普通账号，复制本身只需要 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REPLICATION SLAVE&lt;/code&gt; 权限，从库用这个账号连主库拉 binlog。很多环境还会一起授予 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REPLICATION CLIENT&lt;/code&gt;，它允许执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW BINARY LOG STATUS&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW BINARY LOGS&lt;/code&gt; 这类查看状态的语句，监控和高可用工具会用到，但不是复制必需的。复制相关的语句从 8.0.22 起陆续改成了 SOURCE、REPLICA 的叫法，8.4 里 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW SLAVE STATUS&lt;/code&gt; 这类旧语法已经直接报语法错误，但这个权限名一直没改，在 8.0 和 8.4 里写 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT REPLICATION REPLICA&lt;/code&gt; 都会报语法错误。&lt;/p&gt;

&lt;p&gt;MongoDB 副本集成员之间不用业务账号，而是内部认证（internal authentication），常见的方式是 keyfile：所有成员持有同一个密钥文件，启动时加上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--keyFile&lt;/code&gt;。在服务端日志里，成员之间的认证是这样的：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;msg&quot;:&quot;Successfully authenticated&quot;,&quot;attr&quot;:{&quot;client&quot;:&quot;172.30.0.13:32994&quot;,&quot;isClusterMember&quot;:true,
  &quot;mechanism&quot;:&quot;SCRAM-SHA-256&quot;,&quot;user&quot;:&quot;__system&quot;,&quot;db&quot;:&quot;local&quot;,...}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;成员以内置的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__system@local&lt;/code&gt; 身份、用 SCRAM-SHA-256 互相认证，密码由 keyfile 派生。有两点要注意：一是开启内部认证会同时开启客户端授权，官方文档的原话是 “Enabling internal authentication also enables client authorization”，所以不需要再单独配置 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;security.authorization&lt;/code&gt;。这对 keyfile 和 x509 两种内部认证都成立，例外是过渡期的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--transitionToAuth&lt;/code&gt;，下文会讲到。二是 keyfile 的权限不能对其他用户可读，否则 mongod 拒绝启动。&lt;/p&gt;

&lt;p&gt;内部认证的另一种方式是 x509 证书。x509 在 MongoDB 里有两种用途，一是客户端用证书登录，二是成员之间用证书做内部认证。前者在自建环境里很少见，后者是官方推荐的生产配置，但实际上用 keyfile 的团队仍然很多。另外要分清，开启 TLS 加密传输不等于用 x509 认证，很多集群开着 TLS，认证用的仍然是密码。&lt;/p&gt;

&lt;h2 id=&quot;给运行中的副本集开启认证&quot;&gt;给运行中的副本集开启认证&lt;/h2&gt;

&lt;p&gt;MongoDB 默认不开启访问控制。一个没有开认证的副本集，任何能连上端口的人都能做任何事：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-b]$ mongosh &quot;mongodb://mongo1:27017,mongo2:27017,mongo3:27017/?replicaSet=rs0&quot; \
    --eval &apos;db.getSiblingDB(&quot;admin&quot;).runCommand({listDatabases: 1}).databases.map(d =&amp;gt; d.name)&apos;
[ &apos;admin&apos;, &apos;config&apos;, &apos;local&apos; ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MySQL 没有对应的问题，所以也没有对应的操作。MongoDB 给已经在跑的副本集开启认证，不停机的做法是借助 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--transitionToAuth&lt;/code&gt;，分三步：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在认证还没开启时，先把管理员和业务用户都建好。此时用户已经存在，只是不强制。&lt;/li&gt;
  &lt;li&gt;逐个成员加上 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--keyFile --transitionToAuth&lt;/code&gt; 重启，先重启从节点，最后对主节点 stepDown 后重启。transitionToAuth 模式下，成员之间用 keyfile 认证，客户端带不带账号都能连。&lt;/li&gt;
  &lt;li&gt;把所有客户端改成带账号连接，再逐个成员去掉 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--transitionToAuth&lt;/code&gt; 重启，只保留 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--keyFile&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;为了观察对业务的影响，我在 client-a 上跑了两个持续写入的进程，每秒约 10 次插入，一个不带账号，一个用 app 账号。每个阶段的写入统计如下：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;阶段&lt;/th&gt;
      &lt;th&gt;不带账号的写入&lt;/th&gt;
      &lt;th&gt;带账号的写入&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;未开认证&lt;/td&gt;
      &lt;td&gt;成功 50，失败 0&lt;/td&gt;
      &lt;td&gt;成功 50，失败 0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第 2 步：滚动切到 transitionToAuth&lt;/td&gt;
      &lt;td&gt;成功 534，失败 0&lt;/td&gt;
      &lt;td&gt;成功 534，失败 0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第 3 步：滚动切到 keyFile&lt;/td&gt;
      &lt;td&gt;成功 353，失败 225&lt;/td&gt;
      &lt;td&gt;成功 563，失败 0&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;带账号的写入全程没有一次失败，主节点 stepDown 时的切换由可重试写入（retryable writes）兜住了。不带账号的写入代表漏改的客户端，在主节点切到 keyFile 模式后开始报错：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Command insert requires authentication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为什么不能跳过第 2 步？我试着把一个从节点直接从无认证切到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--keyFile&lt;/code&gt; 重启，20 秒后看副本集状态：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mongo1:27017  PRIMARY  health=1
mongo2:27017  (not reachable/healthy)  health=0
mongo3:27017  SECONDARY  health=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个节点要求其他成员认证，而其他成员没有 keyfile，它自己去连其他成员时，对方也不认识它的认证请求。transitionToAuth 的作用就是让成员在过渡期两种连接都接受。&lt;/p&gt;

&lt;h2 id=&quot;轮换-keyfile&quot;&gt;轮换 keyfile&lt;/h2&gt;

&lt;p&gt;keyfile 用久了需要轮换，但所有成员必须能互相认证，不能一次全换。从 MongoDB 4.2 开始，keyfile 可以写成 YAML 列表，放多个 key：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;旧 key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;新 key&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;轮换分两步：先逐个成员换成同时包含旧 key 和新 key 的 keyfile 并重启；全部完成后，再逐个成员换成只有新 key 的 keyfile 并重启。实验中两步做完，副本集始终健康，主节点也正常切换。&lt;/p&gt;

&lt;p&gt;同样，如果跳过第一步，只给一个成员换上新 key，它就会和其他成员失联，状态和上一节一样是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(not reachable/healthy)&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&quot;分片集群用户存在哪里&quot;&gt;分片集群：用户存在哪里&lt;/h2&gt;

&lt;p&gt;分片集群里有两套用户。通过 mongos 创建的用户是集群用户，存在 config server 上：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mongos&amp;gt; db.getSiblingDB(&quot;admin&quot;).createUser({user: &quot;analyst&quot;, pwd: &quot;analyst-pw&quot;,
          roles: [{role: &quot;read&quot;, db: &quot;sales&quot;}]})
mongos&amp;gt; db.getSiblingDB(&quot;admin&quot;).system.users.find({}, {_id: 1}).toArray()
[ { _id: &apos;admin.admin&apos; }, { _id: &apos;admin.analyst&apos; } ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过 mongos 访问一切正常，但这个用户在分片上并不存在，直连分片会失败，分片的日志里是 UserNotFound：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://analyst:analyst-pw@shard1:27017/sales?authSource=admin&amp;amp;directConnection=true&quot; \
    --eval &apos;db.orders.countDocuments()&apos;
MongoServerError: Authentication failed.
error: UserNotFound: Could not find user &quot;analyst&quot; for db &quot;admin&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;更需要注意的是，分片本身一个用户都没有，所以分片上的 localhost exception 仍然是开着的。任何能登录分片所在机器的人，都可以在上面创建一个 root 用户：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[shard1]$ mongosh admin --eval &apos;db.createUser({user: &quot;shardadmin&quot;, pwd: &quot;shardadmin-pw&quot;, roles: [&quot;root&quot;]}).ok&apos;
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个分片本地用户只存在于 shard1，mongos 不认识它，用它连 mongos 会认证失败。官方建议给每个分片都创建分片本地的管理员用户，一方面用于直连分片做维护，另一方面也把分片上的 localhost exception 关掉。&lt;/p&gt;

&lt;h2 id=&quot;排查清单&quot;&gt;排查清单&lt;/h2&gt;

&lt;p&gt;遇到鉴权问题时，可以按报错对照：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;现象&lt;/th&gt;
      &lt;th&gt;先看哪里&lt;/th&gt;
      &lt;th&gt;常见原因&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB 报 Authentication failed&lt;/td&gt;
      &lt;td&gt;服务端日志 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Failed to authenticate&lt;/code&gt; 的 error 字段&lt;/td&gt;
      &lt;td&gt;UserNotFound：authSource 不对；storedKey mismatch：密码不对&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;同上，但 error 只有 Authentication failed&lt;/td&gt;
      &lt;td&gt;日志里 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unmet authentication restrictions&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;来源 IP 不在 clientSource 里&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;认证失败，但不知道是哪个应用&lt;/td&gt;
      &lt;td&gt;失败日志里的连接号 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connNNN&lt;/code&gt;，再找同一连接的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client metadata&lt;/code&gt; 日志&lt;/td&gt;
      &lt;td&gt;从里面的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.name&lt;/code&gt; 认出应用，前提是应用设置了 appName&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB 报 not authorized on &lt;库名&gt; to execute command&lt;/库名&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.runCommand({connectionStatus: 1, showPrivileges: true})&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;角色里缺这个操作或资源&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MySQL 报 1142，但角色已经授予&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT CURRENT_ROLE()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;角色没有激活&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;副本集成员 not reachable/healthy&lt;/td&gt;
      &lt;td&gt;该成员的日志&lt;/td&gt;
      &lt;td&gt;成员之间 keyfile 不一致，或认证模式不一致&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;日志里有大量 Checking authorization failed，操作是 atlascli 聚合&lt;/td&gt;
      &lt;td&gt;可以忽略&lt;/td&gt;
      &lt;td&gt;mongosh 连接后在后台自动执行的探测，权限有限的用户会留下这条日志&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;对照速查表&quot;&gt;对照速查表&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;话题&lt;/th&gt;
      &lt;th&gt;MySQL&lt;/th&gt;
      &lt;th&gt;MongoDB&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;账号身份&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;user&apos;@&apos;host&apos;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;用户名 + 认证库&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;连接时指定&lt;/td&gt;
      &lt;td&gt;只需要用户名&lt;/td&gt;
      &lt;td&gt;用户名 + authSource，默认取 URI 路径里的库，没有则为 admin&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;限制来源&lt;/td&gt;
      &lt;td&gt;账号的 host 部分&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authenticationRestrictions.clientSource&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;授权&lt;/td&gt;
      &lt;td&gt;可以直接授予用户，也可以通过角色&lt;/td&gt;
      &lt;td&gt;只能通过角色&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;角色生效&lt;/td&gt;
      &lt;td&gt;需要激活：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET ROLE&lt;/code&gt;（当前会话）、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET DEFAULT ROLE&lt;/code&gt;（账号以后的登录）或 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activate_all_roles_on_login&lt;/code&gt;（整个实例）&lt;/td&gt;
      &lt;td&gt;授予后立即生效&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;最细粒度&lt;/td&gt;
      &lt;td&gt;列&lt;/td&gt;
      &lt;td&gt;集合，字段用视图实现&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;权限变更生效&lt;/td&gt;
      &lt;td&gt;账号管理语句立即生效，直接改授权表要 FLUSH PRIVILEGES；静态全局权限、会话当前所在库的库级权限，对已有会话不生效&lt;/td&gt;
      &lt;td&gt;立即生效，包括已有连接；mongos 的用户缓存默认最多 30 秒&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;第一个管理员&lt;/td&gt;
      &lt;td&gt;初始化时设置 root 密码&lt;/td&gt;
      &lt;td&gt;localhost exception&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;默认是否开启认证&lt;/td&gt;
      &lt;td&gt;是&lt;/td&gt;
      &lt;td&gt;否&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;复制或成员之间认证&lt;/td&gt;
      &lt;td&gt;普通账号 + REPLICATION SLAVE 权限（REPLICATION CLIENT 用于查看状态，非必需）&lt;/td&gt;
      &lt;td&gt;keyfile 或 x509，以 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__system&lt;/code&gt; 身份&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;不停机开启认证&lt;/td&gt;
      &lt;td&gt;不涉及&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--transitionToAuth&lt;/code&gt; 三步走&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;分片集群&lt;/td&gt;
      &lt;td&gt;不涉及&lt;/td&gt;
      &lt;td&gt;mongos 用户在 config server，分片另有本地用户&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2026/10/mongodb-vs-mysql-access-control/&quot;&gt;用 MySQL 的经验理解 MongoDB 鉴权：账号、角色与集群内部认证&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on October 11, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[MongoDB Access Control for MySQL Users: Accounts, Roles and Internal Authentication]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2026/10/mongodb-access-control-for-mysql-users/"/>
  <id>http://www.solidstack.io/posts/2026/10/mongodb-access-control-for-mysql-users</id>
  <published>2026-10-11T00:00:00+08:00</published>
  <updated>2026-10-11T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#MongoDB" term="MongoDB" /><category scheme="http://www.solidstack.io/tags/#MySQL" term="MySQL" /><category scheme="http://www.solidstack.io/tags/#Access%20Control" term="Access Control" /><category scheme="http://www.solidstack.io/tags/#Authentication" term="Authentication" /><category scheme="http://www.solidstack.io/tags/#Authorization" term="Authorization" /><category scheme="http://www.solidstack.io/tags/#Replica%20Set" term="Replica Set" /><category scheme="http://www.solidstack.io/tags/#Sharding" term="Sharding" /><category scheme="http://www.solidstack.io/tags/#Security" term="Security" />
  <content type="html">
  
    &lt;p&gt;People who move from MySQL to MongoDB rarely get stuck on CRUD. They get stuck on access control: the user exists but the connection says Authentication failed, a role is granted but the privileges aren’t what they expected, or the replica set members stop talking to each other once authentication is turned on. Most of the time the cause is a MySQL mental model applied to MongoDB, and the two differ in a few important places.&lt;/p&gt;

&lt;p&gt;This post follows MySQL’s concepts in order and compares each one with how MongoDB does it, with the focus on the differences and the traps. All commands and output come from a local lab:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;MySQL 8.4&lt;/li&gt;
  &lt;li&gt;MongoDB 8.0.4: a three-member replica set rs0, plus a minimal sharded cluster (one config server, one shard, one mongos)&lt;/li&gt;
  &lt;li&gt;Two client containers, client-a (172.30.0.21) and client-b (172.30.0.22), to show login restrictions by client IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MySQL examples use 8.4. If you’re still on 8.0, the main authentication differences are these: 8.0 still enables &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql_native_password&lt;/code&gt; by default, while 8.4 disables it, so creating an account with it fails with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Plugin &apos;mysql_native_password&apos; is not loaded&lt;/code&gt;. 8.4 also removed the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default_authentication_plugin&lt;/code&gt; variable in favor of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authentication_policy&lt;/code&gt;, and dropped old replication syntax such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW SLAVE STATUS&lt;/code&gt;. Old drivers that only speak &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql_native_password&lt;/code&gt; can’t connect to 8.4 until you turn it on with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--mysql-native-password=ON&lt;/code&gt;, and in 9.0 the plugin is gone.&lt;/p&gt;

&lt;p&gt;The lab is on GitHub: &lt;a href=&quot;https://github.com/yinx0004/mongo-mysql-auth-lab&quot;&gt;mongo-mysql-auth-lab&lt;/a&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./lab.sh up&lt;/code&gt; starts it, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./lab.sh run 00&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;11&lt;/code&gt; correspond to the sections below; 10 compares MySQL 8.0 with 8.4, and 11 checks the privileges a MySQL replication user needs. Commands are shown in full. In the output, I cut long parts such as the lsid and $clusterTime in error messages and marked them with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;...&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;authentication-and-authorization&quot;&gt;Authentication and authorization&lt;/h2&gt;

&lt;p&gt;Authentication answers “who are you”, authorization answers “what are you allowed to do”. Both MySQL and MongoDB have the two steps, but they put them together differently, and many of the differences later in this post come from here.&lt;/p&gt;

&lt;p&gt;MySQL ties them together. A connection has to authenticate first: the user name, client host and password must match an account in mysql.user before there is a session at all, and every statement after that is checked against the grant tables. There is no state where you’re connected but not yet authenticated:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -e &apos;SELECT 1&apos;
ERROR 1045 (28000): Access denied for user &apos;root&apos;@&apos;172.30.0.21&apos; (using password: NO)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The only way to switch off both is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--skip-grant-tables&lt;/code&gt;, which is meant for emergencies such as recovering a lost root password.&lt;/p&gt;

&lt;p&gt;MongoDB keeps them apart. With access control on, an unauthenticated connection can still be opened, it just can’t do anything beyond commands like hello and ping:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://mongo1:27017/?directConnection=true&quot; \
    --eval &apos;print(&quot;ping:&quot;, db.adminCommand({ping: 1}).ok);
            print(&quot;hello.me:&quot;, db.hello().me);
            db.adminCommand({listDatabases: 1})&apos;
ping: 1
hello.me: mongo1:27017
MongoServerError: Command listDatabases requires authentication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;More importantly, authorization is a separate switch, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;security.authorization&lt;/code&gt; in the configuration. It’s off by default, and it’s turned on automatically when you enable internal authentication (keyfile or x509). With authorization off, a connection that sends a user name and password still has the password checked, and a wrong one still fails with Authentication failed. Once it passes, though, privileges aren’t checked at all. Here is a standalone mongod without access control, where the user reader only has the read role on shop:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://reader:wrong-pw@noauth:27017/shop&quot; --eval &apos;db.orders.countDocuments()&apos;
MongoServerError: Authentication failed.

[client-a]$ mongosh &quot;mongodb://reader:reader-pw@noauth:27017/shop&quot; \
    --eval &apos;print(JSON.stringify(db.runCommand({connectionStatus: 1}).authInfo.authenticatedUsers));
            db.orders.insertOne({item: &quot;pen&quot;}).acknowledged&apos;
[{&quot;user&quot;:&quot;reader&quot;,&quot;db&quot;:&quot;shop&quot;}]
true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;reader authenticated, and even though it only has the read role, the write went through. So having users with passwords that work doesn’t mean the cluster is protected; check that authorization is actually on. In the startup options, no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;security&lt;/code&gt; entry under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parsed&lt;/code&gt; means it’s off:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://reader:reader-pw@noauth:27017/shop&quot; \
    --eval &apos;JSON.stringify(db.adminCommand({getCmdLineOpts: 1}).parsed)&apos;
{&quot;net&quot;:{&quot;bindIp&quot;:&quot;*&quot;}}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the two steps separated, the error messages make more sense:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Error&lt;/th&gt;
      &lt;th&gt;Step&lt;/th&gt;
      &lt;th&gt;Meaning&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MySQL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR 1045 Access denied for user&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Authentication&lt;/td&gt;
      &lt;td&gt;No such account, the host doesn’t match, or the password is wrong&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MySQL &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR 1142&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1143 command denied&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Authorization&lt;/td&gt;
      &lt;td&gt;Logged in, but no privilege on this table or column&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authentication failed&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Authentication&lt;/td&gt;
      &lt;td&gt;No such user in the authentication database, wrong password, or a login restriction not met&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Command &amp;lt;command&amp;gt; requires authentication&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Not authenticated&lt;/td&gt;
      &lt;td&gt;Access control is on and this connection hasn’t authenticated&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;not authorized on &amp;lt;database&amp;gt; to execute command&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Authorization&lt;/td&gt;
      &lt;td&gt;Authenticated, but no role grants this action&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;what-an-account-is-userhost-vs-the-authentication-database&quot;&gt;What an account is: user@host vs the authentication database&lt;/h2&gt;

&lt;p&gt;In MySQL an account is a user name plus a host. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;alice&apos;@&apos;%&apos;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;alice&apos;@&apos;172.30.0.21&apos;&lt;/code&gt; are two separate accounts that can have different passwords and privileges:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;alice&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;IDENTIFIED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;alice-any-host&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;alice&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;172.30.0.21&apos;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDENTIFIED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;alice-client-a&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When alice logs in from client-a, MySQL matches the more specific host first, so only that account’s password works:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u alice -p -e &quot;SELECT USER(), CURRENT_USER()&quot;
+-------------------+-------------------+
| USER()            | CURRENT_USER()    |
+-------------------+-------------------+
| alice@172.30.0.21 | alice@172.30.0.21 |
+-------------------+-------------------+

[client-a]$ mysql -h mysql -u alice -p     # with the password of &apos;alice&apos;@&apos;%&apos;
ERROR 1045 (28000): Access denied for user &apos;alice&apos;@&apos;172.30.0.21&apos; (using password: YES)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From client-b, only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;alice&apos;@&apos;%&apos;&lt;/code&gt; matches, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CURRENT_USER()&lt;/code&gt; becomes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alice@%&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A MongoDB user has no host. Instead it has an authentication database: the database the user was created in. alice in shop and alice in admin are two different users:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alice&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alice-shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;readWrite&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]})&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alice&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alice-admin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reports&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Wherever they were created, all user documents are stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin.system.users&lt;/code&gt;, with an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_id&lt;/code&gt; of “authentication database.user name”:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&quot;_id&quot;:&quot;admin.alice&quot;,&quot;user&quot;:&quot;alice&quot;,&quot;db&quot;:&quot;admin&quot;,&quot;roles&quot;:[{&quot;role&quot;:&quot;read&quot;,&quot;db&quot;:&quot;reports&quot;}]}
{&quot;_id&quot;:&quot;shop.alice&quot;,&quot;user&quot;:&quot;alice&quot;,&quot;db&quot;:&quot;shop&quot;,&quot;roles&quot;:[{&quot;role&quot;:&quot;readWrite&quot;,&quot;db&quot;:&quot;shop&quot;}]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The authentication database only decides where the user’s identity is checked. It doesn’t limit which databases the user can access: alice in admin has read access to reports.&lt;/p&gt;

&lt;p&gt;Since there’s no host in the identity, the same user logs in from client-a and client-b in exactly the same way. To restrict where a user can log in from, you use authenticationRestrictions, covered below.&lt;/p&gt;

&lt;h2 id=&quot;authsource-the-most-common-authentication-failed&quot;&gt;authSource: the most common Authentication failed&lt;/h2&gt;

&lt;p&gt;First, the format of a MongoDB connection string. Drivers, mongosh and the other tools all use the same URI:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mongodb://user:password@host1:port1,host2:port2/defaultdb?option1=value1&amp;amp;option2=value2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Taking one of the connection strings from this post:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mongodb://alice:alice-shop@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&amp;amp;authSource=shop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Part&lt;/th&gt;
      &lt;th&gt;In this example&lt;/th&gt;
      &lt;th&gt;Notes&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Scheme&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongodb://&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The other one is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongodb+srv://&lt;/code&gt;, which reads the host list from DNS SRV records; Atlas uses it&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;User and password&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alice:alice-shop@&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Optional; leave it out and the connection doesn’t authenticate. Characters such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%&lt;/code&gt; in the password must be percent-encoded, for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@&lt;/code&gt; as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%40&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Host list&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongo1:27017,mongo2:27017,mongo3:27017&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;A seed list. Once the driver reaches any one of them it learns every member of the replica set, so you don’t have to list them all, but list a few in case one is down&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Default database&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/shop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The database used when none is specified, and also the default authSource, more on that below&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;replicaSet&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rs0&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Connect as a replica set: the driver finds the primary and follows it after a failover&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;authSource&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The database where the user name and password are checked&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Other common options are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;directConnection=true&lt;/code&gt; (talk to this one node only, without replica set discovery; this post uses it to connect to a specific member or shard), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tls=true&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readPreference&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appName&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appName&lt;/code&gt; shows up in the server log and in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;currentOp&lt;/code&gt;, which makes it easy to tell which application a connection belongs to. It’s self-reported by the client: drivers don’t set one by default and the server doesn’t verify it, so set it explicitly in every service’s connection string, and don’t use it for access control.&lt;/p&gt;

&lt;p&gt;Note that when mongosh connects to a single host without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replicaSet&lt;/code&gt;, it adds &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;directConnection=true&lt;/code&gt; on its own. If that host happens to be a secondary, writes fail:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://alice:alice-shop@mongo1:27017/shop&quot; \
    --eval &apos;print(db.getMongo()._uri); db.orders.insertOne({x: 1})&apos;
mongodb://alice:alice-shop@mongo1:27017/shop?directConnection=true&amp;amp;appName=mongosh+2.3.8
MongoServerError: not primary
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Compare that with MySQL: in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql -h mysql -u alice -p shop&lt;/code&gt; or a JDBC URL like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jdbc:mysql://mysql:3306/shop&lt;/code&gt;, the database name is only the default schema after connecting. It has nothing to do with authentication, because MySQL has no authentication database. In a MongoDB connection string, the database name does affect authentication.&lt;/p&gt;

&lt;p&gt;When connecting, the client has to tell the server which database to check the credentials against. That’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authSource&lt;/code&gt;, and the trap is its default: if the connection string has a database in the path, that database becomes the default authSource; if it doesn’t, the default is admin.&lt;/p&gt;

&lt;p&gt;If the path names the authentication database, everything works:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://alice:alice-shop@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.orders.findOne()&apos;
{ _id: 1, item: &apos;book&apos; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without a database in the path, authSource falls back to admin, where alice has a different password:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://alice:alice-shop@mongo1:27017,mongo2:27017,mongo3:27017/?replicaSet=rs0&quot; \
    --eval &apos;db.getSiblingDB(&quot;shop&quot;).orders.findOne()&apos;
MongoServerError: Authentication failed.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The opposite case is harder to spot. The path names the database you want to work in, and it quietly becomes the authSource too:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://alice:alice-admin@mongo1:27017,mongo2:27017,mongo3:27017/reports?replicaSet=rs0&quot; \
    --eval &apos;db.getName()&apos;
MongoServerError: Authentication failed.

[client-a]$ mongosh &quot;mongodb://alice:alice-admin@mongo1:27017,mongo2:27017,mongo3:27017/reports?replicaSet=rs0&amp;amp;authSource=admin&quot; \
    --eval &apos;db.getName()&apos;
reports
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The client always gets the same Authentication failed. To find the actual reason, look at the error field of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;msg&quot;:&quot;Failed to authenticate&quot;&lt;/code&gt; line in the server log. For the two failures above:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;user=alice db=admin    error: AuthenticationFailed: SCRAM authentication failed, storedKey mismatch
user=alice db=reports  error: UserNotFound: Could not find user &quot;alice&quot; for db &quot;reports&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;UserNotFound means the authSource is wrong. storedKey mismatch means the user does exist in that database but the password is wrong. My advice is to always set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authSource&lt;/code&gt; explicitly in production connection strings instead of relying on the default.&lt;/p&gt;

&lt;h2 id=&quot;restricting-where-users-log-in-from&quot;&gt;Restricting where users log in from&lt;/h2&gt;

&lt;p&gt;MySQL writes the allowed host into the account:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;report&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;172.30.0.21&apos;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDENTIFIED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;report-pw&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-b]$ mysql -h mysql -u report -p -e &quot;SELECT CURRENT_USER()&quot;
ERROR 1045 (28000): Access denied for user &apos;report&apos;@&apos;172.30.0.22&apos; (using password: YES)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The MongoDB equivalent is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authenticationRestrictions&lt;/code&gt; on the user. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clientSource&lt;/code&gt; lists the allowed client IPs or ranges, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serverAddress&lt;/code&gt; can limit which server address the client connects to:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;admin&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;report-pw&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reports&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;authenticationRestrictions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;clientSource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;172.30.0.21&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Logging in from client-a works and from client-b fails. The client again only sees Authentication failed, and even the error field of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Failed to authenticate&lt;/code&gt; log line just says Authentication failed. The real reason is in another log line just before it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Failed to acquire user because of unmet authentication restrictions
reason: Evaluation of direct authentication restrictions failed:
        Restriction &apos;{anyOf: [{allOf: [{&quot;clientSource&quot;: [&quot;172.30.0.21/32&quot;]}]}]}&apos; ... unmet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;authenticationRestrictions can also be set on a role, and every user with that role inherits the restriction. Many people coming from MySQL don’t know MongoDB has this, and assume firewalls and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bindIp&lt;/code&gt; are the only way to control where connections come from.&lt;/p&gt;

&lt;h2 id=&quot;authorization-grant-and-roles&quot;&gt;Authorization: GRANT and roles&lt;/h2&gt;

&lt;p&gt;MySQL 8.0 added roles, but a role granted to a user doesn’t take effect by default:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ROLE&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;app_read&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;app_read&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bob&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IDENTIFIED&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;BY&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bob-pw&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;app_read&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;bob&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u bob -p -e &quot;SHOW GRANTS; SELECT CURRENT_ROLE(); SELECT * FROM shop.orders&quot;
+-----------------------------------+
| Grants for bob@%                  |
+-----------------------------------+
| GRANT USAGE ON *.* TO `bob`@`%`   |
| GRANT `app_read`@`%` TO `bob`@`%` |
+-----------------------------------+
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE           |
+----------------+
ERROR 1142 (42000) at line 1: SELECT command denied to user &apos;bob&apos;@&apos;172.30.0.21&apos; for table &apos;orders&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The role is granted, but no role is active in the session. Roles can be activated at three levels. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET ROLE&lt;/code&gt; applies to the current session only, and a new session starts without a role again:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u bob -p -e &quot;SET ROLE &apos;app_read&apos;; SELECT CURRENT_ROLE(); SELECT * FROM shop.orders&quot;
+----------------+
| CURRENT_ROLE() |
+----------------+
| `app_read`@`%` |
+----------------+
+----+------+
| id | item |
+----+------+
|  1 | book |
+----+------+
[client-a]$ mysql -h mysql -u bob -p -e &quot;SELECT CURRENT_ROLE()&quot;
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE           |
+----------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET DEFAULT ROLE &apos;app_read&apos; TO &apos;bob&apos;@&apos;%&apos;&lt;/code&gt; applies to every future login of that account and does nothing for sessions already open. The global variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activate_all_roles_on_login&lt;/code&gt; applies to the whole instance: when it’s on, every account activates all of its granted roles at login. MySQL has also always let you skip roles and grant privileges straight to a user, for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT INSERT ON shop.orders TO &apos;bob&apos;@&apos;%&apos;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;MongoDB is the other way around: privileges only live in roles, and users can only be given roles. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createUser&lt;/code&gt; has no field for privileges at all:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; db.getSiblingDB(&quot;shop&quot;).createUser({user: &quot;bob&quot;, pwd: &quot;bob-pw&quot;, roles: [],
    privileges: [{resource: {db: &quot;shop&quot;, collection: &quot;orders&quot;}, actions: [&quot;find&quot;]}]})
MongoServerError: BSON field &apos;createUser.privileges&apos; is an unknown field.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So you create a custom role first. A privilege is a resource (a database, a collection or the whole cluster) plus a set of actions (find, insert, createIndex and so on):&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;orderReader&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;privileges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;orders&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}]})&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createUser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;bob&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;bob-pw&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;orderReader&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A granted role takes effect immediately; there’s nothing to activate. bob can read orders, and reading customers is refused:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://bob:bob-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.customers.findOne()&apos;
MongoServerError: not authorized on shop to execute command { find: &quot;customers&quot;, filter: {}, limit: 1, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MongoDB has no default role either. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles&lt;/code&gt; is a required field of createUser, and leaving it out is an error:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; db.getSiblingDB(&quot;shop&quot;).runCommand({createUser: &quot;nobody&quot;, pwd: &quot;nobody-pw&quot;})
MongoServerError: BSON field &apos;createUser.roles&apos; is missing but a required field
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;roles: []&lt;/code&gt; explicitly. Such a user can authenticate, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authenticatedUserRoles&lt;/code&gt; is empty, and it can’t read data, list collections or even change its own password. It’s roughly a new MySQL account with nothing but USAGE:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://nobody:nobody-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.runCommand({connectionStatus: 1}).authInfo&apos;
{
  authenticatedUsers: [ { user: &apos;nobody&apos;, db: &apos;shop&apos; } ],
  authenticatedUserRoles: []
}
[client-a]$ mongosh &quot;mongodb://nobody:nobody-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.changeUserPassword(&quot;nobody&quot;, &quot;new-pw&quot;)&apos;
MongoServerError: not authorized on shop to execute command { updateUser: &quot;nobody&quot;, pwd: &quot;xxx&quot;, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One more rule: a role created in a regular database can only have privileges on that database. Only roles created in admin can include privileges on other databases or on the cluster.&lt;/p&gt;

&lt;p&gt;MongoDB’s built-in roles map to MySQL roughly like this:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;MongoDB built-in role&lt;/th&gt;
      &lt;th&gt;Roughly the MySQL equivalent&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;read&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT SELECT ON db.*&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;readWrite&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT SELECT, INSERT, UPDATE, DELETE ON db.*&lt;/code&gt;, plus creating collections and indexes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;dbAdmin&lt;/td&gt;
      &lt;td&gt;Database-level DDL, statistics and the profiler, but can’t read application data&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;userAdmin&lt;/td&gt;
      &lt;td&gt;Creating users and roles and granting roles in that database&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;dbOwner&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT ALL ON db.*&lt;/code&gt;; includes the three above&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;readAnyDatabase, readWriteAnyDatabase and so on&lt;/td&gt;
      &lt;td&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ON *.*&lt;/code&gt; versions&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;root&lt;/td&gt;
      &lt;td&gt;Close to MySQL’s root&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;On splitting up administrative privileges, MySQL 8.0 moved a step toward MongoDB. Administrative power used to sit in a single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUPER&lt;/code&gt; privilege; 8.0 split it into dynamic privileges grouped by function, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SYSTEM_VARIABLES_ADMIN&lt;/code&gt; for changing system variables, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONNECTION_ADMIN&lt;/code&gt; for killing other users’ connections and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BINLOG_ADMIN&lt;/code&gt; for managing the binary log, and deprecated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUPER&lt;/code&gt;. Dynamic privileges can only be granted globally (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ON *.*&lt;/code&gt;), they’re stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql.global_grants&lt;/code&gt;, and plugins and components can register their own:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u root -p -e &quot;GRANT SUPER ON *.* TO &apos;ops&apos;@&apos;%&apos;; SHOW WARNINGS&quot;
+---------+------+----------------------------------------------+
| Level   | Code | Message                                      |
+---------+------+----------------------------------------------+
| Warning | 1287 | The SUPER privilege identifier is deprecated |
+---------+------+----------------------------------------------+
[client-a]$ mysql -h mysql -u root -p -e &quot;GRANT BINLOG_ADMIN ON shop.* TO &apos;ops&apos;@&apos;%&apos;&quot;
ERROR 3619 (HY000) at line 1: Illegal privilege level specified for BINLOG_ADMIN
[client-a]$ mysql -h mysql -u root -p -e &quot;REVOKE SUPER ON *.* FROM &apos;ops&apos;@&apos;%&apos;; GRANT SYSTEM_VARIABLES_ADMIN, CONNECTION_ADMIN ON *.* TO &apos;ops&apos;@&apos;%&apos;; SELECT * FROM mysql.global_grants WHERE USER = &apos;ops&apos;&quot;
+------+------+------------------------+-------------------+
| USER | HOST | PRIV                   | WITH_GRANT_OPTION |
+------+------+------------------------+-------------------+
| ops  | %    | CONNECTION_ADMIN       | N                 |
| ops  | %    | SYSTEM_VARIABLES_ADMIN | N                 |
+------+------+------------------------+-------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The MongoDB counterparts are actions on the cluster resource, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setParameter&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;killop&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replSetConfigure&lt;/code&gt;. Dynamic and static privileges also differ in when changes take effect; the section on privilege changes below has the test.&lt;/p&gt;

&lt;p&gt;Among the built-in roles, userAdmin deserves a note of its own. In MySQL, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WITH GRANT OPTION&lt;/code&gt; only lets you pass on privileges you already have. MongoDB’s userAdmin can grant any role in its database to any user, including itself:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://ua:ua-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.orders.findOne()&apos;
MongoServerError: not authorized on shop to execute command { find: &quot;orders&quot;, ... }

[client-a]$ mongosh &quot;mongodb://ua:ua-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.grantRolesToUser(&quot;ua&quot;, [&quot;dbOwner&quot;]); db.orders.findOne()&apos;
{ _id: 1, item: &apos;book&apos; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So userAdmin on a database effectively means full control of that database, and userAdminAnyDatabase in admin is effectively root on the whole cluster. Treat both as the highest level of privilege when you hand them out.&lt;/p&gt;

&lt;h2 id=&quot;column-privileges-and-views&quot;&gt;Column privileges and views&lt;/h2&gt;

&lt;p&gt;MySQL privileges go down to the column:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;GRANT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customers&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TO&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;carol&apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;%&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u carol -p -e &quot;SELECT id, name FROM shop.customers&quot;
+----+------+
| id | name |
+----+------+
|  1 | Ann  |
+----+------+
[client-a]$ mysql -h mysql -u carol -p -e &quot;SELECT * FROM shop.customers&quot;
ERROR 1142 (42000) at line 1: SELECT command denied to user &apos;carol&apos;@&apos;172.30.0.21&apos; for table &apos;customers&apos;
[client-a]$ mysql -h mysql -u carol -p -e &quot;SELECT name, phone FROM shop.customers&quot;
ERROR 1143 (42000) at line 1: SELECT command denied to user &apos;carol&apos;@&apos;172.30.0.21&apos; for column &apos;phone&apos; in table &apos;customers&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT *&lt;/code&gt; touching a column you can’t read gives the table-level error 1142; only naming that column explicitly gives the column-level 1143. Don’t let it mislead you when troubleshooting.&lt;/p&gt;

&lt;p&gt;MongoDB privileges stop at the collection; there are no field-level privileges. The usual way to hide fields from some users is to create a view and grant access only to the view:&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createView&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;customers_public&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;customers&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;$project&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;phone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}])&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSiblingDB&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createRole&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;publicReader&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;roles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;privileges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shop&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;collection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;customers_public&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The user only needs find on the view, not on the underlying collection:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://carol:carol-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.customers_public.find().toArray()&apos;
[ { _id: 1, name: &apos;Ann&apos; } ]
[client-a]$ mongosh &quot;mongodb://carol:carol-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;db.customers.find().toArray()&apos;
MongoServerError: not authorized on shop to execute command { find: &quot;customers&quot;, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Views are read-only, so controlling writes field by field has to happen in the application. If the goal is to keep sensitive fields away from DBAs or anyone with access to the storage, that’s a different problem, solved with Client-Side Field Level Encryption (CSFLE) or Queryable Encryption rather than privileges.&lt;/p&gt;

&lt;h2 id=&quot;when-privilege-changes-take-effect-flush-privileges&quot;&gt;When privilege changes take effect: FLUSH PRIVILEGES&lt;/h2&gt;

&lt;p&gt;Many MySQL tutorials follow every GRANT with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLUSH PRIVILEGES&lt;/code&gt;. It isn’t needed. Account management statements such as GRANT, REVOKE, CREATE USER and ALTER USER update both the grant tables and the in-memory privilege cache, so they take effect at once:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u dave -p -e &quot;SELECT * FROM shop.orders&quot;
ERROR 1142 (42000) at line 1: SELECT command denied to user &apos;dave&apos;@&apos;172.30.0.21&apos; for table &apos;orders&apos;

mysql&amp;gt; GRANT SELECT ON shop.orders TO &apos;dave&apos;@&apos;%&apos;;

[client-a]$ mysql -h mysql -u dave -p -e &quot;SELECT * FROM shop.orders&quot;
+----+------+
| id | item |
+----+------+
|  1 | book |
+----+------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLUSH PRIVILEGES&lt;/code&gt; makes MySQL reload the privilege cache from the grant tables. You only need it when you bypass account management statements and change tables like mysql.user directly with INSERT, UPDATE or DELETE. For example, setting the account lock flag directly has no effect until the flush:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mysql&amp;gt; UPDATE mysql.user SET account_locked = &apos;Y&apos; WHERE user = &apos;dave&apos;;

[client-a]$ mysql -h mysql -u dave -p -e &quot;SELECT CURRENT_USER()&quot;
+----------------+
| CURRENT_USER() |
+----------------+
| dave@%         |
+----------------+

mysql&amp;gt; FLUSH PRIVILEGES;

[client-a]$ mysql -h mysql -u dave -p -e &quot;SELECT CURRENT_USER()&quot;
ERROR 3118 (HY000): Access denied for user &apos;dave&apos;@&apos;172.30.0.21&apos;. Account is locked.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sessions that are already open are easy to forget. The MySQL documentation sorts the rules into a few cases: table and column privilege changes take effect with the session’s next statement; database privilege changes take effect at the next USE; static global privilege and password changes don’t affect connected sessions until they reconnect; and dynamic global privilege changes take effect immediately. Static global privileges are the traditional ones such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON *.*&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROCESS&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SUPER&lt;/code&gt;; dynamic global privileges are the function-specific ones added in 8.0, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SESSION_VARIABLES_ADMIN&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BACKUP_ADMIN&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONNECTION_ADMIN&lt;/code&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW PRIVILEGES&lt;/code&gt; lists them all.&lt;/p&gt;

&lt;p&gt;I set up one user for each case. Each opened a session, ran a statement, waited 4 seconds and ran it again, and at the 2-second mark I revoked their privilege:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;User&lt;/th&gt;
      &lt;th&gt;Revoked privilege&lt;/th&gt;
      &lt;th&gt;Second run in the same session&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;dave&lt;/td&gt;
      &lt;td&gt;Table level, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON shop.orders&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Refused (1142)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;hank&lt;/td&gt;
      &lt;td&gt;Database level, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON shop.*&lt;/code&gt;, session had run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;USE shop&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Still works; refused only at the next &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;USE shop&lt;/code&gt; (1044)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ivy&lt;/td&gt;
      &lt;td&gt;Database level, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON shop.*&lt;/code&gt;, session never ran USE&lt;/td&gt;
      &lt;td&gt;Refused (1142)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;erin&lt;/td&gt;
      &lt;td&gt;Static global, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON *.*&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Still works; only new sessions are refused&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;gina&lt;/td&gt;
      &lt;td&gt;Dynamic global, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SESSION_VARIABLES_ADMIN&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Refused (1227)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;jack&lt;/td&gt;
      &lt;td&gt;An active role (holding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT ON shop.*&lt;/code&gt;)&lt;/td&gt;
      &lt;td&gt;Refused (1142)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Comparing hank and ivy shows what “database privileges take effect at the next USE” means: it’s about the session’s current database. That database’s privileges are cached in the session until it switches databases. A session that hasn’t switched to the database is checked against the new privileges on its next statement. Here is hank’s session:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u hank -p -N shop -e &quot;SELECT &apos;before&apos;, COUNT(*) FROM orders; SELECT SLEEP(4) INTO @x; SELECT &apos;after&apos;, COUNT(*) FROM orders; USE shop; SELECT &apos;after USE&apos;, COUNT(*) FROM orders&quot;
before  1
after   1
ERROR 1044 (42000) at line 1: Access denied for user &apos;hank&apos;@&apos;%&apos; to database &apos;shop&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;erin and gina show the difference between static and dynamic global privileges:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mysql -h mysql -u erin -p -N -e &quot;SELECT &apos;before&apos;, COUNT(*) FROM shop.orders; SELECT SLEEP(4) INTO @x; SELECT &apos;after&apos;, COUNT(*) FROM shop.orders&quot;
before  1
after   1

[client-a]$ mysql -h mysql -u gina -p -N -e &quot;SET SESSION sql_log_bin = 1; SELECT &apos;before: SET ok&apos;; SELECT SLEEP(4) INTO @x; SET SESSION sql_log_bin = 1; SELECT &apos;after: SET ok&apos;&quot;
before: SET ok
ERROR 1227 (42000) at line 1: Access denied; you need (at least one of) the SUPER, SYSTEM_VARIABLES_ADMIN or SESSION_VARIABLES_ADMIN privilege(s) for this operation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So when you revoke a static global privilege or a database-level privilege and need it to take effect now, also kill that user’s existing connections.&lt;/p&gt;

&lt;p&gt;MongoDB has no FLUSH PRIVILEGES. Users and roles can only be changed with commands such as createUser and grantRolesToUser, and the change invalidates the user cache on every member. In a replica set, revoking a role also takes effect on connections that are already open. frank reads once, waits 4 seconds and reads again on the same connection, and his read role is revoked in between:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://frank:frank-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;print(&quot;before:&quot;, db.orders.countDocuments());
            sleep(4000);
            print(&quot;after:&quot;, db.orders.countDocuments())&apos;
before: 1
MongoServerError: not authorized on shop to execute command { aggregate: &quot;orders&quot;, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MongoDB also has nothing like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET ROLE&lt;/code&gt;: every role granted to a user is always in effect, so there’s no session versus global distinction. Even login restrictions apply to open connections. kim connected from client-b and read once; then I restricted kim to client-a, and the next read on the same connection was no longer authenticated:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-b]$ mongosh &quot;mongodb://kim:kim-pw@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&quot; \
    --eval &apos;print(&quot;before:&quot;, db.orders.countDocuments());
            sleep(4000);
            print(&quot;after:&quot;, db.orders.countDocuments())&apos;
before: 1
MongoServerError: Command aggregate requires authentication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The command at the 2-second mark was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.getSiblingDB(&quot;shop&quot;).updateUser(&quot;kim&quot;, {authenticationRestrictions: [{clientSource: [&quot;172.30.0.21&quot;]}]})&lt;/code&gt;. The matching server log line is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Removed user with unmet authentication restrictions from session cache of user information&lt;/code&gt;: the user was removed from the connection. In MySQL you change an account’s host with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RENAME USER&lt;/code&gt;, and the host is only checked when a connection is made, so open sessions stay connected. I ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RENAME USER &apos;lee&apos;@&apos;%&apos; TO &apos;lee&apos;@&apos;172.30.0.21&apos;&lt;/code&gt; while lee had a session open from client-b. The session stayed connected, but the table privileges were renamed along with the account, so the next read failed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR 1142&lt;/code&gt;, and a new session from client-b got &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ERROR 1045&lt;/code&gt;. Either way the user can no longer read the data; the difference is that MongoDB turns the connection back into an unauthenticated one, while the MySQL session survives without its privileges.&lt;/p&gt;

&lt;p&gt;Sharded clusters are the exception. mongos caches user information too, and checks whether the cache is stale at the interval set by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;userCacheInvalidationIntervalSecs&lt;/code&gt; (30 seconds by default). A privilege changed through one mongos can therefore take up to 30 seconds to apply on the others. If you need it immediately, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.adminCommand({invalidateUserCache: 1})&lt;/code&gt; on each mongos, which is probably the closest thing MongoDB has to FLUSH PRIVILEGES. I didn’t reproduce this one in the lab; it’s based on the documentation.&lt;/p&gt;

&lt;h2 id=&quot;the-first-user-the-localhost-exception&quot;&gt;The first user: the localhost exception&lt;/h2&gt;

&lt;p&gt;MySQL deals with the root password at initialization: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqld --initialize&lt;/code&gt; generates a temporary password, and the Docker image requires &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MYSQL_ROOT_PASSWORD&lt;/code&gt; or an explicit choice of a random or empty password. Unless you deliberately create a root with an empty password using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--initialize-insecure&lt;/code&gt;, MySQL has authentication on from day one.&lt;/p&gt;

&lt;p&gt;MongoDB uses the localhost exception instead: when access control is on but no user exists yet, a connection from localhost may create the first user. On a replica set with keyfile authentication that hasn’t been initiated:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://mongo1:27017/?directConnection=true&quot; --eval &apos;rs.initiate()&apos;
MongoServerError: Command replSetInitiate requires authentication

[mongo1]$ mongosh --eval &apos;rs.initiate({_id: &quot;rs0&quot;, members: [
    {_id: 0, host: &quot;mongo1:27017&quot;}, {_id: 1, host: &quot;mongo2:27017&quot;}, {_id: 2, host: &quot;mongo3:27017&quot;}]}).ok&apos;
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A remote connection can’t do anything. A local connection can initiate the replica set, but still can’t read or write data:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[mongo1]$ mongosh --eval &apos;db.getSiblingDB(&quot;shop&quot;).orders.insertOne({item: &quot;book&quot;})&apos;
MongoServerError: not authorized on shop to execute command { insert: &quot;orders&quot;, ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What it can do is create the first user. The admin password is put in the PW environment variable beforehand, so it doesn’t end up on the command line or in the shell history. As soon as the user exists, the exception closes and local connections have to authenticate too:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[mongo1]$ mongosh admin --eval &apos;db.createUser({user: &quot;admin&quot;, pwd: process.env.PW, roles: [&quot;root&quot;]}).ok&apos;
1
[mongo1]$ mongosh --eval &apos;db.getSiblingDB(&quot;admin&quot;).createUser({user: &quot;second&quot;, pwd: &quot;second-pw&quot;, roles: [&quot;root&quot;]})&apos;
MongoServerError: Command createUser requires authentication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;One more detail from the lab: after dropping the last user, the localhost exception doesn’t reopen until mongod restarts. It looks like mongod remembers that users have existed and only checks again after a restart. If you don’t want the exception at all, turn it off with the parameter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enableLocalhostAuthBypass=false&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;internal-authentication-keyfile&quot;&gt;Internal authentication: keyfile&lt;/h2&gt;

&lt;p&gt;MySQL replication uses an ordinary account. Replication itself only needs the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REPLICATION SLAVE&lt;/code&gt; privilege; the replica uses that account to connect to the source and read the binary log. Many setups also grant &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REPLICATION CLIENT&lt;/code&gt;, which allows status statements such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW BINARY LOG STATUS&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW BINARY LOGS&lt;/code&gt;. Monitoring and high-availability tools use it, but replication doesn’t need it. The replication statements were renamed to SOURCE and REPLICA terms starting with 8.0.22, and in 8.4 old syntax like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SHOW SLAVE STATUS&lt;/code&gt; is a syntax error. The privilege name never changed, though: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GRANT REPLICATION REPLICA&lt;/code&gt; is a syntax error in both 8.0 and 8.4.&lt;/p&gt;

&lt;p&gt;MongoDB replica set members don’t use application accounts. They use internal authentication, most commonly a keyfile: every member holds the same key file and starts with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--keyFile&lt;/code&gt;. In the server log, authentication between members looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;msg&quot;:&quot;Successfully authenticated&quot;,&quot;attr&quot;:{&quot;client&quot;:&quot;172.30.0.13:32994&quot;,&quot;isClusterMember&quot;:true,
  &quot;mechanism&quot;:&quot;SCRAM-SHA-256&quot;,&quot;user&quot;:&quot;__system&quot;,&quot;db&quot;:&quot;local&quot;,...}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Members authenticate to each other as the built-in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__system@local&lt;/code&gt; user with SCRAM-SHA-256, using a password derived from the keyfile. Two things to note. First, enabling internal authentication also enables client authorization; in the documentation’s words, “Enabling internal authentication also enables client authorization”, so you don’t need to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;security.authorization&lt;/code&gt; separately. That holds for both keyfile and x509 internal authentication, the exception being the transitional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--transitionToAuth&lt;/code&gt;, covered below. Second, the keyfile must not be readable by other users, or mongod refuses to start.&lt;/p&gt;

&lt;p&gt;The other form of internal authentication is x509 certificates. x509 has two uses in MongoDB: clients logging in with certificates, and members authenticating to each other with certificates. The first is rare in self-managed deployments. The second is the configuration MongoDB recommends for production, although plenty of teams still use keyfiles. Also keep in mind that TLS encryption in transit is not x509 authentication; many clusters run TLS and still authenticate with passwords.&lt;/p&gt;

&lt;h2 id=&quot;enabling-authentication-on-a-running-replica-set&quot;&gt;Enabling authentication on a running replica set&lt;/h2&gt;

&lt;p&gt;MongoDB doesn’t enable access control by default. On a replica set without it, anyone who can reach the port can do anything:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-b]$ mongosh &quot;mongodb://mongo1:27017,mongo2:27017,mongo3:27017/?replicaSet=rs0&quot; \
    --eval &apos;db.getSiblingDB(&quot;admin&quot;).runCommand({listDatabases: 1}).databases.map(d =&amp;gt; d.name)&apos;
[ &apos;admin&apos;, &apos;config&apos;, &apos;local&apos; ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;MySQL doesn’t have this problem, so it has no equivalent procedure. To turn on authentication for a running MongoDB replica set without downtime, you use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--transitionToAuth&lt;/code&gt; in three steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;While authentication is still off, create the admin and application users. They exist but aren’t enforced yet.&lt;/li&gt;
  &lt;li&gt;Restart the members one at a time with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--keyFile --transitionToAuth&lt;/code&gt;, secondaries first, then step down the primary and restart it. In transitionToAuth mode, members authenticate to each other with the keyfile, and clients can connect with or without credentials.&lt;/li&gt;
  &lt;li&gt;Switch every client to connect with credentials, then restart the members one at a time again with only &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--keyFile&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To see the effect on applications, I ran two writers on client-a, each inserting about 10 documents a second, one without credentials and one as the app user. Writes per phase:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Phase&lt;/th&gt;
      &lt;th&gt;Writer without credentials&lt;/th&gt;
      &lt;th&gt;Writer with credentials&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Authentication off&lt;/td&gt;
      &lt;td&gt;50 ok, 0 failed&lt;/td&gt;
      &lt;td&gt;50 ok, 0 failed&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Step 2: rolling to transitionToAuth&lt;/td&gt;
      &lt;td&gt;534 ok, 0 failed&lt;/td&gt;
      &lt;td&gt;534 ok, 0 failed&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Step 3: rolling to keyFile&lt;/td&gt;
      &lt;td&gt;353 ok, 225 failed&lt;/td&gt;
      &lt;td&gt;563 ok, 0 failed&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The writer with credentials never failed once; retryable writes covered the primary stepdowns. The writer without credentials stands for a client someone forgot to update, and it started failing once the primary switched to keyFile mode:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Command insert requires authentication
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Why not skip step 2? I restarted one secondary straight from no authentication to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--keyFile&lt;/code&gt; and checked the replica set 20 seconds later:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mongo1:27017  PRIMARY  health=1
mongo2:27017  (not reachable/healthy)  health=0
mongo3:27017  SECONDARY  health=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That member requires the others to authenticate, and they have no keyfile; when it connects to them, they don’t recognize its authentication either. transitionToAuth exists so that members accept both kinds of connection during the transition.&lt;/p&gt;

&lt;h2 id=&quot;rotating-the-keyfile&quot;&gt;Rotating the keyfile&lt;/h2&gt;

&lt;p&gt;Keyfiles need rotating eventually, but every member must still be able to authenticate to the others, so you can’t swap them all at once. Since MongoDB 4.2, a keyfile can be a YAML list holding several keys:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;old key&amp;gt;&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;new key&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Rotation takes two rounds: first restart the members one at a time with a keyfile holding both the old and the new key; when all are done, restart them one at a time again with a keyfile holding only the new key. In the lab, the replica set stayed healthy through both rounds and the primary switched over normally.&lt;/p&gt;

&lt;p&gt;As before, if you skip the first round and give one member only the new key, it loses contact with the others and shows up as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(not reachable/healthy)&lt;/code&gt;, just like in the previous section.&lt;/p&gt;

&lt;h2 id=&quot;sharded-clusters-where-users-live&quot;&gt;Sharded clusters: where users live&lt;/h2&gt;

&lt;p&gt;A sharded cluster has two sets of users. Users created through mongos are cluster users, stored on the config servers:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mongos&amp;gt; db.getSiblingDB(&quot;admin&quot;).createUser({user: &quot;analyst&quot;, pwd: &quot;analyst-pw&quot;,
          roles: [{role: &quot;read&quot;, db: &quot;sales&quot;}]})
mongos&amp;gt; db.getSiblingDB(&quot;admin&quot;).system.users.find({}, {_id: 1}).toArray()
[ { _id: &apos;admin.admin&apos; }, { _id: &apos;admin.analyst&apos; } ]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Access through mongos works, but the user doesn’t exist on the shards, so a direct connection to a shard fails, and the shard’s log says UserNotFound:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[client-a]$ mongosh &quot;mongodb://analyst:analyst-pw@shard1:27017/sales?authSource=admin&amp;amp;directConnection=true&quot; \
    --eval &apos;db.orders.countDocuments()&apos;
MongoServerError: Authentication failed.
error: UserNotFound: Could not find user &quot;analyst&quot; for db &quot;admin&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The bigger issue is that the shard has no users of its own, so its localhost exception is still open. Anyone who can log in to the shard’s host can create a root user there:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[shard1]$ mongosh admin --eval &apos;db.createUser({user: &quot;shardadmin&quot;, pwd: &quot;shardadmin-pw&quot;, roles: [&quot;root&quot;]}).ok&apos;
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This shard-local user exists only on shard1; mongos doesn’t know it, and authenticating to mongos with it fails. MongoDB recommends creating a shard-local admin user on every shard, both for maintenance over direct connections and to close the localhost exception on the shard.&lt;/p&gt;

&lt;h2 id=&quot;troubleshooting-checklist&quot;&gt;Troubleshooting checklist&lt;/h2&gt;

&lt;p&gt;When you hit an access control problem, find the symptom in this table:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Symptom&lt;/th&gt;
      &lt;th&gt;Look at&lt;/th&gt;
      &lt;th&gt;Common cause&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB says Authentication failed&lt;/td&gt;
      &lt;td&gt;The error field of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Failed to authenticate&lt;/code&gt; server log line&lt;/td&gt;
      &lt;td&gt;UserNotFound: wrong authSource; storedKey mismatch: wrong password&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Same, but the error only says Authentication failed&lt;/td&gt;
      &lt;td&gt;A log line with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unmet authentication restrictions&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The client IP isn’t in clientSource&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Authentication failed, but you don’t know which application&lt;/td&gt;
      &lt;td&gt;The connection id &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connNNN&lt;/code&gt; on the failure line, then the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client metadata&lt;/code&gt; line of the same connection&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;application.name&lt;/code&gt; identifies the application, provided it sets appName&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MongoDB says not authorized on &lt;database&gt; to execute command&lt;/database&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db.runCommand({connectionStatus: 1, showPrivileges: true})&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The role lacks this action or resource&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MySQL error 1142 even though the role is granted&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SELECT CURRENT_ROLE()&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The role isn’t active&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;A replica set member is not reachable/healthy&lt;/td&gt;
      &lt;td&gt;That member’s log&lt;/td&gt;
      &lt;td&gt;Keyfiles don’t match between members, or members are in different auth modes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lots of Checking authorization failed in the log for an atlascli aggregation&lt;/td&gt;
      &lt;td&gt;Safe to ignore&lt;/td&gt;
      &lt;td&gt;mongosh runs this probe in the background after connecting; users with limited privileges leave this line&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;quick-reference&quot;&gt;Quick reference&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Topic&lt;/th&gt;
      &lt;th&gt;MySQL&lt;/th&gt;
      &lt;th&gt;MongoDB&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Account identity&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;user&apos;@&apos;host&apos;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;User name + authentication database&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What the client specifies&lt;/td&gt;
      &lt;td&gt;User name only&lt;/td&gt;
      &lt;td&gt;User name + authSource; defaults to the database in the URI path, or admin if there is none&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Restricting the client source&lt;/td&gt;
      &lt;td&gt;The host part of the account&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authenticationRestrictions.clientSource&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Granting privileges&lt;/td&gt;
      &lt;td&gt;Directly to users, or through roles&lt;/td&gt;
      &lt;td&gt;Through roles only&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Roles taking effect&lt;/td&gt;
      &lt;td&gt;Must be activated: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET ROLE&lt;/code&gt; (current session), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SET DEFAULT ROLE&lt;/code&gt; (future logins of the account) or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;activate_all_roles_on_login&lt;/code&gt; (whole instance)&lt;/td&gt;
      &lt;td&gt;Immediately when granted&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Finest granularity&lt;/td&gt;
      &lt;td&gt;Column&lt;/td&gt;
      &lt;td&gt;Collection; fields via views&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Privilege changes take effect&lt;/td&gt;
      &lt;td&gt;Account management statements apply immediately; direct edits to the grant tables need FLUSH PRIVILEGES; static global privileges and the current database’s privileges don’t affect open sessions&lt;/td&gt;
      &lt;td&gt;Immediately, including open connections; mongos user cache up to 30 seconds by default&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;First administrator&lt;/td&gt;
      &lt;td&gt;Root password set at initialization&lt;/td&gt;
      &lt;td&gt;The localhost exception&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Authentication on by default&lt;/td&gt;
      &lt;td&gt;Yes&lt;/td&gt;
      &lt;td&gt;No&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Replication or member authentication&lt;/td&gt;
      &lt;td&gt;Ordinary account + REPLICATION SLAVE (REPLICATION CLIENT for status, not required)&lt;/td&gt;
      &lt;td&gt;Keyfile or x509, as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__system&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Enabling authentication without downtime&lt;/td&gt;
      &lt;td&gt;Not applicable&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--transitionToAuth&lt;/code&gt; in three steps&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Sharded clusters&lt;/td&gt;
      &lt;td&gt;Not applicable&lt;/td&gt;
      &lt;td&gt;mongos users live on the config servers; shards have their own local users&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2026/10/mongodb-access-control-for-mysql-users/&quot;&gt;MongoDB Access Control for MySQL Users: Accounts, Roles and Internal Authentication&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on October 11, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[新加坡从零考驾照全记录（2026）：BTT、PDL 到路考]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2026/10/singapore-driving-licence-from-scratch/"/>
  <id>http://www.solidstack.io/posts/2026/10/singapore-driving-licence-from-scratch</id>
  <published>2026-10-09T00:00:00+08:00</published>
  <updated>2026-10-09T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#%E6%96%B0%E5%8A%A0%E5%9D%A1%E9%A9%BE%E7%85%A7" term="新加坡驾照" /><category scheme="http://www.solidstack.io/tags/#BTT" term="BTT" /><category scheme="http://www.solidstack.io/tags/#PDL" term="PDL" /><category scheme="http://www.solidstack.io/tags/#CDC" term="CDC" /><category scheme="http://www.solidstack.io/tags/#%E4%B8%AD%E5%9B%BD%E9%A9%BE%E7%85%A7%E8%BD%AC%E6%8D%A2" term="中国驾照转换" /><category scheme="http://www.solidstack.io/tags/#Life%20in%20Singapore" term="Life in Singapore" />
  <content type="html">
  
    &lt;p&gt;我在新加坡从零开始考驾照，报的是 CDC（ComfortDelGro Driving Centre）的 Class 3A，也就是自动挡小汽车。从报名到通过 BTT、拿到 PDL，一共用了 11 天。&lt;/p&gt;

&lt;p&gt;这篇先记录到 PDL 为止，后面的实践课、FTT 和路考，考完再更新。如果你有中国驾照、打算直接转换，文末有一条容易被忽略的规定。&lt;/p&gt;

&lt;p&gt;规定部分我都对照了新加坡警察部队（Traffic Police）的官方页面，链接放在文末。规定会调整，办理前请以官网为准。&lt;/p&gt;

&lt;h2 id=&quot;先选路线从零考还是转换&quot;&gt;先选路线：从零考还是转换&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;从零考&lt;/th&gt;
      &lt;th&gt;中国驾照转换&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;适合谁&lt;/td&gt;
      &lt;td&gt;没有驾照，或者手上的驾照不符合转换条件&lt;/td&gt;
      &lt;td&gt;持有有效的中国驾照&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;要考什么&lt;/td&gt;
      &lt;td&gt;BTT、FTT、路考&lt;/td&gt;
      &lt;td&gt;只需要 BTT&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;主要麻烦&lt;/td&gt;
      &lt;td&gt;步骤多，每一步都要抢预约&lt;/td&gt;
      &lt;td&gt;材料要齐，非英文驾照要找新加坡注册的翻译公司翻译&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;外国人在新加坡居住不满 12 个月，可以直接用有效的外国驾照开车。住满 12 个月后，就需要换成新加坡驾照。&lt;/p&gt;

&lt;h2 id=&quot;名词解释&quot;&gt;名词解释&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;名词&lt;/th&gt;
      &lt;th&gt;全称&lt;/th&gt;
      &lt;th&gt;说明&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;TP&lt;/td&gt;
      &lt;td&gt;Traffic Police&lt;/td&gt;
      &lt;td&gt;新加坡警察部队下的交通警察，负责驾照考试和发证&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SPF&lt;/td&gt;
      &lt;td&gt;Singapore Police Force&lt;/td&gt;
      &lt;td&gt;新加坡警察部队，PDL 在它的 e-Services 网站申请&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CDC、BBDC、SSDC&lt;/td&gt;
      &lt;td&gt;ComfortDelGro Driving Centre、Bukit Batok Driving Centre、Singapore Safety Driving Centre&lt;/td&gt;
      &lt;td&gt;新加坡的三家驾校，可以学车，也设有理论考试考点&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BTT&lt;/td&gt;
      &lt;td&gt;Basic Theory Test&lt;/td&gt;
      &lt;td&gt;基础理论考试，学车的第一关；用中国驾照转换也要考&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FTT&lt;/td&gt;
      &lt;td&gt;Final Theory Test&lt;/td&gt;
      &lt;td&gt;终极理论考试，考 Class 3 或 3A 要先通过它才能路考&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;IE&lt;/td&gt;
      &lt;td&gt;Internal Evaluation&lt;/td&gt;
      &lt;td&gt;CDC 的内部模拟考，第一次学车的驾校学员要先通过它才能约 BTT&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Induction Programme&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;CDC 的入学介绍课，讲整个学车流程，不能取消或改期&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PDL&lt;/td&gt;
      &lt;td&gt;Provisional Driving Licence&lt;/td&gt;
      &lt;td&gt;学习驾照，电子版，有效期 2 年，有了它才能上实践课&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;实践课&lt;/td&gt;
      &lt;td&gt;Practical Lesson&lt;/td&gt;
      &lt;td&gt;跟教练上路练车，CDC 每节 100 分钟&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;路考&lt;/td&gt;
      &lt;td&gt;Practical Driving Test&lt;/td&gt;
      &lt;td&gt;实际驾驶考试，由 TP 指派的考官主考&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Class 3、3A&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Class 3 是手动挡；Class 3A 是自动挡，只能开自动挡车&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Standard、One Team、Elite Team&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;CDC 的三种班型，区别见”选哪种班”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PRM&lt;/td&gt;
      &lt;td&gt;Premium fee&lt;/td&gt;
      &lt;td&gt;One Team 和 Elite Team 每节实践课的附加费&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Stored Value&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;CDC 的预付储值账户，各项费用从这里扣&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;VPC&lt;/td&gt;
      &lt;td&gt;Vehicular Pre-operative Check&lt;/td&gt;
      &lt;td&gt;实践课阶段的一个项目，CDC 价目表里单独收费&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;LDCS&lt;/td&gt;
      &lt;td&gt;Learner Driver Competency Screening&lt;/td&gt;
      &lt;td&gt;实践课阶段的一个项目，CDC 价目表里单独收费&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Booking Portal&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;CDC 的网上预约系统，网页版和手机 App 都能用&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;My Learning Journey&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;CDC App 里的学车进度，显示已经完成和下一步要做的环节&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Slot&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;可预约的时段，视力测试、IE、BTT、实践课都要抢&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Singpass&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;新加坡政府的数字身份，申请 PDL 时用来登录&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GST&lt;/td&gt;
      &lt;td&gt;Goods and Services Tax&lt;/td&gt;
      &lt;td&gt;新加坡的消费税，目前是 9%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;从零考的整体流程&quot;&gt;从零考的整体流程&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;报名驾校&lt;/li&gt;
  &lt;li&gt;视力测试（包括色盲测试）&lt;/li&gt;
  &lt;li&gt;Induction Programme 和 IE（Internal Evaluation，驾校内部模拟考），驾校学员才有&lt;/li&gt;
  &lt;li&gt;BTT（Basic Theory Test，基础理论考试）&lt;/li&gt;
  &lt;li&gt;拍照，申请 PDL（Provisional Driving Licence，学习驾照）&lt;/li&gt;
  &lt;li&gt;上实践课&lt;/li&gt;
  &lt;li&gt;FTT（Final Theory Test，终极理论考试）&lt;/li&gt;
  &lt;li&gt;路考&lt;/li&gt;
  &lt;li&gt;拿到驾照&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;我的时间线11-天从报名到-pdl&quot;&gt;我的时间线：11 天从报名到 PDL&lt;/h2&gt;

&lt;figure&gt;
&lt;svg viewBox=&quot;0 0 900 310&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;dl-title dl-desc&quot; style=&quot;display:block;width:100%;max-width:900px;height:auto;margin:0 auto;font-family:&apos;Open Sans&apos;,&apos;PingFang SC&apos;,&apos;Microsoft YaHei&apos;,sans-serif&quot;&gt;
  &lt;title id=&quot;dl-title&quot;&gt;从报名到拿 PDL 的时间线&lt;/title&gt;
  &lt;desc id=&quot;dl-desc&quot;&gt;9 月 28 日在 CDC 报名，10 月 2 日通过视力测试，10 月 3 日参加 Induction Programme 并通过 IE，10 月 3 日起刷了 4 天实践课 slot，10 月 7 日才约到，10 月 9 日通过 BTT 并申请 PDL，共 11 天。之后等待约 7 周，11 月 24 日和 27 日上前两节实践课。&lt;/desc&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;150&quot; x2=&quot;650&quot; y2=&quot;150&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;3&quot; /&gt;
  &lt;line x1=&quot;332&quot; y1=&quot;150&quot; x2=&quot;526&quot; y2=&quot;150&quot; stroke=&quot;#fff&quot; stroke-width=&quot;5&quot; /&gt;
  &lt;line x1=&quot;332&quot; y1=&quot;150&quot; x2=&quot;526&quot; y2=&quot;150&quot; stroke=&quot;#e8a33d&quot; stroke-width=&quot;4&quot; stroke-dasharray=&quot;7 5&quot; /&gt;
  &lt;polyline points=&quot;655,150 665,140 675,160 685,140 695,160 705,150&quot; fill=&quot;none&quot; stroke=&quot;#999&quot; stroke-width=&quot;2&quot; /&gt;
  &lt;line x1=&quot;710&quot; y1=&quot;150&quot; x2=&quot;870&quot; y2=&quot;150&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;3&quot; stroke-dasharray=&quot;6 5&quot; /&gt;
  &lt;circle cx=&quot;60&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;159&quot; x2=&quot;60&quot; y2=&quot;172&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;60&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;9/28&lt;/text&gt;
  &lt;text x=&quot;60&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;CDC 报名&lt;/text&gt;
  &lt;circle cx=&quot;271&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;271&quot; y1=&quot;141&quot; x2=&quot;271&quot; y2=&quot;128&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;271&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;10/2&lt;/text&gt;
  &lt;text x=&quot;271&quot; y=&quot;117&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;视力测试 ✓&lt;/text&gt;
  &lt;circle cx=&quot;324&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;324&quot; y1=&quot;159&quot; x2=&quot;324&quot; y2=&quot;172&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;324&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;10/3&lt;/text&gt;
  &lt;text x=&quot;324&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;Induction + IE ✓&lt;/text&gt;
  &lt;circle cx=&quot;534&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;534&quot; y1=&quot;141&quot; x2=&quot;534&quot; y2=&quot;128&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;534&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;10/7&lt;/text&gt;
  &lt;text x=&quot;534&quot; y=&quot;117&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;预约实践课&lt;/text&gt;
  &lt;circle cx=&quot;640&quot; cy=&quot;150&quot; r=&quot;9&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;640&quot; y1=&quot;159&quot; x2=&quot;640&quot; y2=&quot;172&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;640&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;10/9&lt;/text&gt;
  &lt;text x=&quot;640&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;BTT ✓ · PDL ✓&lt;/text&gt;
  &lt;circle cx=&quot;740&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#fff&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;2&quot; /&gt;
  &lt;line x1=&quot;740&quot; y1=&quot;141&quot; x2=&quot;740&quot; y2=&quot;128&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;740&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;11/24&lt;/text&gt;
  &lt;text x=&quot;740&quot; y=&quot;117&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;实践课 1&lt;/text&gt;
  &lt;circle cx=&quot;840&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#fff&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;2&quot; /&gt;
  &lt;line x1=&quot;840&quot; y1=&quot;159&quot; x2=&quot;840&quot; y2=&quot;172&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;840&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;11/27&lt;/text&gt;
  &lt;text x=&quot;840&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;实践课 2&lt;/text&gt;
  &lt;text x=&quot;455&quot; y=&quot;200&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#a35f00&quot;&gt;刷了 4 天 slot&lt;/text&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;252&quot; x2=&quot;640&quot; y2=&quot;252&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;245&quot; x2=&quot;60&quot; y2=&quot;259&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;line x1=&quot;640&quot; y1=&quot;245&quot; x2=&quot;640&quot; y2=&quot;259&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;350&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;11 天：从报名到拿 PDL&lt;/text&gt;
  &lt;text x=&quot;680&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#777&quot;&gt;等约 7 周&lt;/text&gt;
  &lt;line x1=&quot;725&quot; y1=&quot;252&quot; x2=&quot;860&quot; y2=&quot;252&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;4 3&quot; /&gt;
  &lt;text x=&quot;792&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#2f5d8a&quot;&gt;已预约的实践课&lt;/text&gt;
&lt;/svg&gt;
&lt;/figure&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;日期&lt;/th&gt;
      &lt;th&gt;进度&lt;/th&gt;
      &lt;th&gt;距报名&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;9 月 28 日&lt;/td&gt;
      &lt;td&gt;在 CDC 网上报名 Class 3A&lt;/td&gt;
      &lt;td&gt;第 0 天&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10 月 2 日&lt;/td&gt;
      &lt;td&gt;现场视力测试，通过&lt;/td&gt;
      &lt;td&gt;第 4 天&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10 月 3 日&lt;/td&gt;
      &lt;td&gt;Zoom 参加 Induction Programme，然后现场考 IE，通过&lt;/td&gt;
      &lt;td&gt;第 5 天&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10 月 7 日&lt;/td&gt;
      &lt;td&gt;预约实践课：10 月 3 日起每天刷 slot，第 4 天才约到，最早的空位在 11 月 24 日&lt;/td&gt;
      &lt;td&gt;第 9 天&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;10 月 9 日&lt;/td&gt;
      &lt;td&gt;现场考 BTT，通过；在 CDC 拍照，在线申请 PDL&lt;/td&gt;
      &lt;td&gt;第 11 天&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;能这么快，主要是每一步都抢到了比较早的 slot。&lt;/p&gt;

&lt;h2 id=&quot;选哪种班&quot;&gt;选哪种班&lt;/h2&gt;

&lt;p&gt;CDC 的 Class 3A 在 Ubi 有三种班型，区别在于训练车和教练是否固定，每节实践课的加价也不同（含 GST）：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;班型&lt;/th&gt;
      &lt;th&gt;每节实践课加价&lt;/th&gt;
      &lt;th&gt;训练车&lt;/th&gt;
      &lt;th&gt;教练&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Standard&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
      &lt;td&gt;不固定&lt;/td&gt;
      &lt;td&gt;不固定&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;One Team&lt;/td&gt;
      &lt;td&gt;13.08 新元&lt;/td&gt;
      &lt;td&gt;固定&lt;/td&gt;
      &lt;td&gt;最多 6 位教练组成的团队&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Elite Team&lt;/td&gt;
      &lt;td&gt;16.35 新元&lt;/td&gt;
      &lt;td&gt;固定&lt;/td&gt;
      &lt;td&gt;最多 4 位资深教练组成的团队&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;报名费方面，Standard 是 185.30 新元，One Team 和 Elite Team 是 201.65 新元，都包含 1 年会籍，另外收 10 新元可退押金。我报的是 Elite Team 的电动车（EV）课程，EV 课程只在 Ubi 开。&lt;/p&gt;

&lt;h2 id=&quot;预约工具cdc-网页版和-app&quot;&gt;预约工具：CDC 网页版和 App&lt;/h2&gt;

&lt;p&gt;在 CDC 学车，预约、取消、充值、查收据都在它的网上预约系统（Booking Portal）里完成。网页版首页有十个入口，我最常用的是这三个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;My Bookings&lt;/strong&gt;：查看已经约好的课和考试。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lesson Booking&lt;/strong&gt;：约课，比如 Induction Programme、理论课和实践课。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Test Bookings&lt;/strong&gt;：约 IE、BTT 这类考试。&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;http://www.solidstack.io/images/cdc-booking-portal.png&quot; alt=&quot;CDC 网上预约系统首页：Quick Guides 和十个功能入口，红框标出 My Bookings、Lesson Booking、Test Bookings&quot; style=&quot;display:block;max-width:720px;width:100%;margin:0 auto&quot; /&gt;
  &lt;figcaption&gt;CDC 网上预约系统首页，红框是最常用的三个入口。&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;其他几个也会用到：&lt;strong&gt;Top-Up Store Value&lt;/strong&gt; 给储值账户充值，&lt;strong&gt;Cancel Booking&lt;/strong&gt; 取消预约，&lt;strong&gt;Account Statement / Receipt&lt;/strong&gt; 查流水和收据（下面的费用明细就是从这里整理的），&lt;strong&gt;Online Learning&lt;/strong&gt; 是在线学习资源。&lt;/p&gt;

&lt;p&gt;建议把 CDC 的手机 App 也装上，随时可以查看 My Learning Journey，也就是自己的学车进度到了哪一步、下一步要做什么，预约也很方便。&lt;/p&gt;

&lt;figure style=&quot;display:flex;flex-wrap:wrap;justify-content:center;gap:1rem;max-width:640px;margin:1.5rem auto&quot;&gt;
  &lt;img src=&quot;http://www.solidstack.io/images/cdc-app-learning-journey-1.webp&quot; alt=&quot;CDC App 的 My Learning Journey：报名和会籍、视力测试、PDL 有效期，以及 BTT 和 FTT 的进度&quot; style=&quot;flex:1 1 260px;max-width:300px;width:auto;height:auto;margin:0&quot; /&gt;
  &lt;img src=&quot;http://www.solidstack.io/images/cdc-app-learning-journey-2.webp&quot; alt=&quot;CDC App 的 My Learning Journey：模拟器培训、实践课各模块，以及路考&quot; style=&quot;flex:1 1 260px;max-width:300px;width:auto;height:auto;margin:0&quot; /&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;报名和视力测试&quot;&gt;报名和视力测试&lt;/h2&gt;

&lt;p&gt;在 CDC 网上报名后，会收到一封邮件，按邮件里的方式预约视力测试。邮件可能被归到垃圾邮件，记得检查。&lt;/p&gt;

&lt;p&gt;视力测试包括视力和色觉两部分。按照新加坡《Road Traffic (Motor Vehicles, Driving Licences) Rules》的规定，要求是：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;在 25 米的距离，能读出黑底白字、和车牌一样大小和排列的 6 个字母与数字。平时戴眼镜的，可以戴着眼镜看。&lt;/li&gt;
  &lt;li&gt;在 25 米的距离，能分辨出红色、橙色和绿色。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;视力测试的 slot 很紧张，要靠刷。我的观察是，每天大约早上 9 点半开放预约，晚上 6 点半左右关闭，开放期间可以不停刷新。我的做法是先约一个能约到的时间，然后继续刷，看到更早的就改期。&lt;/p&gt;

&lt;p&gt;要注意顺序：视力测试通过后才能约 IE，IE 通过后才能约 BTT。这三项都要到现场，都要抢 slot，所以每过一项就马上约下一项。&lt;/p&gt;

&lt;h2 id=&quot;induction-programme-和-ie内部模拟考&quot;&gt;Induction Programme 和 IE（内部模拟考）&lt;/h2&gt;

&lt;p&gt;IE（Internal Evaluation）是 CDC 的内部模拟考，形式和 Traffic Police 的正式考试一样。第一次学车的驾校学员必须到现场通过 IE，才能预约正式的 BTT。&lt;/p&gt;

&lt;p&gt;Induction Programme 是 CDC 的入学介绍，讲后面整个学车流程，我约的是 Zoom 的场次。CDC 官网写明，Induction Programme 不可以取消，也不可以改期。&lt;/p&gt;

&lt;p&gt;我把两项约在了同一天：先在线上参加 Induction Programme，然后去考 IE。结果 IE 开考时 Induction Programme 还没结束，我只好提前退出。最后系统还是算我完成了，算是有惊无险。不建议照着这样约，两项之间最好留出足够的时间。&lt;/p&gt;

&lt;h2 id=&quot;btt-正式考试&quot;&gt;BTT 正式考试&lt;/h2&gt;

&lt;p&gt;IE 和正式 BTT 的题型一样，都是 50 道选择题，答对 45 道及格，限时 50 分钟。BTT 可以选英文、中文、马来文或淡米尔文作答，我用的是英文。&lt;/p&gt;

&lt;p&gt;两套系统的界面风格差别很大。IE 是 CDC 自己的系统，界面明显更现代；正式 BTT 用的系统界面还很旧，像早年的 VB 程序，第一次用要花点时间适应。BTT 的系统最近在做升级，希望升级后能好用一些。&lt;/p&gt;

&lt;p&gt;考试时一定要看清楚再点按钮。有学员做完一道题就不小心点了 End Test，考试直接结束，好不容易约到的 slot 也就作废了。&lt;/p&gt;

&lt;p&gt;备考资料我只用了两样：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;SPF 官方的 Basic Theory of Driving 手册&lt;/strong&gt;：中文版和英文版对照着看。用英文考的话，看中文版帮助理解，看英文版熟悉题目里的说法。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;刷题 App “BTT, FTT, RTT - Theory Test SG”&lt;/strong&gt;：模拟题很多，适合反复练。但我发现模拟测试里有个别题目的答案是错的，遇到和手册说法不一样的，以官方手册为准。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CDC 有免费的在线理论课，用 Zoom 上，老师会讲考试重点和容易出错的题目，推荐参加。&lt;/p&gt;

&lt;p&gt;IE 通过、约好正式 BTT 之后，我就开始刷实践课的 slot。连着刷了 4 天都没有合适的空位，到 10 月 7 日才约到，而最早的空位已经排到 11 月 24 日，要等 7 周左右。所以 IE 一过就可以开始约实践课，不用等 BTT 考完。&lt;/p&gt;

&lt;p&gt;实践课的空位没有固定的开放时间。CDC 官网写明，Ubi 的各个班型都”没有固定的放课日期”，要自己经常上网查看。我约到的两节都是早上刷到的，听说空位是不定期释放的。另外，CDC 在 2024 年 7 月公布过预约上限：Ubi 的自动挡学员每月最多约 5 节实践课，手动挡 8 节。&lt;/p&gt;

&lt;h2 id=&quot;拍照和申请-pdl&quot;&gt;拍照和申请 PDL&lt;/h2&gt;

&lt;p&gt;BTT 通过后，我直接在 CDC 拍了证件照，工作人员会把数码照片发到邮箱。拍照和考 BTT 安排在同一趟，就不用为了拍照再跑一次。&lt;/p&gt;

&lt;p&gt;然后到新加坡警察部队的 e-Services 网站，用 Singpass 登录申请 PDL。费用是 25 新元，缴费后就通过了。PDL 是电子驾照，有效期 2 年，可以续期，驾驶教练能在线查到。没有有效的 PDL 去上实践课是违法的。&lt;/p&gt;

&lt;h2 id=&quot;费用明细&quot;&gt;费用明细&lt;/h2&gt;

&lt;p&gt;到拿 PDL 为止，我实际花的钱如下（新元，含 9% GST）：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;项目&lt;/th&gt;
      &lt;th&gt;费用&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CDC 报名费（Elite Team，含 1 年会籍）&lt;/td&gt;
      &lt;td&gt;201.65&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;押金（可退）&lt;/td&gt;
      &lt;td&gt;10.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;视力测试&lt;/td&gt;
      &lt;td&gt;1.96&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Induction Programme&lt;/td&gt;
      &lt;td&gt;23.98&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;IE（Internal Evaluation）&lt;/td&gt;
      &lt;td&gt;7.63&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BTT 考试费&lt;/td&gt;
      &lt;td&gt;7.20&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BTT 行政费&lt;/td&gt;
      &lt;td&gt;2.94&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CDC 在线理论课（2 节）&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;数码证件照&lt;/td&gt;
      &lt;td&gt;7.09&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PDL 申请费（交给 SPF）&lt;/td&gt;
      &lt;td&gt;25.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;合计&lt;/td&gt;
      &lt;td&gt;287.45&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;不算报名费和押金，从视力测试到拿 PDL 一共是 75.80 新元。&lt;/p&gt;

&lt;p&gt;后面的费用按 CDC 官网目前的价格：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;实践课&lt;/strong&gt;：每节 100 分钟。工作日 8:30–10:10 和 12:45–4:15 是非高峰时段，79.57 新元；其他时段和周末是 89.38 新元。再加上班型的附加费。我约的两节都在高峰时段，Elite Team 每节是 89.38 + 16.35 = 105.73 新元。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FTT&lt;/strong&gt;：考试费 7.20 + 行政费 2.94 = 10.14 新元。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;路考&lt;/strong&gt;：考试费 40.00 + 行政费 2.94 + 考试用车和热身费 317.19 = 360.13 新元。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;会籍&lt;/strong&gt;：1 年到期后，续 6 个月是 65.40 新元。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CDC 的费用都从预付的储值账户里扣，要先充值才能预约。&lt;/p&gt;

&lt;h2 id=&quot;我的建议&quot;&gt;我的建议&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;报名后就装上 CDC 的手机 App，随时看学车进度和预约。&lt;/li&gt;
  &lt;li&gt;报名 CDC 后先上它的免费在线理论课，再去考 IE 和 BTT。&lt;/li&gt;
  &lt;li&gt;视力测试、IE、BTT 都要到现场，而且只有前一项通过才能约下一项。每过一项就立刻约下一项。&lt;/li&gt;
  &lt;li&gt;约 slot 时先占一个，再继续刷更早的，有就改期。&lt;/li&gt;
  &lt;li&gt;考 IE 和 BTT 时看清楚按钮，别误点 End Test，否则考试直接结束，slot 作废。&lt;/li&gt;
  &lt;li&gt;去考 BTT 那天顺便在 CDC 拍照，省一趟。&lt;/li&gt;
  &lt;li&gt;IE 通过后就开始约实践课，空位可能要排一个多月。空位不定期释放，没有固定时间，要经常刷，我是早上刷到的。&lt;/li&gt;
  &lt;li&gt;时间允许的话约非高峰时段的实践课，每节便宜将近 10 新元。&lt;/li&gt;
  &lt;li&gt;Induction Programme 不能取消或改期，别和 IE 约得太近。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;接下来实践课ftt-和路考&quot;&gt;接下来：实践课、FTT 和路考&lt;/h2&gt;

&lt;p&gt;这部分正在进行中，考完后会更新到这篇文章里。&lt;/p&gt;

&lt;p&gt;从 App 的 My Learning Journey 可以看到后面还有这些环节：Traffic Police 模拟器培训（3 个模块）；实践课的 3 个模块，其中包括 VPC、LDCS、ATC 等项目；最后是路考前的热身课（Warm Up Lesson）和 Traffic Police 路考。另外，App 里 BTT 的有效期显示为 Lifetime，通过后长期有效。&lt;/p&gt;

&lt;h2 id=&quot;如果你有中国驾照&quot;&gt;如果你有中国驾照&lt;/h2&gt;

&lt;p&gt;用中国驾照转换只需要通过 BTT，不用考 FTT 和路考。流程和材料网上已经有很多攻略，办理时以 Traffic Police 的&lt;a href=&quot;https://www.police.gov.sg/-/media/Spf/Advisories/Checklist-of-Documents-for-Conversion-of-China-Driving-Licence.ashx&quot;&gt;官方材料清单&lt;/a&gt;为准。&lt;/p&gt;

&lt;p&gt;有一条规定容易被忽略：如果你的中国驾照是在拿到第一张新加坡准证（WP、EP、学生准证等）或 PR 之后才考的，需要额外证明在中国住满 1 年，而且实际在中国居住至少 6 个月。可以用护照出入境记录、在中国的工作证明、学历证明或纳税记录来证明。来新加坡之前就考好的驾照，不需要这项证明。&lt;/p&gt;

&lt;p&gt;另外，非英文驾照要由新加坡注册的翻译公司出具英文翻译件，自己翻译的不收。&lt;/p&gt;

&lt;h2 id=&quot;常见问题&quot;&gt;常见问题&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BTT 可以用中文考吗？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;可以，BTT 提供英文、中文、马来文和淡米尔文。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PDL 有效期多久？&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;2 年，可以在网上续期。&lt;/p&gt;

&lt;h2 id=&quot;官方链接&quot;&gt;官方链接&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/Knowledge-Hub/Traffic/Traffic-Matters/Singapore-Driving-Licence&quot;&gt;Singapore Driving Licence（Traffic Police）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/Knowledge-Hub/Traffic/Traffic-Matters/Provisional-Driving-Licence&quot;&gt;Provisional Driving Licence（Traffic Police）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/E-Services/Apply-for-Provisional-Driving-Licence&quot;&gt;申请 PDL（SPF e-Services）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/-/media/Spf/Advisories/Checklist-of-Documents-for-Conversion-of-China-Driving-Licence.ashx&quot;&gt;中国驾照转换材料清单（PDF）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://form.gov.sg/63d22456f2bd6c0012319c64&quot;&gt;外国驾照转换预评估表&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/-/media/SPF/Advisories/TP/BT-ENG-2126.pdf&quot;&gt;Basic Theory of Driving 官方手册（英文 PDF）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cdc.com.sg/course/class-33a/&quot;&gt;CDC Class 3/3A 课程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cdc.com.sg/session-opening-dates/&quot;&gt;CDC 放课日期说明（Session Opening Dates）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cdc.com.sg/2024/07/01/school-motorcar-practical-lesson-booking-limit/&quot;&gt;CDC 实践课每月预约上限&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sso.agc.gov.sg/SL/RTA1961-R27&quot;&gt;Road Traffic (Motor Vehicles, Driving Licences) Rules（视力要求出处）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;费用按 2026 年 10 月 CDC 官网价格。最后更新：2026 年 10 月 9 日。&lt;/em&gt;&lt;/p&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2026/10/singapore-driving-licence-from-scratch/&quot;&gt;新加坡从零考驾照全记录（2026）：BTT、PDL 到路考&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on October 09, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Getting a Singapore Driving Licence from Scratch (2026): BTT to PDL in 11 Days]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2026/10/getting-a-singapore-driving-licence/"/>
  <id>http://www.solidstack.io/posts/2026/10/getting-a-singapore-driving-licence</id>
  <published>2026-10-09T00:00:00+08:00</published>
  <updated>2026-10-09T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#Singapore%20driving%20licence" term="Singapore driving licence" /><category scheme="http://www.solidstack.io/tags/#BTT" term="BTT" /><category scheme="http://www.solidstack.io/tags/#PDL" term="PDL" /><category scheme="http://www.solidstack.io/tags/#CDC" term="CDC" /><category scheme="http://www.solidstack.io/tags/#Class%203A" term="Class 3A" /><category scheme="http://www.solidstack.io/tags/#Life%20in%20Singapore" term="Life in Singapore" />
  <content type="html">
  
    &lt;p&gt;I’m learning to drive in Singapore from scratch. I enrolled for Class 3A (automatic) at CDC, the ComfortDelGro Driving Centre, and it took 11 days to go from enrolment to passing the Basic Theory Test and getting my Provisional Driving Licence.&lt;/p&gt;

&lt;p&gt;This post covers everything up to the PDL. I’ll add the practical lessons, the Final Theory Test and the driving test once I’ve done them. If you already hold a licence from another country and want to convert it instead, there’s a short section on that near the end.&lt;/p&gt;

&lt;p&gt;The rules here are checked against the Traffic Police pages, linked at the bottom. Rules change, so check the official pages before you apply.&lt;/p&gt;

&lt;h2 id=&quot;two-routes-from-scratch-or-converting-a-foreign-licence&quot;&gt;Two routes: from scratch or converting a foreign licence&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;From scratch&lt;/th&gt;
      &lt;th&gt;Converting a foreign licence&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Who it’s for&lt;/td&gt;
      &lt;td&gt;No licence yet, or a foreign licence that can’t be converted&lt;/td&gt;
      &lt;td&gt;A valid licence issued in another country&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Tests&lt;/td&gt;
      &lt;td&gt;BTT, FTT and the practical driving test&lt;/td&gt;
      &lt;td&gt;BTT only&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;What takes effort&lt;/td&gt;
      &lt;td&gt;Many steps, and every one needs a booking slot&lt;/td&gt;
      &lt;td&gt;Getting the documents right, including an official translation if the licence isn’t in English&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Foreigners who have lived in Singapore for less than 12 months can drive on a valid foreign licence. After 12 months, you need a Singapore licence.&lt;/p&gt;

&lt;h2 id=&quot;glossary&quot;&gt;Glossary&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Term&lt;/th&gt;
      &lt;th&gt;Stands for&lt;/th&gt;
      &lt;th&gt;What it means&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;TP&lt;/td&gt;
      &lt;td&gt;Traffic Police&lt;/td&gt;
      &lt;td&gt;The part of the Singapore Police Force that runs driving tests and issues licences&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SPF&lt;/td&gt;
      &lt;td&gt;Singapore Police Force&lt;/td&gt;
      &lt;td&gt;You apply for the PDL on its e-Services website&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CDC, BBDC, SSDC&lt;/td&gt;
      &lt;td&gt;ComfortDelGro Driving Centre, Bukit Batok Driving Centre, Singapore Safety Driving Centre&lt;/td&gt;
      &lt;td&gt;Singapore’s three driving centres. You can learn to drive at any of them, and they also host theory tests&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BTT&lt;/td&gt;
      &lt;td&gt;Basic Theory Test&lt;/td&gt;
      &lt;td&gt;The first theory test. Everyone takes it, including people converting a foreign licence&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;FTT&lt;/td&gt;
      &lt;td&gt;Final Theory Test&lt;/td&gt;
      &lt;td&gt;The second theory test. For Class 3 or 3A you must pass it before the practical driving test&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;IE&lt;/td&gt;
      &lt;td&gt;Internal Evaluation&lt;/td&gt;
      &lt;td&gt;CDC’s in-house mock test. First-time learners at CDC must pass it before they can book the BTT&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Induction Programme&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;CDC’s introduction session covering the whole learning journey. It can’t be cancelled or rescheduled&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PDL&lt;/td&gt;
      &lt;td&gt;Provisional Driving Licence&lt;/td&gt;
      &lt;td&gt;The learner’s licence. It’s digital, valid for 2 years, and required for practical lessons&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Practical lesson&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Driving on the road with an instructor. A CDC lesson is 100 minutes&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Practical driving test&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;The on-road test, conducted by a TP-appointed examiner&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Class 3, 3A&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Class 3 is manual; Class 3A is automatic and only covers cars without a clutch pedal&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Standard, One Team, Elite Team&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;CDC’s coaching schemes, compared below&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PRM&lt;/td&gt;
      &lt;td&gt;Premium fee&lt;/td&gt;
      &lt;td&gt;The per-lesson surcharge for One Team and Elite Team&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Stored Value&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Your prepaid CDC account. All CDC fees are deducted from it&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;VPC&lt;/td&gt;
      &lt;td&gt;Vehicular Pre-operative Check&lt;/td&gt;
      &lt;td&gt;An item in the practical lesson stage, charged separately in CDC’s fee list&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;LDCS&lt;/td&gt;
      &lt;td&gt;Learner Driver Competency Screening&lt;/td&gt;
      &lt;td&gt;An item in the practical lesson stage, charged separately in CDC’s fee list&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Slot&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;A bookable time. Eyesight tests, IEs, BTTs and lessons all need one, and they go fast&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Booking Portal&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;CDC’s online booking system, on the web and in the mobile app&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;My Learning Journey&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Your progress in the CDC app: what you’ve completed and what comes next&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Singpass&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Singapore’s government digital identity, used to log in when applying for the PDL&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GST&lt;/td&gt;
      &lt;td&gt;Goods and Services Tax&lt;/td&gt;
      &lt;td&gt;Singapore’s consumption tax, currently 9%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;the-process-from-scratch&quot;&gt;The process from scratch&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Enrol at a driving centre&lt;/li&gt;
  &lt;li&gt;Eyesight and colour-vision test&lt;/li&gt;
  &lt;li&gt;Induction Programme and IE (for CDC learners)&lt;/li&gt;
  &lt;li&gt;BTT&lt;/li&gt;
  &lt;li&gt;Photo and PDL application&lt;/li&gt;
  &lt;li&gt;Practical lessons&lt;/li&gt;
  &lt;li&gt;FTT&lt;/li&gt;
  &lt;li&gt;Practical driving test&lt;/li&gt;
  &lt;li&gt;Get your licence&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;my-timeline-11-days-from-enrolment-to-pdl&quot;&gt;My timeline: 11 days from enrolment to PDL&lt;/h2&gt;

&lt;figure&gt;
&lt;svg viewBox=&quot;0 0 900 310&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;dlen-title dlen-desc&quot; style=&quot;display:block;width:100%;max-width:900px;height:auto;margin:0 auto;font-family:&apos;Open Sans&apos;,sans-serif&quot;&gt;
  &lt;title id=&quot;dlen-title&quot;&gt;Timeline from enrolment to PDL&lt;/title&gt;
  &lt;desc id=&quot;dlen-desc&quot;&gt;Enrolled at CDC on 28 September, passed the eyesight test on 2 October, attended the Induction Programme and passed the IE on 3 October, refreshed for practical lesson slots for four days from 3 October and booked them on 7 October, and passed the BTT and applied for the PDL on 9 October: 11 days in total. After a wait of about 7 weeks, the first two practical lessons are on 24 and 27 November.&lt;/desc&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;150&quot; x2=&quot;650&quot; y2=&quot;150&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;3&quot; /&gt;
  &lt;line x1=&quot;332&quot; y1=&quot;150&quot; x2=&quot;526&quot; y2=&quot;150&quot; stroke=&quot;#fff&quot; stroke-width=&quot;5&quot; /&gt;
  &lt;line x1=&quot;332&quot; y1=&quot;150&quot; x2=&quot;526&quot; y2=&quot;150&quot; stroke=&quot;#e8a33d&quot; stroke-width=&quot;4&quot; stroke-dasharray=&quot;7 5&quot; /&gt;
  &lt;polyline points=&quot;655,150 665,140 675,160 685,140 695,160 705,150&quot; fill=&quot;none&quot; stroke=&quot;#999&quot; stroke-width=&quot;2&quot; /&gt;
  &lt;line x1=&quot;710&quot; y1=&quot;150&quot; x2=&quot;870&quot; y2=&quot;150&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;3&quot; stroke-dasharray=&quot;6 5&quot; /&gt;
  &lt;circle cx=&quot;60&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;159&quot; x2=&quot;60&quot; y2=&quot;172&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;60&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;28 Sep&lt;/text&gt;
  &lt;text x=&quot;60&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;Enrolled at CDC&lt;/text&gt;
  &lt;circle cx=&quot;271&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;271&quot; y1=&quot;141&quot; x2=&quot;271&quot; y2=&quot;128&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;271&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;2 Oct&lt;/text&gt;
  &lt;text x=&quot;271&quot; y=&quot;117&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;Eyesight test ✓&lt;/text&gt;
  &lt;circle cx=&quot;324&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;324&quot; y1=&quot;159&quot; x2=&quot;324&quot; y2=&quot;172&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;324&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;3 Oct&lt;/text&gt;
  &lt;text x=&quot;324&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;Induction + IE ✓&lt;/text&gt;
  &lt;circle cx=&quot;534&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;534&quot; y1=&quot;141&quot; x2=&quot;534&quot; y2=&quot;128&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;534&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;7 Oct&lt;/text&gt;
  &lt;text x=&quot;534&quot; y=&quot;117&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;Booked practical lessons&lt;/text&gt;
  &lt;circle cx=&quot;640&quot; cy=&quot;150&quot; r=&quot;9&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;line x1=&quot;640&quot; y1=&quot;159&quot; x2=&quot;640&quot; y2=&quot;172&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;640&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;9 Oct&lt;/text&gt;
  &lt;text x=&quot;640&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;BTT ✓ · PDL ✓&lt;/text&gt;
  &lt;circle cx=&quot;740&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#fff&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;2&quot; /&gt;
  &lt;line x1=&quot;740&quot; y1=&quot;141&quot; x2=&quot;740&quot; y2=&quot;128&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;740&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;24 Nov&lt;/text&gt;
  &lt;text x=&quot;740&quot; y=&quot;117&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;Practical lesson 1&lt;/text&gt;
  &lt;circle cx=&quot;840&quot; cy=&quot;150&quot; r=&quot;7&quot; fill=&quot;#fff&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;2&quot; /&gt;
  &lt;line x1=&quot;840&quot; y1=&quot;159&quot; x2=&quot;840&quot; y2=&quot;172&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;840&quot; y=&quot;190&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;27 Nov&lt;/text&gt;
  &lt;text x=&quot;840&quot; y=&quot;209&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#444&quot;&gt;Practical lesson 2&lt;/text&gt;
  &lt;text x=&quot;472&quot; y=&quot;200&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#a35f00&quot;&gt;4 days refreshing for slots&lt;/text&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;252&quot; x2=&quot;640&quot; y2=&quot;252&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;245&quot; x2=&quot;60&quot; y2=&quot;259&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;line x1=&quot;640&quot; y1=&quot;245&quot; x2=&quot;640&quot; y2=&quot;259&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;text x=&quot;350&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;11 days: enrolment to PDL&lt;/text&gt;
  &lt;text x=&quot;680&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#777&quot;&gt;~7 weeks&lt;/text&gt;
  &lt;line x1=&quot;725&quot; y1=&quot;252&quot; x2=&quot;860&quot; y2=&quot;252&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;4 3&quot; /&gt;
  &lt;text x=&quot;792&quot; y=&quot;280&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#2f5d8a&quot;&gt;Booked practical lessons&lt;/text&gt;
&lt;/svg&gt;
&lt;/figure&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Date&lt;/th&gt;
      &lt;th&gt;Step&lt;/th&gt;
      &lt;th&gt;Day&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;28 Sep&lt;/td&gt;
      &lt;td&gt;Enrolled online at CDC for Class 3A&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2 Oct&lt;/td&gt;
      &lt;td&gt;Eyesight test on site, passed&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3 Oct&lt;/td&gt;
      &lt;td&gt;Induction Programme on Zoom, then the IE on site, passed&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;7 Oct&lt;/td&gt;
      &lt;td&gt;Booked practical lessons after refreshing for slots every day since 3 Oct; the earliest slot was 24 Nov&lt;/td&gt;
      &lt;td&gt;9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;9 Oct&lt;/td&gt;
      &lt;td&gt;BTT on site, passed; photo taken at CDC; applied for the PDL online&lt;/td&gt;
      &lt;td&gt;11&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The main reason it went this fast is that I kept getting early slots for each step.&lt;/p&gt;

&lt;h2 id=&quot;choosing-a-coaching-scheme&quot;&gt;Choosing a coaching scheme&lt;/h2&gt;

&lt;p&gt;At Ubi, CDC offers three coaching schemes for Class 3A. They differ in whether you keep the same car and instructors, and in the surcharge per practical lesson (GST included):&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Scheme&lt;/th&gt;
      &lt;th&gt;Extra per lesson&lt;/th&gt;
      &lt;th&gt;Training car&lt;/th&gt;
      &lt;th&gt;Instructors&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Standard&lt;/td&gt;
      &lt;td&gt;S$0&lt;/td&gt;
      &lt;td&gt;Not fixed&lt;/td&gt;
      &lt;td&gt;Not fixed&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;One Team&lt;/td&gt;
      &lt;td&gt;S$13.08&lt;/td&gt;
      &lt;td&gt;Same car&lt;/td&gt;
      &lt;td&gt;A team of up to 6 instructors&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Elite Team&lt;/td&gt;
      &lt;td&gt;S$16.35&lt;/td&gt;
      &lt;td&gt;Same car&lt;/td&gt;
      &lt;td&gt;A team of up to 4 senior instructors&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The enrolment fee is S$185.30 for Standard and S$201.65 for One Team and Elite Team, each including a one-year membership, plus a refundable S$10 deposit. I chose Elite Team on an electric vehicle (EV). The EV course is only available at Ubi.&lt;/p&gt;

&lt;h2 id=&quot;booking-tools-cdc-web-portal-and-app&quot;&gt;Booking tools: CDC web portal and app&lt;/h2&gt;

&lt;p&gt;Everything at CDC, from booking and cancelling to topping up and finding receipts, happens in its online Booking Portal. The web home page has ten sections. These are the three I use most:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;My Bookings&lt;/strong&gt;: lessons and tests you’ve already booked.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Lesson Booking&lt;/strong&gt;: book the Induction Programme, theory lessons and practical lessons.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Test Bookings&lt;/strong&gt;: book tests such as the IE and the BTT.&lt;/li&gt;
&lt;/ul&gt;

&lt;figure&gt;
  &lt;img src=&quot;http://www.solidstack.io/images/cdc-booking-portal.png&quot; alt=&quot;CDC Booking Portal home page with Quick Guides and ten sections; My Bookings, Lesson Booking and Test Bookings are outlined in red&quot; style=&quot;display:block;max-width:720px;width:100%;margin:0 auto&quot; /&gt;
  &lt;figcaption&gt;The CDC Booking Portal home page. The three sections outlined in red are the ones I use most.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The others come up too: &lt;strong&gt;Top-Up Store Value&lt;/strong&gt; to add money to your account, &lt;strong&gt;Cancel Booking&lt;/strong&gt;, &lt;strong&gt;Account Statement / Receipt&lt;/strong&gt; for your transaction history (the costs below come from there), and &lt;strong&gt;Online Learning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Install the CDC mobile app as well. It shows My Learning Journey, so you can see at a glance which step you’re on and what’s next, and booking from it is easy.&lt;/p&gt;

&lt;figure style=&quot;display:flex;flex-wrap:wrap;justify-content:center;gap:1rem;max-width:640px;margin:1.5rem auto&quot;&gt;
  &lt;img src=&quot;http://www.solidstack.io/images/cdc-app-learning-journey-1.webp&quot; alt=&quot;My Learning Journey in the CDC app: enrolment and membership, eyesight test, PDL expiry, and BTT and FTT progress&quot; style=&quot;flex:1 1 260px;max-width:300px;width:auto;height:auto;margin:0&quot; /&gt;
  &lt;img src=&quot;http://www.solidstack.io/images/cdc-app-learning-journey-2.webp&quot; alt=&quot;My Learning Journey in the CDC app: simulator training, practical lesson modules and the practical test&quot; style=&quot;flex:1 1 260px;max-width:300px;width:auto;height:auto;margin:0&quot; /&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;enrolment-and-the-eyesight-test&quot;&gt;Enrolment and the eyesight test&lt;/h2&gt;

&lt;p&gt;After enrolling online, you’ll get an email telling you how to book the eyesight test. Check your spam folder if you don’t see it.&lt;/p&gt;

&lt;p&gt;The test covers eyesight and colour vision. Under Singapore’s Road Traffic (Motor Vehicles, Driving Licences) Rules, you must be able to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;read, at 25 metres, a series of six white letters and numbers on a black background, the same size and layout as a vehicle number plate. If you wear glasses, you can wear them for the test.&lt;/li&gt;
  &lt;li&gt;tell red, amber and green apart at 25 metres.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eyesight test slots are hard to get. From what I saw, bookings open at around 9:30 am each day and close at around 6:30 pm, and you can keep refreshing while it’s open. I booked whatever I could get first, then kept checking and rescheduled whenever an earlier slot appeared.&lt;/p&gt;

&lt;p&gt;The order matters: you can only book the IE after passing the eyesight test, and only book the BTT after passing the IE. All three are on site and need a slot, so book the next one as soon as you pass.&lt;/p&gt;

&lt;h2 id=&quot;induction-programme-and-ie-internal-evaluation&quot;&gt;Induction Programme and IE (Internal Evaluation)&lt;/h2&gt;

&lt;p&gt;The IE is CDC’s in-house mock test, in the same format as the Traffic Police test. First-time learners at CDC have to pass it in person before they can book the real BTT.&lt;/p&gt;

&lt;p&gt;The Induction Programme introduces CDC’s learning journey. I took it on Zoom. CDC says it can’t be cancelled or rescheduled.&lt;/p&gt;

&lt;p&gt;I booked both on the same day: the Induction Programme online first, then the IE on site. The IE started before the Induction Programme ended, so I had to leave early. It still counted as completed, but I wouldn’t plan it that way. Leave a proper gap between the two.&lt;/p&gt;

&lt;h2 id=&quot;the-btt&quot;&gt;The BTT&lt;/h2&gt;

&lt;p&gt;The IE and the BTT have the same format: 50 multiple-choice questions, 45 correct answers to pass, 50 minutes. The BTT is available in English, Chinese, Malay and Tamil. I took it in English.&lt;/p&gt;

&lt;p&gt;The two systems look very different. The IE runs on CDC’s own system, which feels modern. The BTT system looks like an old Visual Basic application and takes a moment to get used to. It’s being upgraded, so hopefully it’ll be friendlier soon.&lt;/p&gt;

&lt;p&gt;Read the buttons carefully during the test. Some learners have clicked End Test after a single question, which ends the test on the spot and wastes a slot that was hard to get.&lt;/p&gt;

&lt;p&gt;I used two things to prepare:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The official Basic Theory of Driving handbook from SPF.&lt;/strong&gt; I read the English and Chinese editions side by side.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The app “BTT, FTT, RTT - Theory Test SG”.&lt;/strong&gt; It has plenty of practice questions, but a few answers in its mock tests are wrong. When it disagrees with the handbook, trust the handbook.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CDC also runs free online theory classes on Zoom, where the instructor goes through the key topics and the questions people often get wrong. They’re worth joining.&lt;/p&gt;

&lt;p&gt;After passing the IE and booking the BTT, I started refreshing for practical lesson slots. It took four days of checking before I got any, on 7 October, and the earliest slot was already 24 November, about seven weeks away. So you can start booking lessons as soon as you pass the IE; there’s no need to wait for the BTT.&lt;/p&gt;

&lt;p&gt;There’s no fixed time when lesson slots open. CDC’s website says there is “no session opening date” for any of its Ubi schemes and asks learners to check online regularly. I got both of my slots by refreshing in the morning, and I’ve heard that slots are released at irregular times. CDC also announced a booking limit in July 2024: at Ubi, automatic-car learners can book at most 5 practical lessons per month, and manual-car learners 8.&lt;/p&gt;

&lt;h2 id=&quot;photo-and-pdl&quot;&gt;Photo and PDL&lt;/h2&gt;

&lt;p&gt;Right after passing the BTT, I had my photo taken at CDC, and the staff emailed me the digital photo. Doing this on the day of the BTT saves a separate trip.&lt;/p&gt;

&lt;p&gt;Then I applied for the PDL on the SPF e-Services website, logging in with Singpass. It costs S$25, and it was approved as soon as I paid. The PDL is digital, valid for two years and renewable, and instructors can verify it online. Taking practical lessons without a valid PDL is an offence.&lt;/p&gt;

&lt;h2 id=&quot;costs&quot;&gt;Costs&lt;/h2&gt;

&lt;p&gt;Here’s what I actually paid up to the PDL (S$, CDC fees include 9% GST):&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Item&lt;/th&gt;
      &lt;th&gt;Cost&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CDC enrolment fee (Elite Team, one-year membership)&lt;/td&gt;
      &lt;td&gt;201.65&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Deposit (refundable)&lt;/td&gt;
      &lt;td&gt;10.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Eyesight test&lt;/td&gt;
      &lt;td&gt;1.96&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Induction Programme&lt;/td&gt;
      &lt;td&gt;23.98&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;IE (Internal Evaluation)&lt;/td&gt;
      &lt;td&gt;7.63&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BTT test fee&lt;/td&gt;
      &lt;td&gt;7.20&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;BTT administrative fee&lt;/td&gt;
      &lt;td&gt;2.94&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CDC online theory classes (2 sessions)&lt;/td&gt;
      &lt;td&gt;0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Digital photo&lt;/td&gt;
      &lt;td&gt;7.09&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PDL application fee (paid to SPF)&lt;/td&gt;
      &lt;td&gt;25.00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Total&lt;/td&gt;
      &lt;td&gt;287.45&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Excluding the enrolment fee and deposit, everything from the eyesight test to the PDL came to S$75.80.&lt;/p&gt;

&lt;p&gt;Later costs, at CDC’s current prices:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Practical lessons&lt;/strong&gt;: 100 minutes each. Weekdays 8:30–10:10 am and 12:45–4:15 pm are off-peak at S$79.57; other times and weekends are S$89.38. Add your scheme’s surcharge. Both of my lessons are at peak times, so each costs 89.38 + 16.35 = S$105.73 on Elite Team.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FTT&lt;/strong&gt;: S$7.20 test fee + S$2.94 administrative fee = S$10.14.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Practical driving test&lt;/strong&gt;: S$40.00 test fee + S$2.94 administrative fee + S$317.19 for the test vehicle and warm-up = S$360.13.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Membership&lt;/strong&gt;: after the first year, a six-month extension costs S$65.40.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All CDC fees come out of your prepaid Stored Value account, so you need to top up before you can book.&lt;/p&gt;

&lt;h2 id=&quot;tips&quot;&gt;Tips&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Install the CDC app as soon as you enrol.&lt;/li&gt;
  &lt;li&gt;Join CDC’s free online theory classes before you sit the IE and the BTT.&lt;/li&gt;
  &lt;li&gt;The eyesight test, IE and BTT are all on site, and each unlocks the next. Book the next one the moment you pass.&lt;/li&gt;
  &lt;li&gt;Grab any slot first, then keep checking for earlier ones and reschedule.&lt;/li&gt;
  &lt;li&gt;Read the buttons carefully in the IE and the BTT. Clicking End Test by mistake ends the test and wastes the slot.&lt;/li&gt;
  &lt;li&gt;Get your photo taken at CDC on the day of your BTT.&lt;/li&gt;
  &lt;li&gt;Start booking practical lessons once you pass the IE. Slots can be more than a month out, and they’re released at no fixed time, so keep checking. I got mine in the morning.&lt;/li&gt;
  &lt;li&gt;If your schedule allows, book off-peak lessons. Each one is almost S$10 cheaper.&lt;/li&gt;
  &lt;li&gt;The Induction Programme can’t be cancelled or rescheduled, so don’t book it too close to your IE.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;whats-next-lessons-ftt-and-the-driving-test&quot;&gt;What’s next: lessons, FTT and the driving test&lt;/h2&gt;

&lt;p&gt;I’m in this stage now. I’ll update this post after the test.&lt;/p&gt;

&lt;p&gt;My Learning Journey in the app shows what’s still ahead: Traffic Police simulator training (3 modules); three practical lesson modules, which include items such as VPC, LDCS and ATC; and finally a warm-up lesson and the Traffic Police practical test. The app also lists the BTT’s expiry as Lifetime, so a pass doesn’t run out.&lt;/p&gt;

&lt;h2 id=&quot;converting-a-foreign-licence&quot;&gt;Converting a foreign licence&lt;/h2&gt;

&lt;p&gt;If you hold a valid licence from another country, you only need to pass the BTT in Singapore; the FTT and the practical test aren’t required. Foreigners need to convert after living in Singapore for 12 months. New citizens and PRs have three months from the change in status. Work Permit and S Pass holders receive a Class 3C licence after converting.&lt;/p&gt;

&lt;p&gt;The Traffic Police publishes document checklists, one for China licences and one for all other foreign licences. The main items are a recent passport-size photo, your identity documents and passes, and your licence. A few points are easy to miss:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If your licence isn’t in English, you need an official translation from a translation company registered in Singapore. Your own translation won’t be accepted.&lt;/li&gt;
  &lt;li&gt;If your licence doesn’t show the date it was first issued, bring an extract of your driving record.&lt;/li&gt;
  &lt;li&gt;If you got your foreign licence after receiving your first Singapore pass (WP, EP, student pass and so on) or PR, or you’re a Singapore citizen applying with a foreign licence, you must also prove you lived in the issuing country for one year, with at least six months of physical residence. Passports, employment documents, education certificates or tax documents can be used. This is to show you didn’t go abroad just to get a licence.&lt;/li&gt;
  &lt;li&gt;The Traffic Police page lists extra documents for some countries. For example, Malaysian licences need a letter from the Road Transport Department showing the licence classes, and Indian licences need an endorsement letter of validity from the Indian embassy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To apply, fill in the online pre-assessment form, book an appointment at Traffic Police Headquarters yourself, and bring the originals and photocopies of your documents. The counter decides on the spot whether you’re eligible, and the Traffic Police may ask for more documents.&lt;/p&gt;

&lt;h2 id=&quot;faq&quot;&gt;FAQ&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can I take the BTT in a language other than English?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. It’s available in English, Chinese, Malay and Tamil.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long is the PDL valid?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two years. You can renew it online.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long does it take to get from enrolment to the PDL?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It took me 11 days, but it depends on how quickly you get slots for the eyesight test, the IE and the BTT.&lt;/p&gt;

&lt;h2 id=&quot;official-links&quot;&gt;Official links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/Knowledge-Hub/Traffic/Traffic-Matters/Singapore-Driving-Licence&quot;&gt;Singapore Driving Licence (Traffic Police)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/Knowledge-Hub/Traffic/Traffic-Matters/Provisional-Driving-Licence&quot;&gt;Provisional Driving Licence (Traffic Police)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/E-Services/Apply-for-Provisional-Driving-Licence&quot;&gt;Apply for a PDL (SPF e-Services)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/-/media/Spf/Advisories/Checklist-of-Documents-for-Conversion-of-China-Driving-Licence.ashx&quot;&gt;Document checklist: China driving licence (PDF)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/-/media/SPF/Advisories/Checklist-of-Documents-for-Conversion-of-Other-Foreign-Driving-Licence.pdf&quot;&gt;Document checklist: other foreign driving licences (PDF)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://form.gov.sg/63d22456f2bd6c0012319c64&quot;&gt;Foreign licence conversion pre-assessment form&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.police.gov.sg/-/media/SPF/Advisories/TP/BT-ENG-2126.pdf&quot;&gt;Basic Theory of Driving handbook (PDF)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cdc.com.sg/course/class-33a/&quot;&gt;CDC Class 3/3A course&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cdc.com.sg/session-opening-dates/&quot;&gt;CDC session opening dates&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.cdc.com.sg/2024/07/01/school-motorcar-practical-lesson-booking-limit/&quot;&gt;CDC practical lesson booking limit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://sso.agc.gov.sg/SL/RTA1961-R27&quot;&gt;Road Traffic (Motor Vehicles, Driving Licences) Rules (eyesight requirements)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Costs are based on CDC’s published prices in October 2026. Last updated: 9 October 2026.&lt;/em&gt;&lt;/p&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2026/10/getting-a-singapore-driving-licence/&quot;&gt;Getting a Singapore Driving Licence from Scratch (2026): BTT to PDL in 11 Days&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on October 09, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Building a Global Multi-Write MongoDB Cluster with Zone Sharding]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2026/09/mongodb-global-multi-write-zone-sharding/"/>
  <id>http://www.solidstack.io/posts/2026/09/mongodb-global-multi-write-zone-sharding</id>
  <published>2026-09-26T00:00:00+08:00</published>
  <updated>2026-09-26T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#MongoDB" term="MongoDB" /><category scheme="http://www.solidstack.io/tags/#Sharding" term="Sharding" /><category scheme="http://www.solidstack.io/tags/#Zone%20Sharding" term="Zone Sharding" /><category scheme="http://www.solidstack.io/tags/#Multi-Region" term="Multi-Region" /><category scheme="http://www.solidstack.io/tags/#High%20Availability" term="High Availability" /><category scheme="http://www.solidstack.io/tags/#Disaster%20Recovery" term="Disaster Recovery" />
  <content type="html">
  
    &lt;p&gt;Say an application has users in China and in North America. With a single replica set, the primary lives in one of the two regions, so users in the other region pay a trans-Pacific round trip, usually well over 100 ms, on every write. If the write concern waits for replication, it’s more.&lt;/p&gt;

&lt;p&gt;MongoDB has no multi-master replication. Each replica set has exactly one primary, which is also why MongoDB never has to resolve write conflicts. Zone sharding gets close to multi-region writes without giving that up. This post covers how to set it up, and what happens when a region goes down, which matters more in operations.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;A sharded cluster is made of several replica sets (shards), each with its own primary. Zone sharding pins ranges of the shard key to specific shards. Combine that with where you place replica set members:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Put a region field such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion&lt;/code&gt; at the front of the shard key.&lt;/li&gt;
  &lt;li&gt;Assign each shard to a zone, and map each region’s key range to that zone.&lt;/li&gt;
  &lt;li&gt;Keep each shard’s primary in its own region, with secondaries in the other region.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A Chinese player’s documents then live on a shard whose primary is in China, and an American player’s documents on a shard whose primary is in North America. Both regions write locally into the same cluster, but any single document still has exactly one primary. Most of the limitations later in this post follow from that.&lt;/p&gt;

&lt;h2 id=&quot;topology&quot;&gt;Topology&lt;/h2&gt;

&lt;figure&gt;
&lt;svg viewBox=&quot;0 0 900 540&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;topo-title topo-desc&quot; style=&quot;display:block;width:100%;max-width:900px;height:auto;margin:0 auto;font-family:&apos;Open Sans&apos;,sans-serif&quot;&gt;
  &lt;title id=&quot;topo-title&quot;&gt;Global MongoDB cluster with zone sharding across China and North America&lt;/title&gt;
  &lt;desc id=&quot;topo-desc&quot;&gt;Each region has application servers and two mongos routers. The config server replica set has its primary and one secondary in China and one secondary in North America; mongos reads routing metadata from it. Shard0 (zone APAC) has its primary and one secondary in China and one secondary in North America. Shard1 (zone NA) has its primary and one secondary in North America and one secondary in China. Each region reads and writes its own shard locally, and replication crosses regions through the oplog.&lt;/desc&gt;
  &lt;defs&gt;
    &lt;marker id=&quot;arrow-red&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;#e51843&quot; /&gt;&lt;/marker&gt;
    &lt;marker id=&quot;arrow-blue&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;#2f5d8a&quot; /&gt;&lt;/marker&gt;
  &lt;/defs&gt;
  &lt;rect x=&quot;10&quot; y=&quot;10&quot; width=&quot;430&quot; height=&quot;520&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;rect x=&quot;460&quot; y=&quot;10&quot; width=&quot;430&quot; height=&quot;520&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;225&quot; y=&quot;40&quot; text-anchor=&quot;middle&quot; font-size=&quot;17&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;China&lt;/text&gt;
  &lt;text x=&quot;675&quot; y=&quot;40&quot; text-anchor=&quot;middle&quot; font-size=&quot;17&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;North America&lt;/text&gt;

  &lt;rect x=&quot;90&quot; y=&quot;56&quot; width=&quot;170&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#888&quot; /&gt;
  &lt;text x=&quot;175&quot; y=&quot;79&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#222&quot;&gt;App servers&lt;/text&gt;
  &lt;line x1=&quot;130&quot; y1=&quot;92&quot; x2=&quot;90&quot; y2=&quot;120&quot; stroke=&quot;#555&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;line x1=&quot;220&quot; y1=&quot;92&quot; x2=&quot;250&quot; y2=&quot;120&quot; stroke=&quot;#555&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;rect x=&quot;40&quot; y=&quot;120&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#222&quot; /&gt;
  &lt;text x=&quot;90&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;mongos&lt;/text&gt;
  &lt;rect x=&quot;200&quot; y=&quot;120&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#222&quot; /&gt;
  &lt;text x=&quot;250&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;mongos&lt;/text&gt;

  &lt;rect x=&quot;640&quot; y=&quot;56&quot; width=&quot;170&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#888&quot; /&gt;
  &lt;text x=&quot;725&quot; y=&quot;79&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#222&quot;&gt;App servers&lt;/text&gt;
  &lt;line x1=&quot;680&quot; y1=&quot;92&quot; x2=&quot;650&quot; y2=&quot;120&quot; stroke=&quot;#555&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;line x1=&quot;770&quot; y1=&quot;92&quot; x2=&quot;810&quot; y2=&quot;120&quot; stroke=&quot;#555&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;rect x=&quot;600&quot; y=&quot;120&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#222&quot; /&gt;
  &lt;text x=&quot;650&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;mongos&lt;/text&gt;
  &lt;rect x=&quot;760&quot; y=&quot;120&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#222&quot; /&gt;
  &lt;text x=&quot;810&quot; y=&quot;143&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;mongos&lt;/text&gt;

  &lt;line x1=&quot;250&quot; y1=&quot;156&quot; x2=&quot;250&quot; y2=&quot;188&quot; stroke=&quot;#888&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;2 3&quot; /&gt;
  &lt;line x1=&quot;650&quot; y1=&quot;156&quot; x2=&quot;650&quot; y2=&quot;188&quot; stroke=&quot;#888&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;2 3&quot; /&gt;
  &lt;line x1=&quot;280&quot; y1=&quot;210&quot; x2=&quot;290&quot; y2=&quot;210&quot; stroke=&quot;#888&quot; stroke-dasharray=&quot;4 3&quot; /&gt;
  &lt;line x1=&quot;370&quot; y1=&quot;210&quot; x2=&quot;610&quot; y2=&quot;210&quot; stroke=&quot;#888&quot; stroke-dasharray=&quot;4 3&quot; /&gt;
  &lt;rect x=&quot;200&quot; y=&quot;190&quot; width=&quot;80&quot; height=&quot;40&quot; rx=&quot;4&quot; fill=&quot;#666&quot; /&gt;
  &lt;text x=&quot;240&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;Config P&lt;/text&gt;
  &lt;rect x=&quot;290&quot; y=&quot;190&quot; width=&quot;80&quot; height=&quot;40&quot; rx=&quot;4&quot; fill=&quot;#eee&quot; stroke=&quot;#888&quot; /&gt;
  &lt;text x=&quot;330&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#333&quot;&gt;Config S&lt;/text&gt;
  &lt;rect x=&quot;610&quot; y=&quot;190&quot; width=&quot;80&quot; height=&quot;40&quot; rx=&quot;4&quot; fill=&quot;#eee&quot; stroke=&quot;#888&quot; /&gt;
  &lt;text x=&quot;650&quot; y=&quot;215&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#333&quot;&gt;Config S&lt;/text&gt;
  &lt;text x=&quot;200&quot; y=&quot;248&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#555&quot;&gt;config server replica set&lt;/text&gt;
  &lt;text x=&quot;610&quot; y=&quot;248&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#555&quot;&gt;config server replica set&lt;/text&gt;

  &lt;line x1=&quot;90&quot; y1=&quot;156&quot; x2=&quot;90&quot; y2=&quot;266&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#arrow-red)&quot; /&gt;
  &lt;text x=&quot;98&quot; y=&quot;182&quot; font-size=&quot;12&quot; fill=&quot;#e51843&quot;&gt;local reads&lt;/text&gt;
  &lt;text x=&quot;98&quot; y=&quot;199&quot; font-size=&quot;12&quot; fill=&quot;#e51843&quot;&gt;and writes&lt;/text&gt;
  &lt;line x1=&quot;810&quot; y1=&quot;156&quot; x2=&quot;810&quot; y2=&quot;386&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#arrow-blue)&quot; /&gt;
  &lt;text x=&quot;802&quot; y=&quot;182&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#2f5d8a&quot;&gt;local reads&lt;/text&gt;
  &lt;text x=&quot;802&quot; y=&quot;199&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#2f5d8a&quot;&gt;and writes&lt;/text&gt;

  &lt;line x1=&quot;150&quot; y1=&quot;290&quot; x2=&quot;165&quot; y2=&quot;290&quot; stroke=&quot;#e51843&quot; stroke-dasharray=&quot;4 3&quot; /&gt;
  &lt;line x1=&quot;285&quot; y1=&quot;290&quot; x2=&quot;490&quot; y2=&quot;290&quot; stroke=&quot;#e51843&quot; stroke-dasharray=&quot;4 3&quot; /&gt;
  &lt;rect x=&quot;30&quot; y=&quot;268&quot; width=&quot;120&quot; height=&quot;44&quot; rx=&quot;4&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;90&quot; y=&quot;295&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;Primary&lt;/text&gt;
  &lt;rect x=&quot;165&quot; y=&quot;268&quot; width=&quot;120&quot; height=&quot;44&quot; rx=&quot;4&quot; fill=&quot;#fde3e9&quot; stroke=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;225&quot; y=&quot;295&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#9c0f2f&quot;&gt;Secondary&lt;/text&gt;
  &lt;rect x=&quot;490&quot; y=&quot;268&quot; width=&quot;120&quot; height=&quot;44&quot; rx=&quot;4&quot; fill=&quot;#fde3e9&quot; stroke=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;550&quot; y=&quot;295&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#9c0f2f&quot;&gt;Secondary&lt;/text&gt;
  &lt;text x=&quot;30&quot; y=&quot;336&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;shard0 · zone APAC · homeRegion &quot;CN&quot;&lt;/text&gt;

  &lt;line x1=&quot;420&quot; y1=&quot;410&quot; x2=&quot;615&quot; y2=&quot;410&quot; stroke=&quot;#2f5d8a&quot; stroke-dasharray=&quot;4 3&quot; /&gt;
  &lt;line x1=&quot;735&quot; y1=&quot;410&quot; x2=&quot;750&quot; y2=&quot;410&quot; stroke=&quot;#2f5d8a&quot; stroke-dasharray=&quot;4 3&quot; /&gt;
  &lt;rect x=&quot;300&quot; y=&quot;388&quot; width=&quot;120&quot; height=&quot;44&quot; rx=&quot;4&quot; fill=&quot;#e3edf7&quot; stroke=&quot;#2f5d8a&quot; /&gt;
  &lt;text x=&quot;360&quot; y=&quot;415&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#1f3f5f&quot;&gt;Secondary&lt;/text&gt;
  &lt;rect x=&quot;615&quot; y=&quot;388&quot; width=&quot;120&quot; height=&quot;44&quot; rx=&quot;4&quot; fill=&quot;#e3edf7&quot; stroke=&quot;#2f5d8a&quot; /&gt;
  &lt;text x=&quot;675&quot; y=&quot;415&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#1f3f5f&quot;&gt;Secondary&lt;/text&gt;
  &lt;rect x=&quot;750&quot; y=&quot;388&quot; width=&quot;120&quot; height=&quot;44&quot; rx=&quot;4&quot; fill=&quot;#2f5d8a&quot; /&gt;
  &lt;text x=&quot;810&quot; y=&quot;415&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;Primary&lt;/text&gt;
  &lt;text x=&quot;870&quot; y=&quot;456&quot; text-anchor=&quot;end&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#2f5d8a&quot;&gt;shard1 · zone NA · homeRegion &quot;US&quot;&lt;/text&gt;

  &lt;text x=&quot;450&quot; y=&quot;500&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; fill=&quot;#555&quot;&gt;dashed lines: replication across regions · dotted lines: mongos reads routing metadata&lt;/text&gt;
&lt;/svg&gt;
&lt;figcaption&gt;Each region writes to the shard whose primary is local. Secondaries in the other region hold a copy for disaster recovery and local reads. Every &lt;code&gt;mongos&lt;/code&gt; gets its routing metadata from the config server replica set.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;A minimal two-region layout:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Component&lt;/th&gt;
      &lt;th&gt;China&lt;/th&gt;
      &lt;th&gt;North America&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Application servers&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; routers&lt;/td&gt;
      &lt;td&gt;2 or more&lt;/td&gt;
      &lt;td&gt;2 or more&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;shard0 (zone &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;APAC&lt;/code&gt;)&lt;/td&gt;
      &lt;td&gt;Primary + Secondary&lt;/td&gt;
      &lt;td&gt;Secondary&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;shard1 (zone &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NA&lt;/code&gt;)&lt;/td&gt;
      &lt;td&gt;Secondary&lt;/td&gt;
      &lt;td&gt;Primary + Secondary&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Config server replica set&lt;/td&gt;
      &lt;td&gt;Primary + Secondary&lt;/td&gt;
      &lt;td&gt;Secondary (see &lt;a href=&quot;#config-servers&quot;&gt;config servers&lt;/a&gt;)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Applications connect to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; routers in their own region. Run at least two per region so a router restart doesn’t take the region’s traffic down. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; routes by shard key using metadata it caches from the config servers, so a write for a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;CN&quot;&lt;/code&gt; document goes to shard0’s primary in China and never leaves the region.&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;p&gt;There are three parts: the data model, shard zones, and zone ranges. The commands below use the current names; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh.addShardTag()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh.addTagRange()&lt;/code&gt; are the older names for the same operations. Run them through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; as a user with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clusterManager&lt;/code&gt; role.&lt;/p&gt;

&lt;h3 id=&quot;1-data-model-and-shard-key&quot;&gt;1. Data model and shard key&lt;/h3&gt;

&lt;p&gt;The example is player profiles in a game, stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;game.players&lt;/code&gt;. The application sets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion&lt;/code&gt; when the player signs up, based on the region they pick:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ObjectId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CN&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;// set by the application at sign-up&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100234&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;lotus&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;lastLoginAt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ISODate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;2026-09-26T08:00:00Z&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;inventory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Zone ranges can only be defined on a prefix of the shard key, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion&lt;/code&gt; has to come first. The second field should have high cardinality and appear in your queries, so chunks can still be split within a zone:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A document inserted without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion&lt;/code&gt; won’t end up where you expect. Schema validation is a cheap way to prevent that:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;validator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;$jsonSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;enum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CN&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;US&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-add-shards-to-zones&quot;&gt;2. Add shards to zones&lt;/h3&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addShardToZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shard0&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;APAC&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;addShardToZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;shard1&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;NA&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A shard can be in several zones, and a zone can have several shards. One shard per zone is enough to start with. Add shards to a zone when it runs out of capacity.&lt;/p&gt;

&lt;h3 id=&quot;3-define-zone-ranges&quot;&gt;3. Define zone ranges&lt;/h3&gt;

&lt;p&gt;The lower bound is inclusive and the upper bound is exclusive. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MinKey&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MaxKey&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;playerId&lt;/code&gt;, each range covers a whole region:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;updateZoneKeyRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;game.players&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CN&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MinKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CN&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MaxKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;APAC&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;updateZoneKeyRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;game.players&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;US&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MinKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;US&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;MaxKey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;NA&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you can, create the zones and ranges before you shard the collection. For an empty or non-existent collection, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shardCollection&lt;/code&gt; then creates chunks on the zone boundaries and puts them on the right shards straight away, so the balancer doesn’t have to move data across the Pacific later:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;enableSharding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;shardCollection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;game.players&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For an existing collection, the balancer migrates every chunk that sits in the wrong zone. Across regions that’s a lot of traffic, so run it inside a balancing window.&lt;/p&gt;

&lt;h3 id=&quot;4-keep-each-primary-in-its-home-region&quot;&gt;4. Keep each primary in its home region&lt;/h3&gt;

&lt;p&gt;Zones decide which shard owns a document. Replica set configuration decides where that shard’s primary runs. Set priorities and tags so the primary is elected in the home region:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// on shard0 (home: China)&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;cfg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;conf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;members&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;priority&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;nx&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;members&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cn&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;members&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;priority&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;nx&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;members&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cn&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;members&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;priority&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;members&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;us&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reconfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Don’t set the remote member’s priority to 0. A priority-0 member can never become primary, so it couldn’t take over if the home region went down.&lt;/p&gt;

&lt;h3 id=&quot;5-check-routing&quot;&gt;5. Check routing&lt;/h3&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;sh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;                                   &lt;span class=&quot;c1&quot;&gt;// zones, ranges, chunk placement&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getShardDistribution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;             &lt;span class=&quot;c1&quot;&gt;// data per shard&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CN&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100234&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;explain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                                              &lt;span class=&quot;c1&quot;&gt;// should target only shard0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;reads&quot;&gt;Reads&lt;/h2&gt;

&lt;p&gt;Reads of a region’s own data go to the local primary, which covers most traffic.&lt;/p&gt;

&lt;p&gt;For data that belongs to the other region, there are two options. Reading from the primary is consistent, but it crosses the ocean. Reading from a local secondary is fast but may be stale: use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readPreference: &quot;nearest&quot;&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;secondaryPreferred&quot;&lt;/code&gt;) with a region tag such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readPreferenceTags=region:cn&lt;/code&gt;. Set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maxStalenessSeconds&lt;/code&gt; (at least 90) so the driver skips secondaries that have fallen too far behind.&lt;/p&gt;

&lt;p&gt;A query without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion&lt;/code&gt; can’t be targeted, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; sends it to every shard in both regions. A support tool that looks up player 100234 by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;playerId&lt;/code&gt; alone runs a global query every time. Keep &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion&lt;/code&gt; in the filter of every frequent query.&lt;/p&gt;

&lt;h2 id=&quot;write-concern-and-region-failure&quot;&gt;Write concern and region failure&lt;/h2&gt;

&lt;p&gt;Zone sharding makes writes local. Whether those writes survive the loss of a region depends on where the write concern’s majority comes from. You can’t have local writes, zero data loss, and automatic failover at the same time. There are two common layouts.&lt;/p&gt;

&lt;h3 id=&quot;layout-a-2-members-at-home-1-remote&quot;&gt;Layout A: 2 members at home, 1 remote&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w: &quot;majority&quot;&lt;/code&gt; needs 2 of 3 acknowledgements, and both can come from the home region, so writes stay local.&lt;/p&gt;

&lt;p&gt;If the home region goes down, the remote member is 1 of 3 and can’t be elected. Someone has to force a reconfiguration by hand, and whatever hadn’t replicated across the ocean yet is lost. That replication lag is your RPO.&lt;/p&gt;

&lt;h3 id=&quot;layout-b-5-members-across-3-regions-2--2--1&quot;&gt;Layout B: 5 members across 3 regions (2 + 2 + 1)&lt;/h3&gt;

&lt;p&gt;A majority is 3, so at least one acknowledgement has to come from outside the home region. Every majority write pays at least one cross-region round trip. Putting the fifth member in a region close to home keeps that cost small.&lt;/p&gt;

&lt;p&gt;If the home region goes down, the other 3 members still form a majority and elect a new primary on their own. No acknowledged majority write is lost.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Layout A (2 + 1)&lt;/th&gt;
      &lt;th&gt;Layout B (2 + 2 + 1)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Write latency with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;w: &quot;majority&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;local&lt;/td&gt;
      &lt;td&gt;at least one cross-region round trip&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Home region lost: automatic failover&lt;/td&gt;
      &lt;td&gt;✘ manual forced reconfig&lt;/td&gt;
      &lt;td&gt;✔&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Home region lost: acknowledged writes lost&lt;/td&gt;
      &lt;td&gt;possible (replication lag)&lt;/td&gt;
      &lt;td&gt;none&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Cost&lt;/td&gt;
      &lt;td&gt;3 nodes per shard&lt;/td&gt;
      &lt;td&gt;5 nodes per shard, 3 regions&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;A common split is layout A for most data, accepting a small RPO, and layout B for data where losing a write isn’t acceptable. Either is fine, as long as your runbook matches the layout you actually deployed.&lt;/p&gt;

&lt;h2 id=&quot;config-servers&quot;&gt;Config servers&lt;/h2&gt;

&lt;p&gt;The config server replica set stores chunk ownership, zones, and ranges. If it loses its primary, reads and writes keep working with the routing table &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; already has, but chunk migrations, splits, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shardCollection&lt;/code&gt; stop until a primary is back.&lt;/p&gt;

&lt;p&gt;With two regions, one of them has to hold the config servers’ majority. In the diagram that’s China, with two of the three members, so losing China freezes metadata changes. That’s usually acceptable during an incident. A small third region fixes it for the config servers, and it’s also where layout B’s fifth member lives.&lt;/p&gt;

&lt;h2 id=&quot;failure-scenarios&quot;&gt;Failure scenarios&lt;/h2&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Scenario&lt;/th&gt;
      &lt;th&gt;What happens&lt;/th&gt;
      &lt;th&gt;What to plan&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Cross-region link down&lt;/td&gt;
      &lt;td&gt;Each region keeps writing its own shard locally. Replication to remote secondaries lags, and remote reads get staler.&lt;/td&gt;
      &lt;td&gt;Alert on replication lag, and watch oplog window size so secondaries can catch up without a full resync.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;One member in the home region down&lt;/td&gt;
      &lt;td&gt;The other members still form a majority, and writes continue.&lt;/td&gt;
      &lt;td&gt;Normal replica set operations.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Whole home region down (layout A)&lt;/td&gt;
      &lt;td&gt;That zone’s data is read-only elsewhere, and it has no primary.&lt;/td&gt;
      &lt;td&gt;A runbook for forced reconfiguration, with the RPO it implies written down in advance.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Whole home region down (layout B)&lt;/td&gt;
      &lt;td&gt;A new primary is elected in another region, and writes continue with higher latency.&lt;/td&gt;
      &lt;td&gt;Capacity in the surviving regions to absorb the extra load.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Player travels temporarily&lt;/td&gt;
      &lt;td&gt;Requests route from the local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; to the home shard’s primary across the ocean: slower, but correct.&lt;/td&gt;
      &lt;td&gt;Nothing. Travel is rare, so accept the latency.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Player moves to another region&lt;/td&gt;
      &lt;td&gt;Their documents stay pinned to the old zone until the shard key changes.&lt;/td&gt;
      &lt;td&gt;Update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion&lt;/code&gt; (see below).&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;players-who-travel&quot;&gt;Players who travel&lt;/h3&gt;

&lt;p&gt;Most region changes are trips. A North American player visiting China keeps &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion: &quot;US&quot;&lt;/code&gt;. Their app connects to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; in China, which sends every read and write to shard1’s primary in North America. Each request crosses the Pacific, so it’s slower, but it’s correct and no data moves.&lt;/p&gt;

&lt;p&gt;This doesn’t happen often, so I’d accept the latency rather than build anything for it. Reads that can tolerate some staleness can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readPreference: &quot;nearest&quot;&lt;/code&gt;. Writes still go to the home primary.&lt;/p&gt;

&lt;h3 id=&quot;players-who-move&quot;&gt;Players who move&lt;/h3&gt;

&lt;p&gt;Only a permanent move is worth moving data for. Changing a shard key value moves the document to another shard. MongoDB allows it under a few conditions: the update runs through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt;, as a retryable write or inside a transaction, with an equality match on the full shard key, one document at a time (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updateOne&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updateMany&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;withTransaction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;players&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;updateOne&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;CN&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;playerId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100234&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;$set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;homeRegion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;US&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If the player also has documents in other zoned collections, like match history or friend lists, it becomes a small migration job. Decide whether old data needs to move at all, or only new data.&lt;/p&gt;

&lt;h2 id=&quot;limitations&quot;&gt;Limitations&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;There’s no global uniqueness. On a sharded collection, a unique index has to be prefixed by the shard key, so you can make an email unique per region but not worldwide. A global registry needs a separate design, for example a small unzoned collection or an external service.&lt;/li&gt;
  &lt;li&gt;A transaction that touches both zones is a distributed transaction across the Pacific. Keep transactions within one region.&lt;/li&gt;
  &lt;li&gt;Chunks outside every zone range can end up on any shard. Make sure every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homeRegion&lt;/code&gt; value is covered by a range.&lt;/li&gt;
  &lt;li&gt;Remote secondaries are full copies of the other region’s data. If the reason for pinning data is data residency rather than latency, this layout breaks it, and DR members would have to stay inside the allowed jurisdiction.&lt;/li&gt;
  &lt;li&gt;Zone sharding doesn’t apply to time series collections. The balancer always spreads them evenly across all shards.&lt;/li&gt;
  &lt;li&gt;Dropping a collection also drops its zone ranges. Recreate them, ideally before sharding the collection again.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;alternative-independent-clusters-with-two-way-sync&quot;&gt;Alternative: independent clusters with two-way sync&lt;/h2&gt;

&lt;p&gt;The other common approach skips the shared cluster. Each region runs its own independent MongoDB deployment, a replica set or a sharded cluster, and a tool such as &lt;a href=&quot;https://github.com/alibaba/MongoShake&quot;&gt;MongoShake&lt;/a&gt; replicates changes in both directions.&lt;/p&gt;

&lt;figure&gt;
&lt;svg viewBox=&quot;0 0 900 440&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;shake-title shake-desc&quot; style=&quot;display:block;width:100%;max-width:900px;height:auto;margin:0 auto;font-family:&apos;Open Sans&apos;,sans-serif&quot;&gt;
  &lt;title id=&quot;shake-title&quot;&gt;Two-way sync between regional clusters with two MongoShake instances&lt;/title&gt;
  &lt;desc id=&quot;shake-desc&quot;&gt;China and North America each run an independent MongoDB cluster holding a full copy of the data. MongoShake instance A fetches China&apos;s oplog with its Collector, filters it, and replays it through the Direct tunnel into North America. MongoShake instance B does the same from North America to China. Each instance saves its checkpoint in its source database. Loop filtering by gid is only available in Alibaba Cloud&apos;s internal version.&lt;/desc&gt;
  &lt;defs&gt;
    &lt;marker id=&quot;shk-red&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;#e51843&quot; /&gt;&lt;/marker&gt;
    &lt;marker id=&quot;shk-blue&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;#2f5d8a&quot; /&gt;&lt;/marker&gt;
  &lt;/defs&gt;

  &lt;rect x=&quot;10&quot; y=&quot;10&quot; width=&quot;220&quot; height=&quot;420&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;120&quot; y=&quot;40&quot; text-anchor=&quot;middle&quot; font-size=&quot;17&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;China&lt;/text&gt;
  &lt;rect x=&quot;40&quot; y=&quot;60&quot; width=&quot;160&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#888&quot; /&gt;
  &lt;text x=&quot;120&quot; y=&quot;83&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#222&quot;&gt;App servers&lt;/text&gt;
  &lt;line x1=&quot;120&quot; y1=&quot;96&quot; x2=&quot;120&quot; y2=&quot;114&quot; stroke=&quot;#555&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;rect x=&quot;40&quot; y=&quot;114&quot; width=&quot;160&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#222&quot; /&gt;
  &lt;text x=&quot;120&quot; y=&quot;137&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;mongos&lt;/text&gt;
  &lt;line x1=&quot;120&quot; y1=&quot;150&quot; x2=&quot;120&quot; y2=&quot;180&quot; stroke=&quot;#555&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;rect x=&quot;18&quot; y=&quot;180&quot; width=&quot;204&quot; height=&quot;150&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;120&quot; y=&quot;202&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;Independent cluster&lt;/text&gt;
  &lt;rect x=&quot;26&quot; y=&quot;216&quot; width=&quot;60&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;56&quot; y=&quot;238&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;Primary&lt;/text&gt;
  &lt;rect x=&quot;90&quot; y=&quot;216&quot; width=&quot;60&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#fde3e9&quot; stroke=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;120&quot; y=&quot;238&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#9c0f2f&quot;&gt;Secondary&lt;/text&gt;
  &lt;rect x=&quot;154&quot; y=&quot;216&quot; width=&quot;60&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#fde3e9&quot; stroke=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;184&quot; y=&quot;238&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#9c0f2f&quot;&gt;Secondary&lt;/text&gt;
  &lt;text x=&quot;120&quot; y=&quot;310&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;full copy of all data&lt;/text&gt;

  &lt;rect x=&quot;670&quot; y=&quot;10&quot; width=&quot;220&quot; height=&quot;420&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;780&quot; y=&quot;40&quot; text-anchor=&quot;middle&quot; font-size=&quot;17&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;North America&lt;/text&gt;
  &lt;rect x=&quot;700&quot; y=&quot;60&quot; width=&quot;160&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#888&quot; /&gt;
  &lt;text x=&quot;780&quot; y=&quot;83&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; fill=&quot;#222&quot;&gt;App servers&lt;/text&gt;
  &lt;line x1=&quot;780&quot; y1=&quot;96&quot; x2=&quot;780&quot; y2=&quot;114&quot; stroke=&quot;#555&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;rect x=&quot;700&quot; y=&quot;114&quot; width=&quot;160&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#222&quot; /&gt;
  &lt;text x=&quot;780&quot; y=&quot;137&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;mongos&lt;/text&gt;
  &lt;line x1=&quot;780&quot; y1=&quot;150&quot; x2=&quot;780&quot; y2=&quot;180&quot; stroke=&quot;#555&quot; stroke-width=&quot;1.5&quot; /&gt;
  &lt;rect x=&quot;678&quot; y=&quot;180&quot; width=&quot;204&quot; height=&quot;150&quot; rx=&quot;4&quot; fill=&quot;#fff&quot; stroke=&quot;#2f5d8a&quot; /&gt;
  &lt;text x=&quot;780&quot; y=&quot;202&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#2f5d8a&quot;&gt;Independent cluster&lt;/text&gt;
  &lt;rect x=&quot;686&quot; y=&quot;216&quot; width=&quot;60&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#2f5d8a&quot; /&gt;
  &lt;text x=&quot;716&quot; y=&quot;238&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;Primary&lt;/text&gt;
  &lt;rect x=&quot;750&quot; y=&quot;216&quot; width=&quot;60&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#e3edf7&quot; stroke=&quot;#2f5d8a&quot; /&gt;
  &lt;text x=&quot;780&quot; y=&quot;238&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#1f3f5f&quot;&gt;Secondary&lt;/text&gt;
  &lt;rect x=&quot;814&quot; y=&quot;216&quot; width=&quot;60&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#e3edf7&quot; stroke=&quot;#2f5d8a&quot; /&gt;
  &lt;text x=&quot;844&quot; y=&quot;238&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#1f3f5f&quot;&gt;Secondary&lt;/text&gt;
  &lt;text x=&quot;780&quot; y=&quot;310&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;full copy of all data&lt;/text&gt;

  &lt;rect x=&quot;250&quot; y=&quot;70&quot; width=&quot;400&quot; height=&quot;130&quot; rx=&quot;6&quot; fill=&quot;#fff&quot; stroke=&quot;#e51843&quot; stroke-dasharray=&quot;6 4&quot; /&gt;
  &lt;text x=&quot;450&quot; y=&quot;94&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;MongoShake A · China → North America&lt;/text&gt;
  &lt;rect x=&quot;275&quot; y=&quot;112&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#f5b731&quot; /&gt;
  &lt;text x=&quot;325&quot; y=&quot;135&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#3d2c00&quot;&gt;Collector&lt;/text&gt;
  &lt;rect x=&quot;400&quot; y=&quot;112&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#f1f1f1&quot; stroke=&quot;#999&quot; /&gt;
  &lt;text x=&quot;450&quot; y=&quot;135&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; fill=&quot;#333&quot;&gt;Filter&lt;/text&gt;
  &lt;rect x=&quot;525&quot; y=&quot;112&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#ec5fa3&quot; /&gt;
  &lt;text x=&quot;575&quot; y=&quot;135&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;Direct&lt;/text&gt;
  &lt;line x1=&quot;375&quot; y1=&quot;130&quot; x2=&quot;398&quot; y2=&quot;130&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#shk-red)&quot; /&gt;
  &lt;line x1=&quot;500&quot; y1=&quot;130&quot; x2=&quot;523&quot; y2=&quot;130&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#shk-red)&quot; /&gt;
  &lt;text x=&quot;450&quot; y=&quot;178&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#555&quot;&gt;checkpoint saved in the China cluster&lt;/text&gt;
  &lt;path d=&quot;M222 205 H240 V130 H273&quot; fill=&quot;none&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#shk-red)&quot; /&gt;
  &lt;text x=&quot;257&quot; y=&quot;123&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#e51843&quot;&gt;fetch&lt;/text&gt;
  &lt;path d=&quot;M625 130 H660 V205 H676&quot; fill=&quot;none&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#shk-red)&quot; /&gt;
  &lt;text x=&quot;642&quot; y=&quot;123&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#e51843&quot;&gt;replay&lt;/text&gt;

  &lt;rect x=&quot;250&quot; y=&quot;230&quot; width=&quot;400&quot; height=&quot;130&quot; rx=&quot;6&quot; fill=&quot;#fff&quot; stroke=&quot;#2f5d8a&quot; stroke-dasharray=&quot;6 4&quot; /&gt;
  &lt;text x=&quot;450&quot; y=&quot;254&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#2f5d8a&quot;&gt;MongoShake B · North America → China&lt;/text&gt;
  &lt;rect x=&quot;275&quot; y=&quot;272&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#ec5fa3&quot; /&gt;
  &lt;text x=&quot;325&quot; y=&quot;295&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#fff&quot;&gt;Direct&lt;/text&gt;
  &lt;rect x=&quot;400&quot; y=&quot;272&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#f1f1f1&quot; stroke=&quot;#999&quot; /&gt;
  &lt;text x=&quot;450&quot; y=&quot;295&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; fill=&quot;#333&quot;&gt;Filter&lt;/text&gt;
  &lt;rect x=&quot;525&quot; y=&quot;272&quot; width=&quot;100&quot; height=&quot;36&quot; rx=&quot;4&quot; fill=&quot;#f5b731&quot; /&gt;
  &lt;text x=&quot;575&quot; y=&quot;295&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; font-weight=&quot;700&quot; fill=&quot;#3d2c00&quot;&gt;Collector&lt;/text&gt;
  &lt;line x1=&quot;525&quot; y1=&quot;290&quot; x2=&quot;502&quot; y2=&quot;290&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#shk-blue)&quot; /&gt;
  &lt;line x1=&quot;400&quot; y1=&quot;290&quot; x2=&quot;377&quot; y2=&quot;290&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;1.5&quot; marker-end=&quot;url(#shk-blue)&quot; /&gt;
  &lt;text x=&quot;450&quot; y=&quot;338&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#555&quot;&gt;checkpoint saved in the North America cluster&lt;/text&gt;
  &lt;line x1=&quot;678&quot; y1=&quot;290&quot; x2=&quot;627&quot; y2=&quot;290&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#shk-blue)&quot; /&gt;
  &lt;text x=&quot;652&quot; y=&quot;282&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#2f5d8a&quot;&gt;fetch&lt;/text&gt;
  &lt;line x1=&quot;275&quot; y1=&quot;290&quot; x2=&quot;224&quot; y2=&quot;290&quot; stroke=&quot;#2f5d8a&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#shk-blue)&quot; /&gt;
  &lt;text x=&quot;249&quot; y=&quot;282&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#2f5d8a&quot;&gt;replay&lt;/text&gt;

  &lt;text x=&quot;450&quot; y=&quot;392&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#555&quot;&gt;Filter: namespace whitelist / blacklist. Loop filtering by gid is only&lt;/text&gt;
  &lt;text x=&quot;450&quot; y=&quot;408&quot; text-anchor=&quot;middle&quot; font-size=&quot;11&quot; fill=&quot;#555&quot;&gt;available in Alibaba Cloud&apos;s internal version of MongoShake.&lt;/text&gt;
&lt;/svg&gt;
&lt;figcaption&gt;Two MongoShake instances, one per direction. Component names follow MongoShake&apos;s &lt;a href=&quot;https://github.com/alibaba/MongoShake&quot;&gt;architecture overview&lt;/a&gt;.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;MongoShake replicates in one direction, so two-way sync means two instances: one for China → North America and one for North America → China. Each instance fetches the source oplog with its Collector. For a sharded source, it connects to every shard, preferably reading from secondaries. It filters namespaces with whitelists and blacklists, and replays the changes into the target &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongos&lt;/code&gt; through the Direct tunnel. It saves a checkpoint, by default in the source database, so it can resume after a restart, and it exposes metrics through a REST API and Prometheus.&lt;/p&gt;

&lt;p&gt;Every region can write every document and holds a full copy of the data.&lt;/p&gt;

&lt;h3 id=&quot;pros&quot;&gt;Pros&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;The regions are isolated from each other. They share no config servers, elections, or balancer, so an outage or network partition in one region never blocks writes in the other.&lt;/li&gt;
  &lt;li&gt;Every document can be read and written locally in every region. There’s no region field in the shard key, no zone ranges, no relocation, and no queries broadcast across regions.&lt;/li&gt;
  &lt;li&gt;Each side can run a different topology, version, or hardware, and filters can keep some data out of a region.&lt;/li&gt;
  &lt;li&gt;It’s easy to add to an existing single-region deployment: stand up a second cluster and start syncing, the same way many migrations are done.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;cons&quot;&gt;Cons&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Conflicts. If a document is updated in both regions within the sync lag, each side applies the other’s change after its own, and the two copies can end up permanently different. MongoShake doesn’t resolve conflicts, so the application has to make sure each document is only written in one region. That’s the same rule zone sharding enforces, except here nothing enforces it.&lt;/li&gt;
  &lt;li&gt;Duplicate keys. Two players registering the same email in both regions at once both succeed locally. Replaying one of them then hits a duplicate key error, and the sync either stalls or drops the change, depending on configuration.&lt;/li&gt;
  &lt;li&gt;It’s asynchronous only. There’s no cross-region write concern, so a region failure loses whatever was inside the sync lag, and cross-region reads are eventually consistent.&lt;/li&gt;
  &lt;li&gt;Loops. Each instance has to tell local writes from replicated ones, or changes bounce back and forth. MongoShake does this with a gid, but its README says gid is only available in Alibaba Cloud’s internal version, because it needs MongoDB kernel changes. The open-source version can’t filter loops that way, and the &lt;a href=&quot;https://github.com/alibaba/MongoShake/wiki/FAQ&quot;&gt;FAQ&lt;/a&gt; describes workarounds.&lt;/li&gt;
  &lt;li&gt;Transactions and DDL. Multi-document transactions can be replayed as individual writes, so the target briefly sees half a transaction. MongoShake only syncs DDL when the source is a replica set.&lt;/li&gt;
  &lt;li&gt;More to operate. The sync instances need their own high availability, monitoring, upgrades, and checkpoint handling. If one falls behind past the oplog window, you have to do a full resync. You also need a regular way to check the two sides for drift.&lt;/li&gt;
  &lt;li&gt;Every region stores all the data: double the storage, and the same data residency problem as remote secondaries.&lt;/li&gt;
  &lt;li&gt;No vendor support. MongoShake is a third-party tool, and MongoDB’s own Cluster-to-Cluster Sync (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mongosync&lt;/code&gt;) only does one-way replication.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;comparison&quot;&gt;Comparison&lt;/h3&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Zone sharding (one cluster)&lt;/th&gt;
      &lt;th&gt;Independent clusters + two-way sync&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Who can write a document&lt;/td&gt;
      &lt;td&gt;only its home shard’s primary&lt;/td&gt;
      &lt;td&gt;any region&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Write conflicts&lt;/td&gt;
      &lt;td&gt;impossible by design&lt;/td&gt;
      &lt;td&gt;possible; the application must avoid them&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Cross-region durability&lt;/td&gt;
      &lt;td&gt;tunable with write concern (layout A / B)&lt;/td&gt;
      &lt;td&gt;async only; RPO = sync lag&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Region failure&lt;/td&gt;
      &lt;td&gt;depends on member placement&lt;/td&gt;
      &lt;td&gt;other region unaffected&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Global unique indexes&lt;/td&gt;
      &lt;td&gt;not possible (shard key prefix rule)&lt;/td&gt;
      &lt;td&gt;not possible; cross-region duplicates break sync&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Data per region&lt;/td&gt;
      &lt;td&gt;own zone + remote replicas&lt;/td&gt;
      &lt;td&gt;full copy&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Operational burden&lt;/td&gt;
      &lt;td&gt;sharding, balancer, config servers&lt;/td&gt;
      &lt;td&gt;two clusters plus a sync pipeline&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Support&lt;/td&gt;
      &lt;td&gt;built into MongoDB&lt;/td&gt;
      &lt;td&gt;third-party tools&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Zone sharding enforces single ownership in the database, so you can’t break it by accident. Two-way sync isolates the regions better and is more flexible, but it moves conflict avoidance and consistency checks into the application and into operations. If the data already has a natural home region, I’d choose zone sharding. Two-way sync makes more sense when regional isolation is the top priority and the application can guarantee that each document is only written in one region.&lt;/p&gt;

&lt;h2 id=&quot;when-zone-sharding-fits&quot;&gt;When zone sharding fits&lt;/h2&gt;

&lt;p&gt;It works well when data has a natural home region, most traffic stays in that region, and you can accept either a small RPO (layout A) or slower writes (layout B) when a region fails.&lt;/p&gt;

&lt;p&gt;It works badly for documents written from several regions at once, like a global counter or a shared document. Every write still goes to one primary, so for most users that document is remote no matter how you zone it. Global uniqueness or cross-region transactions on the hot path are also a bad fit.&lt;/p&gt;

&lt;p&gt;On Atlas, Global Clusters provide the same setup as a managed feature. It’s still zone sharding underneath, and the same write concern trade-offs apply.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/docs/manual/core/zone-sharding/&quot;&gt;MongoDB Manual: Zones&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/docs/manual/tutorial/sharding-segmenting-data-by-location/&quot;&gt;Segmenting Data by Location&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/docs/manual/reference/method/sh.updateZoneKeyRange/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh.updateZoneKeyRange()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.mongodb.com/docs/manual/core/sharding-change-shard-key-value/&quot;&gt;Change a Document’s Shard Key Value&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/alibaba/MongoShake&quot;&gt;MongoShake&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2026/09/mongodb-global-multi-write-zone-sharding/&quot;&gt;Building a Global Multi-Write MongoDB Cluster with Zone Sharding&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on September 26, 2026.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[用 Prometheus 指标给分布式数据库做容量规划：以 TiDB 为例]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2024/10/tidb-capacity-planning-prometheus/"/>
  <id>http://www.solidstack.io/posts/2024/10/tidb-capacity-planning-prometheus</id>
  <published>2024-10-10T00:00:00+08:00</published>
  <updated>2024-10-10T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#%E5%AE%B9%E9%87%8F%E8%A7%84%E5%88%92" term="容量规划" /><category scheme="http://www.solidstack.io/tags/#TiDB" term="TiDB" /><category scheme="http://www.solidstack.io/tags/#Prometheus" term="Prometheus" /><category scheme="http://www.solidstack.io/tags/#%E7%A8%B3%E5%AE%9A%E6%80%A7" term="稳定性" /><category scheme="http://www.solidstack.io/tags/#%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F" term="分布式系统" />
  <content type="html">
  
    &lt;p&gt;“下个季度要扩多少台？”做数据库的团队几乎都被问过这个问题，通常是在大促或者新业务上线前几周。常见的做法是先拍一个数，再乘个系数“保险一点”，结果要么当天不够用，要么一年里大部分时间都在浪费机器。&lt;/p&gt;

&lt;p&gt;之前我和一位同事写过一个小工具 &lt;a href=&quot;https://github.com/yinx0004/tidbcloud-metrics&quot;&gt;tidbcloud-metrics&lt;/a&gt;，用 Prometheus 里现成的监控数据来回答这个问题。超大规模的 TiDB Cloud 集群每次大促前要做容量评估、决定扩多少节点，我们都靠它来算。代码和当时的环境绑得比较紧，但方法本身是通用的。这篇文章用虚构的数据把这套方法过一遍：采哪些指标，怎么汇总，留多少余量，以及这个简单模型在哪些地方不准。&lt;/p&gt;

&lt;p&gt;例子用的是 TiDB，换成其他分布式数据库，或者任何按节点暴露指标的无状态服务，思路都一样。&lt;/p&gt;

&lt;h2 id=&quot;核心公式&quot;&gt;核心公式&lt;/h2&gt;

&lt;p&gt;对每个组件的每种资源，需要的节点数是：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;所需节点数 = (单节点用量 × 当前节点数 × 业务增长倍数) / (单节点容量 × 目标水位)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;单节点用量：观察窗口内每个节点对这种资源的用量汇总值，比如 P99。&lt;/li&gt;
  &lt;li&gt;单节点用量 × 当前节点数：大致等于集群当前的总需求。&lt;/li&gt;
  &lt;li&gt;业务增长倍数：预计流量会涨多少，1.5 就是涨 50%。&lt;/li&gt;
  &lt;li&gt;单节点容量：一个节点能提供多少，比如 32 核或 2 TiB 磁盘。&lt;/li&gt;
  &lt;li&gt;目标水位：你愿意让节点跑到多满。除以 0.5 意味着希望节点利用率不超过 50%。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;每个组件的每种资源都算一遍，需要节点最多的那种资源就是瓶颈，它决定这个组件最终要多少节点。&lt;/p&gt;

&lt;figure&gt;
&lt;svg viewBox=&quot;0 30 900 110&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;cpz-title cpz-desc&quot; style=&quot;display:block;width:100%;max-width:900px;height:auto;margin:0 auto;font-family:&apos;Open Sans&apos;,&apos;PingFang SC&apos;,&apos;Microsoft YaHei&apos;,sans-serif&quot;&gt;
  &lt;title id=&quot;cpz-title&quot;&gt;容量规划流程&lt;/title&gt;
  &lt;desc id=&quot;cpz-desc&quot;&gt;从 Prometheus 取每个节点的资源用量，汇总成分位数，乘以节点数和增长倍数，再除以单节点容量乘目标水位，得到每种资源所需节点数，取最大值作为瓶颈。&lt;/desc&gt;
  &lt;defs&gt;
    &lt;marker id=&quot;cpz-arrow&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;#e51843&quot; /&gt;&lt;/marker&gt;
  &lt;/defs&gt;
  &lt;rect x=&quot;10&quot; y=&quot;40&quot; width=&quot;150&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;85&quot; y=&quot;78&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;Prometheus&lt;/text&gt;
  &lt;text x=&quot;85&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;各节点资源用量&lt;/text&gt;
  &lt;rect x=&quot;197&quot; y=&quot;40&quot; width=&quot;150&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;272&quot; y=&quot;78&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;汇总&lt;/text&gt;
  &lt;text x=&quot;272&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;P50 … P99.9、最大值&lt;/text&gt;
  &lt;rect x=&quot;384&quot; y=&quot;40&quot; width=&quot;150&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;459&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;换算&lt;/text&gt;
  &lt;text x=&quot;459&quot; y=&quot;92&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;× 节点数 × 增长&lt;/text&gt;
  &lt;text x=&quot;459&quot; y=&quot;110&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;/ (容量 × 水位)&lt;/text&gt;
  &lt;rect x=&quot;571&quot; y=&quot;40&quot; width=&quot;150&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;646&quot; y=&quot;78&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;所需节点数&lt;/text&gt;
  &lt;text x=&quot;646&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;按资源分别计算&lt;/text&gt;
  &lt;rect x=&quot;758&quot; y=&quot;40&quot; width=&quot;132&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fde3e9&quot; stroke=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;824&quot; y=&quot;78&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#9c0f2f&quot;&gt;瓶颈&lt;/text&gt;
  &lt;text x=&quot;824&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#9c0f2f&quot;&gt;取最大值&lt;/text&gt;
  &lt;line x1=&quot;160&quot; y1=&quot;85&quot; x2=&quot;195&quot; y2=&quot;85&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#cpz-arrow)&quot; /&gt;
  &lt;line x1=&quot;347&quot; y1=&quot;85&quot; x2=&quot;382&quot; y2=&quot;85&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#cpz-arrow)&quot; /&gt;
  &lt;line x1=&quot;534&quot; y1=&quot;85&quot; x2=&quot;569&quot; y2=&quot;85&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#cpz-arrow)&quot; /&gt;
  &lt;line x1=&quot;721&quot; y1=&quot;85&quot; x2=&quot;756&quot; y2=&quot;85&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#cpz-arrow)&quot; /&gt;
&lt;/svg&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;第一步选观察窗口&quot;&gt;第一步：选观察窗口&lt;/h2&gt;

&lt;p&gt;数字取决于你查的是哪段时间，所以这段时间里必须包含你要规划的那种负载。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;至少覆盖一个完整的业务周期，一般是一周，这样工作日和周末的规律都在里面。&lt;/li&gt;
  &lt;li&gt;确认窗口里有已知的高峰：月底跑批、上一次大促、每天的报表任务。&lt;/li&gt;
  &lt;li&gt;如果是为某个特定活动做规划，就去查最接近的一次历史活动，再用增长倍数放大。&lt;/li&gt;
  &lt;li&gt;注意查询步长。窗口很长、步长又大，短时间的尖峰会被平均掉。我当时查两天数据用 60 秒步长，查一天用 30 秒。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;第二步按组件和资源采集用量&quot;&gt;第二步：按组件和资源采集用量&lt;/h2&gt;

&lt;p&gt;TiDB 集群主要有四个组件。每个节点上都跑着 node_exporter，所以 CPU、内存、磁盘 IOPS、磁盘带宽、网络带宽这些主机指标，四个组件都有，也都要算一遍。不过各组件用磁盘和网络的方式不一样，最先耗尽的资源也不一样：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;组件&lt;/th&gt;
      &lt;th&gt;磁盘和网络用在哪&lt;/th&gt;
      &lt;th&gt;通常先碰到的上限&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;TiDB（SQL 层）&lt;/td&gt;
      &lt;td&gt;磁盘：日志，大查询内存不够时排序和 hash join 落盘。网络：接收客户端请求，从 TiKV、TiFlash 拉数据，返回结果&lt;/td&gt;
      &lt;td&gt;CPU、内存、网络带宽&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;TiKV（行存）&lt;/td&gt;
      &lt;td&gt;磁盘：数据、Raft 日志、compaction。网络：Raft 复制到其他副本，响应 TiDB 的读写&lt;/td&gt;
      &lt;td&gt;CPU、存储、磁盘 IOPS、磁盘带宽&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;TiFlash（列存）&lt;/td&gt;
      &lt;td&gt;磁盘：列存数据和后台合并。网络：从 TiKV 同步数据，MPP 计算时节点之间交换数据&lt;/td&gt;
      &lt;td&gt;CPU、内存、存储、磁盘带宽&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PD（调度和授时）&lt;/td&gt;
      &lt;td&gt;磁盘：etcd 存集群元数据，对 fsync 延迟敏感。网络：心跳、TSO 请求&lt;/td&gt;
      &lt;td&gt;CPU、内存、磁盘延迟&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;磁盘 IOPS、磁盘带宽和网络带宽是三个不同的上限，要分开算：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;磁盘 IOPS 是每秒读写的次数。小而随机的 IO 先碰到它，比如 TiKV 的点查，RocksDB 读不在缓存里的数据块。&lt;/li&gt;
  &lt;li&gt;磁盘带宽是每秒读写的字节数。大块的顺序 IO 先碰到它，比如 compaction、大范围扫描、TiFlash 读列存文件、备份。&lt;/li&gt;
  &lt;li&gt;网络带宽是网卡每秒收发的字节数。TiKV 每次写入，leader 都要把日志发给另外两个副本，而且是跨可用区的。TiDB 从 TiKV 拉数据、把结果返回给客户端，还有备份和数据均衡，也都走网络。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;同一块盘可能 IOPS 只用了 30%，带宽已经到了 80%，合在一起看就会漏掉真正的瓶颈。&lt;/p&gt;

&lt;p&gt;CPU、内存和存储用 TiDB 自己暴露的指标，磁盘和网络用 node_exporter 的主机指标：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# 每个 TiDB 实例使用的 CPU 核数
rate(process_cpu_seconds_total{component=&quot;tidb&quot;}[2m])

# 每个 TiDB 实例的常驻内存
process_resident_memory_bytes{component=&quot;tidb&quot;}

# 每个 TiKV store 已用存储
sum(tikv_store_size_bytes{type=&quot;used&quot;}) by (instance)

# 每个 TiFlash store 已用存储
sum(tiflash_system_current_metric_StoreSizeUsed) by (instance)

# 磁盘 IOPS（读 + 写）
sum(rate(node_disk_reads_completed_total{device=&quot;nvme1n1&quot;}[1m])
  + rate(node_disk_writes_completed_total{device=&quot;nvme1n1&quot;}[1m])) by (instance)

# 磁盘带宽，字节/秒（读 + 写）
sum(rate(node_disk_read_bytes_total{device=&quot;nvme1n1&quot;}[1m])
  + rate(node_disk_written_bytes_total{device=&quot;nvme1n1&quot;}[1m])) by (instance)

# 网络带宽，字节/秒，入和出分开看
sum(rate(node_network_receive_bytes_total{device=&quot;eth0&quot;}[1m])) by (instance)
sum(rate(node_network_transmit_bytes_total{device=&quot;eth0&quot;}[1m])) by (instance)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nvme1n1&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; 要换成实际的数据盘和网卡名。&lt;/p&gt;

&lt;p&gt;单节点容量能从指标拿就从指标拿，比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tikv_store_size_bytes{type=&quot;capacity&quot;}&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node_memory_MemTotal_bytes&lt;/code&gt;。拿不到的就查机型和云盘的官方规格，有几处要注意：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;磁盘 IOPS 和带宽同时受云盘和机型限制。以 AWS 为例，gp3 盘的 IOPS 和吞吐按盘配置，实例本身也有 EBS 带宽和 IOPS 上限，取两者中较小的那个。&lt;/li&gt;
  &lt;li&gt;网络带宽看基线值。很多机型标的是 “up to 10 Gbps”，那是突发上限，能持续的基线要低得多。&lt;/li&gt;
  &lt;li&gt;单位要统一。网络规格一般用 bit/s，指标是 byte/s，要乘 8 再比较。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CPU 建议用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rate()&lt;/code&gt; 而不是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irate()&lt;/code&gt;。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irate()&lt;/code&gt; 只看最后两个采样点，放在监控面板上看瞬时值没问题，但拿来算长窗口的分位数会抖得比较厉害。&lt;/p&gt;

&lt;h2 id=&quot;第三步用分位数不用峰值&quot;&gt;第三步：用分位数，不用峰值&lt;/h2&gt;

&lt;p&gt;按最大值规划听起来最稳，但最大值往往只是一次尖峰：一次备份、一次数据均衡、一条烂 SQL。假设 TiDB 单节点 CPU 峰值是 22 核，P99 是 14 核，按峰值规划要多买大约 57% 的节点，只为了覆盖每天那几分钟，而集群本来就扛得住。&lt;/p&gt;

&lt;p&gt;我会对每种资源都算一组统计值，从 P50 到 P99.9 再加最大值，然后决定用哪个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CPU、磁盘 IOPS、磁盘带宽和网络带宽默认用 P99，能过滤偶发尖峰，又保留了日常的忙时。&lt;/li&gt;
  &lt;li&gt;存储不看分位数，看最新值和增长趋势。存储基本只增不减，要关心的是下个季度会涨到多少，具体算法在文章后面。&lt;/li&gt;
  &lt;li&gt;忽略最大值之前，先看看它是什么。如果那个尖峰正是业务最忙的时段，而不是某个一次性任务，那就应该按它来规划。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;第四步定水位线&quot;&gt;第四步：定水位线&lt;/h2&gt;

&lt;p&gt;目标水位是整套方法里最需要判断的地方。我们最早的版本对所有资源统一用 3.3 倍冗余，相当于目标水位 30% 左右。这样很好解释，但它把 CPU 和磁盘当成一回事，而它们的行为差别很大。&lt;/p&gt;

&lt;p&gt;每种资源要分别考虑：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;故障余量。节点平均分布在三个可用区时，挂掉一个可用区，它的负载会压到另外两个上。要扛住这种情况，平时利用率就得低于三分之二左右，这还没算其他余量。&lt;/li&gt;
  &lt;li&gt;延迟。CPU 真正的上限是延迟开始上升的那个点，而不是 100%。很多 OLTP 业务在这个点上离跑满还很远，要给突发留空间。&lt;/li&gt;
  &lt;li&gt;后台任务。存储引擎做 compaction 需要空闲磁盘，加减节点时数据均衡也要空间，磁盘快满的时候这两件事都做不了。&lt;/li&gt;
  &lt;li&gt;TiDB 内存可以按线性算。TiDB 是无状态的 SQL 层，内存主要是执行查询时临时分配的，比如 hash join、排序、聚合和结果集，查询结束就释放，没有一块预先占满的大缓存池。所以它大致跟并发和查询量成正比，流量涨多少，内存也差不多涨多少。算的时候先从单节点容量里减掉一块固定开销（系统、Go runtime、统计信息缓存等），剩下的再按流量放大。另外 RSS 里包含 GC 还没回收的内存，大查询也会带来尖峰，这也是用 P99 而不是平均值的原因。&lt;/li&gt;
  &lt;li&gt;池化的内存不能按线性算。TiKV 的 block cache 和 MySQL 的 InnoDB buffer pool 一样，是按配置预先划好的缓存，用着用着就会被填满，不管流量多少，内存看起来都一直很高。它反映的是配置而不是需求，所以 TiKV 不按内存规划，缓存够不够要看命中率。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;第五步找瓶颈&quot;&gt;第五步：找瓶颈&lt;/h2&gt;

&lt;p&gt;一个虚构的 TiKV 例子：9 个节点，平均分布在 3 个可用区，每台 32 核、2,000 GiB 磁盘，预计下季度流量涨 50%。算出来的节点数按 3 的倍数向上取整，原因在本节最后。&lt;/p&gt;

&lt;p&gt;所有资源统一用 3.3 倍冗余：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;资源&lt;/th&gt;
      &lt;th&gt;单节点用量&lt;/th&gt;
      &lt;th&gt;计算&lt;/th&gt;
      &lt;th&gt;所需节点&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CPU（P99）&lt;/td&gt;
      &lt;td&gt;12 核&lt;/td&gt;
      &lt;td&gt;12 × 9 × 1.5 × 3.3 / 32&lt;/td&gt;
      &lt;td&gt;16.7 → 18&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;存储（最新值）&lt;/td&gt;
      &lt;td&gt;900 GiB&lt;/td&gt;
      &lt;td&gt;900 × 9 × 1.5 × 3.3 / 2000&lt;/td&gt;
      &lt;td&gt;20.05 → 21&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;按资源分别设水位，CPU 50%，存储 70%：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;资源&lt;/th&gt;
      &lt;th&gt;单节点用量&lt;/th&gt;
      &lt;th&gt;计算&lt;/th&gt;
      &lt;th&gt;所需节点&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CPU（P99）&lt;/td&gt;
      &lt;td&gt;12 核&lt;/td&gt;
      &lt;td&gt;12 × 9 × 1.5 / (32 × 0.5)&lt;/td&gt;
      &lt;td&gt;10.1 → 12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;存储（最新值）&lt;/td&gt;
      &lt;td&gt;900 GiB&lt;/td&gt;
      &lt;td&gt;900 × 9 × 1.5 / (2000 × 0.7)&lt;/td&gt;
      &lt;td&gt;8.7 → 9&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;统一系数算出来要 21 台，瓶颈是存储；分资源设水位算出来要 12 台，瓶颈是 CPU。两次用的是同一个公式，差别全部来自水位线，所以时间应该花在这里。为了简单，这里存储也乘了 1.5，更好的做法是按历史趋势算，后面单独讲。&lt;/p&gt;

&lt;p&gt;最后还要取整，但不同组件的规则不一样：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;TiKV：默认 3 副本，PD 按可用区把同一个 Region 的 3 个副本放到 3 个可用区，每个可用区各存一份完整数据。某个可用区的节点少，那里的每个节点就要多扛数据和写入，整个集群的上限会被它拖住。所以各可用区的节点数要一样多。TiDB Cloud Dedicated 直接把这个做成了规则：TiKV 至少 3 台，扩缩容时 3 个可用区同时增减，步长是 3。所以上面例子里 CPU 算出 10.1 台，要取到 12 台，而不是 11 台。自建集群如果没打 label，或者隔离域比副本多，就没有这个限制。&lt;/li&gt;
  &lt;li&gt;TiFlash：同样平均部署到各可用区，副本数按表设置，生产环境建议至少 2 副本。各可用区节点数保持一致可以避免倾斜，但没有 TiKV 那样硬性的 3 的倍数要求。&lt;/li&gt;
  &lt;li&gt;TiDB：无状态，前面挂负载均衡，算出几台就是几台，不需要凑倍数。跨可用区部署时，保证每个可用区至少有一台就行。&lt;/li&gt;
  &lt;li&gt;PD：一般固定 3 个或 5 个节点，Raft 需要多数派，所以取奇数。它通常不随业务量扩容，不用这个公式算。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;这个模型在哪里不准&quot;&gt;这个模型在哪里不准&lt;/h2&gt;

&lt;p&gt;公式假设流量翻倍资源用量也翻倍，并且加节点后负载能均匀分摊。这两点都只是大致成立。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;热点。集群整体的分位数可能很健康，但某一个节点已经被热点表或热点 key 范围打满了。除了看汇总，也要看最忙的那个节点，先解决倾斜再考虑加机器。&lt;/li&gt;
  &lt;li&gt;非线性增长。有些开销涨得比流量快：写入很重时的 compaction、锁冲突、跨节点事务。增长幅度大的时候，最好用压测验证一下。&lt;/li&gt;
  &lt;li&gt;突发性能。云主机的网络和磁盘吞吐通常有基线和突发两个上限。要按基线规划，不然突发额度会在最要命的时候用完。&lt;/li&gt;
  &lt;li&gt;硬件以外的限制。连接数上限、Region 数量、PD 授时延迟，都可能比 CPU 和磁盘更早成为瓶颈。当时我们在容量规划之外还配了一个健康检查，按阈值把这些指标标出来。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;如果现在重做&quot;&gt;如果现在重做&lt;/h2&gt;

&lt;h3 id=&quot;用-slo-定水位&quot;&gt;用 SLO 定水位&lt;/h3&gt;

&lt;p&gt;前面的目标水位，CPU 50%、存储 70%，都是经验值。更好的做法是从 SLO 反推。&lt;/p&gt;

&lt;p&gt;先要分清 SLA 和 SLO。TiDB Cloud 对外的 SLA 只有可用性一个指标：每分钟探测一次连接，一分钟内全部失败才算这一分钟不可用，Dedicated 多节点部署承诺每月 99.99%。这是底线，集群慢到延迟翻了几倍，只要还能连上就不算违约，所以拿它判断资源是否饱和没有意义。容量规划要看的是业务自己的 SLO，一般由延迟和错误率组成，比如 P99 延迟低于 50 ms、错误率低于 0.1%。&lt;/p&gt;

&lt;p&gt;TiDB 里对应的指标：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# TiDB 层 P99 查询延迟
histogram_quantile(0.99,
  sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))

# 错误率
sum(rate(tidb_server_query_total{result=&quot;Error&quot;}[1m]))
  / sum(rate(tidb_server_query_total[1m]))

# TiKV 层 P99 gRPC 延迟，用来判断慢在哪一层
histogram_quantile(0.99,
  sum(rate(tikv_grpc_msg_duration_seconds_bucket[1m])) by (le, type))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;接下来要找饱和点，也就是回答一个问题：CPU 利用率涨到多少时，延迟会超出 SLO。&lt;/p&gt;

&lt;p&gt;数据有两种来源。一种是历史监控：把过去几周每一分钟的 CPU 利用率和同一分钟的 P99 延迟配成一对，画在图上，横轴是 CPU 利用率，纵轴是 P99 延迟。另一种是压测：在测试环境逐步加压，比如每 10 分钟把 QPS 提高 10%，记录每一档的 CPU 利用率和 P99 延迟。&lt;/p&gt;

&lt;p&gt;画出来的图通常像下面这样。CPU 利用率低的时候，请求不用排队，延迟基本不变。过了某个利用率，请求开始排队，延迟急剧上升。在 SLO 的位置画一条横线，它和曲线交点对应的 CPU 利用率就是饱和点，CPU 再往上就会违反 SLO。&lt;/p&gt;

&lt;figure&gt;
&lt;svg viewBox=&quot;0 0 600 305&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;slz-title slz-desc&quot; style=&quot;display:block;width:100%;max-width:600px;height:auto;margin:0 auto;font-family:&apos;Open Sans&apos;,&apos;PingFang SC&apos;,&apos;Microsoft YaHei&apos;,sans-serif&quot;&gt;
  &lt;title id=&quot;slz-title&quot;&gt;CPU 利用率与 P99 延迟&lt;/title&gt;
  &lt;desc id=&quot;slz-desc&quot;&gt;示意图：CPU 利用率低于约 50% 时 P99 延迟稳定在 10 ms 左右，之后快速上升，在 75% 时达到 50 ms 的 SLO，这就是饱和点；考虑挂掉一个可用区后负载变为 1.5 倍，目标水位为 50%。&lt;/desc&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;40&quot; x2=&quot;60&quot; y2=&quot;260&quot; stroke=&quot;#999&quot; /&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;260&quot; x2=&quot;560&quot; y2=&quot;260&quot; stroke=&quot;#999&quot; /&gt;
  &lt;text x=&quot;52&quot; y=&quot;264&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;0&lt;/text&gt;
  &lt;text x=&quot;52&quot; y=&quot;154&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;50 ms&lt;/text&gt;
  &lt;text x=&quot;52&quot; y=&quot;44&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;100 ms&lt;/text&gt;
  &lt;text x=&quot;60&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;0%&lt;/text&gt;
  &lt;text x=&quot;185&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;25%&lt;/text&gt;
  &lt;text x=&quot;310&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;50%&lt;/text&gt;
  &lt;text x=&quot;435&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;75%&lt;/text&gt;
  &lt;text x=&quot;560&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;100%&lt;/text&gt;
  &lt;text x=&quot;310&quot; y=&quot;300&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; fill=&quot;#222&quot;&gt;CPU 利用率&lt;/text&gt;
  &lt;text x=&quot;14&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; fill=&quot;#222&quot; transform=&quot;rotate(-90 14 150)&quot;&gt;P99 延迟&lt;/text&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;150&quot; x2=&quot;560&quot; y2=&quot;150&quot; stroke=&quot;#9c0f2f&quot; stroke-dasharray=&quot;6 4&quot; /&gt;
  &lt;text x=&quot;556&quot; y=&quot;143&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#9c0f2f&quot;&gt;SLO 50 ms&lt;/text&gt;
  &lt;line x1=&quot;310&quot; y1=&quot;60&quot; x2=&quot;310&quot; y2=&quot;260&quot; stroke=&quot;#888&quot; stroke-dasharray=&quot;4 4&quot; /&gt;
  &lt;text x=&quot;302&quot; y=&quot;74&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#333&quot;&gt;目标水位 50%&lt;/text&gt;
  &lt;text x=&quot;302&quot; y=&quot;90&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#333&quot;&gt;= 75% / 1.5&lt;/text&gt;
  &lt;line x1=&quot;435&quot; y1=&quot;150&quot; x2=&quot;435&quot; y2=&quot;260&quot; stroke=&quot;#e51843&quot; stroke-dasharray=&quot;4 4&quot; /&gt;
  &lt;polyline fill=&quot;none&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2.5&quot; points=&quot;60,238 310,238 335,236.5 360,229.2 385,213.4 410,187.6 435,150 460,99.4 480,48.1&quot; /&gt;
  &lt;circle cx=&quot;435&quot; cy=&quot;150&quot; r=&quot;4.5&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;443&quot; y=&quot;200&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;饱和点 75%&lt;/text&gt;
&lt;/svg&gt;
&lt;/figure&gt;

&lt;p&gt;图里的饱和点是 75%。三个可用区挂掉一个时，剩下两个要多扛 50% 的负载，所以平时的利用率不能超过 75% / 1.5 = 50%，这就是 CPU 的目标水位。前面例子里的 50% 就是这么来的。不同业务的饱和点差别很大，OLTP 小查询可能到 80% 延迟都很稳，带大量排序和聚合的查询可能 60% 就开始抖，所以每个集群要单独看。&lt;/p&gt;

&lt;h3 id=&quot;存储按趋势算&quot;&gt;存储按趋势算&lt;/h3&gt;

&lt;p&gt;存储的增长主要取决于写入量和数据保留策略，跟 QPS 的关系不大，所以不适合乘一个流量增长倍数，按历史趋势算更准。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;取过去 3 到 6 个月的已用存储，每天取一个点，用当天的最大值或平均值把 compaction 造成的锯齿抹平。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum(tikv_store_size_bytes{type=&quot;used&quot;})&lt;/code&gt; 是所有副本加起来的总量，和单节点容量比的时候要用同一个口径。&lt;/li&gt;
  &lt;li&gt;用线性回归求出斜率，比如集群每天增长 60 GiB。Prometheus 自带的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;predict_linear()&lt;/code&gt; 可以直接给出预测值，比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;predict_linear(sum(tikv_store_size_bytes{type=&quot;used&quot;})[30d:1h], 90*86400)&lt;/code&gt; 是 90 天后的用量。不过 Prometheus 一般只保留 15 到 30 天的数据，要看更长的趋势，需要 Thanos、VictoriaMetrics 这类长期存储，或者每天把总量记到一张表里。&lt;/li&gt;
  &lt;li&gt;算出规划时点的用量：未来用量 = 当前用量 + 斜率 × (到下次评审的天数 + 扩容准备时间)。准备时间一定要算进去，加完节点后数据均衡也要时间，不能等到快满了才扩。&lt;/li&gt;
  &lt;li&gt;把未来用量代入公式，增长倍数取 1，因为趋势里已经包含了增长。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;接着前面的例子：9 台 TiKV 现在一共用了 8,100 GiB，每天涨 60 GiB，规划 90 天，再留 30 天准备时间。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;未来用量 = 8,100 + 60 × (90 + 30) = 15,300 GiB
所需节点 = 15,300 / (2,000 × 0.7) = 10.9 → 12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;同样的数据还能算出一个很实用的告警指标：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;距离到达水位的天数 = (总容量 × 目标水位 − 当前用量) / 每天增长量
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个天数小于扩容准备时间，就该扩了。&lt;/p&gt;

&lt;p&gt;拟合时有几个坑：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;一次性事件会把斜率带偏，比如批量导入、TTL 删除、DROP 表。DROP 表之后空间要等 GC 和 compaction 才会释放，曲线会先平一段再往下掉。拟合前要把这些时段剔掉。&lt;/li&gt;
  &lt;li&gt;增长不一定是线性的。如果每周增长的百分比比较稳定，说明是复利式增长，要先对用量取对数再拟合，否则会低估。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;其他&quot;&gt;其他&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;报告里加上单节点倾斜，让热点和总量出现在同一份结果里。&lt;/li&gt;
  &lt;li&gt;定期跑。每周跑一次，预测的节点数变化时告警，而不是每次大促前临时跑一下。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;检查清单&quot;&gt;检查清单&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;观察窗口至少一周，并且包含业务高峰。&lt;/li&gt;
  &lt;li&gt;按组件、按资源分别采集用量和容量。&lt;/li&gt;
  &lt;li&gt;CPU 和网络用 P99，存储用最新值加趋势。&lt;/li&gt;
  &lt;li&gt;每种资源单独定水位，CPU 从延迟 SLO 反推，再考虑故障余量和后台任务。&lt;/li&gt;
  &lt;li&gt;按资源分别算节点数，最大的那个就是瓶颈。&lt;/li&gt;
  &lt;li&gt;TiKV 各可用区节点数保持一样（TiDB Cloud 上是 3 的倍数），TiDB 不用凑倍数。&lt;/li&gt;
  &lt;li&gt;看一眼最忙的节点，变化大的时候做压测。&lt;/li&gt;
&lt;/ol&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2024/10/tidb-capacity-planning-prometheus/&quot;&gt;用 Prometheus 指标给分布式数据库做容量规划：以 TiDB 为例&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on October 10, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Capacity Planning a Distributed Database from Prometheus Metrics]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2024/10/capacity-planning-distributed-database-prometheus/"/>
  <id>http://www.solidstack.io/posts/2024/10/capacity-planning-distributed-database-prometheus</id>
  <published>2024-10-10T00:00:00+08:00</published>
  <updated>2024-10-10T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#Capacity%20Planning" term="Capacity Planning" /><category scheme="http://www.solidstack.io/tags/#TiDB" term="TiDB" /><category scheme="http://www.solidstack.io/tags/#Prometheus" term="Prometheus" /><category scheme="http://www.solidstack.io/tags/#Reliability" term="Reliability" /><category scheme="http://www.solidstack.io/tags/#Distributed%20Systems" term="Distributed Systems" />
  <content type="html">
  
    &lt;p&gt;“How many nodes do we need for next quarter?” is a question every database team gets, usually a few weeks before a big launch or a sales event. The honest answer often starts with a guess, gets padded “to be safe”, and ends with a cluster that is either too small on the day or far too big for the rest of the year.&lt;/p&gt;

&lt;p&gt;A while ago a colleague and I wrote a small tool, &lt;a href=&quot;https://github.com/yinx0004/tidbcloud-metrics&quot;&gt;tidbcloud-metrics&lt;/a&gt;, that answered it from the metrics we already had in Prometheus. Before every big sales event, we used it to assess capacity for very large TiDB Cloud clusters and decide how many nodes to add. The code was tied to one environment, but the method carries over. This post walks through it with made-up numbers: what to measure, how to summarize it, how much headroom to keep, and where the simple model breaks.&lt;/p&gt;

&lt;p&gt;The examples use TiDB, but the same approach works for most distributed databases and stateless services that expose per-node metrics.&lt;/p&gt;

&lt;h2 id=&quot;the-idea-in-one-formula&quot;&gt;The idea in one formula&lt;/h2&gt;

&lt;p&gt;For each component and each resource, the number of nodes you need is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nodes needed = (usage per node × current node count × growth) / (capacity per node × target utilization)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;usage per node: a summary of how much of the resource each node uses today, such as the 99th percentile over the observation window.&lt;/li&gt;
  &lt;li&gt;current node count × usage per node: roughly the total demand on the cluster today.&lt;/li&gt;
  &lt;li&gt;growth: how much more traffic you expect. 1.5 means 50% more.&lt;/li&gt;
  &lt;li&gt;capacity per node: what one node can provide, such as 32 cores or 2 TiB of disk.&lt;/li&gt;
  &lt;li&gt;target utilization: how full you are willing to run each node. Dividing by 0.5 means you want nodes at 50% or less.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You calculate this for every resource of every component. The resource that needs the most nodes is the bottleneck, and that sets the node count for that component.&lt;/p&gt;

&lt;figure&gt;
&lt;svg viewBox=&quot;0 30 900 110&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;cp-title cp-desc&quot; style=&quot;display:block;width:100%;max-width:900px;height:auto;margin:0 auto;font-family:&apos;Open Sans&apos;,sans-serif&quot;&gt;
  &lt;title id=&quot;cp-title&quot;&gt;Capacity planning pipeline&lt;/title&gt;
  &lt;desc id=&quot;cp-desc&quot;&gt;Prometheus usage metrics per node are summarized into percentiles, then scaled by node count and growth and divided by node capacity times target utilization, giving the nodes needed per resource; the largest result is the bottleneck and sets the node count.&lt;/desc&gt;
  &lt;defs&gt;
    &lt;marker id=&quot;cp-arrow&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;#e51843&quot; /&gt;&lt;/marker&gt;
  &lt;/defs&gt;
  &lt;rect x=&quot;10&quot; y=&quot;40&quot; width=&quot;150&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;85&quot; y=&quot;78&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;Prometheus&lt;/text&gt;
  &lt;text x=&quot;85&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;usage per node&lt;/text&gt;
  &lt;rect x=&quot;197&quot; y=&quot;40&quot; width=&quot;150&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;272&quot; y=&quot;78&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;Summarize&lt;/text&gt;
  &lt;text x=&quot;272&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;p50 … p99.9, max&lt;/text&gt;
  &lt;rect x=&quot;384&quot; y=&quot;40&quot; width=&quot;150&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;459&quot; y=&quot;72&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;Scale&lt;/text&gt;
  &lt;text x=&quot;459&quot; y=&quot;92&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;× nodes × growth&lt;/text&gt;
  &lt;text x=&quot;459&quot; y=&quot;110&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;/ (capacity × target)&lt;/text&gt;
  &lt;rect x=&quot;571&quot; y=&quot;40&quot; width=&quot;150&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fafafa&quot; stroke=&quot;#d0d0d0&quot; /&gt;
  &lt;text x=&quot;646&quot; y=&quot;78&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#222&quot;&gt;Nodes needed&lt;/text&gt;
  &lt;text x=&quot;646&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;per resource&lt;/text&gt;
  &lt;rect x=&quot;758&quot; y=&quot;40&quot; width=&quot;132&quot; height=&quot;90&quot; rx=&quot;6&quot; fill=&quot;#fde3e9&quot; stroke=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;824&quot; y=&quot;78&quot; text-anchor=&quot;middle&quot; font-size=&quot;14&quot; font-weight=&quot;700&quot; fill=&quot;#9c0f2f&quot;&gt;Bottleneck&lt;/text&gt;
  &lt;text x=&quot;824&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#9c0f2f&quot;&gt;largest wins&lt;/text&gt;
  &lt;line x1=&quot;160&quot; y1=&quot;85&quot; x2=&quot;195&quot; y2=&quot;85&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#cp-arrow)&quot; /&gt;
  &lt;line x1=&quot;347&quot; y1=&quot;85&quot; x2=&quot;382&quot; y2=&quot;85&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#cp-arrow)&quot; /&gt;
  &lt;line x1=&quot;534&quot; y1=&quot;85&quot; x2=&quot;569&quot; y2=&quot;85&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#cp-arrow)&quot; /&gt;
  &lt;line x1=&quot;721&quot; y1=&quot;85&quot; x2=&quot;756&quot; y2=&quot;85&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2&quot; marker-end=&quot;url(#cp-arrow)&quot; /&gt;
&lt;/svg&gt;
&lt;/figure&gt;

&lt;h2 id=&quot;step-1-pick-the-observation-window&quot;&gt;Step 1: Pick the observation window&lt;/h2&gt;

&lt;p&gt;The numbers come from whatever period you query, so the period has to contain the load you are planning for.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Cover at least one full business cycle, usually a week, so weekday and weekend patterns are both in the data.&lt;/li&gt;
  &lt;li&gt;Make sure the window includes your known peaks: month-end batch jobs, the last sales event, the daily reporting run.&lt;/li&gt;
  &lt;li&gt;If you are planning for a specific event, measure the most similar past event and use growth to scale it.&lt;/li&gt;
  &lt;li&gt;Watch the query step. A long window with a large step averages away short spikes. I used a 60-second step for two days of data and 30 seconds for one day.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-2-measure-usage-per-component-and-resource&quot;&gt;Step 2: Measure usage per component and resource&lt;/h2&gt;

&lt;p&gt;A TiDB cluster has four main components. Every node runs node_exporter, so host metrics for CPU, memory, disk IOPS, disk bandwidth and network bandwidth exist for all four, and all of them should go through the calculation. Each component uses disk and network differently, though, so they tend to run out of different things:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Component&lt;/th&gt;
      &lt;th&gt;What disk and network are used for&lt;/th&gt;
      &lt;th&gt;Usually runs out of&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;TiDB (SQL layer)&lt;/td&gt;
      &lt;td&gt;Disk: logs, and sorts and hash joins that spill when a large query runs out of memory. Network: client requests, reading from TiKV and TiFlash, returning results&lt;/td&gt;
      &lt;td&gt;CPU, memory, network bandwidth&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;TiKV (row storage)&lt;/td&gt;
      &lt;td&gt;Disk: data, Raft logs, compaction. Network: Raft replication to other replicas, serving reads and writes from TiDB&lt;/td&gt;
      &lt;td&gt;CPU, storage, disk IOPS, disk bandwidth&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;TiFlash (column storage)&lt;/td&gt;
      &lt;td&gt;Disk: columnar data and background merges. Network: replicating from TiKV, exchanging data between nodes in MPP queries&lt;/td&gt;
      &lt;td&gt;CPU, memory, storage, disk bandwidth&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;PD (placement and timestamps)&lt;/td&gt;
      &lt;td&gt;Disk: etcd for cluster metadata, sensitive to fsync latency. Network: heartbeats, TSO requests&lt;/td&gt;
      &lt;td&gt;CPU, memory, disk latency&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Disk IOPS, disk bandwidth and network bandwidth are three separate limits, and each needs its own calculation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Disk IOPS is the number of reads and writes per second. Small random IO hits it first, such as TiKV point lookups and RocksDB reading blocks that aren’t in the cache.&lt;/li&gt;
  &lt;li&gt;Disk bandwidth is the number of bytes read and written per second. Large sequential IO hits it first, such as compaction, range scans, TiFlash reading column files, and backups.&lt;/li&gt;
  &lt;li&gt;Network bandwidth is the bytes per second the network interface sends and receives. On every TiKV write, the leader sends the log to the other two replicas, across availability zones. TiDB pulling data from TiKV, results going back to clients, backups and rebalancing all use the network too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same disk can be at 30% of its IOPS and 80% of its bandwidth, so lumping them together hides the real bottleneck.&lt;/p&gt;

&lt;p&gt;CPU, memory and storage come from TiDB’s own metrics. Disk and network come from node_exporter:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# CPU cores used per TiDB instance
rate(process_cpu_seconds_total{component=&quot;tidb&quot;}[2m])

# Resident memory per TiDB instance
process_resident_memory_bytes{component=&quot;tidb&quot;}

# Used storage per TiKV store
sum(tikv_store_size_bytes{type=&quot;used&quot;}) by (instance)

# Used storage per TiFlash store
sum(tiflash_system_current_metric_StoreSizeUsed) by (instance)

# Disk IOPS (reads + writes)
sum(rate(node_disk_reads_completed_total{device=&quot;nvme1n1&quot;}[1m])
  + rate(node_disk_writes_completed_total{device=&quot;nvme1n1&quot;}[1m])) by (instance)

# Disk bandwidth in bytes per second (reads + writes)
sum(rate(node_disk_read_bytes_total{device=&quot;nvme1n1&quot;}[1m])
  + rate(node_disk_written_bytes_total{device=&quot;nvme1n1&quot;}[1m])) by (instance)

# Network bandwidth in bytes per second, inbound and outbound separately
sum(rate(node_network_receive_bytes_total{device=&quot;eth0&quot;}[1m])) by (instance)
sum(rate(node_network_transmit_bytes_total{device=&quot;eth0&quot;}[1m])) by (instance)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nvme1n1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eth0&lt;/code&gt; with your actual data disk and network interface.&lt;/p&gt;

&lt;p&gt;For per-node capacity, use the matching capacity metrics where they exist, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tikv_store_size_bytes{type=&quot;capacity&quot;}&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node_memory_MemTotal_bytes&lt;/code&gt;. Otherwise use the published limits of the instance type and the volume, with a few things to watch:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Disk IOPS and bandwidth are limited by both the volume and the instance. On AWS, for example, a gp3 volume has its own provisioned IOPS and throughput, and the instance has its own EBS bandwidth and IOPS limits. The lower of the two applies.&lt;/li&gt;
  &lt;li&gt;Use the baseline network bandwidth. Many instance types are listed as “up to 10 Gbps”, which is the burst limit; the sustained baseline is much lower.&lt;/li&gt;
  &lt;li&gt;Keep units consistent. Network limits are usually in bits per second and the metrics are in bytes, so multiply by 8 before comparing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rate()&lt;/code&gt; rather than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irate()&lt;/code&gt; for CPU. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;irate()&lt;/code&gt; only looks at the last two samples, which is useful on a dashboard but makes percentiles over a long window noisier than they should be.&lt;/p&gt;

&lt;h2 id=&quot;step-3-summarize-with-percentiles-not-the-peak&quot;&gt;Step 3: Summarize with percentiles, not the peak&lt;/h2&gt;

&lt;p&gt;Planning on the maximum sounds safe, but the maximum is usually a single spike: a backup, a rebalance, one bad query. Say TiDB CPU peaks at 22 cores per node while the 99th percentile is 14. Planning on the peak adds about 57% more nodes to cover a few minutes a day that the cluster already handles.&lt;/p&gt;

&lt;p&gt;I calculate a range of statistics for every resource, from p50 to p99.9 plus the max, and decide which one to plan on:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;p99 is a sensible default for CPU, disk IOPS, disk bandwidth and network bandwidth. It ignores rare spikes but keeps the regular busy periods.&lt;/li&gt;
  &lt;li&gt;For storage, look at the latest value and the trend rather than a percentile. Storage rarely shrinks, and next quarter’s size is what matters. How to calculate the trend is covered later.&lt;/li&gt;
  &lt;li&gt;Check what the max actually was before ignoring it. If the spike was your peak business hour rather than a one-off job, plan on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-4-decide-how-full-a-node-may-get&quot;&gt;Step 4: Decide how full a node may get&lt;/h2&gt;

&lt;p&gt;The target utilization is where most of the judgement goes. My first version used a single redundancy factor of 3.3 for everything, which means a target of about 30% utilization. That is easy to explain, but it treats CPU and disk as if they behaved the same way, and they don’t.&lt;/p&gt;

&lt;p&gt;Things to consider for each resource:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Failure headroom. With nodes spread evenly across three availability zones, losing one zone moves its load onto the other two. To survive that without being overloaded, normal utilization has to stay below about two thirds, before any other headroom.&lt;/li&gt;
  &lt;li&gt;Latency. For CPU, the useful limit is where latency starts to climb, not 100%. For many OLTP workloads that is well below full usage, so leave room for bursts.&lt;/li&gt;
  &lt;li&gt;Background work. Storage engines need free disk for compaction, and a cluster needs space to rebalance data when a node is added or lost. A disk that is nearly full can’t absorb either of them.&lt;/li&gt;
  &lt;li&gt;TiDB memory can be treated as linear. TiDB is a stateless SQL layer. Most of its memory is allocated while queries run, for hash joins, sorts, aggregations and result sets, and released when they finish. There is no large cache that is filled up front, so memory roughly follows concurrency and query volume, and grows about as fast as traffic does. I subtract a fixed overhead from each node’s capacity first (the OS, the Go runtime, the statistics cache and so on) and scale the rest. RSS also includes memory the garbage collector hasn’t reclaimed yet, and large queries cause spikes, which is another reason to plan on p99 rather than the average.&lt;/li&gt;
  &lt;li&gt;Pooled memory can’t. TiKV’s block cache works like MySQL’s InnoDB buffer pool: it is sized by configuration and fills up over time, so memory looks steadily high no matter how much traffic there is. It reflects the configuration, not demand, so I don’t plan TiKV nodes on memory. Whether the cache is big enough is a question for the hit rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-5-find-the-bottleneck&quot;&gt;Step 5: Find the bottleneck&lt;/h2&gt;

&lt;p&gt;Here is a made-up TiKV example: 9 nodes spread evenly across 3 availability zones, each with 32 cores and 2,000 GiB of disk, and 50% more traffic expected next quarter. Results are rounded up to a multiple of 3; the reason is at the end of this section.&lt;/p&gt;

&lt;p&gt;With one factor of 3.3 for every resource:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Resource&lt;/th&gt;
      &lt;th&gt;Usage per node&lt;/th&gt;
      &lt;th&gt;Calculation&lt;/th&gt;
      &lt;th&gt;Nodes needed&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CPU (p99)&lt;/td&gt;
      &lt;td&gt;12 cores&lt;/td&gt;
      &lt;td&gt;12 × 9 × 1.5 × 3.3 / 32&lt;/td&gt;
      &lt;td&gt;16.7 → 18&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Storage (latest)&lt;/td&gt;
      &lt;td&gt;900 GiB&lt;/td&gt;
      &lt;td&gt;900 × 9 × 1.5 × 3.3 / 2000&lt;/td&gt;
      &lt;td&gt;20.05 → 21&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;With a target per resource, CPU at 50% and storage at 70%:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Resource&lt;/th&gt;
      &lt;th&gt;Usage per node&lt;/th&gt;
      &lt;th&gt;Calculation&lt;/th&gt;
      &lt;th&gt;Nodes needed&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;CPU (p99)&lt;/td&gt;
      &lt;td&gt;12 cores&lt;/td&gt;
      &lt;td&gt;12 × 9 × 1.5 / (32 × 0.5)&lt;/td&gt;
      &lt;td&gt;10.1 → 12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Storage (latest)&lt;/td&gt;
      &lt;td&gt;900 GiB&lt;/td&gt;
      &lt;td&gt;900 × 9 × 1.5 / (2000 × 0.7)&lt;/td&gt;
      &lt;td&gt;8.7 → 9&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The uniform factor says 21 nodes, with storage as the bottleneck. The per-resource targets say 12 nodes, with CPU as the bottleneck. The formula is the same in both cases. The difference comes entirely from the targets, so that is where to spend the time. To keep it simple, storage is multiplied by 1.5 here as well; a trend is a better way to project it, covered later.&lt;/p&gt;

&lt;p&gt;Finally, round the result, but the rule depends on the component:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;TiKV keeps 3 replicas by default, and PD places the 3 replicas of each Region in 3 different availability zones, so each zone holds a full copy of the data. If one zone has fewer nodes, each of its nodes carries more data and more writes, and that zone caps the whole cluster. Keep the same number of nodes in every zone. TiDB Cloud Dedicated enforces this: TiKV starts at 3 nodes, and scaling adds or removes nodes in all 3 zones at the same time, so the step is 3. That is why the 10.1 nodes for CPU in the example become 12, not 11. A self-managed cluster without location labels, or with more isolation domains than replicas, doesn’t have this constraint.&lt;/li&gt;
  &lt;li&gt;TiFlash is also spread evenly across zones, with replicas set per table and at least 2 recommended for production. Keeping zones even avoids skew, but there is no strict multiple-of-3 rule as there is for TiKV.&lt;/li&gt;
  &lt;li&gt;TiDB is stateless behind a load balancer. Use whatever number the formula gives; there is nothing to round to. If it spans availability zones, just keep at least one node in each.&lt;/li&gt;
  &lt;li&gt;PD usually runs a fixed 3 or 5 nodes, an odd number because Raft needs a majority. It rarely scales with traffic and isn’t sized with this formula.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;where-the-simple-model-breaks&quot;&gt;Where the simple model breaks&lt;/h2&gt;

&lt;p&gt;The formula assumes that doubling traffic doubles resource usage and that adding nodes spreads it evenly. Both are only roughly true.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Hotspots. A cluster-wide percentile can look healthy while one node is saturated by a hot table or a hot key range. Look at the busiest node as well as the summary, and fix skew before buying hardware.&lt;/li&gt;
  &lt;li&gt;Non-linear growth. Some costs grow faster than traffic: compaction under heavy writes, lock contention, cross-node transactions. Validate the plan with a load test when the growth is large.&lt;/li&gt;
  &lt;li&gt;Burstable limits. Cloud instances often have a baseline network and disk throughput and a higher burst limit. Plan on the baseline, or you will run out of burst credits at the worst moment.&lt;/li&gt;
  &lt;li&gt;Things that aren’t hardware. Connection limits, region or shard counts, and time-oracle (PD) latency can become the limit before CPU or disk does. I paired the capacity plan with a health check that flagged these against thresholds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-i-would-add-today&quot;&gt;What I would add today&lt;/h2&gt;

&lt;h3 id=&quot;targets-from-slos&quot;&gt;Targets from SLOs&lt;/h3&gt;

&lt;p&gt;The targets above, 50% for CPU and 70% for storage, are rules of thumb. A better way is to work them out from an SLO.&lt;/p&gt;

&lt;p&gt;First, an SLA is not an SLO. TiDB Cloud’s SLA covers availability only: a probe tries to connect every minute, and a minute counts as down only if every attempt fails. Dedicated clusters with multiple nodes are committed to 99.99% a month. That is a floor. A cluster can be several times slower than usual and still meet it, so it says nothing about saturation. For capacity planning, use your own SLO, usually a latency and an error rate, such as p99 latency under 50 ms and errors under 0.1%.&lt;/p&gt;

&lt;p&gt;The matching TiDB metrics:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# p99 query latency at the TiDB layer
histogram_quantile(0.99,
  sum(rate(tidb_server_handle_query_duration_seconds_bucket[1m])) by (le))

# Error rate
sum(rate(tidb_server_query_total{result=&quot;Error&quot;}[1m]))
  / sum(rate(tidb_server_query_total[1m]))

# p99 gRPC latency at the TiKV layer, to see which layer is slow
histogram_quantile(0.99,
  sum(rate(tikv_grpc_msg_duration_seconds_bucket[1m])) by (le, type))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, find the saturation point, which answers one question: at what CPU utilization does latency break the SLO?&lt;/p&gt;

&lt;p&gt;There are two ways to get the data. One is history: for every minute over the last few weeks, pair the CPU utilization with the p99 latency in the same minute and plot them, with CPU utilization on the x-axis and p99 latency on the y-axis. The other is a load test: raise the load step by step in a test environment, for example 10% more QPS every 10 minutes, and record CPU utilization and p99 latency at each step.&lt;/p&gt;

&lt;p&gt;The result usually looks like the chart below. At low utilization, requests don’t wait for each other and latency barely moves. Past some point, requests start to queue and latency rises steeply. Draw a horizontal line at the SLO. The CPU utilization where it meets the curve is the saturation point, and any more CPU than that breaks the SLO.&lt;/p&gt;

&lt;figure&gt;
&lt;svg viewBox=&quot;0 0 600 305&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-labelledby=&quot;sle-title sle-desc&quot; style=&quot;display:block;width:100%;max-width:600px;height:auto;margin:0 auto;font-family:&apos;Open Sans&apos;,sans-serif&quot;&gt;
  &lt;title id=&quot;sle-title&quot;&gt;CPU utilization vs p99 latency&lt;/title&gt;
  &lt;desc id=&quot;sle-desc&quot;&gt;Illustration: p99 latency stays around 10 ms until about 50% CPU, then climbs quickly and reaches the 50 ms SLO at 75%, the saturation point. Allowing for 1.5 times the load after losing one zone gives a 50% target.&lt;/desc&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;40&quot; x2=&quot;60&quot; y2=&quot;260&quot; stroke=&quot;#999&quot; /&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;260&quot; x2=&quot;560&quot; y2=&quot;260&quot; stroke=&quot;#999&quot; /&gt;
  &lt;text x=&quot;52&quot; y=&quot;264&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;0&lt;/text&gt;
  &lt;text x=&quot;52&quot; y=&quot;154&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;50 ms&lt;/text&gt;
  &lt;text x=&quot;52&quot; y=&quot;44&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;100 ms&lt;/text&gt;
  &lt;text x=&quot;60&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;0%&lt;/text&gt;
  &lt;text x=&quot;185&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;25%&lt;/text&gt;
  &lt;text x=&quot;310&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;50%&lt;/text&gt;
  &lt;text x=&quot;435&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;75%&lt;/text&gt;
  &lt;text x=&quot;560&quot; y=&quot;278&quot; text-anchor=&quot;middle&quot; font-size=&quot;12&quot; fill=&quot;#555&quot;&gt;100%&lt;/text&gt;
  &lt;text x=&quot;310&quot; y=&quot;300&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; fill=&quot;#222&quot;&gt;CPU utilization&lt;/text&gt;
  &lt;text x=&quot;14&quot; y=&quot;150&quot; text-anchor=&quot;middle&quot; font-size=&quot;13&quot; fill=&quot;#222&quot; transform=&quot;rotate(-90 14 150)&quot;&gt;p99 latency&lt;/text&gt;
  &lt;line x1=&quot;60&quot; y1=&quot;150&quot; x2=&quot;560&quot; y2=&quot;150&quot; stroke=&quot;#9c0f2f&quot; stroke-dasharray=&quot;6 4&quot; /&gt;
  &lt;text x=&quot;556&quot; y=&quot;143&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#9c0f2f&quot;&gt;SLO 50 ms&lt;/text&gt;
  &lt;line x1=&quot;310&quot; y1=&quot;60&quot; x2=&quot;310&quot; y2=&quot;260&quot; stroke=&quot;#888&quot; stroke-dasharray=&quot;4 4&quot; /&gt;
  &lt;text x=&quot;302&quot; y=&quot;74&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#333&quot;&gt;target 50%&lt;/text&gt;
  &lt;text x=&quot;302&quot; y=&quot;90&quot; text-anchor=&quot;end&quot; font-size=&quot;12&quot; fill=&quot;#333&quot;&gt;= 75% / 1.5&lt;/text&gt;
  &lt;line x1=&quot;435&quot; y1=&quot;150&quot; x2=&quot;435&quot; y2=&quot;260&quot; stroke=&quot;#e51843&quot; stroke-dasharray=&quot;4 4&quot; /&gt;
  &lt;polyline fill=&quot;none&quot; stroke=&quot;#e51843&quot; stroke-width=&quot;2.5&quot; points=&quot;60,238 310,238 335,236.5 360,229.2 385,213.4 410,187.6 435,150 460,99.4 480,48.1&quot; /&gt;
  &lt;circle cx=&quot;435&quot; cy=&quot;150&quot; r=&quot;4.5&quot; fill=&quot;#e51843&quot; /&gt;
  &lt;text x=&quot;443&quot; y=&quot;200&quot; font-size=&quot;12&quot; font-weight=&quot;700&quot; fill=&quot;#e51843&quot;&gt;saturation 75%&lt;/text&gt;
&lt;/svg&gt;
&lt;/figure&gt;

&lt;p&gt;In the chart it is 75%. If one of three availability zones goes down, the other two take 50% more load, so normal utilization has to stay at or below 75% / 1.5 = 50%. That is the CPU target, and it is where the 50% in the example came from. Saturation points vary a lot between workloads. Small OLTP queries may stay flat past 80%, while queries with heavy sorts and aggregations may start to wobble at 60%, so measure each cluster.&lt;/p&gt;

&lt;h3 id=&quot;storage-from-a-trend&quot;&gt;Storage from a trend&lt;/h3&gt;

&lt;p&gt;Storage grows with write volume and retention, not with QPS, so multiplying it by a traffic growth factor is a rough guess at best. A trend works better.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Take the used storage for the last 3 to 6 months, one point per day, using the daily max or average to smooth out the sawtooth from compaction. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum(tikv_store_size_bytes{type=&quot;used&quot;})&lt;/code&gt; includes every replica, so compare it with capacity on the same basis.&lt;/li&gt;
  &lt;li&gt;Fit a straight line to get the slope, for example 60 GiB a day for the whole cluster. Prometheus can do this with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;predict_linear()&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;predict_linear(sum(tikv_store_size_bytes{type=&quot;used&quot;})[30d:1h], 90*86400)&lt;/code&gt; gives the value 90 days from now. Most Prometheus setups keep only 15 to 30 days, though, so for a longer trend you need long-term storage such as Thanos or VictoriaMetrics, or a daily row in a table.&lt;/li&gt;
  &lt;li&gt;Project the usage at the planning date: future usage = current usage + slope × (days until the next review + lead time to scale). Include the lead time. New nodes need time to rebalance data, so you can’t wait until the disks are nearly full.&lt;/li&gt;
  &lt;li&gt;Use the projected usage in the formula with a growth factor of 1, because the trend already includes the growth.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Continuing the example: the 9 TiKV nodes use 8,100 GiB in total and grow by 60 GiB a day. The plan covers 90 days, with 30 days of lead time.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;future usage = 8,100 + 60 × (90 + 30) = 15,300 GiB
nodes needed = 15,300 / (2,000 × 0.7) = 10.9 → 12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The same numbers give a useful alert:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;days until target = (total capacity × target utilization − current usage) / daily growth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When that number drops below your lead time, it is time to scale.&lt;/p&gt;

&lt;p&gt;A few traps when fitting the line:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;One-off events skew the slope: bulk imports, TTL deletes, dropped tables. After a DROP TABLE the space comes back only after GC and compaction, so the curve stays flat for a while and then falls. Leave those periods out of the fit.&lt;/li&gt;
  &lt;li&gt;Growth isn’t always linear. If the weekly growth percentage is steady, the growth is compounding. Fit the logarithm of usage instead, or the projection will come out too low.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;other-things&quot;&gt;Other things&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Per-node skew in the output, so hotspots show up in the same report as the totals.&lt;/li&gt;
  &lt;li&gt;A schedule. Run it every week and alert when the projected node count changes, rather than running it once before each event.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;checklist&quot;&gt;Checklist&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;Measure at least a week that includes your peaks.&lt;/li&gt;
  &lt;li&gt;Collect usage and capacity per component and per resource.&lt;/li&gt;
  &lt;li&gt;Plan on p99 for CPU and network, and on the latest value plus trend for storage.&lt;/li&gt;
  &lt;li&gt;Set a target utilization per resource. Work out the CPU target from your latency SLO, then allow for failure headroom and background work.&lt;/li&gt;
  &lt;li&gt;Calculate nodes per resource. The largest result is your bottleneck.&lt;/li&gt;
  &lt;li&gt;Keep TiKV nodes even across zones (a multiple of 3 on TiDB Cloud). TiDB needs no rounding.&lt;/li&gt;
  &lt;li&gt;Check the busiest node, and load-test large changes.&lt;/li&gt;
&lt;/ol&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2024/10/capacity-planning-distributed-database-prometheus/&quot;&gt;Capacity Planning a Distributed Database from Prometheus Metrics&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on October 10, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[How to ensure TiDB cluster can use TiCDC to synchronize data?]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2024/08/ticdc-applicability/"/>
  <id>http://www.solidstack.io/posts/2024/08/tidb-ticdc-applicability</id>
  <published>2024-08-03T00:00:00+08:00</published>
  <updated>2024-08-03T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#TiCDC" term="TiCDC" />
  <content type="html">
  
    &lt;p&gt;This article focuses on the more &lt;strong&gt;difficult to determine&lt;/strong&gt; parts of the TiCDC’s limitations.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
Currently, TiCDC unsupported scenarios and limitations as below:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A TiKV cluster that uses RawKV alone.&lt;/li&gt;
  &lt;li&gt;The CREATE SEQUENCE DDL operation and the SEQUENCE function in TiDB. When the upstream TiDB uses SEQUENCE, TiCDC ignores SEQUENCE DDL operations/functions performed upstream. However, DML operations using SEQUENCE functions can be correctly replicated.&lt;/li&gt;
  &lt;li&gt;Currently, performing BR data recovery and TiDB Lightning physical import imports on tables and databases that are being replicated by TiCDC is not supported. For more information, see Why does replication using TiCDC stall or even stop after data restore using TiDB Lightning and BR from upstream.&lt;/li&gt;
  &lt;li&gt;TiCDC only replicates tables that have at least one valid index&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For details, refer to the official documentation &lt;a href=&quot;https://docs.pingcap.com/tidb/stable/ticdc-overview#unsupported-scenarios&quot;&gt;unsupported scenarios&lt;/a&gt; and &lt;a href=&quot;https://docs.pingcap.com/tidb/stable/ticdc-overview#best-practices&quot;&gt;best practices&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
The most difficult part of these limitations to be identified is &lt;strong&gt;the valid index&lt;/strong&gt;:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;TiCDC only replicates tables that have at least one valid index. A valid index is defined as follows:&lt;/p&gt;

  &lt;p&gt;A primary key (PRIMARY KEY) is a valid index.&lt;/p&gt;

  &lt;p&gt;A unique index (UNIQUE INDEX) is valid if every column of the index is explicitly defined as non-nullable (NOT NULL) and the index does not have a virtual generated column (VIRTUAL GENERATED COLUMNS).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;br /&gt;
If there are tables in the TiDB cluster without at least one valid index, then these tables cannot use TiCDC to synchronize data. How to find out which tables in the cluster do not have any valid index?&lt;/p&gt;

&lt;p&gt;1.Find tables that have primary key&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.Find tables have unique index and every column of the index is explicitly defined as non-nullable (NOT NULL) and the index does not have a virtual generated column&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NON_UNIQUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TABLE_SCHEMA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TABLE_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COLUMN_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;NO&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.Combine the result of the above two sets are the tables have valid index&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;all&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NON_UNIQUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TABLE_SCHEMA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TABLE_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COLUMN_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;NO&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tables Without Valid Index&lt;/strong&gt; = &lt;strong&gt;All Tables&lt;/strong&gt; - &lt;strong&gt;Tables With Valid Index&lt;/strong&gt;, if the result set of the follow SQL is not empty, means the TiDB cluster contains tables can not be replicated by TiCDC.&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tables&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;BASE TABLE&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NON_UNIQUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TABLE_SCHEMA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TABLE_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COLUMN_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;NO&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;others&quot;&gt;Others&lt;/h3&gt;
&lt;p&gt;About another limitation not supporting SEQUENCE related operations. It is very simple to find out whether SEQUENCE is used in the cluster. The SQL is as follows. If the query result is not empty, it means that SEQUENCE is used:&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_type&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tables&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SEQUENCE&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt; 
 
 
 &lt;/p&gt;

&lt;h2 id=&quot;中文版&quot;&gt;中文版&lt;/h2&gt;
&lt;p&gt;本文主要围绕 TiCDC 的限制中比较&lt;strong&gt;难确定&lt;/strong&gt;的部分内容，TiCDC 官方文档中已经清楚的描述了不支持的场景以及必须满足的条件。&lt;/p&gt;

&lt;p&gt;目前 TiCDC 暂不支持的场景和限制如下：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;暂不支持单独使用 RawKV 的 TiKV 集群。&lt;/li&gt;
  &lt;li&gt;暂不支持在 TiDB 中创建 SEQUENCE 的 DDL 操作和 SEQUENCE 函数。在上游 TiDB 使用 SEQUENCE 时，TiCDC 将会忽略掉上游执行的 SEQUENCE DDL 操作/函数，但是使用 SEQUENCE 函数的 DML 操作可以正确地同步。&lt;/li&gt;
  &lt;li&gt;暂不支持在同步的过程中对 TiCDC 正在同步的表和库进行 BR 数据恢复 和 TiDB Lightning 物理导入。详情请参考为什么在上游使用了 TiDB Lightning 和 BR 恢复了数据之后，TiCDC 同步会出现卡顿甚至卡住。&lt;/li&gt;
  &lt;li&gt;TiCDC 同步的表需要至少存在一个有效索引的表。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;详见&lt;a href=&quot;https://docs.pingcap.com/tidb/stable/ticdc-overview#ticdc-overview&quot;&gt;官方文档&lt;/a&gt; 中 &lt;a href=&quot;https://docs.pingcap.com/tidb/stable/ticdc-overview#unsupported-scenarios&quot;&gt;暂不支持的场景&lt;/a&gt;  和 &lt;a href=&quot;https://docs.pingcap.com/tidb/stable/ticdc-overview#best-practices&quot;&gt;最佳实践&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这些限制中最不好判断的是&lt;strong&gt;有效索引&lt;/strong&gt;这一条：&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;TiCDC同步的表需要至少存在一个有效索引的表，有效索引的定义如下：&lt;/p&gt;

  &lt;p&gt;主键 (PRIMARY KEY) 为有效索引。&lt;/p&gt;

  &lt;p&gt;唯一索引 (UNIQUE INDEX) 中每一列在表结构中明确定义非空 (NOT NULL) 且不存在虚拟生成列 (VIRTUAL GENERATED COLUMNS)。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如果 TiDB 集群中有表没有有效索引，那么这些表是不能利用 TiCDC 来同步数据的，如何集群中哪些表没有有效索引呢？推导如下：&lt;/p&gt;

&lt;p&gt;1.找出有主键的表&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.找出有唯一索引并且索引中每一列在表结构中明确定义非空 (NOT NULL) 且不存在虚拟生成列的表&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NON_UNIQUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TABLE_SCHEMA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TABLE_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COLUMN_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;NO&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.以上两种表的并集就是含有有效索引的表。&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;all&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NON_UNIQUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TABLE_SCHEMA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TABLE_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COLUMN_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;NO&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;总结&quot;&gt;总结&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;没有有效索引的表&lt;/strong&gt; = &lt;strong&gt;所有的表&lt;/strong&gt; - &lt;strong&gt;含有有效索引的表&lt;/strong&gt;，如果以下 SQL 的查询结果为非空，则表示 TiDB 集群中包含有不能使用 TiCDC 来同步数据的表：&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tables&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;BASE TABLE&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;union&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;distinct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tidb_indexes&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KEY_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PRIMARY&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NON_UNIQUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;left&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;join&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TABLE_SCHEMA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;TABLE_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;COLUMN_NAME&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;METRICS_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;PERFORMANCE_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFORMATION_SCHEMA&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;sys&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;column_name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IS_NULLABLE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;NO&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GENERATION_EXPRESSION&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;其他&quot;&gt;其他&lt;/h3&gt;
&lt;p&gt;还有一个关于不支持 SEQUENCE 相关操作的限制，找出集群中是否使用了 SEQUENCE 是很简单的，SQL 如下，如果查询结果为非空，则说明使用了 SEQUENCE：&lt;/p&gt;

&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_type&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;information_schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tables&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SEQUENCE&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2024/08/ticdc-applicability/&quot;&gt;How to ensure TiDB cluster can use TiCDC to synchronize data?&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on August 03, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[TiDB Operator Enable TLS For MySQL Client With cert-manager]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2024/06/tidb-operator-tls-mysql-cleint-cert-manager/"/>
  <id>http://www.solidstack.io/posts/2024/06/tidb-operator-tls-mysql-client-cert-manager</id>
  <published>2024-06-06T00:00:00+08:00</published>
  <updated>2024-06-06T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#TiDB" term="TiDB" /><category scheme="http://www.solidstack.io/tags/#TiDB%20Operator" term="TiDB Operator" /><category scheme="http://www.solidstack.io/tags/#TLS" term="TLS" /><category scheme="http://www.solidstack.io/tags/#Kubernetes" term="Kubernetes" />
  <content type="html">
  
    &lt;p&gt;TiDB Operatior supports enable and disable TLS for MySQL client for an existing TiDB cluster. This article will show you how to enable it.
Certificates can be issued by &lt;strong&gt;cfssl&lt;/strong&gt; and &lt;strong&gt;cert-manager&lt;/strong&gt;, here I will choose the cloud native way &lt;em&gt;cert-manager&lt;/em&gt;.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;We need a self-signed CA&lt;/li&gt;
  &lt;li&gt;Use the CA to sign certificate for TiDB cluster&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;prepare-ca&quot;&gt;Prepare CA&lt;/h3&gt;
&lt;h4 id=&quot;install-cert-manager&quot;&gt;Install cert-manager&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm repo add jetstack https://charts.jetstack.io &lt;span class=&quot;nt&quot;&gt;--force-update&lt;/span&gt;
helm &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;cert-manager jetstack/cert-manager &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; cert-manager &lt;span class=&quot;nt&quot;&gt;--create-namespace&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt; v1.15.2 &lt;span class=&quot;nt&quot;&gt;--set&lt;/span&gt; crds.enabled&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It will take some time, be patient.&lt;/p&gt;

&lt;h4 id=&quot;prepare-issuers&quot;&gt;Prepare Issuers&lt;/h4&gt;
&lt;p&gt;TiDB is not a internet facing public services, self-signed issuer and CA is enough.
There’s no naming restrictions for the issuers for TiDB Operator.
&lt;br /&gt;
1.Create tidb-server-issuer.yaml&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Issuer&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-selfsigned-ca-issuer&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-cluster&lt;/span&gt; 
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selfSigned&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Certificate&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-ca&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-cluster&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-ca-secret&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# specify a secret to store the CA certificate and private key&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;commonName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;TiDB&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;CA&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;isCA&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;                                  &lt;span class=&quot;c1&quot;&gt;# Make this certificate is valid for certificate signing&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;87600h&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 10yrs&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;renewBefore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;720h&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 30d&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;issuerRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-selfsigned-ca-issuer&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# Must specify an issuer for the certificate, we want a CA certificate, the issuer should be self-signed issuer&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Issuer&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Issuer&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-issuer&lt;/span&gt;                           &lt;span class=&quot;c1&quot;&gt;# This is the issuer issues TLS certificates for the TiDB cluster&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-cluster&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ca&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-ca-secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.Create Issuers&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; tidb-server-issuer.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get issuer &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; tidb-cluster
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plain&quot;&gt;NAMESPACE      NAME                        READY   AGE
tidb-cluster   tidb-issuer                 True    18s
tidb-cluster   tidb-selfsigned-ca-issuer   True    18s
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;enable-tidb-cluster-tls-for-client&quot;&gt;Enable TiDB Cluster TLS for Client&lt;/h3&gt;
&lt;h4 id=&quot;perpare-server-certificates&quot;&gt;Perpare Server Certificates&lt;/h4&gt;
&lt;p&gt;We need to issue two sets of certificates:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A set of server-side certificates for TiDB server&lt;/li&gt;
  &lt;li&gt;A set of client-side certificates for MySQL client.
    &lt;blockquote&gt;
      &lt;p&gt;Certificate and keys will be stored in secrets, ${cluster_name}-tidb-server-secret and ${cluster_name}-tidb-client-secrete.&lt;/p&gt;

      &lt;p&gt;The Secret objects you created must follow the above naming convention. Otherwise, the deployment of the TiDB cluster will fail.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1.Create tidb-server-cert.yaml&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: advanced-test-tidb-server-secret
  namespace: tidb-cluster
spec:
  secretName: advanced-test-tidb-server-secret
  duration: 8760h &lt;span class=&quot;c&quot;&gt;# 365d&lt;/span&gt;
  renewBefore: 360h &lt;span class=&quot;c&quot;&gt;# 15d&lt;/span&gt;
  subject:
    organizations:
    - Sherry Yin Xi
  commonName: &lt;span class=&quot;s2&quot;&gt;&quot;TiDB Server&quot;&lt;/span&gt;
  usages:
    - server auth
  dnsNames:
    - &lt;span class=&quot;s2&quot;&gt;&quot;advanced-test-tidb&quot;&lt;/span&gt;
    - &lt;span class=&quot;s2&quot;&gt;&quot;advanced-test-tidb.tidb-cluster&quot;&lt;/span&gt;
    - &lt;span class=&quot;s2&quot;&gt;&quot;advanced-test-tidb.tidb-cluster.svc&quot;&lt;/span&gt;
    - &lt;span class=&quot;s2&quot;&gt;&quot;*.advanced-test-tidb&quot;&lt;/span&gt;
    - &lt;span class=&quot;s2&quot;&gt;&quot;*.advanced-test-tidb.tidb-cluster&quot;&lt;/span&gt;
    - &lt;span class=&quot;s2&quot;&gt;&quot;*.advanced-test-tidb.tidb-cluster.svc&quot;&lt;/span&gt;
    - &lt;span class=&quot;s2&quot;&gt;&quot;*.advanced-test-tidb-peer&quot;&lt;/span&gt;
    - &lt;span class=&quot;s2&quot;&gt;&quot;*.advanced-test-tidb-peer.tidb-cluster&quot;&lt;/span&gt;
    - &lt;span class=&quot;s2&quot;&gt;&quot;*.advanced-test-tidb-peer.tidb-clsuter.svc&quot;&lt;/span&gt;
  ipAddresses:
    - 127.0.0.1
    - ::1
  issuerRef:
    name: tidb-issuer
    kind: Issuer
    group: cert-manager.io
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.Issue Certificate for TiDB Server&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; tidb-server-cert.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;perpare-client-certificates&quot;&gt;Perpare Client Certificates&lt;/h4&gt;
&lt;p&gt;1.Create tidb-client-cert.yaml&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Certificate&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;advanced-test-tidb-client-secret&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-cluster&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;secretName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;advanced-test-tidb-client-secret&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8760h&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 365d&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;renewBefore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;360h&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 15d&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;subject&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;organizations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Sherry Yin Xi&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;commonName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;TiDB&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Client&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;usages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;client auth&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;issuerRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tidb-issuer&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Issuer&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cert-manager.io&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2.Issue Cerficate for MySQL Client&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; tidb-client-cert.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.Other Client Components
Please refer to official &lt;a href=&quot;https://docs.pingcap.com/tidb-in-kubernetes/v1.5/enable-tls-for-mysql-client#using-cert-manager&quot;&gt;documentaiton&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;enable-tls-for-client&quot;&gt;Enable TLS for Client&lt;/h4&gt;
&lt;p&gt;For an existing TiDB cluster, just update your tidb cluster manifest set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.spec.tidb.tlsClient.enabled&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; as below.&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;tidb&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;tlsClient&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;                  
      &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;             
  &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After apply the above changes, the &lt;strong&gt;tidb&lt;/strong&gt; and &lt;strong&gt;pd&lt;/strong&gt; pods of your TiDB cluster will rolling restart.&lt;/p&gt;

&lt;p&gt;&lt;br /&gt;
Check the logs of PD and TiDB pods&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl logs advanced-test-pd-0 &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; tidb-cluster |grep &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; tls
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;2024/06/06 02:37:20.061 +00:00] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;server.go:249] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;PD config&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;client-urls&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://0.0.0.0:2379&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;peer-urls&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://0.0.0.0:2380&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;advertise-client-urls&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://advanced-test-pd-0.advanced-test-pd-peer.tidb-cluster.svc:2379&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;advertise-peer-urls&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;http://advanced-test-pd-0.advanced-test-pd-peer.tidb-cluster.svc:2380&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;advanced-test-pd-0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data-dir&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/lib/pd&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;force-new-cluster&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-grpc-gateway&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;initial-cluster&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;advanced-test-pd-0=http://advanced-test-pd-0.advanced-test-pd-peer.tidb-cluster.svc:2380&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;initial-cluster-state&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;initial-cluster-token&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pd-cluster&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;lease&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable-timestamp&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-days&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-backups&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;development&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable-caller&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable-stacktrace&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable-error-verbose&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;sampling&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;error-output-path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-concurrent-tso-proxy-streamings&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:5000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tso-proxy-recv-from-client-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1h0m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tso-save-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;3s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tso-update-physical-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;50ms&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-local-tso&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;metric&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;job&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;advanced-test-pd-0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;15s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-snapshot-count&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:64,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-pending-peer-count&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:64,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-merge-region-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:20,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-merge-region-keys&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;split-merge-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1h0m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;switch-witness-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1h0m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-one-way-merge&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-cross-table-merge&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;patrol-region-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;10ms&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-store-down-time&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;30m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-store-preparing-time&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;48h0m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;leader-schedule-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:4,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;leader-schedule-policy&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;region-schedule-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:2048,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;witness-schedule-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:4,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;replica-schedule-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:64,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;merge-schedule-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:8,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hot-region-schedule-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:4,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hot-region-cache-hits-threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;store-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tolerant-size-ratio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;low-space-ratio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.8,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;high-space-ratio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.7,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;region-score-formula-version&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;scheduler-max-waiting-operator&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:5,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-remove-down-replica&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-replace-offline-replica&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-make-up-replica&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-remove-extra-replica&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-location-replacement&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-debug-metrics&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-joint-consensus&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-tikv-split-region&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;schedulers-v2&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:[{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;balance-region&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args-payload&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;balance-leader&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args-payload&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;balance-witness&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args-payload&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hot-region&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args-payload&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;transfer-witness-leader&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;args-payload&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}],&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;schedulers-payload&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hot-regions-write-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;10m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;hot-regions-reserved-days&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:7,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-movable-hot-peer-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:512,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-diagnostic&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-witness&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;slow-store-evicting-affected-store-ratio-threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.3,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;store-limit-version&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;replication&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-replicas&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;location-labels&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;strictly-match-label&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-placement-rules&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-placement-rules-cache&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;isolation-level&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pd-server&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;use-region-storage&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-gap-reset-ts&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;24h0m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;key-type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;runtime-services&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;metric-storage&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dashboard-address&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;trace-region-flow&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;flow-round-by-digit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;min-resolved-ts-persistence-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;server-memory-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;server-memory-limit-gc-trigger&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.7,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-gogc-tuner&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;gc-tuner-threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.6,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;block-safe-point-v1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cluster-version&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0.0.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;quota-backend-bytes&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;8GiB&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;auto-compaction-mode&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;periodic&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;auto-compaction-retention-v2&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1h&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;TickInterval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;500ms&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ElectionInterval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;3s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;PreVote&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-request-bytes&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:157286400,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cacert-path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cert-path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;key-path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cert-allowed-cn&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;SSLCABytes&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;SSLCertBytes&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;SSLKEYBytes&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;redact-info-log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;encryption&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data-encryption-method&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;plaintext&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data-key-rotation-period&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;168h0m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;master-key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;plaintext&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;key-id&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}}},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;label-property&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;WarningMsgs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;DisableStrictReconfigCheck&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;HeartbeatStreamBindInterval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;LeaderPriorityCheckInterval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dashboard&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-cacert-path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/lib/tidb-client-tls/ca.crt&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-cert-path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/lib/tidb-client-tls/tls.crt&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-key-path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/lib/tidb-client-tls/tls.key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;public-path-prefix&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;internal-proxy&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-telemetry&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-experimental&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;replication-mode&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;replication-mode&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;majority&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dr-auto-sync&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;label-key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;primary&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dr&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;primary-replicas&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dr-replicas&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;wait-store-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pause-region-split&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;keyspace&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pre-alloc&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;wait-region-split&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;wait-region-split-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;30s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;check-region-split-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;50ms&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;degraded-mode-wait-duration&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ltb-max-wait-duration&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;30s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;request-unit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;read-base-cost&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.125,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;read-per-batch-base-cost&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.5,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;read-cost-per-byte&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.0000152587890625,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;write-base-cost&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;write-per-batch-base-cost&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;write-cost-per-byte&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.0009765625,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;read-cpu-ms-cost&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.3333333333333333}}}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl logs advanced-test-tidb-0 &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; tidb &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; tidb-cluster |grep tidb-server-tls
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;2024/06/06 03:38:53.325 +00:00] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;INFO] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;printer.go:52] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;loaded config&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;advertise-address&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;advanced-test-tidb-0.advanced-test-tidb-peer.tidb-cluster.svc&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:4000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tikv&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;advanced-test-pd:2379&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/tmp/tidb-4000.sock&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;lease&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;45s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;split-table&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;token-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;temp-dir&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/tmp/tidb&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tmp-storage-path&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/tmp/0_tidb/MC4wLjAuMDo0MDAwLzAuMC4wLjA6MTAwODA=/tmp-storage&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tmp-storage-quota&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:-1,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;server-version&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;version-comment&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-edition&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-release-version&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;keyspace-name&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable-timestamp&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-timestamp&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disable-error-stack&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-error-stack&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:300,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-days&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-backups&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;slow-query-file&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/log/tidb/slowlog&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;expensive-threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:10000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;query-log-max-len&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:4096,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-slow-log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;slow-threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;record-plan-in-slow-log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_general_log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_pprof_sql_cpu&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ddl_slow_threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:300,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_expensive_query_time_threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:60,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_expensive_txn_time_threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:600,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_stmt_summary_enable_persistent&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_stmt_summary_filename&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-statements.log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_stmt_summary_file_max_days&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_stmt_summary_file_max_size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:64,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_stmt_summary_file_max_backups&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_enable_slow_log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_slow_log_threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_record_plan_in_slow_log&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_check_mb4_value_in_utf8&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_force_priority&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;NO_PRIORITY&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_memory_usage_alarm_ratio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.8,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_enable_collect_execution_info&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;plugin_dir&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/data/deploy/plugin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;plugin_load&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max_connections&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_enable_ddl&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_rc_read_check_ts&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb_service_scope&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;skip-grant-table&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ssl-ca&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/lib/tidb-server-tls/ca.crt&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ssl-cert&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/lib/tidb-server-tls/tls.crt&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ssl-key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/var/lib/tidb-server-tls/tls.key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cluster-ssl-ca&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cluster-ssl-cert&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cluster-ssl-key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cluster-verify-cn&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:null,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;session-token-signing-cert&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;session-token-signing-key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;spilled-file-encryption-method&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;plaintext&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-sem&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;auto-tls&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tls-version&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;rsa-key-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:4096,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;secure-bootstrap&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;auth-token-jwks&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;auth-token-refresh-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1h0m0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disconnect-on-expired-password&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;status-host&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0.0.0.0&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;metrics-addr&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;status-port&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:10080,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;metrics-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:15,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;report-status&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;record-db-qps&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;record-db-label&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-keepalive-time&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:10,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-keepalive-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-concurrent-streams&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1024,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-initial-window-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:2097152,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-max-send-msg-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:2147483647},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;performance&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-procs&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-memory&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;server-memory-quota&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;stats-lease&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;3s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;stmt-count-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:5000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pseudo-estimate-ratio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.8,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;bind-info-lease&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;3s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;txn-entry-size-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:6291456,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;txn-total-size-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:104857600,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tcp-keep-alive&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tcp-no-delay&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cross-join&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;distinct-agg-push-down&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;projection-push-down&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-txn-ttl&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3600000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;index-usage-sync-lease&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;0s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;plan-replayer-gc-lease&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;10m&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;gogc&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:100,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enforce-mpp&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;stats-load-concurrency&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:5,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;stats-load-queue-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;analyze-partition-concurrency-quota&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:16,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;plan-replayer-dump-worker-concurrency&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-stats-cache-mem-quota&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;committer-concurrency&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:128,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;run-auto-analyze&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;force-priority&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;NO_PRIORITY&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;memory-usage-alarm-ratio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.8,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-load-fmsketch&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;lite-init-stats&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;force-init-stats&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;prepared-plan-cache&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;capacity&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:100,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;memory-guard-ratio&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0.1},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;opentracing&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;rpc-metrics&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;sampler&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;sampling-server-url&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-operations&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;sampling-refresh-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reporter&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;queue-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;buffer-flush-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;log-spans&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;local-agent-host-port&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;}},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;proxy-protocol&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;header-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:5,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;fallbackable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pd-client&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pd-server-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tikv-client&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-connection-count&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:4,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-keepalive-time&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:10,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-keepalive-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;grpc-compression-type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;commit-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;41s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;async-commit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;keys-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:256,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;total-key-size-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:4096,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;safe-window&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:2000000000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;allowed-clock-drift&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:500000000},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-batch-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:128,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;overload-threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:200,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-batch-wait-time&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;batch-wait-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:8,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-chunk-rpc&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;region-cache-ttl&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:600,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;store-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;store-liveness-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;1s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;copr-cache&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;capacity-mb&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1000},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;copr-req-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:60000000000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ttl-refreshed-txn-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:33554432,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;resolve-lock-lite-threshold&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:16},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;binlog&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ignore-error&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;write-timeout&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;15s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;binlog-socket&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;compatible-kill-query&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pessimistic-txn&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-retry-count&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:256,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;deadlock-history-capacity&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:10,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;deadlock-history-collect-retryable&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;pessimistic-auto-commit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;constraint-check-in-place-pessimistic&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-index-length&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:3072,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;index-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:64,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;table-column-count-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1017,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;graceful-wait-before-shutdown&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alter-primary-key&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;treat-old-version-utf8-as-utf8mb4&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-table-lock&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;delay-clean-table-lock&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;split-region-max-num&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1000,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;top-sql&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;receiver-address&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;repair-mode&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;repair-table-list&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:[],&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;isolation-read&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;engines&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:[&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tikv&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tiflash&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;]},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;new_collations_enabled_on_first_bootstrap&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;experimental&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;allow-expression-index&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;skip-register-to-dashboard&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-telemetry&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-global-index&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;deprecate-integer-display-length&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-enum-length-limit&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;stores-refresh-interval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:60,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-tcp4-only&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-forwarding&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-ballast-object-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;ballast-object-size&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;transaction-summary&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;transaction-summary-capacity&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:500,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;transaction-id-digest-min-duration&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:2147483647},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-global-kill&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-32bits-connection-id&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;initialize-sql-file&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-batch-dml&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mem-quota-query&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:1073741824,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;oom-action&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;oom-use-tmp-storage&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;check-mb4-value-in-utf8&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;enable-collect-execution-info&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/data/deploy/plugin&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;max-server-connections&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:0,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;run-ddl&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:true,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;disaggregated-tiflash&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;autoscaler-type&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;aws&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;autoscaler-addr&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tiflash-autoscale-lb.tiflash-autoscale.svc.cluster.local:8081&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;is-tiflashcompute-fixed-pool&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;autoscaler-cluster-id&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;use-autoscaler&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-max-reuse-chunk&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:64,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-max-reuse-column&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:256,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;tidb-enable-exit-check&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:false,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;in-mem-slow-query-topn-num&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:30,&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;in-mem-slow-query-recent-num&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:500}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;mysql-client-connect-to-tidb-cluster-with-tls&quot;&gt;MySQL Client Connect to TiDB Cluster with TLS&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;Save CA/Client Certificate/Private Key
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get secret -n tidb-cluster advanced-test-tidb-client-secret  -ojsonpath=&apos;{.data.tls\.crt}&apos; | base64 --decode &amp;gt; client-tls.crt
kubectl get secret -n tidb-cluster advanced-test-tidb-client-secret  -ojsonpath=&apos;{.data.tls\.key}&apos; | base64 --decode &amp;gt; client-tls.key
kubectl get secret -n tidb-cluster advanced-test-tidb-client-secret  -ojsonpath=&apos;{.data.ca\.crt}&apos;  | base64 --decode &amp;gt; client-ca.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Connect with MySQL Client
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mysql --comments -uroot -p -P 4000 -h ${tidb_host} --ssl-cert=client-tls.crt --ssl-key=client-tls.key --ssl-ca=client-ca.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;mysql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;SHOW&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;STATUS&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;LIKE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Ssl%&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;-----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Variable_name&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;-----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ssl_cipher&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TLS_AES_128_GCM_SHA256&lt;/span&gt;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ssl_cipher_list&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RC4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CBC3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES256&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GCM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES256&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GCM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA384&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RC4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES256&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RC4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CBC3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES256&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GCM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES128&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GCM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES256&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GCM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA384&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AES256&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GCM&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SHA384&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CHACHA20&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POLY1305&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDHE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ECDSA&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CHACHA20&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;POLY1305&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLS_AES_128_GCM_SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLS_AES_256_GCM_SHA384&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TLS_CHACHA20_POLY1305_SHA256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ssl_server_not_after&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jun&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;01&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2025&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTC&lt;/span&gt;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ssl_server_not_before&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Jun&lt;/span&gt;  &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;02&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;22&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;01&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2024&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UTC&lt;/span&gt;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ssl_verify_mode&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ssl_version&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TLSv1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;-----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+&lt;/span&gt;
&lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;rows&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2024/06/tidb-operator-tls-mysql-cleint-cert-manager/&quot;&gt;TiDB Operator Enable TLS For MySQL Client With cert-manager&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on June 06, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[S3 Compatible MinIO Operator Deployment]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2024/04/minio-operator-deployment/"/>
  <id>http://www.solidstack.io/posts/2024/04/minio-operator-deployment</id>
  <published>2024-04-12T00:00:00+08:00</published>
  <updated>2024-04-12T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#S3" term="S3" /><category scheme="http://www.solidstack.io/tags/#MinIO" term="MinIO" /><category scheme="http://www.solidstack.io/tags/#MinIO%20Operator" term="MinIO Operator" /><category scheme="http://www.solidstack.io/tags/#Kubernetes" term="Kubernetes" />
  <content type="html">
  
    &lt;p&gt;Object store service is a common service nowdays, especially S3 compatible. The introduction of MinIO can be found &lt;a href=&quot;https://min.io/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article will demostrate how to setup MinIO Operator for testing purpose, this is &lt;strong&gt;NOT&lt;/strong&gt; a comprehensive guide for production scenario.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Deploy MinIO Operator&lt;/li&gt;
  &lt;li&gt;Deploy MinIO Tenant&lt;/li&gt;
  &lt;li&gt;Create bucket in MinIO Console&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;depoly-minio-operator&quot;&gt;Depoly MinIO Operator&lt;/h3&gt;
&lt;p&gt;We use Helm to deploy the operator.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm repo add minio-operator https://operator.min.io
helm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; minio-operator  &lt;span class=&quot;nt&quot;&gt;--create-namespace&lt;/span&gt; operator minio-operator/operator
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;deploy-minio-tenant&quot;&gt;Deploy MinIO Tenant&lt;/h3&gt;
&lt;p&gt;1.Download tenant CR file tenant-values.yaml&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-sLo&lt;/span&gt; tenant-values.yaml https://raw.githubusercontent.com/minio/operator/master/helm/tenant/values.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;2.Configure Tenant
Update tenant-values.yaml&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;tenant&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myminio&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;quay.io/minio/minio&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;RELEASE.2024-07-16T23-46-41Z&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pullPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;IfNotPresent&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;imagePullSecret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;scheduler&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myminio-env-configuration&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;configSecret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;myminio-env-configuration&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;accessKey&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;minio&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;secretKey&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;minio123&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pools&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;pool-0&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;volumesPerServer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10Gi&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;storageAnnotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;tolerations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;nodeSelector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;affinity&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;securityContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;runAsUser&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;runAsGroup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;fsGroup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;fsGroupChangePolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;OnRootMismatch&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;runAsNonRoot&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;containerSecurityContext&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;runAsUser&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;runAsGroup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;runAsNonRoot&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;allowPrivilegeEscalation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;capabilities&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ALL&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;seccompProfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;RuntimeDefault&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;topologySpreadConstraints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/export&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;subPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/data&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;9000&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;http&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;certificate&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;externalCaCertSecret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;externalCertSecret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;requestAutoCert&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;certConfig&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;features&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;bucketDNS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;domains&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enableSFTP&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;buckets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;podManagementPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Parallel&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;liveness&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;readiness&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;startup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;lifecycle&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;exposeServices&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;serviceAccountName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;prometheusOperator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;serviceMetadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;priorityClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;additionalVolumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;additionalVolumeMounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ingressClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;minio.local&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pathType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Prefix&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ingressClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;minio-console.local&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pathType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Prefix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;3.Deploy tenant&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;  &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; myminio &lt;span class=&quot;nt&quot;&gt;--create-namespace&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--values&lt;/span&gt; tenant-values.yaml myminio minio-operator/tenant
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;4.Expose console service&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl port-forward svc/myminio-console 9443 &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; myminio &lt;span class=&quot;nt&quot;&gt;--address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.0.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;5.Access MinIO console and create bucket&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Username: minio    (accessKey)&lt;/li&gt;
  &lt;li&gt;Password: minio123 (secretKey)&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/minio-operator/minio-login.png&quot; alt=&quot;minio-login&quot; width=&quot;600&quot; /&gt;
&lt;/figure&gt;

&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/minio-operator/create-bucket.png&quot; alt=&quot;create-bucket&quot; width=&quot;600&quot; /&gt;
&lt;/figure&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2024/04/minio-operator-deployment/&quot;&gt;S3 Compatible MinIO Operator Deployment&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on April 12, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[TiDB Operator Logging EFK Integration]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2024/04/tidb-operator-logging-efk-integration/"/>
  <id>http://www.solidstack.io/posts/2024/04/tidb-operator-logging-efk-integration</id>
  <published>2024-04-07T00:00:00+08:00</published>
  <updated>2024-04-07T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#TiDB" term="TiDB" /><category scheme="http://www.solidstack.io/tags/#TiDB%20Operator" term="TiDB Operator" /><category scheme="http://www.solidstack.io/tags/#EFK" term="EFK" /><category scheme="http://www.solidstack.io/tags/#ECK" term="ECK" /><category scheme="http://www.solidstack.io/tags/#Kubernetes" term="Kubernetes" />
  <content type="html">
  
    &lt;p&gt;❗ This is not a production setup, it’s only for testing and demo purpose&lt;/p&gt;

&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Since TiDB components deployed by TiDB Operator output the logs in the &lt;strong&gt;stdout&lt;/strong&gt; and &lt;strong&gt;stderr&lt;/strong&gt; of the container by default, as long as you have EFK deployed in your Kubernetes cluster, all the TiDB components logs will be collected by Fluentd running on each Kubernetes node, you can find TiDB logs same as other Kubernetes serviceslogs.&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/integration.png&quot; alt=&quot;integraton&quot; width=&quot;600&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;This article demonstrates how to collect TiDB logs using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fluent-bit&lt;/code&gt; in Kubernetes based on EFK, especially for &lt;strong&gt;slow logs&lt;/strong&gt; which need to be properly formated before streaming to EFK, for the EFK deployment please refer to my previous blog &lt;a href=&quot;http://www.solidstack.io/posts/2024/04/kubernetes-logging-efk-deployment/&quot;&gt;Kubernetes Logging EFK Deployment&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;enviornments&quot;&gt;Enviornments&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;Fluent-bit v1.8 (needed by multiline parser)&lt;/li&gt;
  &lt;li&gt;TiDB operator v1.5.2&lt;/li&gt;
  &lt;li&gt;TiDB v7.5&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;step-by-step-setup&quot;&gt;Step by Step Setup&lt;/h3&gt;
&lt;h4 id=&quot;1-deploy-tidb-operator&quot;&gt;1. Deploy TiDB Operator&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/pingcap/tidb-operator/v1.5.2/manifests/crd.yaml
kubectl create namespace tidb-admin
helm repo add pingcap https://charts.pingcap.org/
helm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; tidb-admin tidb-operator pingcap/tidb-operator &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt; v1.5.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;2-deploy-tidb-cluster-with-fluent-bit&quot;&gt;2. Deploy TiDB Cluster with Fluent-bit&lt;/h4&gt;
&lt;p&gt;Fluent bit running as sidecar in TiDB pod, you can use Fluentd as well. I choose fluent-bit as it’s light weight.&lt;/p&gt;

&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/fluent-bit.png&quot; alt=&quot;fluent-bit&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;h5 id=&quot;21-prepare-fluent-bit-config&quot;&gt;2.1 Prepare Fluent bit config&lt;/h5&gt;
&lt;p&gt;For demo purpose, I will manually mount Fluent-bit config. You may customize your image for a clean deployment, as slow query log is multiline format, we need to handle it properly, mutiline format parser is included in the config.&lt;/p&gt;

&lt;p&gt;1.Create fluent-bit ConfigMap&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; tidb-fluentbit-cm-slowlog.yaml 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;tidb-fluentbit-cm-slowlog.yaml&lt;/strong&gt; can be found &lt;a href=&quot;https://github.com/yinx0004/tidb-operator-logging-efk-integration-demo/blob/main/tidb-operator-logging/tidb-fluentbit-cm-slowlog.yaml&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h5 id=&quot;22-deploy-tidb-cluster-with-fluent-bit-as-sidecar-container&quot;&gt;2.2 Deploy TiDB Cluster with fluent-bit as sidecar container&lt;/h5&gt;
&lt;h6 id=&quot;option-1-fluent-bit-in-additional-container&quot;&gt;Option 1: fluent-bit in additional container&lt;/h6&gt;
&lt;p&gt;This deployment will keep the default slowlog container, fluent-bit as an additional container, tidb pod in total 3 containers.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; tidb-advanced-cluster-additional-container.yaml 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;tidb-advanced-cluster-additional-container.yaml&lt;/strong&gt; can be found &lt;a href=&quot;https://github.com/yinx0004/tidb-operator-logging-efk-integration-demo/blob/main/tidb-operator-logging/tidb-advanced-cluster-additional-container.yaml&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/advanced-cluster.png&quot; alt=&quot;advanced-cluster&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/tc.png&quot; alt=&quot;tc&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;h6 id=&quot;option2-fluent-bit-merged-with-default-slowlog-container&quot;&gt;Option2: Fluent-bit Merged with default slowlog container&lt;/h6&gt;
&lt;p&gt;We customize the default slowlog container to run Fluent bit by merging the default slowlog container and additional container.
tidb pod only 2 containers&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; tidb-advanced-cluster-fluentbit-merged-with-slowlog.yaml 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;tidb-advanced-cluster-fluentbit-merged-with-slowlog.yaml&lt;/strong&gt; can be found &lt;a href=&quot;https://github.com/yinx0004/tidb-operator-logging-efk-integration-demo/blob/main/tidb-operator-logging/tidb-advanced-cluster-fluentbit-merged-with-slowlog.yaml&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/tc-merged.png&quot; alt=&quot;tc-merged&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;h4 id=&quot;3-view-tidb-slow-query-logs-in-kibana&quot;&gt;3. View TiDB Slow Query Logs in Kibana&lt;/h4&gt;
&lt;p&gt;1.You will find tidb-yyyy.mm.dd index&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/kibana-tidb-log-index.png&quot; alt=&quot;es-operator&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;2.Create data view for tidb index&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/kibana-tidb-slowlog-view.png&quot; alt=&quot;es-operator&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;3.View TiDB Slow Logs&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/kb-example1.png&quot; alt=&quot;kb-example1&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;4.Formated TiDB Slow Log&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/tidb-operator-logging/kb-example2.png&quot; alt=&quot;kb-example2&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;


  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2024/04/tidb-operator-logging-efk-integration/&quot;&gt;TiDB Operator Logging EFK Integration&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on April 07, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Kubernetes Logging EFK Deployment]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2024/04/kubernetes-logging-efk-deployment/"/>
  <id>http://www.solidstack.io/posts/2024/04/kubernetes-logging-efk-deployment</id>
  <published>2024-04-02T00:00:00+08:00</published>
  <updated>2024-04-02T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#Kubernetes" term="Kubernetes" /><category scheme="http://www.solidstack.io/tags/#EFK" term="EFK" /><category scheme="http://www.solidstack.io/tags/#ECK" term="ECK" /><category scheme="http://www.solidstack.io/tags/#Logging" term="Logging" />
  <content type="html">
  
    &lt;p&gt;❗ This is not a production setup, it’s only for testing and demo purpose&lt;/p&gt;

&lt;h3 id=&quot;envrionments&quot;&gt;Envrionments&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;minikube v1.32.0&lt;/li&gt;
  &lt;li&gt;Kubernetes v1.24&lt;/li&gt;
  &lt;li&gt;ECK v2.12&lt;/li&gt;
  &lt;li&gt;Elasticsearch + Kibana v8.13.0&lt;/li&gt;
  &lt;li&gt;Fluentd v1.16&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;This is a simple infra level logging system setup, Fluentd as a daemonset running on each Kubernetes node as an agent to collect all containers’ logs to elasticseach. Again, this is not for production setup, just meet basic log collection requirement, production setup colud be more conprehensive.&lt;/p&gt;

&lt;figure&gt;
	&lt;img src=&quot;http://www.solidstack.io/images/efk/efk.png&quot; alt=&quot;efk&quot; /&gt;
	&lt;figcaption&gt;A glance of the architecture&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h3 id=&quot;step-by-step-setup&quot;&gt;Step By Step Setup&lt;/h3&gt;
&lt;h4 id=&quot;1-prepare-kubernetes&quot;&gt;1. Prepare Kubernetes&lt;/h4&gt;
&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;minikube start &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;6G &lt;span class=&quot;nt&quot;&gt;--disk-size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;25G &lt;span class=&quot;nt&quot;&gt;--kubernetes-version&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;v1.24
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;2-deploy-eck&quot;&gt;2. Deploy ECK&lt;/h4&gt;
&lt;p&gt;Refer to official documentation &lt;a href=&quot;https://www.elastic.co/guide/en/cloud-on-k8s/2.12/k8s-deploy-eck.html&quot;&gt;Deploy ECK in your Kubernetes cluster&lt;/a&gt;&lt;/p&gt;
&lt;h5 id=&quot;21-deploy-elastic-operator&quot;&gt;2.1 Deploy Elastic Operator&lt;/h5&gt;
&lt;p&gt;1.Create CRD&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; https://download.elastic.co/downloads/eck/2.12.1/crds.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;2.Create Operator&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; https://download.elastic.co/downloads/eck/2.12.1/operator.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;22-deploy-elasticsearch&quot;&gt;2.2 Deploy Elasticsearch&lt;/h5&gt;
&lt;p&gt;1.Create namespace &lt;strong&gt;eck&lt;/strong&gt; for ECK&lt;/p&gt;

&lt;p&gt;2.Deploy Elaisticsearch&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; elasticsearch.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;elasticsearch.yaml&lt;/strong&gt; can be found &lt;a href=&quot;https://github.com/yinx0004/tidb-operator-logging-efk-integration-demo/blob/main/efk-deployment/elasticsearch.yaml&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/efk/es.png&quot; alt=&quot;es&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Make sure HEALTH is &lt;strong&gt;green&lt;/strong&gt;, and PHASE is &lt;strong&gt;Ready&lt;/strong&gt;.&lt;/p&gt;

&lt;h5 id=&quot;23-deploy-kibana&quot;&gt;2.3 Deploy Kibana&lt;/h5&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; kibana.yaml 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;kibana.yaml&lt;/strong&gt; can be found &lt;a href=&quot;https://github.com/yinx0004/tidb-operator-logging-efk-integration-demo/blob/main/efk-deployment/kibana.yaml&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/efk/kb.png&quot; alt=&quot;kibana&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;h5 id=&quot;24-access-kibana&quot;&gt;2.4 Access Kibana&lt;/h5&gt;
&lt;p&gt;1.Get password&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get secret quickstart-es-elastic-user -n eck -o go-template=&apos;{{.data.elastic | base64decode}}&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/efk/kb-secret.png&quot; alt=&quot;kb-secret&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;2.Expose kibana service access from outside Kubernetes&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl port-forward service/quickstart-kb-http &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; eck 5601 &lt;span class=&quot;nt&quot;&gt;--address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0.0.0.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3.Login
Access Kibana URL &lt;a href=&quot;https://{your host}:5601/&quot;&gt;https://{your host}:5601/&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/efk/kb-login.png&quot; alt=&quot;kb-login&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;3-deploy-fluentd&quot;&gt;3. Deploy Fluentd&lt;/h4&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; fluentd-daemonset-elasticsearch-rbac.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;fluentd-daemonset-elasticsearch-rbac.yaml&lt;/strong&gt; can be found &lt;a href=&quot;https://github.com/yinx0004/tidb-operator-logging-efk-integration-demo/blob/main/efk-deployment/fluentd-daemonset-elasticsearch-rbac.yaml&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Replace the environment varibles accordingly, especically FLUENT_ELASTICSEARCH_PASSWORD&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/efk/fluentd.png&quot; alt=&quot;fluentd&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;h4 id=&quot;4-create-data-view-in-kibana&quot;&gt;4. Create Data View in Kibana&lt;/h4&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/efk/es-dataview.png&quot; alt=&quot;es-dataview&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;
&lt;h4 id=&quot;5-view-k8s-logs-in-kibana&quot;&gt;5. View K8S Logs in Kibana&lt;/h4&gt;
&lt;figure&gt;
        &lt;img src=&quot;http://www.solidstack.io/images/efk/es-view.png&quot; alt=&quot;es-view&quot; width=&quot;900&quot; /&gt;
&lt;/figure&gt;

&lt;p&gt;Enjoy EFK!!!&lt;/p&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2024/04/kubernetes-logging-efk-deployment/&quot;&gt;Kubernetes Logging EFK Deployment&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on April 02, 2024.&lt;/p&gt;</content>
</entry>


<entry>
  <title type="html"><![CDATA[Rust Begin Here]]></title>
  <link rel="alternate" type="text/html" href="http://www.solidstack.io/posts/2023/08/rust-begin-here/"/>
  <id>http://www.solidstack.io/posts/2023/08/rust-begin-here</id>
  <published>2023-08-15T00:00:00+08:00</published>
  <updated>2023-08-15T00:00:00+08:00</updated>
  <author>
    <name>Sherry Yin</name>
    <uri>http://www.solidstack.io</uri>
    <email>yinxi@ymail.com</email>
  </author>
  <category scheme="http://www.solidstack.io/tags/#Rust" term="Rust" />
  <content type="html">
  
    &lt;h3 id=&quot;local-rust-installation&quot;&gt;Local Rust Installation&lt;/h3&gt;
&lt;h4 id=&quot;install&quot;&gt;Install&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;--proto&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;=https&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--tlsv1&lt;/span&gt;.2 &lt;span class=&quot;nt&quot;&gt;-sSf&lt;/span&gt; https://sh.rustup.rs | sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;More details refer to the &lt;a href=&quot;https://doc.rust-lang.org/book/ch01-01-installation.html&quot;&gt;guide&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;components-installed&quot;&gt;Components installed&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;cargo: the Rust’s build system and package manager.&lt;/li&gt;
  &lt;li&gt;rustfmt: a tool for formatting Rust code according to style guidelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;hello-world&quot;&gt;Hello World!&lt;/h3&gt;
&lt;h4 id=&quot;new&quot;&gt;New&lt;/h4&gt;
&lt;p&gt;Create a new project with Cargo&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo new hello_world
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Output: “Created binary (application) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello_world&lt;/code&gt; package”&lt;/p&gt;

&lt;p&gt;A quick glance&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;hello_world/src/main.rs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fn main() {
    println!(&quot;Hello, world!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;build&quot;&gt;Build&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;hello_world
cargo build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Output: “binary hello_world was created under target/debug”&lt;/p&gt;

&lt;h4 id=&quot;run&quot;&gt;Run&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;target/debug/hello_world
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;output: “Hello, world!”&lt;/p&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cargo run
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/hello_world`
Hello, world!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;project-directory&quot;&gt;Project Directory&lt;/h4&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tree
&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
└── target
    ├── CACHEDIR.TAG
    └── debug
        ├── build
        ├── deps
        │   ├── hello_world-a65d4c6029deb9db
        │   ├── hello_world-a65d4c6029deb9db.2m06qpscokrdldr2.rcgu.o
        │   ├── hello_world-a65d4c6029deb9db.337tnzf71uan4bv8.rcgu.o
        │   ├── hello_world-a65d4c6029deb9db.3lakkzjgkq4s2ufz.rcgu.o
        │   ├── hello_world-a65d4c6029deb9db.3nyq6f5mboo59omw.rcgu.o
        │   ├── hello_world-a65d4c6029deb9db.3txvt6tt1wrkqp6g.rcgu.o
        │   ├── hello_world-a65d4c6029deb9db.d
        │   └── hello_world-a65d4c6029deb9db.okmjk6ajfymokvw.rcgu.o
        ├── examples
        ├── hello_world
        ├── hello_world.d
        └── incremental
            └── hello_world-27bxg3u8cn054
                ├── s-gq9ubsyjer-cu1tnq-8pp2iqcnvjen9b1yyqrks0gg0
                │   ├── 2m06qpscokrdldr2.o
                │   ├── 337tnzf71uan4bv8.o
                │   ├── 3lakkzjgkq4s2ufz.o
                │   ├── 3nyq6f5mboo59omw.o
                │   ├── 3txvt6tt1wrkqp6g.o
                │   ├── dep-graph.bin
                │   ├── okmjk6ajfymokvw.o
                │   ├── query-cache.bin
                │   └── work-products.bin
                └── s-gq9ubsyjer-cu1tnq.lock

10 directories, 24 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;ide-recommendation&quot;&gt;IDE Recommendation&lt;/h3&gt;
&lt;h4 id=&quot;vscode&quot;&gt;VSCode&lt;/h4&gt;
&lt;p&gt;Some plugins to recommend:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Rust Syntax: provides a TextMate grammar for Rust.&lt;/li&gt;
  &lt;li&gt;rust-analyzer: an implementation of Language Server Protocol for the Rust programming language. It provides features like completion and goto definition for many code editors, including VS Code, Emacs and Vim.&lt;/li&gt;
  &lt;li&gt;crates: helps Rust developers managing dependencies with Cargo.toml.&lt;/li&gt;
  &lt;li&gt;Even Better Toml: a TOML language support extension.&lt;/li&gt;
  &lt;li&gt;Rust Test Lens: adds a code lens to quickly run or debug a single test for your Rust code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;rustrover&quot;&gt;RustRover&lt;/h4&gt;
&lt;p&gt;From JetBrains, currently RustRover is free to use during the public preview, &lt;a href=&quot;https://www.jetbrains.com/rust/nextversion/&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;

  
  &lt;p&gt;&lt;a href=&quot;http://www.solidstack.io/posts/2023/08/rust-begin-here/&quot;&gt;Rust Begin Here&lt;/a&gt; was originally published by Sherry Yin at &lt;a href=&quot;http://www.solidstack.io&quot;&gt;Sherry Yin&lt;/a&gt; on August 15, 2023.&lt;/p&gt;</content>
</entry>

</feed>
