HaniBlindBox/docs/数据库迁移计划.md
2026-01-02 01:00:52 +08:00

24 KiB
Raw Blame History

数据库迁移计划MySQL 5.7 → SQL Server 2022

迁移概述

🎯 迁移目标

  • 平台升级: MySQL 5.7.44 → SQL Server 2022
  • 性能提升: 查询性能提升50-80%
  • 生态集成: 与.NET 8完美集成
  • 现代化: 利用SQL Server 2022企业级特性

🏆 选择SQL Server的理由

  1. 与.NET生态完美集成: EF Core原生支持最佳
  2. 企业级性能: 查询优化器、内存管理更强
  3. 并发控制: 更精细的锁机制,适合抽奖场景
  4. 开发效率: Visual Studio集成调试更方便
  5. 长期支持: Microsoft官方支持到2033年

📊 当前数据库状况

  • 数据规模: 2,202用户503商品15订单
  • 表数量: 82个表
  • 数据大小: 约50MB
  • 存储引擎: InnoDB + MyISAM混用
  • 字符集: utf8/utf8mb4/gbk混用
  • 目标平台: SQL Server 2022 Developer Edition

迁移策略选择

🎯 推荐方案:平台迁移 + 结构优化

优势:

  • 性能大幅提升50-80%
  • 与.NET 8完美集成
  • 企业级特性支持
  • 更好的开发体验

考虑因素:

  • ⚠️ 需要重新设计表结构
  • ⚠️ 数据类型映射调整
  • ⚠️ 团队学习成本

详细迁移步骤

阶段1环境准备 (0.5天)

1.1 SQL Server 2022环境配置

-- 创建数据库
CREATE DATABASE [HoneyBox]
ON (
    NAME = 'HoneyBox_Data',
    FILENAME = 'C:\Data\HoneyBox.mdf',
    SIZE = 100MB,
    MAXSIZE = 10GB,
    FILEGROWTH = 10MB
)
LOG ON (
    NAME = 'HoneyBox_Log',
    FILENAME = 'C:\Data\HoneyBox.ldf',
    SIZE = 10MB,
    MAXSIZE = 1GB,
    FILEGROWTH = 10%
);

-- 设置数据库选项
ALTER DATABASE [HoneyBox] SET RECOVERY SIMPLE;
ALTER DATABASE [HoneyBox] SET AUTO_CREATE_STATISTICS ON;
ALTER DATABASE [HoneyBox] SET AUTO_UPDATE_STATISTICS ON;
ALTER DATABASE [HoneyBox] SET PAGE_VERIFY CHECKSUM;

1.2 备份当前MySQL数据

# 导出MySQL数据为CSV格式
mysqldump -h localhost -u root -p \
  --tab=/tmp/mysql_export \
  --fields-terminated-by=',' \
  --fields-enclosed-by='"' \
  --lines-terminated-by='\n' \
  youdas

# 或使用MySQL Workbench导出向导

阶段2表结构设计 (1天)

2.1 用户表重设计

-- 用户主表
CREATE TABLE [Users] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [OpenId] NVARCHAR(50) NOT NULL,
    [UnionId] NVARCHAR(255) NULL,
    [GzhOpenId] NVARCHAR(255) NULL,
    [Mobile] NVARCHAR(15) NULL,
    [Nickname] NVARCHAR(255) NOT NULL,
    [HeadImg] NVARCHAR(500) NOT NULL,
    [Pid] INT NOT NULL DEFAULT 0,
    [Money] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [Money2] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [Integral] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [Score] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [OuQi] INT NOT NULL DEFAULT 0,
    [OuQiLevel] INT NOT NULL DEFAULT 0,
    [Vip] TINYINT NOT NULL DEFAULT 1,
    [Status] TINYINT NOT NULL DEFAULT 1,
    [IsTest] INT NOT NULL DEFAULT 0,
    [Uid] NVARCHAR(16) NOT NULL,
    [ClickId] INT NULL,
    [AddTime] DATETIME2 NOT NULL DEFAULT GETDATE(),
    [UpdateTime] DATETIME2 NOT NULL DEFAULT GETDATE(),
    [LastLoginTime] DATETIME2 NULL,
    
    -- 索引
    INDEX IX_Users_OpenId (OpenId),
    INDEX IX_Users_Mobile (Mobile),
    INDEX IX_Users_UnionId (UnionId),
    INDEX IX_Users_Uid (Uid),
    INDEX IX_Users_Status_AddTime (Status, AddTime),
    
    -- 约束
    CONSTRAINT UK_Users_OpenId UNIQUE (OpenId),
    CONSTRAINT UK_Users_Uid UNIQUE (Uid)
);

