Skip to content

数据源与读写分离 ​

SmartSql 内置了读写分离支持,允许你将读查询定向到一个或多个读副本,同时将写操作路由到单个主库。此功能对于扩展读密集型应用至关重要,通过 XML 或编程式 API 进行配置。路由决策在中间件管道内通过 DataSourceFilterMiddleware 完成,它委托给 IDataSourceFilter 实现。

概要 ​

方面详情
抽象基类AbstractDataSource,具有 Name、ConnectionString、DbProvider
写入源WriteDataSource -- 单个主库
读取源ReadDataSource -- 一个或多个副本,具有 Weight 属性
过滤器接口IDataSourceFilter,具有 Elect(AbstractRequestContext)
默认过滤器DataSourceFilter,通过 WeightFilter<T> 进行加权负载均衡
选择逻辑语句类型(Read/Write)决定源选择
扩展点通过 SmartSqlBuilder.UseDataSourceFilter() 替换 IDataSourceFilter

DataSource 类层次结构 ​

mermaid
classDiagram
    class AbstractDataSource {
        <<abstract>>
        +String Name
        +String ConnectionString
        +DbProvider DbProvider
        +CreateConnection() DbConnection
    }
    class WriteDataSource {
    }
    class ReadDataSource {
        +int Weight
    }
    class Database {
        +DbProvider DbProvider
        +WriteDataSource Write
        +IDictionary~String, ReadDataSource~ Reads
    }
    class DataSourceChoice {
        <<enumeration>>
        Unknow
        Write
        Read
    }

    AbstractDataSource <|-- WriteDataSource
    AbstractDataSource <|-- ReadDataSource
    Database --> WriteDataSource
    Database --> ReadDataSource
    Database --> DbProvider

数据源选择工作原理 ​

当中间件管道到达 DataSourceFilterMiddleware 时,它委托给 IDataSourceFilter.Elect() 来确定使用哪个数据库连接。

mermaid
sequenceDiagram
autonumber
    participant DSM as DataSourceFilterMiddleware
    participant DSF as DataSourceFilter
    participant SS as SessionStore
    participant DB as Database
    participant WF as WeightFilter

    DSM->>DSM: Check if session already has DataSource
    alt Session already has DataSource
        DSM-->>DSM: Reuse existing (e.g., transaction context)
    else No DataSource assigned
        DSM->>DSF: Elect(requestContext)
        DSF->>SS: Check LocalSession.DataSource
        alt LocalSession exists with DataSource
            DSF-->>DSM: Return existing DataSource
        else No existing DataSource
            DSF->>DB: Read requestContext.DataSourceChoice
            alt Choice == Write
                DSF-->>DSM: Return Database.Write
            else Choice == Read
                alt ReadDb specified
                    DSF->>DB: Reads[ReadDb]
                    DSF-->>DSM: Return specific ReadDataSource
                else No ReadDb specified
                    DSF->>WF: Elect(readSources with weights)
                    WF-->>DSF: Selected ReadDataSource
                    DSF-->>DSM: Return elected read source
                end
            end
        end
    end
    DSM->>DSM: dbSession.SetDataSource(electedSource)

DataSourceChoice 判定 ​

DataSourceChoice(读或写)在 InitializerMiddleware 阶段根据语句的 StatementType 确定:

StatementType 映射结果选择
StatementType.Write(或包含 Write 标志)DataSourceChoice.Write
所有其他类型(Select 等)DataSourceChoice.Read
Statement 上显式的 SourceChoice覆盖自动检测
mermaid
flowchart TD
    subgraph Decision["DataSourceChoice Decision"]
        style Decision fill:#161b22,stroke:#30363d,color:#e6edf3
        Start["Request has Statement"] --> Check1{"Statement.SourceChoice<br>explicitly set?"}
        Check1 -->|Yes| Use["Use explicit SourceChoice"]
        Check1 -->|No| Check2{"StatementType<br>contains Write?"}
        Check2 -->|Yes| Write["DataSourceChoice.Write"]
        Check2 -->|No| Read["DataSourceChoice.Read"]
        Write --> Result["Set on requestContext"]
        Read --> Result
        Use --> Result
    end

    style Start fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Check1 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Check2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Use fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Write fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Read fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Result fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

读副本加权负载均衡 ​

当配置了多个读副本且未指定特定 ReadDb 时,DataSourceFilter 使用 WeightFilter<T> 执行加权随机选择。每个 ReadDataSource 有一个 Weight 属性影响选择概率。

副本权重选择概率
Read-110050%
Read-26030%
Read-34020%

这允许你将更多流量导向更强大的副本,同时仍保持负载分配。

XML 配置 ​

数据库源在 SmartSqlMapConfig.xml 文件中配置:

xml
<SmartSqlMapConfig>
  <Database>
    <DbProvider Name="MySql"/>
    <Write Name="WriteDB"
           ConnectionString="Server=master-db;Database=MyDb;Uid=root;Pwd=123456;"/>
    <Read Name="ReadDB-1" Weight="100"
          ConnectionString="Server=replica-1;Database=MyDb;Uid=readonly;Pwd=123456;"/>
    <Read Name="ReadDB-2" Weight="60"
          ConnectionString="Server=replica-2;Database=MyDb;Uid=readonly;Pwd=123456;"/>
  </Database>
  <SmartSqlMaps>
    <SmartSqlMap Resource="Maps/User.xml"/>
  </SmartSqlMaps>
</SmartSqlMapConfig>

编程式配置 ​

使用 SmartSqlBuilder 时,可以直接配置数据源而无需 XML:

csharp
// 单数据库(无读写分离)
new SmartSqlBuilder()
    .UseDataSource("MySql", connectionString)
    .Build();

// 或使用 WriteDataSource 对象
new SmartSqlBuilder()
    .UseDataSource(new WriteDataSource
    {
        Name = "Write",
        ConnectionString = masterConnectionString,
        DbProvider = dbProvider
    })
    .Build();

显式 ReadDb 选择 ​

个别语句或请求上下文可以指定 ReadDb 属性以定向到特定读副本,绕过加权选择:

xml
<Statement Id="GetReport" ReadDb="ReadDB-1">
  SELECT * FROM Reports WHERE Id = @Id
</Statement>

事务行为 ​

当事务活跃时(IDbSession.Transaction != null),DataSourceFilter 始终返回已分配给会话的数据源。这确保事务内的所有操作都访问同一个数据库连接,无论读写指定。

IDataSourceFilter 接口 ​

csharp
public interface IDataSourceFilter
{
    AbstractDataSource Elect(AbstractRequestContext context);
}

要实现自定义路由逻辑(例如基于租户、区域或延迟),创建一个实现 IDataSourceFilter 的类并注册:

csharp
new SmartSqlBuilder()
    .UseDataSourceFilter(new MyCustomDataSourceFilter())
    .Build();

连接创建 ​

AbstractDataSource.CreateConnection() 使用 DbProvider.Factory 创建新的 DbConnection 实例并分配 ConnectionString:

mermaid
flowchart LR
    subgraph Creation["Connection Creation"]
        style Creation fill:#161b22,stroke:#30363d,color:#e6edf3
        DS["AbstractDataSource"] --> F["DbProvider.Factory"]
        F --> Conn["Factory.CreateConnection()"]
        Conn --> CS["conn.ConnectionString = this.ConnectionString"]
        CS --> Return["Return DbConnection"]
    end

    style DS fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style F fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Conn fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style CS fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Return fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

相关页面 ​

参考资料 ​

基于 MIT 许可证发布。