用Vitess节制数据库的负载的教程

472 阅读4分钟

不久前,Vitess已经有了节流功能,本周末我一发现这个消息,就迫不及待地去尝试,并总结我的印象。

设置集群

我们将按照Vitess Operator的入门指南来设置一个虚拟的Vitess集群。

题外话:我发现Vitess Operator非常容易使用;它涵盖了我所有的基本需求,而且它的文档足够好,不必去找资料。

我将使用一个GKE集群,因为这是我最熟悉的,但你也可以使用Minikube或其他Kubernetes产品。

# create a tiny cluster
$ gcloud container clusters create sample-vitess-cluster --cluster-version 1.17 --zone us-east1-b --num-nodes 5

$ git clone git@github.com:vitessio/vitess.git
$ cd vitess/examples/operator

# before running kubectl, make sure to select the context with newly created cluster

# install vitess operator
$ kubectl apply -f operator.yaml

# provision VitessCluster
$ kubectl apply -f 101_initial_cluster.yaml

# port-forward to the cluster
$ ./pf.sh

# install vtctlclient if you haven't yet
$ go get vitess.io/vitess/go/cmd/vtctlclient

# setup the schema
$ vtctlclient ApplySchema -sql="$(cat create_commerce_schema.sql)" commerce
$ vtctlclient ApplyVSchema -vschema="$(cat vschema_commerce_initial.json)" commerce

现在你的集群已经运行了随着pf.sh 的运行,你有端口转发,这应该允许你用mysql -h 127.0.0.1 -P 15306 -u user 连接到它,并探索一些东西。

启用节流器

按照节流器的文档,我们可以发现节流器目前是默认禁用的。我们必须把-enable-lag-throttler 传递给vttablet来启用它。

Vitess操作符让这一切变得非常简单。

diff --git a/examples/operator/101_initial_cluster.yaml b/examples/operator/101_initial_cluster.yaml
index 8df5c19c8..f2e5de108 100644
--- a/examples/operator/101_initial_cluster.yaml
+++ b/examples/operator/101_initial_cluster.yaml
@@ -62,6 +62,7 @@ spec:
             vttablet:
               extraFlags:
                 db_charset: utf8mb4
+                "enable-lag-throttler": "true"
               resources:
                 requests:

你可以用我们上面使用的同样的kubectl apply -f 101_initial_cluster.yaml ,应用修改过的YAML。

我们还需要以某种方式让应用程序通过HTTP与vttablet上的节流器端点对话。

默认情况下,Vitess运营商并没有公开它,所以我们将自己创建一个Service

apiVersion: v1
kind: Service
metadata:
  name: example-vttablet-commerce
spec:
  selector:
    "planetscale.com/component": vttablet
    "planetscale.com/keyspace": commerce
    "planetscale.com/cluster": example
  ports:
    - name: web
      port: 15000
      protocol: TCP
      targetPort: web
    - name: grpc
      port: 15999
      protocol: TCP
      targetPort: grpc
    - name: metrics
      port: 9104
      protocol: TCP
      targetPort: metrics

请注意,我们的服务特别指向商务钥匙空间的vttablet。这将使我们的应用程序能够与http://example-vttablet-commerce.default.svc.cluster.local:15000/throttler/check 上的节流器端点对话。

如果你愿意,你可以运行kubectl port-forward service/example-vttablet-commerce 15000 ,并浏览http://localhost:15000/,以看到vttablet的内部仪表板。

如果你通过curl或浏览器访问http://localhost:15000/throttler/check,你可以预览我们要从脚本中点击的节流器端点。下面是它的样子。

{
  "StatusCode": 200,
  "Value": 0.243247,
  "Threshold": 1,
  "Message": ""
}

Value 大于Threshold ,你会看到StatusCode 不等于200。这意味着客户端应该节流。

节流的客户端

我们已经准备好了一切--让我们创建一个样本脚本,大量地写入数据库并检查节流情况。我将使用Ruby作为我的首选语言。

为了给数据库带来更大的压力,我们将在脚本中使用多个线程。

require 'bundler/inline'
gemfile do
  source 'https://rubygems.org'
  gem 'mysql2'
end

require 'mysql2'
require 'net/http'
require 'json'

THROTTLER_URI = URI('http://example-vttablet-commerce.default.svc.cluster.local:15000/throttler/check').freeze

puts "connecting..."

def db_healthy?
  resp = Net::HTTP.get(THROTTLER_URI)
  status = JSON.parse(resp)
  # Unhealthy would return 429
  status.fetch("StatusCode") == 200
end

threads = 20.times.map do
  Thread.new do
    client = Mysql2::Client.new(
      host: "example-vtgate-ae7df4b6.default.svc.cluster.local",
      username: "user",
      port: 3306
    )
    loop do
      unless db_healthy?
        puts "throttling!"
        sleep 1
      end

      values = 1000.times.map do |t|
        "(#{rand(1..5)}, 'SKU-1001', #{rand(100..200)})"
      end
      client.query("insert into corder(customer_id, sku, price) values #{values.join(', ')}")
    end
  end
end

threads.each(&:join)

脚本为每个线程创建一个MySQL客户端(这一点很重要,因为连接不能被线程共享),每个线程一次批量插入1000条记录。

运行实验

你应该想办法在同一个Kubernetes集群中运行该脚本,可以把它放到现有的应用中,或者建立一个新的Docker镜像。

脚本一运行,你就可以在http://localhost:15000/,关注vttablet的统计数据。在那里你会看到锯齿状的QPS图表

image.png

锯齿状显示,客户端进行写入,然后回退,然后随着数据库健康状况的恢复再次写入。这很有效!

总结

当进行在线模式迁移、写回填或将数据导入数据库时,客户在写之前检查数据库健康状况是很重要的。这个小演示展示了如何利用Vitess给你的东西来实现这一点。请务必阅读描述所有功能的完整指南,所有节流器。

下面是一些我在生产中要注意的事情。

  • vttablet运行在MySQL进程的旁边。如果对节流器的HTTP调用会进入一个热点或关键路径,你真的不希望MySQL主机的所有网络带宽被来自各种客户端的对节流器的调用所吃掉。客户端在查询节流器时使用某种缓存是很重要的。例如,freno确实建议使用一个读过的缓存。
  • 你的应用程序必须决定它需要点击什么vttablet来检查它是否需要节流。这在某种程度上违背了Vitess的目的,即让客户保持哑巴,不知道DB的拓扑结构。我想最终把节流检查放到vtgate ,在Vitess前面的SQL代理。

总的来说,我很高兴看到Vitess从基础设施的角度使节流等事情变得非常容易。