2.2 商品表重设计

-- 商品主表
CREATE TABLE [Goods] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [CategoryId] INT NOT NULL DEFAULT 0,
    [Title] NVARCHAR(255) NOT NULL,
    [ImgUrl] NVARCHAR(500) NOT NULL,
    [ImgUrlDetail] NVARCHAR(500) NOT NULL,
    [Price] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [Stock] INT NOT NULL DEFAULT 0,
    [SaleStock] INT NOT NULL DEFAULT 0,
    [Type] TINYINT NOT NULL DEFAULT 0,
    [Status] TINYINT NOT NULL DEFAULT 1,
    [LockIs] TINYINT NOT NULL DEFAULT 0,
    [LockTime] INT NOT NULL DEFAULT 0,
    [IsShouZhe] TINYINT NOT NULL DEFAULT 0,
    [NewIs] TINYINT NOT NULL DEFAULT 0,
    [ShowIs] TINYINT NOT NULL DEFAULT 0,
    [UnlockAmount] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [DailyXiangou] INT NOT NULL DEFAULT 0,
    [QuanjuXiangou] INT NOT NULL DEFAULT 0,
    [Sort] INT NOT NULL DEFAULT 1,
    [SaleTime] DATETIME2 NULL,
    [AddTime] DATETIME2 NOT NULL DEFAULT GETDATE(),
    [UpdateTime] DATETIME2 NOT NULL DEFAULT GETDATE(),
    [DeleteTime] DATETIME2 NULL,
    
    -- 索引
    INDEX IX_Goods_Type_Status (Type, Status),
    INDEX IX_Goods_Status_Sort (Status, Sort DESC),
    INDEX IX_Goods_CategoryId (CategoryId),
    INDEX IX_Goods_AddTime (AddTime)
);

2.3 订单表重设计

-- 订单主表
CREATE TABLE [Orders] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [OrderNo] NVARCHAR(60) NOT NULL,
    [UserId] INT NOT NULL,
    [GoodsId] INT NOT NULL,
    [GoodsNum] INT NOT NULL,
    [GoodsTitle] NVARCHAR(255) NOT NULL,
    [GoodsImgUrl] NVARCHAR(500) NULL,
    [OrderType] TINYINT NOT NULL DEFAULT 0,
    [OrderTotal] DECIMAL(18,2) NOT NULL,
    [OrderZheTotal] DECIMAL(18,2) NOT NULL,
    [Price] DECIMAL(18,2) NOT NULL,
    [UseMoney] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [UseIntegral] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [UseMoney2] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [CouponId] INT NULL DEFAULT 0,
    [UseCoupon] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [PrizeNum] INT NOT NULL DEFAULT 0,
    [PayType] TINYINT NOT NULL DEFAULT 1,
    [Status] TINYINT NOT NULL DEFAULT 0,
    [AddTime] DATETIME2 NOT NULL DEFAULT GETDATE(),
    [PayTime] DATETIME2 NULL,
    [ClickId] INT NULL,
    
    -- 索引
    INDEX IX_Orders_UserId_Status (UserId, Status),
    INDEX IX_Orders_GoodsId_Num (GoodsId, GoodsNum),
    INDEX IX_Orders_OrderNo (OrderNo),
    INDEX IX_Orders_AddTime (AddTime),
    INDEX IX_Orders_PayTime (PayTime),
    
    -- 约束
    CONSTRAINT UK_Orders_OrderNo UNIQUE (OrderNo),
    CONSTRAINT FK_Orders_Users FOREIGN KEY (UserId) REFERENCES Users(Id),
    CONSTRAINT FK_Orders_Goods FOREIGN KEY (GoodsId) REFERENCES Goods(Id)
);

