数据库 Getting Started with Rails & TiDB

hooopo · 2021年04月21日 · 最后由 hooopo 回复于 2021年04月22日 · 1024 次阅读
本帖已被管理员设置为精华贴

Getting Started with Rails & TiDB

也许是第一份 Rails + TiDB 集成的资料,网上新手入门方面的文章太少了,而且 ActiveRecord 这种复杂的 ORM 和 TiDB 集成还确实有一些门槛,所以就写了这么一个入门教程。

搭建本地 TiDB 开发环境

安装 TiUP

TiUP 安装过程十分简洁,无论是 Darwin 还是 Linux 操作系统,执行一行命令即可安装成功:

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

本机启动集群

tiup playground

输出

tiup playground
Starting component `playground`: /Users/hooopo/.tiup/components/playground/v1.4.1/tiup-playground
Use the latest stable version: v5.0.0

    Specify version manually:   tiup playground <version>
    The stable version:         tiup playground v4.0.0
    The nightly version:        tiup playground nightly

Playground Bootstrapping...
Start pd instance
Start tikv instance
Start tidb instance
Waiting for tidb instances ready
127.0.0.1:4000 ... Done
Start tiflash instance
Waiting for tiflash instances ready
127.0.0.1:3930 ... Done
CLUSTER START SUCCESSFULLY, Enjoy it ^-^
To connect TiDB: mysql --host 127.0.0.1 --port 4000 -u root -p (no password)
To view the dashboard: http://127.0.0.1:2379/dashboard
To view the Prometheus: http://127.0.0.1:9090
To view the Grafana: http://127.0.0.1:3000

dashboard

详细文档

Rails & TiDB 配置

创建 Rails 项目

rails new myapp --database=mysql

database.yml 配置

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  port: 4000
  username: root
  password:
  host: 127.0.0.1
  variables:
    tidb_enable_noop_functions: ON

注意,tiup 启动的本地集群默认端口是 4000,设置数据库 connection 变量 tidb_enable_noop_functions: ON,因为 Rails 需要使用 get_lock 函数,tidb 里默认是关闭的。

如果你使用 URI 的方式配置数据库链接,也是类似:

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  url: <%= ENV.fetch("DB_URL") || "mysql2://root:pass@localhost:4000/myapp" %>
  variables:
    tidb_enable_noop_functions: ON

主键、自增、唯一索引

创建一个 users 表:

class CreateUsers < ActiveRecord::Migration[6.1]
  def change
    create_table :users do |t|
      t.string :email
      t.string :password
      t.string :username

      t.timestamps
    end
  end
end

加一个唯一索引

class AddUniqueIndexForEmail < ActiveRecord::Migration[6.1]
  def change
    add_index :users, :email, unique: true
  end
end

和使用单机 MySQL 没有任何区别,TiDB 已经兼容的很好了,相比其他分布式数据库上手起来容易很多,一些分布式数据库主键、自增、唯一索引这些功能都是不兼容的,需要额外处理。

看看生成的数据表:

mysql> show create table users;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                                                                                                                                             |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| users | CREATE TABLE `users` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `username` varchar(255) DEFAULT NULL,
  `created_at` datetime(6) NOT NULL,
  `updated_at` datetime(6) NOT NULL,
  PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,
  UNIQUE KEY `index_users_on_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=30001 |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

savepoint patch

TiDB 和 ActiveRecord 结合的唯一障碍就是 TiDB 不支持 savepoint,我写了个简单的 patch 来解决:

# https://github.com/rails/rails/blob/6-1-stable/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L313
require 'active_record/connection_adapters/abstract/database_statements.rb'


module DisableSavepoint
  def transaction(requires_new: nil, isolation: nil, joinable: true)
    if requires_new
      requires_new = nil
      Rails.logger.warn "savepoint statement was used, but your db not support, ignored savepoint."
      Rails.logger.warn caller
      super(requires_new: requires_new, isolation: isolation, joinable: joinable)
    else
      super(requires_new: requires_new, isolation: isolation, joinable: joinable)
    end
  end
end

ActiveRecord::ConnectionAdapters::DatabaseStatements.send(:prepend, DisableSavepoint)

原理就是,Rails 里只有在 transaction 传参 requires_new 为 true 的时候才会引入savepoint,通过 patch 把 requires_new 为 true 的地方变成 nil,再输出日志来迁移。我的经验大部分 Rails 项目用到savepoint的地方不多,如果想迁移不是很难。跑 migration 的时候会引入 savepoint,但在没有并发执行 migration 的场景直接去掉也没有什么影响。

jasl 将本帖设为了精华贴。 04月21日 23:49

你们用 TiDB 来干啥了

紧箍咒 api 是有访问权限的,不是 admin 记录下来也没用。

huacnlee 回复

我对 TiFlash 感兴趣,打算来试试分析类场景的应用。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号