你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用数据依赖型路由可将查询路由到相应的数据库

适用于:Azure SQL 数据库

数据依赖型路由是使用查询中的数据将请求路由到相应数据库的功能。 在使用分片数据库时,数据依赖型路由是一种基础模式。 请求上下文还可用于路由请求,尤其是在分片键不是查询的一部分时。 将应用程序中使用数据依赖型路由的每个特定查询和事务限制为针对每个请求访问一个数据库。 对于 Azure SQL 数据库弹性工具,此路由是使用 ShardMapManagerJava.NET) 类完成的。

应用程序无需在分片环境中跟踪与不同的数据片相关联的各种连接字符串或数据库位置。 但是,使用分片映射管理器横向扩展数据库在需要时基于分片映射中的数据以及作为应用程序请求目标的分片键值,建立与正确数据库的连接。 该键通常为 customer_id、tenant_id、date_key 或一些作为数据库请求的基础参数的其他特定标识符。

有关详细信息,请参阅使用数据依赖型路由横向扩展 SQL Server

下载客户端库

若要下载:

在依赖于数据的路由应用程序中使用 ShardMapManager

应用程序应在初始化期间使用工厂调用ShardMapManagerGetSQLShardMapManager.NET)实例化。 在本示例中,将同时初始化 ShardMapManager 以及它所包含的特定 ShardMap。 本例演示 GetSqlShardMapManager 和 GetRangeShardMap(Java.NET)方法。

ShardMapManager smm = ShardMapManagerFactory.getSqlShardMapManager(connectionString, ShardMapManagerLoadPolicy.Lazy);
RangeShardMap<int> rangeShardMap = smm.getRangeShardMap(Configuration.getRangeShardMapName(), ShardKeyType.Int32);
ShardMapManager smm = ShardMapManagerFactory.GetSqlShardMapManager(smmConnectionString, ShardMapManagerLoadPolicy.Lazy);
RangeShardMap<int> customerShardMap = smm.GetRangeShardMap<int>("customerMap"); 

尽可能使用最低特权凭据来获取分片映射

如果应用程序本身无法处理分片映射,在工厂方法中使用的凭据应该对全局分片映射数据库具有只读权限。 这些凭据通常与用于分发到分片映射管理器的开放连接的凭据不同。 另请参阅用于访问弹性数据库客户端库的凭据

调用 OpenConnectionForKey 方法

方法 ShardMap.OpenConnectionForKeyJava.NET)返回一个连接,可用于根据参数的值 key 向相应的数据库发出命令。 分片信息由 ShardMapManager应用程序缓存,因此这些请求通常不涉及针对 全局分片映射 数据库的数据库查找。

// Syntax:
public Connection openConnectionForKey(Object key, String connectionString, ConnectionOptions options)
// Syntax:
public SqlConnection OpenConnectionForKey<TKey>(TKey key, string connectionString, ConnectionOptions options)
  • key 参数用作分片映射中的查找键,以确定请求的相应数据库。
  • connectionString 用于传递仅用于目标连接的用户凭据。 此 connectionString 中不包含任何数据库名称或服务器名称,因为该方法使用 <a0/> 确定数据库和服务器。
  • 在分片映射可能更改且行可能由于拆分或合并操作而移动到其他数据库的环境中,应将connectionOptionsJava.NET)设置为ConnectionOptions.Validate。 此验证涉及在将连接传送到应用程序之前,在目标数据库上简要查询局部分片映射(而不是全局分片映射)。

如果针对局部分片映射进行的验证失败(指示缓存不正确),分片映射管理器会查询全局分片映射来获取新的正确值以供查找、更新缓存以及获取和返回相应的数据库连接。

仅在不期望分片映射更改且应用程序处于联机状态时才使用 ConnectionOptions.None 。 在该情况下,可以假设缓存的值始终正确,并且可以安全地跳过对目标数据库的额外双向验证调用。 这可以减少数据库流量。 还可以通过配置文件中的某个值设置 connectionOptions,以指示在此期间是否按预期进行了分片更改。

此示例使用整数键 CustomerID 的值,使用名为 ShardMapcustomerShardMap 对象。