2.4 订单详情表

-- 订单详情表
CREATE TABLE [OrderDetails] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [OrderId] NVARCHAR(60) NOT NULL,
    [UserId] INT NOT NULL,
    [GoodsId] INT NOT NULL,
    [GoodsNum] INT NOT NULL,
    [GoodsListId] INT NOT NULL,
    [GoodsListTitle] NVARCHAR(255) NOT NULL,
    [GoodsListImgUrl] NVARCHAR(500) NULL,
    [ShangId] INT NOT NULL,
    [OrderType] TINYINT NOT NULL,
    [Source] TINYINT NOT NULL DEFAULT 1,
    [Price] DECIMAL(18,2) NOT NULL DEFAULT 0,
    [AddTime] DATETIME2 NOT NULL DEFAULT GETDATE(),
    
    -- 索引
    INDEX IX_OrderDetails_OrderId (OrderId),
    INDEX IX_OrderDetails_UserId (UserId),
    INDEX IX_OrderDetails_GoodsId_Num (GoodsId, GoodsNum),
    INDEX IX_OrderDetails_ShangId (ShangId),
    INDEX IX_OrderDetails_AddTime (AddTime)
);

阶段3数据迁移 (1天)

3.1 数据迁移脚本

-- 用户数据迁移
INSERT INTO [Users] (
    OpenId, UnionId, GzhOpenId, Mobile, Nickname, HeadImg, 
    Pid, Money, Money2, Integral, Score, OuQi, OuQiLevel, 
    Vip, Status, IsTest, Uid, ClickId, AddTime, UpdateTime, LastLoginTime
)
SELECT 
    openid,
    unionid,
    gzh_openid,
    mobile,
    nickname,
    headimg,
    pid,
    money,
    money2,
    integral,
    score,
    ou_qi,
    ou_qi_level,
    vip,
    status,
    istest,
    ISNULL(uid, CAST(id AS NVARCHAR(16))),
    click_id,
    DATEADD(SECOND, addtime, '1970-01-01'),
    DATEADD(SECOND, update_time, '1970-01-01'),
    CASE WHEN last_login_time > 0 
         THEN DATEADD(SECOND, last_login_time, '1970-01-01') 
         ELSE NULL END
FROM mysql_user_temp; -- 临时导入表

-- 商品数据迁移
INSERT INTO [Goods] (
    CategoryId, Title, ImgUrl, ImgUrlDetail, Price, Stock, SaleStock,
    Type, Status, LockIs, LockTime, IsShouZhe, NewIs, ShowIs,
    UnlockAmount, DailyXiangou, QuanjuXiangou, Sort, SaleTime,
    AddTime, UpdateTime
)
SELECT 
    category_id,
    title,
    imgurl,
    imgurl_detail,
    price,
    stock,
    sale_stock,
    type,
    status,
    lock_is,
    lock_time,
    is_shou_zhe,
    new_is,
    show_is,
    unlock_amount,
    daily_xiangou,
    quanju_xiangou,
    sort,
    CASE WHEN sale_time > 0 
         THEN DATEADD(SECOND, sale_time, '1970-01-01') 
         ELSE NULL END,
    DATEADD(SECOND, addtime, '1970-01-01'),
    DATEADD(SECOND, update_time, '1970-01-01')
FROM mysql_goods_temp;

-- 订单数据迁移
INSERT INTO [Orders] (
    OrderNo, UserId, GoodsId, GoodsNum, GoodsTitle, GoodsImgUrl,
    OrderType, OrderTotal, OrderZheTotal, Price, UseMoney, UseIntegral,
    UseMoney2, CouponId, UseCoupon, PrizeNum, PayType, Status,
    AddTime, PayTime, ClickId
)
SELECT 
    order_num,
    user_id,
    goods_id,
    num,
    goods_title,
    goods_imgurl,
    order_type,
    order_total,
    order_zhe_total,
    price,
    use_money,
    use_integral,
    use_money2,
    coupon_id,
    use_coupon,
    prize_num,
    pay_type,
    status,
    DATEADD(SECOND, addtime, '1970-01-01'),
    CASE WHEN pay_time > 0 
         THEN DATEADD(SECOND, pay_time, '1970-01-01') 
         ELSE NULL END,
    click_id
