1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/xuejm-sharding-core

Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Gitee star Github star fork

Английский | Китайский

ShardingCore

высокопроизводительное легковесное решение для разбиения таблиц и баз данных в EF Core, поддержка разделения на чтение и запись.

  • нулевая зависимость
  • нулевые затраты на обучение
  • нулевое вторжение

  • GitCode китайский репозиторий кода

  • Gitee китайский зеркальный репозиторий

  • Донат

Сообщество и спонсоры

📚 Документация

Китайская документация

Как выбрать версию

  • shardingcore lastversion.efcoreversion.x.x

  • первая версия — версия shardingcore

  • вторая версия — версия EF Core

  • остальные версии используют последнюю версию

  • EF Core 9 использует ShardingCore 7.9.x.x

  • EF Core 8 использует ShardingCore 7.8.x.x

  • EF Core 7 использует ShardingCore 7.7.x.x

  • EF Core 6 использует ShardingCore 7.6.x.x

  • EF Core 5 использует ShardingCore 7.5.x.x- EF Core 3 использует ShardingCore 7.3.x.x

  • EF Core 2 использует ShardingCore 7.2.x.x

Abp.VNext, WTM, FURION

Зависимости

Перед версией ShardingCore 6.7.0.0

Выпуск EF Core .NET .NET (Core)
6.x.x.x 6.0.0 net 6.0 6.0+
5.x.x.x 5.0.10 Standard 2.1 5.0+
3.x.x.x 3.1.18 Standard 2.0 2.0+
2.x.x.x 2.2.6 Standard 2.0 2.0+

После версии ShardingCore 6.7.0.0

Используйте условное компиляцию с NetFramework

Релиз EF Core .NET (Core)
6.7.0.0+ 6.x net6
6.7.0.0+ 5.x net5 или netstandard2.1
6.7.0.0+ 3.x netcoreapp3 или netstandard2.0
6.7.0.0+ 2.x netcoreapp2

Быстрый старт

5 шагов для реализации шардинга по месяцам и поддержки автоматического создания таблиц по месяцам

Шаг 1: Установка пакета

Выберите драйвер базы данных для EF Core

# базовый пакет [README-zh.md](README-zh.md)
PM> Install-Package ShardingCore
# драйвер для SQL Server
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer
# драйвер для MySQL
#PM> Install-Package Pomelo.EntityFrameworkCore.MySql
# использование других драйверов базы данных, если EF Core поддерживает

Шаг 2: Запрос сущности

Запрос сущности Order

    /// Таблица заказов
    /// </summary>
    public class Order
    {
        /// <summary>
        /// Идентификатор заказа
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// Идентификатор плательщика
        /// </summary>
        public string Payer { get; set; }
        /// <summary>
        /// Сумма платежа в центах
        /// </summary>
        public long Money { get; set; }
        /// <summary>
        /// Регион
        /// </summary>
        public string Area { get; set; }
        /// <summary>
        /// Статус заказа
        /// </summary>
        public OrderStatusEnum OrderStatus { get; set; }
        /// <summary>
        /// Время создания
        /// </summary>
        public DateTime CreationTime { get; set; }
    }
    public enum OrderStatusEnum
    {
        Неоплачен = 1,
        Оплачивается = 2,
        Оплачен = 3,
        ОплатаНесуспешна = 4
    }

Шаг 3: Создание DbContext

Создайте MyDbContext, расширяющий AbstractShardingDbContext, затем этот DbContext поддерживает шардинг базы данных, если вы хотите поддержать шардинг таблиц (например, order_202101, order_202102, order_202103...), вам нужно реализовать IShardingTableDbContext

    {
        public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
        {
        }

        ```markdown
        ```csharp
        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>();
                // действительно имя таблицы Order_202101, Order_202102, Order_202103 и т.д.
                entity.ToTable(nameof(Order));
            });
        }
        /// <summary>
        /// пустая реализация, если используется разбиение на таблицы
        /// </summary>
        public IRouteTail RouteTail { get; set; }
        ```
        ```

### Шаг 4: Создание маршрута для отображения имени таблицы, используемой для запроса данных (Order)

