5

我有一个关于 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 的替代解决方案。

4

1 回答 1

5

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

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”作为它关心的列。

有关更多描述,请参阅mapper() 的文档。

于 2012-11-06T22:31:29.507 回答