FROM mysql_order_temp;

3.2 数据验证

-- 验证数据完整性
SELECT 
    'Users' as TableName,
    COUNT(*) as RecordCount,
    MAX(Id) as MaxId
FROM Users
UNION ALL
SELECT 
    'Goods' as TableName,
    COUNT(*) as RecordCount,
    MAX(Id) as MaxId
FROM Goods
UNION ALL
SELECT 
    'Orders' as TableName,
    COUNT(*) as RecordCount,
    MAX(Id) as MaxId
FROM Orders;

-- 验证关键业务数据
SELECT 
    SUM(Money) as TotalUserMoney,
    SUM(Integral) as TotalIntegral,
    COUNT(CASE WHEN Status = 1 THEN 1 END) as ActiveUsers
FROM Users;

SELECT 
    SUM(OrderTotal) as TotalOrderAmount,
    COUNT(CASE WHEN Status = 1 THEN 1 END) as PaidOrders
FROM Orders;

SQL Server 2022 新特性利用

🚀 性能优化特性

1. 智能查询处理

-- 自适应连接
SELECT 
    u.Nickname,
    COUNT(o.Id) as OrderCount,
    SUM(o.OrderTotal) as TotalAmount
FROM Users u
LEFT JOIN Orders o ON u.Id = o.UserId AND o.Status = 1
GROUP BY u.Id, u.Nickname
HAVING COUNT(o.Id) > 10; -- SQL Server会自动选择最优连接算法

-- 批处理模式
SELECT 
    GoodsId,
    COUNT(*) as DrawCount,
    AVG(CAST(Price AS FLOAT)) as AvgPrice
FROM OrderDetails
WHERE AddTime >= DATEADD(DAY, -30, GETDATE())
GROUP BY GoodsId
ORDER BY DrawCount DESC;

2. 内存优化表(抽奖高并发场景)

-- 创建内存优化表用于抽奖库存控制
CREATE TABLE [GoodsInventory] (
    [GoodsId] INT NOT NULL,
    [GoodsNum] INT NOT NULL,
    [SurplusStock] INT NOT NULL,
    [LastUpdateTime] DATETIME2 NOT NULL,
    
    CONSTRAINT PK_GoodsInventory PRIMARY KEY NONCLUSTERED (GoodsId, GoodsNum)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);

-- 内存优化存储过程
CREATE PROCEDURE [dbo].[DeductInventory]
    @GoodsId INT,
    @GoodsNum INT,
    @Quantity INT = 1
WITH NATIVE_COMPILATION, SCHEMABINDING
AS BEGIN ATOMIC WITH (
    TRANSACTION ISOLATION LEVEL = SNAPSHOT,
    LANGUAGE = N'us_english'
)
    DECLARE @CurrentStock INT;
    
    SELECT @CurrentStock = SurplusStock 
    FROM dbo.GoodsInventory 
    WHERE GoodsId = @GoodsId AND GoodsNum = @GoodsNum;
    
    IF @CurrentStock >= @Quantity
    BEGIN
        UPDATE dbo.GoodsInventory 
        SET SurplusStock = SurplusStock - @Quantity,
            LastUpdateTime = GETDATE()
        WHERE GoodsId = @GoodsId AND GoodsNum = @GoodsNum;
        
        RETURN 1; -- 成功
    END
    
    RETURN 0; -- 库存不足
END

3. 列存储索引(分析查询优化)

-- 为订单分析创建列存储索引
CREATE NONCLUSTERED COLUMNSTORE INDEX IX_Orders_Analytics
ON Orders (UserId, GoodsId, OrderTotal, AddTime, Status);

-- 高性能分析查询
SELECT 
    YEAR(AddTime) as Year,
    MONTH(AddTime) as Month,
    COUNT(*) as OrderCount,
    SUM(OrderTotal) as Revenue,
    COUNT(DISTINCT UserId) as UniqueUsers