```csharp
// Конструктор маршрута поддерживает внедрение зависимостей, что означает, что его жизненный цикл — это `Singleton`

    public class OrderVirtualTableRoute : AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<Order>
    {
        // private readonly IServiceProvider _serviceProvider;
```        //public OrderVirtualTableRoute(IServiceProvider serviceProvider)
        //{
            //_serviceProvider = serviceProvider;
        //}
        /// <summary>
        /// фиксированное значение, не используйте DateTime.Now, так как при перезапуске приложения это значение изменится.
        /// </summary>
        /// <returns></returns>
        public override DateTime GetBeginTime()
        {
            return new DateTime(2021, 1, 1);
        }
        /// <summary>
        /// настройка свойства разбиения.
        /// </summary>
        /// <param name="builder"></param>```markdown
        public void ConfigureServices(IServiceCollection services)
        {

            //конфигурация шардинга
            services.AddShardingDbContext<MyDbContext>()
                .UseRouteConfig(op =>
                {
                    op.AddShardingTableRoute<OrderVirtualTableRoute>();
                }).UseConfig(op =>
                {
                    op.UseShardingQuery((connStr, builder) =>
                    {
                        //connStr — это входной параметр делегата
                        builder.UseSqlServer(connStr);
                    });
                    op.UseShardingTransaction((connection, builder) =>
                    {
                        //connection — это входной параметр делегата
                        builder.UseSqlServer(connection);
                    });
                    //используйте строку подключения к базе данных
                    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();
            }
            // Не обязательно, включает проверку отсутствующих таблиц и автоматическое создание
            app.ApplicationServices.UseAutoTryCompensateTable();
            // другие настройки....
        }
[Route("api/[controller]")]
public class ValuesController : Controller
{
        private readonly MyDbContext _myDbContext;
```        public ValuesController(MyDbContext myDbContext)
        {
            _myDbContext = myDbContext;
        }

        [HttpGet]
        public async Task<IActionResult> Get()
        {
            
            //_myDbContext.Add(order);
            //_myDbContext.Update(order);
            //_myDbContext.Remove(order);
            //_myDbContext.SaveChanges();
            var order = await _myDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Id == "2");
            return Ok(order);
        }
}

Высокая производительность

Тестирование

  • кэширования выражений
  • версия ShardingCore 3.1.63+
  • версия efcore 6.0
  • идентификатор заказа является строкой, шардинг мод (hashcode%5)
  • N означает количество выполнений

Пример тестирования

Потеря производительности SQL Server 2012, строк данных 7734363 = 773w

// * Summary *

BenchmarkDotNet=v0.13.1, OS=Windows Yöntem N Среднее Ошибка Стандартное отклонение
NoShardingIndexFirstOrDefaultAsync 10 1.512 ms 0.0071 ms 0.0063 ms
ShardingIndexFirstOrDefaultAsync 10 1.567 ms 0.0127 ms 0.0113 ms

При тестировании производительности запросов к нешардированной таблице, можно заметить, что разница в 10 запросах составляет 0.05 мс, что эквивалентно потере около 5 микросекунд или 0.005 мс на одном запросе, что составляет 3% от общего времени выполнения.Заключение: запросы с использованием efcore и ShardingCore для нешардированных объектов имеют производительность на уровне 97% от оригинальной, что свидетельствует о высокой производительности.

Тестирование производительности

SQL Server 2012, строк данных 7734363 = 773w

// * Summary *

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.18363.1500 (1909/November2019Update/19H2) AMD Ryzen 9 3900X, 1 CPU, 24 логических и 12 физических ядер .NET SDK=6.0.101 [Host] : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT DefaultJob : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT| Метод | N | Среднее | Ошибка | Стандартное отклонение | |------------------------------------- |--- |---------------:|-------------:|---------------------------:| | NoShardingIndexFirstOrDefaultAsync | 10 | 1.678 мс | 0.0323 мс | 0.0359 мс | | ShardingIndexFirstOrDefaultAsync | 10 | 2.005 мс | 0.0161 мс | 0.0143 мс | | NoShardingNoIndexFirstOrDefaultAsync | 10 | 495.933 мс | 9.4911 мс | 10.5494 мс | | ShardingNoIndexFirstOrDefaultAsync | 10 | 596.112 мс | 11.8907 мс | 13.2165 мс | | NoShardingNoIndexCountAsync | 10 | 477.537 мс | 1.4817 мс | 1.2373 мс | | ShardingNoIndexCountAsync | 10 | 594.833 мс | 7.4057 мс | 5.7819 мс | | NoShardingNoIndexLikeToListAsync | 10 | 665.277 мс | 1.3382 мс | 1.1174 мс | | ShardingNoIndexLikeToListAsync | 10 | 840.865 мс | 16.1917 мс | 17.3249 мс | | NoShardingNoIndexToListAsync | 10 | 480.368 мс | 1.3688 мс | 1.2134 мс | | ShardingNoIndexToListAsync | 10 | 604.850 мс | 8.6204 мс | 8.0635 мс |

MySQL 5.7, строк данных 7553790=755w, размер буфера innodb_buffer_pool_size=3G

// * Summary *

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.18363.1500 (1909/November2019Update/19H2) AMD Ryzen 9 3900X, 1 CPU, 24 логических и 12 физических ядер .NET SDK=6.0.101 [Host] : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT DefaultJob : .NET 6.0.1 (6.0.121.56705), X64 RyuJIT| Метод | N | Среднее | Ошибка | Стандартное отклонение | |------------------------------------- |--- |--------------:|------------:|------------:| | NoShardingIndexFirstOrDefaultAsync | 10 | 5.646 мс | 0.0164 мс | 0.0145 мс | | ShardingIndexFirstOrDefaultAsync | 10 | 5.679 мс | 0.0359 мс | 0.0319 мс | | NoShardingNoIndexFirstOrDefaultAsync | 10 | 5,212.736 мс | 230.0841 мс | 678.4080 мс | | ShardingNoIndexFirstOrDefaultAsync | 10 | 2,013.107 мс | 10.4256 мс | 9.2420 мс | | NoShardingNoIndexCountAsync | 10 | 9,483.988 мс | 42.0931 мс | 39.3739 мс | | ShardingNoIndexCountAsync | 10 | 2,029.698 мс | 12.4008 мс | 10.9929 мс | | NoShardingNoIndexLikeToListAsync | 10 | 10,569.283 мс | 20.9163 мс | 16.3301 мс | | ShardingNoIndexLikeToListAsync | 10 | 2,208.804 мс | 11.0483 мс | 10.3346 мс | | NoShardingNoIndexToListAsync | 10 | 9,485.263 мс | 21.2558 мс | 17.7496 мс | | ShardingNoIndexToListAsync | 10 | 2,012.086 мс | 39.2986 мс | 45.2563 мс |Измерение времени выполнения запросов показывает, что разница между первыми двумя запросами составляет около 0,04 миллисекунды. В случае с SQL Server, данные показывают, что разница между использованием и неиспользованием шардинга минимальна, что указывает на то, что база данных не является ограничивающим фактором при работе с набором данных из 770 000 строк. В случае с MySQL, если запросы включают полное сканирование таблицы без индексов, то разница в производительности между использованием и неиспользованием шардинга может быть значительной. В тестах эта разница составляет около 5–6 раз.

ВведениеВсе приведенные ниже примеры основаны на SQL Server и демонстрируют код для разделения таблиц. Если вам требуется разделение баз данных, вы можете обратиться к Sample.SqlServerShardingDataSource. Это справедливо и для других баз данных.

ВведениеКраткое введение в этот пакет. Все версии этого пакета названы в соответствии с версией EF Core, где второй номер версии указывает на поддержку только разделения баз данных (если это число 2), или на поддержку разделения как баз данных, так и таблиц (если это число 3 или выше). Пакет разделен на два основных ветвления: ветку main и ветку shardingTableOnly. Этот пакет поддерживает полностью настраиваемое маршрутизирование для разделения баз данных, что подходит для 95% бизнес-потребностей. Для разделения таблиц поддерживаются следующие параметры: x+y+z, где x — это фиксированное имя таблицы, y — это связь между фиксированным именем таблицы и фиксированным префиксом (может быть пустым), z — это префикс таблицы, который можно настраивать по своему усмотрению в соответствии с бизнес-логикой. Например: user_0, user_1 или user202101, user202102. . . Этот пакет также подходит для изоляции в многоарендном режиме. Поддерживается множество запросов, включая join, group by, max, count, min, avg, sum и другие. Использование этого пакета очень простое и сводится к расширению IQueryable. Чтобы обеспечить чистоту и независимость этого пакета, автоматическое создание таблиц можно реализовать с помощью задачи планировщика. Для этого пакет предоставляет IShardingTableCreator как зависимость для создания таблиц.Если вам это нужно, вы можете обратиться к автоматическому созданию таблиц по дате для примера, который предназначен для динамического добавления разделения баз данных.## Основные понятияНесколько основных концепций этого пакета:### Концепция шардинга по источникам данных

  • [DataSourceName] Имя источника данных используется для маршрутизации объектов к конкретному источнику данных
  • [IVirtualDataSource] Виртуальный источник данных IVirtualDataSource
  • [IVirtualDataSourceRoute] Маршрутизация источников данных IVirtualDataSourceRoute

Концепция шардинга по таблицам

  • [Tail] Хвост, конец физической таблицы
  • [TailPrefix] Префикс хвоста, символы между префиксом хвоста и физической таблицей
  • [Физическая таблица] Как следует из названия, это фактическая таблица в базе данных, имя таблицы (tablename + tailprefix + tail) IPhysicTable
  • [Виртуальная таблица] Виртуальная таблица представляет собой абстракцию всех физических таблиц в системе, соответствует одной таблице в программе IVirtualTable
  • [Виртуальный маршрут] Виртуальный маршрут является промежуточным средством между виртуальной и физической таблицами. Виртуальная таблица существует в программе в единственном экземпляре, поэтому программа должна знать, какую таблицу в системе она должна запросить.Самый простой способ — через маршрут, соответствующий виртуальной таблице IVirtualTableRoute, поскольку большинство маршрутов связаны с бизнес-логикой, виртуальный маршрут должен быть реализован пользователем, а этот фреймворк предоставляет более высокий уровень абстракции## Преимущества- [Поддержка кастомного шардинга по базам данных]
  • [Поддержка разделения чтения и записи]
  • [Поддержка высокопроизводительного пагинации]
  • [Поддержка ручного маршрутизации]
  • [Поддержка массовых операций]
  • [Поддержка кастомного шардинга по таблицам]
  • [Поддержка произвольных ключей шардинга]
  • [Нулевой порог вхождения для dbcontext]
  • [Поддержка соединений между таблицами в рамках шардинга] join,group by,max,count,min,avg,sum
  • [Поддержка использования для пакетной обработки] EFCore.BulkExtensions ...поддержка расширений для EFCore
  • [Предоставление множества кастомных правил маршрутизации] по времени, по модулю, можно настроить
  • [Оптимизация для пагинации] поддержка низкого потребления памяти при больших объемах пагинации, высокопроизводительное пагинация

Недостатки

  • [Увеличение количества соединений] при соединении между таблицами шардинга, если условия не могут быть индексированы до конкретной таблицы, будет сгенерирован произведение декартовых множеств что приводит к взрывному росту соединений, в будущем будет добавлена конфигурация для решения этой проблемы

Установка

<PackageReference Include="ShardingCore" Version="5.LastVersion" />
или
<PackageReference Include="ShardingCore" Version="3.LastVersion" />
или
<PackageReference Include="ShardingCore" Version="2.LastVersion" />

Начало

ШардингВ качестве примера мы будем использовать пользователей с распределением по модулю, конфигурируем entity, рекомендуется использовать fluent api

    public class SysUserMod
    {
        /// <summary>
        /// Идентификатор пользователя для шардинга
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// Имя пользователя
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Возраст пользователя
        /// </summary>
        public int Age { get; set; }
    }
    

Создаем виртуальный маршрут реализуем AbstractShardingOperatorVirtualTableRoute<T, TKey> абстракцию, или используем системные виртуальные маршрутизации по умолчанию предоставляются несколько простых маршрутизаций [по умолчанию](#по умолчанию)


    public class SysUserModVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualRoute<SysUserMod>
    {
        //2 хвост длиной:00,01,02......99
        //3 хэш % 3: [0,1,2]
        public SysUserModVirtualTableRoute() : base(2, 3)
        {
        }
        public override void Configure(EntityMetadataTableBuilder<SysUserMod> builder)
        {
            builder.ShardingProperty(o => o.Id);
        }
    }

Если вы используете шардинг, необходимо создать контекст, наследуя от IShardingTableDbContext интерфейса, необходимо реализовать IShardingDbContext, по умолчанию предоставляется AbstractShardingDbContext

  //DefaultShardingDbContext — это фактический исполняемый dbcontext
    public class DefaultShardingDbContext : AbstractShardingDbContext, IShardingTableDbContext
    {
        public DefaultShardingDbContext(DbContextOptions<DefaultShardingDbContext> options) : base(options)
        {
        }
    }
``````c#

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.ApplyConfiguration(new SysUserModMap());
        }
        public IRouteTail RouteTail { get; set; }

    }

Startup.cs в методе ConfigureServices(IServiceCollection services)


        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            //если вы хотите использовать операции без шардинга
            //services.AddDbContext<DefaultTableDbContext>(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDB;Integrated Security=True"));

    //добавить поддержку шардинг контекста для жизненного цикла
                
        services.AddShardingDbContext<DefaultShardingDbContext>(
                   (conStr, builder) => builder.UseSqlServer(conStr)
                )
                .Begin(o =>
                {
                    o.CreateShardingTableOnStart = true; //создать шардинг таблицу
                    o.EnsureCreatedWithOutShardingTable = true; //создать источник данных без шардинг таблицы
                })  .AddShardingTransaction((connection, builder) =>
                    builder.UseSqlServer(connection))
                .AddDefaultDataSource("ds0", "Data Source=localhost;Initial Catalog=ShardingCoreDB1;Integrated Security=True;")
                .AddShardingTableRoute(o =>
                {
                    o.AddShardingTableRoute<SysUserModVirtualTableRoute>();
                }).End();

Startup.cs в методе Configure(IApplicationBuilder app, IWebHostEnvironment env)


            var shardingBootstrapper = app.ApplicationServices.GetRequiredService<IShardingBootstrapper>();
            shardingBootstrapper.Start();

Как использовать

    
        private readonly DefaultShardingDbContext _defaultShardingDbContext;
``````markdown
## Распределение баз данных

```c#
```Для примера возьмем модуль пользователя. Рекомендуется использовать [fluent api](https://docs.microsoft.com/en-us/ef/core/modeling/) для конфигурации entity.
Объекты баз данных `IShardingDataSource` должны наследовать этот интерфейс.
Поле распределения баз данных `ShardingDataSourceKey` должно использовать этот атрибут.
```c#
    public class SysUserMod
    {
        /// <summary>
        /// Идентификатор пользователя для распределения по базам данных
        /// </summary>
        public string Id { get; set; }
        /// <summary>
        /// Имя пользователя
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Возраст пользователя
        /// </summary>
        public int Age { get; set; }
    }
    

Создать виртуальную маршрут Реализовать AbstractShardingOperatorVirtualTableRoute<T, TKey> Абстракция или реализация системного по умолчанию виртуального маршрута Фреймворк по умолчанию предоставляет несколько простых маршрутов по умолчанию

``````markdown
Если вы используете разделение баз данных, вам не потребуется интерфейс `IShardingTableDbContext`.
Для создания контекста баз данных с разделением таблиц необходимо наследовать от `AbstractShardingDbContext`.


```c#
// DefaultShardingDbContext является фактическим контекстом баз данных
public class DefaultShardingDbContext : AbstractShardingDbContext
{
    public DefaultShardingDbContext(DbContextOptions<DefaultShardingDbContext> options) : base(options)
    {
    }
}
``````c#
}

Startup.cs файл содержит метод ConfigureServices(IServiceCollection services):

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    // добавляем поддержку ShardingDbContext для жизненного цикла
    services.AddShardingDbContext<DefaultShardingDbContext>(
        (conStr, builder) => builder.UseSqlServer(conStr)
    ).Begin(o =>
    {
        o.CreateShardingTableOnStart = true;
        o.EnsureCreatedWithOutShardingTable = true;
    })
    .AddShardingTransaction((connection, builder) =>
        builder.UseSqlServer(connection))
    .AddDefaultDataSource("ds0", "Data Source=localhost;Initial Catalog=ShardingCoreDBxx0;Integrated Security=True;")
    .AddShardingDataSource(sp =>
    {
        return new Dictionary<string, string>()
        {
            {"ds1", "Data Source=localhost;Initial Catalog=ShardingCoreDBxx1;Integrated Security=True;"},
            {"ds2", "Data Source=localhost;Initial Catalog=ShardingCoreDBxx2;Integrated Security=True;"},
        };
    }).AddShardingDataSourceRoute(o =>
    {
        o.AddShardingDatabaseRoute<SysUserModVirtualDataSourceRoute>();
    }).End();

Startup.cs файл также содержит метод Configure(IApplicationBuilder app, IWebHostEnvironment env):

var shardingBootstrapper = app.ApplicationServices.GetRequiredService<IShardingBootstrapper>();
shardingBootstrapper.Start();

Использование:

private readonly DefaultShardingDbContext _defaultShardingDbContext;
``````csharp
        public async Task Insert_1000()
        {
            if (!_defaultShardingDbContext.Set<SysUserMod>().Any())
            {
                var ids = Enumerable.Range(1, 1000);
                var userMods = new List<SysUserMod>();
                foreach (var id in ids)
                {
                    userMods.Add(new SysUserMod()
                    {
                        Id = id.ToString(),
                        Age = id,
                        Name = $"имя_{id}",
                        AgeGroup = Math.Abs(id % 10)
                    });
                }
        ``````markdown
                    _defaultShardingDbContext.AddRange(userMods);                   await _defaultShardingDbContext.SaveChangesAsync();
                }
        }
        public async Task ToList_All()
        {
            
            var mods = await _defaultShardingDbContext.Set<SysUserMod>().ToListAsync();
            Assert.Equal(1000, mods.Count);

            var modOrders1 = await _defaultShardingDbContext.Set<SysUserMod>().OrderBy(o => o.Age).ToListAsync();
            int ascAge = 1;
            foreach (var sysUserMod in modOrders1)
            {
                Assert.Equal(ascAge, sysUserMod.Age);
                ascAge++;
            }

            var modOrders2 = await _defaultShardingDbContext.Set<SysUserMod>().OrderByDescending(o => o.Age).ToListAsync();
            int descAge = 1000;
            foreach (var sysUserMod in modOrders2)
            {
                Assert.Equal(descAge, sysUserMod.Age);
                descAge--;
            }
        }

Дополнительные операции можно найти в юнит-тестах

API

Метод Описание Юнит-тест
Получить коллекцию ToListAsync да
Первый элемент FirstOrDefaultAsync да
Максимальное значение MaxAsync да
Минимальное значение MinAsync да
Существование AnyAsync да
Количество CountAsync да
Количество LongCountAsync да
Сумма SumAsync да
Среднее значение AverageAsync да
Включение ContainsAsync да
Группировка GroupByAsync да
Разделение по базам данных предоставляет маршрут по умолчанию, а разделение по таблицам требует реализации самостоятельно. Конкретная реализация может быть найдена в разделении по базам данных. Абстрактный | Правила маршрутизации | хвост | индекс
--- |--- |--- |---
AbstractSimpleShardingModKeyIntVirtualTableRoute | по модулю | 0, 1, 2... | `=, contains`
AbstractSimpleShardingModKeyStringVirtualTableRoute | по модулю | 0, 1, 2... | `=, contains`
AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute | по времени | yyyyMMdd | `>, >=, <, <=, =, contains`
AbstractSimpleShardingDayKeyLongVirtualTableRoute | по времени (timestamp) | yyyyMMdd | `>, >=, <, <=, =, contains`
AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute | по времени | yyyyMMdd_dd | `>, >=, <, <=, =, contains`
AbstractSimpleShardingWeekKeyLongVirtualTableRoute | по времени (timestamp) | yyyyMMdd_dd | `>, >=, <, <=, =, contains`
AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute | по времени | yyyyMM | `>, >=, <, <=, =, contains`
AbstractSimpleShardingMonthKeyLongVirtualTableRoute | по времени (timestamp) | yyyyMM | `>, >=, <, <=, =, contains`
AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute | по времени | yyyy | `>, >=, <, <=, =, contains`
AbstractSimpleShardingYearKeyLongVirtualTableRoute | по времени (timestamp) | yyyy | `>, >=, <, <=, =, contains`

Примечание: `contains` указывает на `o => ids.contains(o.shardingkey)`

Примечание: Использование по умолчанию правила маршрутизации по времени разделения таблиц будет требовать переопределения метода `GetBeginTime`. Этот метод должен использовать статическое значение, например: `new DateTime(2021, 1, 1)`, динамическое значение, например `DateTime.Now`, использовать нельзя, так как при каждом перезапуске будет вызван этот метод, что приведет к несоответствиям.# Продвинутый

## code-first
В настоящее время `sharding-core` поддерживает code-first, подробная реализация можно посмотреть в [Migrations](https://github.com/xuejmnet/sharding-core/tree/main/samples/Sample.Migrations/readme.md)

## Автоматическое отслеживание
По умолчанию `shardingcore` не поддерживает автоматическое отслеживание, и его не рекомендуется использовать. Если вам это необходимо, `shardingcore` по умолчанию предоставляет функцию автоматического отслеживания.
Важно отметить следующие моменты:
1. В настоящее время поддерживается только объект с одним первичным ключом.
2. `shardingcore` поддерживает только модели `dbcontext`, анонимные типы запросов не поддерживаются.
3. Одиночные запросы `shardingcore` выполняются напрямую в базе данных, а не в кэше. Если результат запроса уже находится в кэше, он будет возвращен из кэша, а не из базы данных.
4. Операции, такие как `ToList`, выполняют запрос к базе данных и возвращают результат. Если результат уже отслеживается, он будет возвращен из кэша.
5. Поддерживается `first`, `firstordefault`, `last`, `lastordefault`, `single`, `singleordefault`.
Как включить:
```c#
services.AddShardingDbContext<DefaultShardingDbContext>(.......)
            .Begin(o => {
                    o.CreateShardingTableOnStart = true;
                    o.EnsureCreatedWithOutShardingTable = true;
                    //поддержка автоматического отслеживания asnotracking astracking QueryTrackingBehavior.TrackAll
                    o.AutoTrackEntity = true; 
                })

Ручное маршрутизирование

ctor inject IShardingRouteManager shardingRouteManager
``````markdown
## Автоматическое создание таблиц
[Ссылка](https://github.com/xuejmnet/sharding-core/tree/main/samples/Samples.AutoByDate.SqlServer)

## Транзакции
1. По умолчанию поддерживается транзакция с помощью `SaveChangesAsync`
```c#
await _defaultShardingDbContext.SaveChangesAsync();
  1. Ручное открытие транзакции см. документацию Microsoft
using (var tran = _defaultTableDbContext.DataBase.BeginTransaction())
{
    ........
    _defaultTableDbContext.SaveChanges();
    tran.Commit();
}

Батч-операции

Батч-операции разделяют соответствующий DbContext и данные, позволяя пользователю самостоятельно выбрать сторонний фреймворк, например, Z.EntityFramework.Plus.EFCore для выполнения батч-операций или EFCore.BulkExtensions, поддерживающий все сторонние батч-фреймворки.

var list = new List<SysUserMod>();
var dbContexts = _defaultTableDbContext.BulkShardingEnumerable(list);
``````csharp
foreach (var dataSourceMap in dbContexts)
{
    foreach (var tailMap in dataSourceMap.Value)
    {
        tailMap.Key.BulkInsert(tailMap.Value.ToList());
        //tailMap.Key.BulkDelete(tailMap.Value.ToList());
        //tailMap.Key.BulkUpdate(tailMap.Value.ToList());
    }
}
_defaultTableDbContext.SaveChanges();
//или
var dbContexts = _defaultTableDbContext.BulkShardingEnumerable(list);
using (var tran = _defaultTableDbContext.Database.BeginTransaction())
{
    foreach (var dataSourceMap in dbContexts)
    {
        foreach (var tailMap in dataSourceMap.Value)
        {
            tailMap.Key.BulkInsert(tailMap.Value.ToList());
            //tailMap.Key.BulkDelete(tailMap.Value.ToList());
            //tailMap.Key.BulkUpdate(tailMap.Value.ToList());
        }
    }
    _defaultTableDbContext.SaveChanges();
    tran.Commit();
}

работа с кодом-first

Данная фреймворк поддерживает разделение на один основной и несколько дополнительных узлов для чтения и записи `AddReadWriteSeparation`. Поддерживает два типа стратегий разделения на чтение и запись: циклический `Loop` и случайный `Random`. Однако, при использовании нескольких соединений для чтения и записи могут возникнуть проблемы с несоответствием данных (например, при пагинации это происходит в два этапа: сначала получение количества записей, затем получение списка записей), что может привести к проблемам с количеством данных на последних страницах.

Для решения этой проблемы фреймворк реализовал стратегию получения соединений для чтения `ReadConnStringGetStrategyEnum.LatestEveryTime`, которая означает, что каждое соединение будет новым (что может привести к вышеупомянутым проблемам), и `ReadConnStringGetStrategyEnum.LatestFirstTime`, которая означает, что соединение будет получено один раз на уровне `dbcontext` (что не приведет к проблемам на уровне `dbcontext`).Также из-за различных проблем с сетью, связанных с разделением на чтение и запись, новые записи могут не быть доступны. Поэтому система по умолчанию добавляет флаг использования разделения на чтение и запись на уровне `dbcontext`. Если `false`, то по умолчанию используется строка для записи для чтения `_defaultTableDbContext.ReadWriteSeparation=false` или используются два готовых метода.
```c#
// Переключение на только чтение, только запись и только конфигурация для чтения A и B источников данных
_virtualDbContext.ReadWriteSeparationReadOnly();
_virtualDbContext.ReadWriteSeparationWriteOnly();
services.AddShardingDbContext<DefaultShardingDbContext>(
    (conStr, builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger)
).Begin(o =>
{
    o.CreateShardingTableOnStart = true;
    o.EnsureCreatedWithOutShardingTable = true;
})
.AddShardingTransaction((connection, builder) =>
    builder.UseSqlServer(connection).UseLoggerFactory(efLogger))
.AddDefaultDataSource("ds0",
    "Data Source=localhost;Initial Catalog=ShardingCoreDB1;Integrated Security=True;"
)
.AddShardingTableRoute(o =>
{
    o.AddShardingTableRoute<SysUserModVirtualTableRoute>();
})
.AddReadWriteSeparation(o =>
{
    return new Dictionary<string, ISet<string>>()
    {
        {
            "ds0", new HashSet<string>(){
            "Data Source=localhost;Initial Catalog=ShardingCoreDBReadOnly1;Integrated Security=True;",
            "Data Source=localhost;Initial Catalog=ShardingCoreDBReadOnly2;Integrated Security=True;"}
        }
    };
}, ReadStrategyEnum.Loop, defaultEnable: true)
.End();

Высокопроизводительная пагинацияsharding-core сам использует потоковое обработки для получения данных, и в обычных условиях разница с одиночной таблицей практически отсутствует. Однако при пагинации, пропускающей X страниц, производительность уменьшается в зависимости от увеличения X (O(X)).В настоящее время этот фреймворк реализует высокопроизводительную пагинацию, которая может быть настроена по предпочтениям пользователя для реализации функции пагинации.

Поддерживаемые версии x.2.0.16+1. Как включить конфигурацию пагинации, например, если мы хотим настроить пагинацию для таблицы с новыми пользователями за месяц, сначала реализуем интерфейс IPaginationConfiguration<>, который является интерфейсом конфигурации пагинации. c#markdown 2. Добавление конфигурации В соответствующем пользовательском маршруте добавьте конфигурацию [XXXXXXVirtualTableRoute]

         public override IPaginationConfiguration<SysUserSalary> CreatePaginationConfiguration()
         {
             return new SysUserSalaryPaginationConfiguration();
         }
  1. Что означает конфигурация внутри метода Configure?
  1. builder.PaginationSequence(o => o.Id) конфигурирует последовательность таблиц при использовании поля Id в качестве поля для сортировки. Правила сравнения маршрутов устанавливаются с помощью UseRouteCompare, где string представляет хвост таблицы или имя источника данных. Если, например, текущая страница пагинации состоит из трех таблиц (table1, table2, table3), и если Id не был конфигурирован, то потребуется запрос к каждой из трех таблиц и последующее объединение результатов. Если же Id был конфигурирован, и текущий SQL-запрос использует Id в качестве поля для сортировки, то запрос будет направлен только к первой таблице (table1). Если количество записей в table1 превышает количество записей, которые нужно пропустить, то время выполнения запроса значительно сокращается.Например, если table1 содержит 100 записей, table2 — 200, table3 — 300, и нужно пропустить 90 записей, чтобы получить 10 записей, то время выполнения запроса сокращается с O(100) до O(10).
  2. Что означает UseQueryMatch? Это правило, которое определяет, какие поля должны быть использованы для сортировки. Это могут быть поля текущего класса или просто поля с одинаковым именем. Например, если используется select new {} для создания анонимного объекта, то поля могут иметь разные типы. PrimaryMatch указывает, что требуется только первое поле для сортировки. orderby условие должно соответствовать, `UseAppendIfOrderNone` указывает, нужно ли добавлять сортировку по этому полю в случае отсутствия соответствующего условия сортировки, что гарантирует оптимальное производительство сортировки.3) builder.ConfigReverseShardingPage указывает, нужно ли включать обратную сортировку, так как прямая сортировка при большом значении skip может привести к необходимости пропускать слишком много данных, особенно на последних страницах. Если включить обратную сортировку, то последние страницы будут представлять собой обратную сортировку первых страниц. Первый параметр указывает коэффициент пропуска, то есть значение skip должно быть больше общего количества записей (total) умноженного на этот коэффициент (0-1). Второй параметр указывает минимальное количество записей (total), которое должно быть больше 500 для включения обратной сортировки, и приоритет обратной сортировки ниже, чем приоритет прямой сортировки.
  1. Как использовать
var shardingPageResultAsync = await _defaultTableDbContext.Set<SysUserMod>().OrderBy(o => o.Age).ToShardingPageAsync(pageIndex, pageSize);
```### Внимание: если вы сортируете по времени, рекомендуется включить сортировку по времени независимо от направления. Если вы используете модульное или пользовательское распределение по таблицам, рекомендуется использовать Id для прямой сортировки и добавить обратную сортировку для оптимизации производительности. Если entity поддерживает распределение по таблицам и базам данных, и оба маршрута поддерживают сортировку по одному и тому же полю, приоритет будет следующим: сначала распределение по базам данных, затем по таблицам.

## Кэширование выражений
Кэширование выражений может быть включено через маршрутизацию для кэширования выражений для отдельного tail. Поддерживает операторы =, >, >=, <, <=, equal.

```c#
    public class OrderCreateTimeVirtualTableRoute : AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<Order>
    {
        // включение кэширования выражений
        public override bool EnableRouteParseCompileCache => true;
    }

Для кэширования выражений можно переопределить методы родительского класса для реализации собственной логики, или реализовать методы для нескольких tail выражений AbstractShardingRouteParseCompileCacheVirtualTableRoute, AbstractShardingRouteParseCompileCacheVirtualDataSourceRoute.

    public virtual Func<string, bool> CachingCompile(Expression<Func<string, bool>> parseWhere)
    {
        if (EnableRouteParseCompileCache)
        {
            var doCachingCompile = DoCachingCompile(parseWhere);
            if (doCachingCompile != null)
                return doCachingCompile;
            doCachingCompile = CustomerCachingCompile(parseWhere);
            if (doCachingCompile != null)
```                    return doCachingCompile;
             }
             return parseWhere.Compile();
         }
         /// <summary>
         /// Системное по умолчанию постоянное кэширование одного выражения
         /// </summary>
         /// <param name="parseWhere"></param>
         /// <returns>Если возвращается null, то вызывается метод <see cref="CustomerCachingCompile"/>, если он возвращает null, то вызывается метод <see cref="Compile"/></returns>
         protected virtual Func<string, bool> DoCachingCompile(Expression<Func<string, bool>> parseWhere)
         {
             var shouldCache = ShouldCache(parseWhere);
             if (shouldCache)
                 return _routeCompileCaches.GetOrAdd(parseWhere, key => parseWhere.Compile());
             return null;
         }
         protected virtual Func<string, bool> CustomerCachingCompile(Expression<Func<string, bool>> parseWhere)
         {
             return null;
         }
 ```Включение кэширования выражений может улучшить производительность маршрутизации, уменьшив время компиляции с 0.14 мс до 0.013 мс, что составляет приблизительно 10 раз быстрее. # Важные замечания
При использовании данного фреймворка обратите внимание на два момента: если ваш `ShardingDbContext` переопределил следующие службы, они могут не работать. Если вы хотите использовать их, вам потребуется самостоятельно переопределить расширение [см. пример](https://github.com/xuejmnet/sharding-core/blob/main/src/ShardingCore/DIExtension.cs)
1. `ShardingDbContext`
```c#
return optionsBuilder.UseShardingWrapMark()
                 .ReplaceService<IDbSetSource, ShardingDbSetSource>()
                 .ReplaceService<IQueryCompiler, ShardingQueryCompiler>()
                 .ReplaceService<IDbContextTransactionManager, ShardingRelationalTransactionManager<TShardingDbContext>>()
                 .ReplaceService<IRelationalTransactionFactory, ShardingRelationalTransactionFactory<TShardingDbContext>>();
  1. DefaultDbContext
return optionsBuilder.ReplaceService<IModelCacheKeyFactory, ShardingModelCacheKeyFactory>()
                 .ReplaceService<IModelCustomizer, ShardingModelCustomizer<TShardingDbContext>>();

В настоящее время фреймворк использует AppDomain.CurrentDomain.GetAssemblies();, что может привести к тому, что некоторые сборки не будут загружены, поэтому рекомендуется загружать необходимые DLL на уровне API. При использовании следует учитывать:

  • Наследуют ли сущности для разделенных таблиц интерфейс IShardingTable?

  • Имеют ли сущности для разделенных таблиц поле ShardingKey?

  • Наследуют ли сущности для разделенных источников данных интерфейс IShardingDataSource?- Имеют ли сущности для разделённых источников данных поле ShardingDataSourceKey?

  • Наследуют ли сущности виртуальный маршрут?

  • Был ли виртуальный маршрут добавлен в startup?

  • Был ли метод bootstrapper.start() добавлен в startup?```c# // Поддержка окончательной модификации var sresult = _defaultTableDbContext.Set().ToList();

          var sysUserMod98 = result.FirstOrDefault(o => o.Id == "98");
          sysUserMod98.Name = "name_update" + new Random().Next(1, 99) + "_98";
          await _defaultTableDbContext.SaveChangesAsync();

-- Лог информации Выполненная DbCommand (1ms) [Параметры=[@p1='?' (Размер = 128), @p0='?' (Размер = 128)], CommandType='Text', CommandTimeout='30'] SET NOCOUNT ON; UPDATE [SysUserMod_02] SET [Name] = @p0 WHERE [Id] = @p1; SELECT @@ROWCOUNT;



# План
- [Предоставить официальный сайт, если проект будет успешным]
- [Разработать более полную документацию]
- [Переформатировать для поддержки других ORM .NET]

# Заключение
Этот фреймворк был создан, опираясь на идеи большинства компонентов для разделенных таблиц. В настоящее время все предоставляемые интерфейсы реализованы и поддерживают запросы к нескольким таблицам. Для запросов на основе пагинации фреймворк использует потоковое чтение, чтобы избежать переполнения памяти при пропуске больших объемов данных. В настоящее время это библиотека находится на ранней стадии разработки и имеет множество недоработок, поэтому просим прощения за возможные недостатки. Если вам понравился проект, пожалуйста, оставьте звезды.

Этот документ был создан ночью, и я надеюсь, что он будет полезен и привлечет больше внимания. Также я надеюсь, что он позволит людям общаться и обмениваться идеями.Этот фреймворк был создан с использованием отличного кода и идей из различных открытых экосистем .NET. Я надеюсь, что этот фреймворк сможет внести свой вклад в развитие экосистемы .NET. Я буду поддерживать этот проект на протяжении долгого времени. Если у вас есть предложения или идеи, пожалуйста, свяжитесь со мной по следующим контактам. Добро пожаловать к оставлению звезд!

# Донат
<img src="./imgs/zfb.jpg" title="JetBrains" width=200 />
<img src="./imgs/wx.jpg" title="JetBrains" width=222 />
[Блог](https://www.cnblogs.com/xuejiaming)

QQ-группа: 771630778

Личный QQ: 326308290 (добро пожаловать к технической поддержке и предоставлению ценных замечаний)

Личная почта: 326308290@qq.com

Комментарии ( 0 )

Вы можете оставить комментарий после Вход в систему

Введение

Высокопроизводительное легковесное решение для шардинга таблиц и баз данных в EF-Core, поддержка разделения на чтение и запись. Это решение не требует зависимостей, имеет нулевую стоимость обучения и не вторгается в бизнес-код. Развернуть Свернуть
Apache-2.0
Отмена

Обновления (99)

все

Участники

все

Язык

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/xuejm-sharding-core.git
git@api.gitlife.ru:oschina-mirror/xuejm-sharding-core.git
oschina-mirror
xuejm-sharding-core
xuejm-sharding-core
main