【问题标题】:SQLAlchemy, UUIDs, Sharding, and AUTO_INCREMENT primary key... how to get them to work together?SQLAlchemy、UUID、Sharding 和 AUTO_INCREMENT 主键......如何让它们一起工作?
【发布时间】:2012-10-31 21:35:39
【问题描述】:

我有一个关于 SQLAlchemy、数据库分片和 UUID 的问题,请教各位好心人。

我目前正在使用 MySQL,其中有一个表格:

CREATE TABLE foo (
    added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    id BINARY(16) NOT NULL,
    ... other stuff ...
    UNIQUE KEY(id)
);

这张桌子的一点背景。我从不关心' added_id',我只是用来确保插入的项目在磁盘上聚集在一起(因为用于在 MySQL 中索引表的 B-Tree 使用主键作为聚集索引)。 “id”列包含 UUID 的二进制表示——这是我真正关心的列,所有其他事物都引用此 ID。同样,我不希望 UUID 成为主键,因为 UUID 是随机的,因此为索引表创建的 B-Tree 具有可怕的 IO 特征(至少这是其他地方所说的)。此外,虽然 UUID1 包含时间戳以确保以“顺序”顺​​序生成 ID,但在 ID 中包含 MAC 地址使我宁愿避免这样做。因此,我想使用 UUID4s。

好的,现在进入 SQLAlchemy 部分。在 SQLAlchemy 中,可以使用他们的 ORM 为上表定义一个模型,方法是:

# The SQL Alchemy ORM base class
Base = declerative_base()

# The model for table 'foo'
class Foo(Base):
    __table__ = 'foo'
    add_id = Column(Integer, primary_key=True, nullable=False)
    id = Column(Binary, index=True, unique=True, nullable=False)
    ...

同样,这与上面的 SQL 基本相同。

现在是问题。假设这个数据库将被分片(水平分区)到 2 个(或更多)单独的数据库中。现在,(假设没有删除)这些数据库中的每一个都将在表 foo 中具有 added_id 为 1、2、3 等的记录。由于 SQLAlchemy 使用会话来管理正在处理的对象,以便每个对象仅由其主键标识,因此似乎有可能出现这样一种情况,即我可以结束尝试从这两个对象中访问两个 Foo 对象具有相同 added_id 的分片会导致托管会话中的一些冲突。

有人遇到过这个问题吗?你做了什么来解决它?或者,很可能,我是否遗漏了 SQLAlchemy 文档中的某些内容,以确保不会发生这种情况。但是,查看 SQLAlchemy 下载(examples/sharding/attribute_shard.py)提供的分片示例,他们似乎通过将一个数据库分片指定为 ID 生成器来回避这个问题...... INSERTS 必须针对该单个数据库来获取 ID。 (他们还提到使用 UUID,但显然这会导致索引的性能问题。)

或者,有没有办法将 UUID 设置为主键并使用 added_id 将数据聚集在磁盘上?如果在 MySQL 中不可能,那么在 Postgres 等其他数据库中是否有可能?

提前感谢您的任何和所有输入!

--- 更新 ---- 我只想为这个问题添加一个我收到的带外答案。下面的文字不是我写的,我只是想把它包括在这里,以防有人觉得它有用。

使用 MySQL 和自动增量键避免这种情况的最简单方法是为每个数据库使用不同的自动增量偏移量,例如:

ALTER TABLE foo AUTO_INCREMENT=100000;

缺点是您需要注意如何配置每个分片,并且您需要对您使用的分片总数进行一些计划。

没有任何方法可以说服 MySQL 为聚集索引使用非主键。如果您不关心使用 SQLAlchemy 来管理您的数据库架构(尽管您可能应该这样做),您可以简单地将 UUID 设置为 SQLAlchemy 架构中的主键,并将 add_id 保留为实际表中的 pk。

我还看到了仅使用外部服务器(例如 redis)来维护行 ID 的替代解决方案。

【问题讨论】:

    标签: database performance sqlalchemy uuid sharding


    【解决方案1】:

    是的,您可以使用“primary_key”映射器参数将表的任何列指定为主键,该参数是 Column 对象的列表或单个 Column:

    Base = declarative_base()
    
    # The model for table 'foo'
    class Foo(Base):
        __table__ = 'foo'
        add_id = Column(Integer, primary_key=True, nullable=False)
        id = Column(Binary, index=True, unique=True, nullable=False)
    
        __mapper_args__ = {'primary_key': id}
    

    上面,虽然 SQLAlchemy Core 将“add_id”视为“autoincrement”列,但映射器大多对​​它不感兴趣,而是在考虑对象的“identity”时使用“id”作为它关心的列.

    更多描述请参见documentation for mapper()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-09
      • 2010-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多