在分片之间迁移数据
Migrate data between shards
对于我正在进行的项目,我们需要客户附近数据库中的客户数据。为此,我们采用了 Microsoft 的新 Elastic Scale 解决方案。这消除了分片的复杂性,并且仍然赋予您在全球范围内扩展的能力。
目前我面临一个比较重要的问题。我需要将数据从 1 个分片迁移到另一个分片。有一个示例应用程序 (Merge/Split) 可以执行某些操作,但它适用于范围(1..100、101..400 等)。我正在处理的数据库使用 Guid,因此我们不能使用示例代码。
我自己创建了一个 Move/Merge 管理工具,但在这里遇到了一个问题。
起初我想用 ORM 插入所有对象和依赖项。由于一些循环键,我无法轻易做到这一点。因此,我现在正在创建一个 SQL 脚本。
SQL 脚本大约 130MB,仅包含 INSERT
个命令。
所有这些都必须在 1 个事务内完成,因为您不希望迁移完成一半。如果有错误,一切都应该回滚。
运行 这个 130MB 的脚本给我一些错误。我的本地开发机器和 SQL Azure 我 运行 内存不足。
SQL蔚蓝:
There is insufficient memory available in the buffer pool
和本地:
There is insufficient system memory in resource pool 'default' to run this query.
我试过禁用索引,所以这不会在每个 INSERT
上重建。这没有解决任何问题。
关于如何进行的任何建议?我真的不能拆分脚本,因为所有数据都必须一次 INSERT
ed。我认为 SSIS 包也不是一个选项。
创建我自己的数据库事务系统似乎有些矫枉过正且容易出错。
除了 INSERT
脚本,我还需要在 'old' shard/database 上执行 DELETE
脚本,所以我想解决方案必须适用于此脚本还。我很想在 1 个事务中执行 INSERT
和 DELETE
脚本,但这在 SQL Azure(分布式事务)上(还)不可能。
Azure SQL DB Elastic Scale 当前预览中的 Split/Merge 版本具有已知限制,即它仅适用于范围分片映射。我假设您当前正在为您的 guid 使用列表分片映射。虽然我们目前正在努力在 Elastic Scale 预览更新中提供对列表分片映射和 Split/Merge 的支持,但我鼓励您尝试一种解决方法。这种解决方法可能比编写您自己的分片间数据移动基础设施更简单,并且可以为您节省大量工作(希望如此)。
这是我的建议:
- 用 guid 类型的范围分片映射替换您的列表分片映射。
- 使数据中的每个 guid 成为单一范围:使用 guid 值本身作为左边界,
使用在其二进制表示中递增 1 的 guid 值作为其右边界(请记住,右边界是独占的,而左边界是包含在内的)。您可以使用 ShardKey class 的 RawKey 属性 轻松检索左边界点的二进制表示。
- 将您的 Split/Merge 服务指向您的新范围分片映射。
- 在范围分片映射上使用 Split/Merge 的 shardlet 移动操作将给定的 guid 从一个分片移动到另一个分片。
让我知道这是如何工作的。如果您 运行 遇到麻烦 - 特别是在增加 guid 时 - 请在 torsteng(at)microsoft(dot)com 上给我留言。
最好的,
托尔斯滕
这里有一段代码可以帮助您增加 guid 值。
static void CreateMappings()
{
ShardKey guid1 = new ShardKey(new Guid("<yourgui1d>"));
ShardKey guid2 = new ShardKey(new Guid("<yourguid2>"));
ShardKey guid1_next = NextShardKeyForGuid(guid1);
ShardKey guid2_next = NextShardKeyForGuid(guid2);
_map.CreateRangeMapping(new Range<Guid>(guid1.GetValue<Guid>(), guid1_next.GetValue<Guid>()), _shard1);
_map.CreateRangeMapping(new Range<Guid>(guid2.GetValue<Guid>(), guid2_next.GetValue<Guid>()), _shard2);
}
static ShardKey NextShardKeyForGuid(ShardKey shardkey)
{
int len = 16;
byte[] b = new byte[len];
shardkey.RawValue.CopyTo(b, 0);
while (--len >= 0 && ++b[len] == 0) ;
// Treat overflow if the current key's value is the maximum in the domain
if (len < 0)
{
return new ShardKey(ShardKeyType.Guid, null);
}
else
{
return ShardKey.FromRawValue(ShardKeyType.Guid, b);
}
}
}
对于我正在进行的项目,我们需要客户附近数据库中的客户数据。为此,我们采用了 Microsoft 的新 Elastic Scale 解决方案。这消除了分片的复杂性,并且仍然赋予您在全球范围内扩展的能力。
目前我面临一个比较重要的问题。我需要将数据从 1 个分片迁移到另一个分片。有一个示例应用程序 (Merge/Split) 可以执行某些操作,但它适用于范围(1..100、101..400 等)。我正在处理的数据库使用 Guid,因此我们不能使用示例代码。
我自己创建了一个 Move/Merge 管理工具,但在这里遇到了一个问题。
起初我想用 ORM 插入所有对象和依赖项。由于一些循环键,我无法轻易做到这一点。因此,我现在正在创建一个 SQL 脚本。
SQL 脚本大约 130MB,仅包含 INSERT
个命令。
所有这些都必须在 1 个事务内完成,因为您不希望迁移完成一半。如果有错误,一切都应该回滚。
运行 这个 130MB 的脚本给我一些错误。我的本地开发机器和 SQL Azure 我 运行 内存不足。 SQL蔚蓝:
There is insufficient memory available in the buffer pool
和本地:
There is insufficient system memory in resource pool 'default' to run this query.
我试过禁用索引,所以这不会在每个 INSERT
上重建。这没有解决任何问题。
关于如何进行的任何建议?我真的不能拆分脚本,因为所有数据都必须一次 INSERT
ed。我认为 SSIS 包也不是一个选项。
创建我自己的数据库事务系统似乎有些矫枉过正且容易出错。
除了 INSERT
脚本,我还需要在 'old' shard/database 上执行 DELETE
脚本,所以我想解决方案必须适用于此脚本还。我很想在 1 个事务中执行 INSERT
和 DELETE
脚本,但这在 SQL Azure(分布式事务)上(还)不可能。
Azure SQL DB Elastic Scale 当前预览中的 Split/Merge 版本具有已知限制,即它仅适用于范围分片映射。我假设您当前正在为您的 guid 使用列表分片映射。虽然我们目前正在努力在 Elastic Scale 预览更新中提供对列表分片映射和 Split/Merge 的支持,但我鼓励您尝试一种解决方法。这种解决方法可能比编写您自己的分片间数据移动基础设施更简单,并且可以为您节省大量工作(希望如此)。
这是我的建议:
- 用 guid 类型的范围分片映射替换您的列表分片映射。
- 使数据中的每个 guid 成为单一范围:使用 guid 值本身作为左边界, 使用在其二进制表示中递增 1 的 guid 值作为其右边界(请记住,右边界是独占的,而左边界是包含在内的)。您可以使用 ShardKey class 的 RawKey 属性 轻松检索左边界点的二进制表示。
- 将您的 Split/Merge 服务指向您的新范围分片映射。
- 在范围分片映射上使用 Split/Merge 的 shardlet 移动操作将给定的 guid 从一个分片移动到另一个分片。
让我知道这是如何工作的。如果您 运行 遇到麻烦 - 特别是在增加 guid 时 - 请在 torsteng(at)microsoft(dot)com 上给我留言。
最好的, 托尔斯滕
这里有一段代码可以帮助您增加 guid 值。
static void CreateMappings()
{
ShardKey guid1 = new ShardKey(new Guid("<yourgui1d>"));
ShardKey guid2 = new ShardKey(new Guid("<yourguid2>"));
ShardKey guid1_next = NextShardKeyForGuid(guid1);
ShardKey guid2_next = NextShardKeyForGuid(guid2);
_map.CreateRangeMapping(new Range<Guid>(guid1.GetValue<Guid>(), guid1_next.GetValue<Guid>()), _shard1);
_map.CreateRangeMapping(new Range<Guid>(guid2.GetValue<Guid>(), guid2_next.GetValue<Guid>()), _shard2);
}
static ShardKey NextShardKeyForGuid(ShardKey shardkey)
{
int len = 16;
byte[] b = new byte[len];
shardkey.RawValue.CopyTo(b, 0);
while (--len >= 0 && ++b[len] == 0) ;
// Treat overflow if the current key's value is the maximum in the domain
if (len < 0)
{
return new ShardKey(ShardKeyType.Guid, null);
}
else
{
return ShardKey.FromRawValue(ShardKeyType.Guid, b);
}
}
}