FROM Orders
WHERE Status = 1
GROUP BY YEAR(AddTime), MONTH(AddTime)
ORDER BY Year DESC, Month DESC;

4. JSON支持增强

-- 商品配置JSON化
ALTER TABLE Goods ADD ConfigJson NVARCHAR(MAX);

-- 更新配置为JSON格式
UPDATE Goods SET ConfigJson = JSON_OBJECT(
    'lock_is', LockIs,
    'lock_time', LockTime,
    'daily_xiangou', DailyXiangou,
    'quanju_xiangou', QuanjuXiangou,
    'unlock_amount', UnlockAmount
);

-- JSON查询和索引
CREATE INDEX IX_Goods_ConfigJson_LockIs 
ON Goods (CAST(JSON_VALUE(ConfigJson, '$.lock_is') AS BIT));

SELECT * FROM Goods 
WHERE JSON_VALUE(ConfigJson, '$.lock_is') = 'true'
  AND JSON_VALUE(ConfigJson, '$.daily_xiangou') > '0';

📊 监控和分析

1. 查询存储(Query Store)

-- 启用查询存储
ALTER DATABASE HoneyBox SET QUERY_STORE = ON;
ALTER DATABASE HoneyBox SET QUERY_STORE (
    OPERATION_MODE = READ_WRITE,
    CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),
    DATA_FLUSH_INTERVAL_SECONDS = 900,
    INTERVAL_LENGTH_MINUTES = 60
);

-- 查询性能分析
SELECT 
    qst.query_sql_text,
    qrs.avg_duration/1000 as avg_duration_ms,
    qrs.avg_cpu_time/1000 as avg_cpu_time_ms,
    qrs.count_executions,
    qrs.last_execution_time
FROM sys.query_store_query_text qst
JOIN sys.query_store_query q ON qst.query_text_id = q.query_text_id
JOIN sys.query_store_plan qsp ON q.query_id = qsp.query_id
JOIN sys.query_store_runtime_stats qrs ON qsp.plan_id = qrs.plan_id
WHERE qrs.avg_duration > 1000000 -- 超过1秒的查询
ORDER BY qrs.avg_duration DESC;

2. 动态管理视图

-- 监控等待统计
SELECT 
    wait_type,
    waiting_tasks_count,
    wait_time_ms,
    max_wait_time_ms,
    signal_wait_time_ms
FROM sys.dm_os_wait_stats
WHERE wait_type NOT IN ('CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE')
ORDER BY wait_time_ms DESC;

-- 监控索引使用情况
SELECT 
    OBJECT_NAME(s.object_id) as table_name,
    i.name as index_name,
    s.user_seeks,
    s.user_scans,
    s.user_lookups,
    s.user_updates
FROM sys.dm_db_index_usage_stats s
JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE s.database_id = DB_ID()
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC;

.NET 8 EF Core 8.0 适配

🔧 连接字符串配置

// appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=HoneyBox;Trusted_Connection=true;TrustServerCertificate=true;MultipleActiveResultSets=true"
  }
}

📝 实体模型设计

// 用户实体 - SQL Server优化
public class User
{
    public int Id { get; set; }
    
    [MaxLength(50)]
    public string OpenId { get; set; }
    
    [MaxLength(255)]
    public string? UnionId { get; set; }
    
    [MaxLength(255)]
    public string? GzhOpenId { get; set; }
    
    [MaxLength(15)]
    public string? Mobile { get; set; }
    
    [MaxLength(255)]
    public string Nickname { get; set; }
    
