ShardingCore Documentation

repository·main·Indexed 23 days ago

https://github.com/dotnetcore/sharding-core

A high-performance, lightweight solution for EF Core that enables sharding of tables and databases, including support for read-write separation. It provides custom database sharding (routing to different data sources) and table sharding (splitting entities into multiple physical tables) while maintaining compatibility with standard EF Core operations, including support for join, group by, and advanced aggregations.

Tokens
18.9K
Snippets
28
Records
53
Agent score
78%

What's inside ShardingCore

  1. Overview of ShardingCore capabilities

    main

    ShardingCore is a library designed for database sharding (splitting data across multiple databases or tables) with minimal learning curve for EF Core users. It provides extensions for IQueryable to support sharding while maintaining compatibility with standard EF Core operations.

    Key Features

    • Custom Sharding: Supports custom database sharding (routing to different data sources) and table sharding (splitting a single entity into multiple physical tables).
    • Flexible Table Sharding: Uses a pattern of [BaseName] + [TailPrefix] + [Tail] (e.g., user_0, user_1 or user202101, user202102).
    • Advanced Query Support: Supports join, group by, max, count, min, avg, and sum across sharded tables.
    • Performance Optimizations: Includes high-performance pagination with low-memory streaming for large page jumps.
    • Ecosystem Compatibility: Supports extensions like EFCore.BulkExtensions for batch operations.
    • Read/Write Splitting: Built-in support for separating read and write operations.

    Limitations

    • Connection Consumption: Performing join operations between sharded tables without index-based filtering can result in Cartesian products, potentially leading to connection exhaustion. Future updates aim to address this via configuration.
  2. Understand the ShardingCoreDemo project structure

    main

    The ShardingCoreDemo project serves as a step-by-step guide for migrating from a standard EF Core Code-First project to a sharded database architecture (both static and dynamic sharding).

    Project Directory Structure:

    • WebApplication1: The main web application project.
    • WebApplication1.Data: Contains data models, DbContext, and common methods.
    • WebApplication1.Migrations.NoSharding: Migration assembly for the state before sharding was implemented.
    • WebApplication1.Migrations.Sharding: Migration assembly for the state after sharding was implemented.
    • WebApplication1.Migrations.Tool: A migration tool project used to create migration files via EF tools.
  3. Performance characteristics of ShardingCore

    main

    ShardingCore is designed for high performance with minimal overhead when querying non-sharded data.

    Key Performance Insights:

    • Overhead: When querying data where the sharding index is known, the overhead compared to native EF Core is approximately 3% (roughly 0.005ms per query).
    • MySQL vs SQL Server: In MySQL, sharding provides significant performance benefits during full table scans (without indexes) because it limits the scan to specific shards rather than the entire dataset. In SQL Server, the performance difference for non-indexed queries is less pronounced at smaller scales (e.g., 7.7m rows).
  4. Core concepts of Database Sharding (分库)

    main

    Database sharding (splitting data across different databases/data sources) in ShardingCore relies on the following abstractions:

    • DataSourceName: A unique identifier used to route an object to a specific data source.
    • IVirtualDataSource: Represents a virtual data source.
    • IVirtualDataSourceRoute: The routing logic that determines which IVirtualDataSource to use based on the business context.

    If you need to implement database-level sharding, you will typically implement IVirtualDataSourceRoute to map your business keys to specific data sources.

  5. Understand ShardingCore versioning and capabilities

    main

    ShardingCore versions are aligned with EF Core versions. The second digit in the version number indicates the level of sharding support:

    • Version X.2.y: Supports Database Sharding (分库) only.
    • Version X.3.y+: Supports both Database Sharding (分库) and Table Sharding (分表).

    Example: A version like 5.3.0 supports both database and table sharding, whereas 5.2.0 only supports database sharding.

  6. Use the Dynamic Sharding branch for dynamic database routing

    main

    The 动态分库 (Dynamic Sharding) branch demonstrates how to manage database routing dynamically.

    Key Concepts:

    • Dynamic Configuration: A table named TestModelKey in the main database maintains the dynamic database keys. These are synchronized to a configuration file named muitDbConfig.json.
    • Configuration Location: The muitDbConfig.json file is used by the routing logic (see TestModelVirtualDataSourceRoute.cs for implementation details) and is typically located in WebApplication1/bin/net 6.0.
    • Database Engine: This sample uses PostgreSQL via Npgsql. If you use SQL Server or MySQL, you must update the corresponding data drivers.
  7. Configure Read-Write Separation

    main

    ShardingCore supports a primary-replica (one master, multiple slaves) architecture via AddReadWriteSeparation.

    Strategies

    • ReadStrategyEnum.Loop: Rotates through read connections.
    • ReadStrategyEnum.Random: Selects a read connection randomly.

    Consistency and Connection Strategies

    To prevent data inconsistency (e.g., during pagination where a count and list are fetched separately), use ReadConnStringGetStrategyEnum:

    • LatestEveryTime: Fetches a new connection every time (may cause data gaps in pagination).
    • LatestFirstTime: Fetches a connection once per DbContext unit (ensures consistency within that context).

    Manual Connection Switching

    You can manually force a DbContext to use a specific connection type:

    • _virtualDbContext.ReadWriteSeparationReadOnly(): Switch to read-only databases.
    • _virtualDbContext.ReadWriteSeparationWriteOnly(): Switch to the write connection (useful to resolve read-write delay issues).
    • _virtualDbContext.ReadWriteSeparation = false: Disables read-write separation for that context, defaulting to the write connection.
  8. Requirements for Sharding Entities and Setup

    main

    To ensure the sharding logic works correctly, verify the following requirements in your project:

    Entity Requirements:

    • Table Sharding: Entities intended for table sharding must inherit from IShardingTable and contain a ShardingKey.
    • Database Sharding: Entities intended for database sharding must inherit from IShardingDataSource and contain a ShardingDataSourceKey.
    • Routing: Ensure the entity object has implemented a virtual route.

    Application Setup Requirements:

    • Assembly Loading: The framework uses AppDomain.CurrentDomain.GetAssemblies(). To prevent issues where required assemblies are not loaded, ensure all necessary DLLs are loaded at the API layer.
    • Routing Configuration: Ensure the virtual route has been added to your Startup configuration.
    • Initialization: Ensure bootstrapper.start() has been called in your Startup configuration.
  9. Important usage notes and service overrides

    main

    If you override certain services in your ShardingDbContext or DefaultDbContext, the ShardingCore framework may fail to function correctly. If you need to use these services, you must manually implement the extensions provided by the framework.

    Specifically, the framework relies on replacing the following services:

    1. ShardingDbContext overrides:

    • IDbSetSource (replaced with ShardingDbSetSource)
    • IQueryCompiler (replaced with ShardingQueryCompiler)
    • IDbContextTransactionManager (replaced with ShardingRelationalTransactionManager<TShardingDbContext>)
    • IRelationalTransactionFactory (replaced with ShardingRelationalTransactionFactory<TShardingDbContext>)

    2. DefaultDbContext overrides:

    • IModelCacheKeyFactory (replaced with ShardingModelCacheKeyFactory)
    • IModelCustomizer (replaced with ShardingModelCustomizer<TShardingDbContext>)

    Additionally, the framework uses AppDomain.CurrentDomain.GetAssemblies() to locate components. To prevent issues where required assemblies are not loaded, ensure that all necessary DLLs are loaded at the API layer.

    // Example of the service replacement pattern used by the framework
    return optionsBuilder.UseShardingWrapMark()
                    .ReplaceService<IDbSetSource, ShardingDbSetSource>()
                    .ReplaceService<IQueryCompiler, ShardingQueryCompiler>()
                    .ReplaceService<IDbContextTransactionManager, ShardingRelationalTransactionManager<TShardingDbContext>>()
                    .ReplaceService<IRelationalTransactionFactory, ShardingRelationalTransactionFactory<TShardingDbContext>>();
  10. Core concepts of Table Sharding (分表)

    main

    Table sharding uses a mapping system to connect a single logical entity to multiple physical tables:

    • Virtual Table (IVirtualTable): An abstraction representing the entire set of sharded tables. In your application, this corresponds to a single Entity.
    • Physical Table (IPhysicTable): The actual table in the database. The name is typically constructed as tablename + tailprefix + tail.
    • Tail: The suffix of the physical table.
    • TailPrefix: The character(s) between the virtual table name and the physical table suffix.
    • Virtual Route (IVirtualTableRoute): The bridge between the Virtual Table and the Physical Table. Users implement this interface to define how the system identifies which physical table to query based on business logic.
  11. How to implement ShardingCore DbContext

    main

    To enable sharding capabilities, your DbContext must inherit from AbstractShardingDbContext.

    If you are implementing table sharding (splitting tables), you must also implement the IShardingTableDbContext interface. If you are only implementing database sharding (splitting databases), implementing the interface is optional.

    When implementing IShardingTableDbContext, you must provide a RouteTail property of type IRouteTail.

    public class MyDbContext : AbstractShardingDbContext, IShardingTableDbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
        {
        }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Order>(entity =>
            {
                entity.HasKey(o => o.Id);
                entity.Property(o => o.Id).IsRequired().IsUnicode(false).HasMaxLength(50);
                entity.Property(o=>o.Payer).IsRequired().IsUnicode(false).HasMaxLength(50);
                entity.Property(o => o.Area).IsRequired().IsUnicode(false).HasMaxLength(50);
                entity.Property(o => o.OrderStatus).HasConversion<int>();
                entity.ToTable(nameof(Order));
            });
        }
    
        /// <summary>
        /// empty impl if use sharding table
        /// </summary>
        public IRouteTail RouteTail { get; set; }
    }
  12. Configure ShardingCore in Startup

    main

    Register ShardingCore in your IServiceCollection using AddShardingDbContext.

    When configuring the data source via AddDefaultDataSource, provide your connection string. Important: Do not modify the internal parameters of the UseXXX delegates (like UseSqlServer); instead, use the provided connStr or connection parameters to ensure the sharding logic correctly applies the connection to the routed shards.

    public void ConfigureServices(IServiceCollection services)
    {
        // Add sharding configuration
        services.AddShardingDbContext<MyDbContext>()
            .UseRouteConfig(op =>
            {
                op.AddShardingTableRoute<OrderVirtualTableRoute>();
            }).UseConfig(op =>
            {
                op.UseShardingQuery((connStr, builder) =>
                {
                    // connStr is delegate input param
                    builder.UseSqlServer(connStr);
                });
                op.UseShardingTransaction((connection, builder) =>
                {
                    // connection is delegate input param
                    builder.UseSqlServer(connection);
                });
                // use your database connection string
                op.AddDefaultDataSource(Guid.NewGuid().ToString("n"),
                    "Data Source=localhost;Initial Catalog=EFCoreShardingTableDB;Integrated Security=True;");
            }).AddShardingCore();
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        // Optional: enable check for missing tables and auto-creation on startup
        app.ApplicationServices.UseAutoTryCompensateTable();
        // other configure....
    }