int customerId = 12345;
int productId = 4321;
// Looks up the key in the shard map and opens a connection to the shard
try (Connection conn = shardMap.openConnectionForKey(customerId, Configuration.getCredentialsConnectionString())) {
    // Create a simple command that will insert or update the customer information
    PreparedStatement ps = conn.prepareStatement("UPDATE Sales.Customer SET PersonID = ? WHERE CustomerID = ?");

    ps.setInt(1, productId);
    ps.setInt(2, customerId);
    ps.executeUpdate();
} catch (SQLException e) {
    e.printStackTrace();
}
int customerId = 12345;
int newPersonId = 4321;

// Connect to the shard for that customer ID. No need to call a SqlConnection
// constructor followed by the Open method.
using (SqlConnection conn = customerShardMap.OpenConnectionForKey(customerId, Configuration.GetCredentialsConnectionString(), ConnectionOptions.Validate))
{
    // Execute a simple command.
    SqlCommand cmd = conn.CreateCommand();
    cmd.CommandText = @"UPDATE Sales.Customer
                        SET PersonID = @newPersonID WHERE CustomerID = @customerID";

    cmd.Parameters.AddWithValue("@customerID", customerId);cmd.Parameters.AddWithValue("@newPersonID", newPersonId);
    cmd.ExecuteNonQuery();
}  

OpenConnectionForKey 方法返回与正确数据库建立的、新的且已打开的连接。 此方法中所采用的连接仍然可以充分利用连接池。

OpenConnectionForKeyAsync如果应用程序使用异步编程,此方法(Java.NET)也可用。

与暂时性故障处理集成

在云中开发数据访问应用程序的最佳实践是,确保暂时性故障由应用引起,并且确保在引发错误之前重试几次这些操作。 暂时性故障处理(Java.NET)中讨论了云应用程序的暂时性故障处理。

暂时性故障处理在本质上可以与数据依赖型路由模式共存。 关键需求是重试整个数据访问请求,包括已获取依赖于数据的路由连接的 using 块。 可以将上述示例重写为以下内容。

示例 - 数据依赖型路由与暂时性故障处理

int customerId = 12345;
int productId = 4321;
try {
    SqlDatabaseUtils.getSqlRetryPolicy().executeAction(() -> {
        // Looks up the key in the shard map and opens a connection to the shard
        try (Connection conn = shardMap.openConnectionForKey(customerId, Configuration.getCredentialsConnectionString())) {
            // Create a simple command that will insert or update the customer information
            PreparedStatement ps = conn.prepareStatement("UPDATE Sales.Customer SET PersonID = ? WHERE CustomerID = ?");

            ps.setInt(1, productId);
            ps.setInt(2, customerId);
            ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    });
} catch (Exception e) {
    throw new StoreException(e.getMessage(), e);
}
int customerId = 12345;
int newPersonId = 4321;

Configuration.SqlRetryPolicy.ExecuteAction(() -> {

    // Connect to the shard for a customer ID.
    using (SqlConnection conn = customerShardMap.OpenConnectionForKey(customerId, Configuration.GetCredentialsConnectionString(), ConnectionOptions.Validate))
    {
        // Execute a simple command
        SqlCommand cmd = conn.CreateCommand();

        cmd.CommandText = @"UPDATE Sales.Customer
                            SET PersonID = @newPersonID
                            WHERE CustomerID = @customerID";

        cmd.Parameters.AddWithValue("@customerID", customerId);
        cmd.Parameters.AddWithValue("@newPersonID", newPersonId);
        cmd.ExecuteNonQuery();

        Console.WriteLine("Update completed");
    }
});

生成弹性数据库示例应用程序时,会自动下载需要实现暂时性故障处理的程序包。

事务一致性

确保分片的所有局部操作的事务属性。 例如,通过依赖于数据的路由提交的事务会在目标分片范围内执行以供连接。 此时,没有提供用于将多个连接包含在一个事务中的功能,因此对于在分片上执行的操作,没有事务保证。

后续步骤

尚未使用弹性数据库工具? 请查看入门指南。 如有问题,请在有关 SQL 数据库的 Microsoft Q&A 问题页面上联系我们;如有功能请求,请在 SQL 数据库反馈论坛添加新意见或为现有意见投票。