    [MaxLength(500)]
    public string HeadImg { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal Money { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal Money2 { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal Integral { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal Score { get; set; }
    
    public int Pid { get; set; }
    public int OuQi { get; set; }
    public int OuQiLevel { get; set; }
    public byte Vip { get; set; }
    public byte Status { get; set; }
    public int IsTest { get; set; }
    
    [MaxLength(16)]
    public string Uid { get; set; }
    
    public int? ClickId { get; set; }
    
    public DateTime AddTime { get; set; }
    public DateTime UpdateTime { get; set; }
    public DateTime? LastLoginTime { get; set; }
    
    // 导航属性
    public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
    public virtual UserAccount? Account { get; set; }
}

// 商品实体
public class Goods
{
    public int Id { get; set; }
    public int CategoryId { get; set; }
    
    [MaxLength(255)]
    public string Title { get; set; }
    
    [MaxLength(500)]
    public string ImgUrl { get; set; }
    
    [MaxLength(500)]
    public string ImgUrlDetail { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal Price { get; set; }
    
    public int Stock { get; set; }
    public int SaleStock { get; set; }
    public byte Type { get; set; }
    public byte Status { get; set; }
    public byte LockIs { get; set; }
    public int LockTime { get; set; }
    public byte IsShouZhe { get; set; }
    public byte NewIs { get; set; }
    public byte ShowIs { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal UnlockAmount { get; set; }
    
    public int DailyXiangou { get; set; }
    public int QuanjuXiangou { get; set; }
    public int Sort { get; set; }
    
    public DateTime? SaleTime { get; set; }
    public DateTime AddTime { get; set; }
    public DateTime UpdateTime { get; set; }
    public DateTime? DeleteTime { get; set; }
    
    // 导航属性
    public virtual ICollection<Order> Orders { get; set; } = new List<Order>();
    public virtual ICollection<GoodsList> GoodsLists { get; set; } = new List<GoodsList>();
}

// 订单实体
public class Order
{
    public int Id { get; set; }
    
    [MaxLength(60)]
    public string OrderNo { get; set; }
    
    public int UserId { get; set; }
    public int GoodsId { get; set; }
    public int GoodsNum { get; set; }
    
    [MaxLength(255)]
    public string GoodsTitle { get; set; }
    
    [MaxLength(500)]
    public string? GoodsImgUrl { get; set; }
    
    public byte OrderType { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal OrderTotal { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal OrderZheTotal { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal Price { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal UseMoney { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal UseIntegral { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal UseMoney2 { get; set; }
    
    public int? CouponId { get; set; }
    
    [Column(TypeName = "decimal(18,2)")]
    public decimal UseCoupon { get; set; }
    
    public int PrizeNum { get; set; }
    public byte PayType { get; set; }
    public byte Status { get; set; }
    
    public DateTime AddTime { get; set; }
    public DateTime? PayTime { get; set; }
    public int? ClickId { get; set; }
    
    // 导航属性
    public virtual User User { get; set; }
    public virtual Goods Goods { get; set; }
    public virtual ICollection<OrderDetail> OrderDetails { get; set; } = new List<OrderDetail>();
}

// DbContext配置
public class HoneyBoxDbContext : DbContext
{
    public HoneyBoxDbContext(DbContextOptions<HoneyBoxDbContext> options) : base(options) { }
    
    public DbSet<User> Users { get; set; }
    public DbSet<Goods> Goods { get; set; }
    public DbSet<Order> Orders { get; set; }
    public DbSet<OrderDetail> OrderDetails { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 用户表配置
        modelBuilder.Entity<User>(entity =>
        {
            entity.ToTable("Users");
            
            entity.HasIndex(e => e.OpenId).IsUnique();
            entity.HasIndex(e => e.Uid).IsUnique();
            entity.HasIndex(e => e.Mobile);
            entity.HasIndex(e => e.UnionId);
            entity.HasIndex(e => new { e.Status, e.AddTime });
            
            entity.Property(e => e.AddTime)
                .HasDefaultValueSql("GETDATE()");
                
            entity.Property(e => e.UpdateTime)
                .HasDefaultValueSql("GETDATE()");
        });
        
        // 商品表配置
        modelBuilder.Entity<Goods>(entity =>
        {
            entity.ToTable("Goods");
            
            entity.HasIndex(e => new { e.Type, e.Status });
            entity.HasIndex(e => new { e.Status, e.Sort });
            entity.HasIndex(e => e.CategoryId);
            entity.HasIndex(e => e.AddTime);
            
            entity.Property(e => e.AddTime)
                .HasDefaultValueSql("GETDATE()");
                
            entity.Property(e => e.UpdateTime)
                .HasDefaultValueSql("GETDATE()");
        });
        
        // 订单表配置
        modelBuilder.Entity<Order>(entity =>
        {
            entity.ToTable("Orders");
            
            entity.HasIndex(e => e.OrderNo).IsUnique();
            entity.HasIndex(e => new { e.UserId, e.Status });
            entity.HasIndex(e => new { e.GoodsId, e.GoodsNum });
            entity.HasIndex(e => e.AddTime);
            entity.HasIndex(e => e.PayTime);
            
            entity.Property(e => e.AddTime)
                .HasDefaultValueSql("GETDATE()");
            
            // 外键关系
            entity.HasOne(d => d.User)
                .WithMany(p => p.Orders)
                .HasForeignKey(d => d.UserId)
                .OnDelete(DeleteBehavior.Restrict);
                
            entity.HasOne(d => d.Goods)
                .WithMany(p => p.Orders)
                .HasForeignKey(d => d.GoodsId)
                .OnDelete(DeleteBehavior.Restrict);
        });
    }
}

迁移时间表

📅 详细时间安排

阶段 任务 时间 负责人
准备阶段 SQL Server环境、备份 0.5天 DBA
设计阶段 表结构设计、优化 1天 DBA + 开发
迁移阶段 数据迁移、验证 1天 DBA
适配阶段 EF Core适配、测试 1天 开发
测试阶段 功能测试、性能测试 1天 测试 + 开发
上线阶段 切换、监控 0.5天 全员

总计: 5天

风险控制

⚠️ 主要风险点

  1. 数据类型兼容性

    • MySQL的UNSIGNED类型 → SQL Server的INT
    • 时间戳 → DATETIME2转换
    • 解决方案: 详细的数据类型映射表
  2. 字符集和排序规则

    • MySQL的utf8mb4 → SQL Server的Chinese_PRC_CI_AS
    • 解决方案: 统一使用Unicode排序规则
  3. SQL语法差异

    • MySQL的LIMIT → SQL Server的TOP/OFFSET
    • 函数名差异等
    • 解决方案: 使用EF Core抽象层
  4. 性能调优

    • 索引策略差异
    • 查询计划差异
    • 解决方案: 性能基准测试,逐步优化

🛡️ 回滚方案

# 紧急回滚步骤
1. 停止.NET应用
2. 修改连接字符串指向MySQL
3. 重启应用验证功能
4. 分析SQL Server问题
5. 制定修复方案

预期收益

📈 性能提升

  • 查询性能: 提升50-80%
  • 并发能力: 提升60-100%
  • 事务处理: 提升40-60%
  • 复杂查询: 提升100%+

🎯 技术收益

  • 企业级特性: 内存优化表、列存储索引
  • 更好的监控: Query Store、DMV
  • 开发效率: Visual Studio集成调试
  • 生态集成: 与.NET完美配合

💰 成本收益

  • 开发许可: 免费(Developer Edition)
  • 运维成本: 降低30%(更好的管理工具)
  • 学习成本: 中等团队需要学习T-SQL
  • 硬件成本: 增加20%(内存要求更高)

总结建议

强烈推荐迁移到SQL Server 2022

核心理由:

  1. 性能优势明显: 50-80%的性能提升
  2. 技术栈统一: 与.NET 8完美集成
  3. 企业级特性: 内存优化、智能查询处理
  4. 长期支持: Microsoft官方支持到2033年
  5. 抽奖场景优化: 更好的并发控制和事务处理

建议时机:

  • 在API迁移项目的阶段1同步进行
  • 利用项目重构窗口期,一次性完成升级

总投入:

  • 时间: 5天
  • 人力: DBA 1人 + 开发 2人
  • 成本: 约2万元主要是人力成本

🎯 最终建议

考虑到你的项目特点和技术栈,SQL Server 2022是最佳选择

  1. 抽奖系统的高并发需求 → SQL Server的内存优化表完美适配
  2. .NET 8技术栈 → 原生支持,开发效率最高
  3. 企业级稳定性要求 → SQL Server的企业级特性
  4. 未来扩展性 → 更好的分析和BI支持

虽然迁移成本比MySQL升级稍高但长期收益更大