如何使ActiveRecord与遗留的分区/分片数据库/表一起工作?

4

首先感谢您的时间...在通过Google、GitHub和这里进行了所有搜索之后,对于诸如分区/分片/联邦等大词汇感到更加困惑,我意识到我必须描述我遇到的具体问题并寻求帮助。

我的公司数据库处理大量用户和订单,因此我们以各种方式拆分数据库和表,其中一些如下所述:

way             database and table name      shard by (maybe it's should be called partitioned by?)
YZ.X            db_YZ.tb_X                   order serial number last three digits
YYYYMMDD.       db_YYYYMMDD.tb               date
YYYYMM.DD       db_YYYYMM.tb_ DD             date too

基本概念是根据字段(不一定是主键)将数据库和表分开,由于存在太多数据库和表,因此编写或自动生成一个database.yml配置文件和每个表的一个model并非最佳解决方案,甚至不可能。
我研究了drnic的魔法解决方案、datafabric甚至active record的源代码,也许我可以使用ERB生成database.yml并在around过滤器中进行数据库连接,也许我可以使用named_scope动态决定find的表名,但更新/创建操作绑定到“self.class.quoted_table_name”,所以我无法轻松解决我的问题。即使我可以为每个表生成一个模型,因为它们的数量最多只有30个。
但这并不是DRY!
我需要的是像以下DSL一样的清晰解决方案:
class Order < ActiveRecord::Base
   shard_by :order_serialno do |key|
      [get_db_config_by(key), #because some or all of the databaes might share the same machine in a regular way or can be configed by a hash of regex, and it can also be a const
       get_db_name_by(key), 
       get_tb_name_by(key),        
      ]
   end
end

有人能给我指点一下吗?任何帮助都将不胜感激~~~

3个回答

2

第二种情况(仅更改数据库名称)可以使用DbCharmer轻松实现。您需要在DbCharmer中创建自己的分片方法,该方法将基于密钥返回连接参数哈希。

其他两种情况目前不受支持,但可以轻松添加到您的系统中:

  1. You implement sharding method that knows how to deal with database names in your sharded dabatase, this would give you an ability to do shard_for(key) calls to your model to switch db connection.

  2. You add a method like this:

    class MyModel < ActiveRecord::Base
      db_magic :sharded => { :sharded_connection => :my_sharding_method }
    
      def switch_shard(key)
        set_table_name(table_for_key(key))  # switch table
        shard_for(key)                      # switch connection
      end
    end
    
  3. Now you could use your model like this:

    MyModel.switch_shard(key).first
    MyModel.switch_shard(key).count
    

    and, considering you have shard_for(key) call results returned from the switch_shard method, you could use it like this:

    m = MyModel.switch_shard(key) # Switch connection and get a connection proxy
    m.first                       # Call any AR methods on the proxy
    m.count 
    

感谢您提供详细的解决方案,非常有启发性。但是似乎存在一个致命问题:set_table_name是一个类方法,其影响是类范围内的,我无法将其限定在特定对象中。这就是为什么我考虑使用更内部的东西,比如“为每个表自动生成一个模型”,这似乎在您的魔术解决方案中被使用(仍在努力理解DbCharmer的源代码)。如果可能的话,我想将其添加到DbCharm中,但另一个致命问题是,DbCharmer没有单元测试...那么在过程中如何避免破坏某些东西呢? - Utensil
DbCharmer支持作用域连接切换。您可以始终执行MyModel.on_db(:foo) 或者 MyModel.switch_shard(key) 并且它只会在一个代码块内切换连接。至于测试,有一个独立的项目来编写规格(覆盖了该宝石几乎100%的代码):https://github.com/kovyrin/db-charmer-sandbox - kovyrin

1

如果你想要那种特定的DSL,或者与传统分片背后的逻辑相匹配的东西,你需要深入了解ActiveRecord并编写一个宝石来提供这种能力。你提到的所有现有解决方案不一定是针对你的情况编写的。你可能能够弯曲任何数量的解决方案来符合你的意愿,但最终你可能必须编写自定义代码来获得你所寻找的东西。


谢谢你的回答!我已经深入研究了AR源代码,并深入研究了我找到的不同解决方案的源代码。我做这些事情是为了找到编写自己的AR插件的方法。其中最接近的解决方案是sharded_database插件,尽管它不支持表分区...我猜“class << self; set_table_name; end”可能会让我有能力为每个AR实例更改表名。但代价,我不知道。而且每次更改数据库连接,都会放弃池的所有好处...通过提问,我试图寻找处于类似情况的人... - Utensil
如果有一个优雅的设计,只需轻微的触碰和AR上的hack,我会非常感激这种启示... - Utensil

1

听起来,在这种情况下,您应该考虑不使用SQL。

如果数据集很大并且可以表示为键/值对(稍微去规范化一下),则应该尝试couchDB或其他noSQL解决方案。这些解决方案快速、可完全扩展,并基于REST,因此易于扩展、备份和复制。

我们总是试图用同一工具解决所有问题(相信我,我也尝试过)。

与其重写activeRecord,转换到noSQL解决方案将更容易。


感谢您的回答!拥有开放式解决方案总是太棒了~CouchDB 是一个很棒的东西。我热爱 Erlang。不幸的是,在我的情况下,非 SQL 不是一个选项。MySQL 数据库和许多其他 C/C++ 程序是真正且严肃的业务的大部分,我只是想使用 Rails 与它们交流。 - Utensil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接