大量消息收件箱完成第一层性能优化

This commit is contained in:
2026-08-10 17:09:51 +08:00 Unverified
parent 7731a343d7
commit e4f1d88ca1
6 changed files with 7024 additions and 4 deletions
@@ -58,6 +58,7 @@ public sealed class NotificationsController(
var total = await source.CountAsync(cancellationToken); var total = await source.CountAsync(cancellationToken);
var items = await source var items = await source
.OrderByDescending(x => x.CreatedAt) .OrderByDescending(x => x.CreatedAt)
.ThenByDescending(x => x.Id)
.Skip((page - 1) * pageSize) .Skip((page - 1) * pageSize)
.Take(pageSize) .Take(pageSize)
.Select(x => new .Select(x => new
@@ -1475,8 +1475,11 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
entity.Property(x => x.Title).HasMaxLength(200); entity.Property(x => x.Title).HasMaxLength(200);
entity.Property(x => x.Content).HasColumnType("longtext"); entity.Property(x => x.Content).HasColumnType("longtext");
entity.Property(x => x.LinkUrl).HasMaxLength(300); entity.Property(x => x.LinkUrl).HasMaxLength(300);
entity.HasIndex(x => new { x.UserId, x.IsRead }); // Each inbox query starts with its recipient. Keep the selected
entity.HasIndex(x => new { x.UserId, x.Category, x.CreatedAt }); // sort fields in the index so large inboxes do not need a filesort.
entity.HasIndex(x => new { x.UserId, x.CreatedAt, x.Id });
entity.HasIndex(x => new { x.UserId, x.IsRead, x.CreatedAt, x.Id });
entity.HasIndex(x => new { x.UserId, x.Category, x.CreatedAt, x.Id });
entity.HasIndex(x => x.MessageDispatchId); entity.HasIndex(x => x.MessageDispatchId);
entity.HasIndex(x => x.CreatedAt); entity.HasIndex(x => x.CreatedAt);
entity.HasOne(x => x.MessageDispatch) entity.HasOne(x => x.MessageDispatch)
@@ -102,6 +102,8 @@ public sealed class DevelopmentSqliteMigrator(
"20260809_53_course_grade_statistics_refresh_settings"; "20260809_53_course_grade_statistics_refresh_settings";
private const string ExperimentCourseGradesMigration = private const string ExperimentCourseGradesMigration =
"20260809_54_experiment_course_grades"; "20260809_54_experiment_course_grades";
private const string NotificationInboxIndexesMigration =
"20260810_55_notification_inbox_indexes";
public async Task MigrateAsync(CancellationToken cancellationToken = default) public async Task MigrateAsync(CancellationToken cancellationToken = default)
{ {
@@ -731,6 +733,10 @@ public sealed class DevelopmentSqliteMigrator(
ExperimentCourseGradesMigration, ExperimentCourseGradesMigration,
experimentCourseGradesExist ? [] : ExperimentCourseGradesStatements, experimentCourseGradesExist ? [] : ExperimentCourseGradesStatements,
cancellationToken); cancellationToken);
await ApplyMigrationAsync(
NotificationInboxIndexesMigration,
NotificationInboxIndexesStatements,
cancellationToken);
} }
private async Task ApplyMigrationAsync( private async Task ApplyMigrationAsync(
@@ -2999,6 +3005,15 @@ public sealed class DevelopmentSqliteMigrator(
"""CREATE UNIQUE INDEX IF NOT EXISTS "IX_CourseGradeStatisticsRefreshSettings_Key" ON "CourseGradeStatisticsRefreshSettings" ("Key");""" """CREATE UNIQUE INDEX IF NOT EXISTS "IX_CourseGradeStatisticsRefreshSettings_Key" ON "CourseGradeStatisticsRefreshSettings" ("Key");"""
]; ];
private static readonly string[] NotificationInboxIndexesStatements =
[
"""DROP INDEX "IX_Notifications_UserId_IsRead";""",
"""DROP INDEX "IX_Notifications_UserId_Category_CreatedAt";""",
"""CREATE INDEX "IX_Notifications_UserId_CreatedAt_Id" ON "Notifications" ("UserId", "CreatedAt", "Id");""",
"""CREATE INDEX "IX_Notifications_UserId_IsRead_CreatedAt_Id" ON "Notifications" ("UserId", "IsRead", "CreatedAt", "Id");""",
"""CREATE INDEX "IX_Notifications_UserId_Category_CreatedAt_Id" ON "Notifications" ("UserId", "Category", "CreatedAt", "Id");"""
];
private static readonly string[] ExperimentCourseGradesStatements = private static readonly string[] ExperimentCourseGradesStatements =
[ [
""" """
@@ -0,0 +1,63 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
{
/// <inheritdoc />
public partial class OptimizeNotificationInboxIndexes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Notifications_UserId_Category_CreatedAt",
table: "Notifications");
migrationBuilder.DropIndex(
name: "IX_Notifications_UserId_IsRead",
table: "Notifications");
migrationBuilder.CreateIndex(
name: "IX_Notifications_UserId_Category_CreatedAt_Id",
table: "Notifications",
columns: new[] { "UserId", "Category", "CreatedAt", "Id" });
migrationBuilder.CreateIndex(
name: "IX_Notifications_UserId_CreatedAt_Id",
table: "Notifications",
columns: new[] { "UserId", "CreatedAt", "Id" });
migrationBuilder.CreateIndex(
name: "IX_Notifications_UserId_IsRead_CreatedAt_Id",
table: "Notifications",
columns: new[] { "UserId", "IsRead", "CreatedAt", "Id" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Notifications_UserId_Category_CreatedAt_Id",
table: "Notifications");
migrationBuilder.DropIndex(
name: "IX_Notifications_UserId_CreatedAt_Id",
table: "Notifications");
migrationBuilder.DropIndex(
name: "IX_Notifications_UserId_IsRead_CreatedAt_Id",
table: "Notifications");
migrationBuilder.CreateIndex(
name: "IX_Notifications_UserId_Category_CreatedAt",
table: "Notifications",
columns: new[] { "UserId", "Category", "CreatedAt" });
migrationBuilder.CreateIndex(
name: "IX_Notifications_UserId_IsRead",
table: "Notifications",
columns: new[] { "UserId", "IsRead" });
}
}
}
@@ -3417,9 +3417,11 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
b.HasIndex("MessageDispatchId"); b.HasIndex("MessageDispatchId");
b.HasIndex("UserId", "IsRead"); b.HasIndex("UserId", "CreatedAt", "Id");
b.HasIndex("UserId", "Category", "CreatedAt"); b.HasIndex("UserId", "Category", "CreatedAt", "Id");
b.HasIndex("UserId", "IsRead", "CreatedAt", "Id");
b.ToTable("Notifications"); b.ToTable("Notifications");
}); });