33 KiB
33 KiB
阶段5:功能测试和性能优化
阶段概述
时间: 1天
目标: 全面测试迁移后的功能,优化数据库性能,确保系统稳定运行
优先级: P0 (最高优先级)
详细任务清单
5.1 功能测试 (0.4天)
任务描述
全面测试所有业务功能,确保迁移后功能正常
具体工作
- 用户认证和登录测试
- 商品展示和查询测试
- 抽奖功能测试
- 订单和支付测试
- 财务系统测试
功能测试用例
1. 用户系统测试
// Tests/UserSystemTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using HoneyBox.Data;
using HoneyBox.Core.Entities;
using Xunit;
namespace HoneyBox.Tests.Integration
{
public class UserSystemTests : IClassFixture<TestWebApplicationFactory>
{
private readonly TestWebApplicationFactory _factory;
private readonly HoneyBoxDbContext _context;
public UserSystemTests(TestWebApplicationFactory factory)
{
_factory = factory;
var scope = factory.Services.CreateScope();
_context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
}
[Fact]
public async Task UserLogin_NewUser_ShouldCreateUser()
{
// Arrange
var client = _factory.CreateClient();
var loginRequest = new
{
openId = "test_openid_001",
unionId = "test_unionid_001",
nickname = "测试用户",
headImg = "https://example.com/avatar.jpg"
};
// Act
var response = await client.PostAsJsonAsync("/api/user/login", loginRequest);
// Assert
response.EnsureSuccessStatusCode();
var user = await _context.Users.FirstOrDefaultAsync(u => u.OpenId == loginRequest.openId);
Assert.NotNull(user);
Assert.Equal(loginRequest.nickname, user.Nickname);
Assert.Equal(loginRequest.headImg, user.HeadImg);
}
[Fact]
public async Task UserLogin_ExistingUser_ShouldUpdateInfo()
{
// Arrange
var existingUser = new User
{
OpenId = "test_openid_002",
Nickname = "旧昵称",
HeadImg = "old_avatar.jpg",
Uid = "U001",
CreatedAt = DateTime.Now.AddDays(-1)
};
_context.Users.Add(existingUser);
await _context.SaveChangesAsync();
var client = _factory.CreateClient();
var loginRequest = new
{
openId = "test_openid_002",
nickname = "新昵称",
headImg = "new_avatar.jpg"
};
// Act
var response = await client.PostAsJsonAsync("/api/user/login", loginRequest);
// Assert
response.EnsureSuccessStatusCode();
var updatedUser = await _context.Users.FirstOrDefaultAsync(u => u.OpenId == loginRequest.openId);
Assert.NotNull(updatedUser);
Assert.Equal(loginRequest.nickname, updatedUser.Nickname);
Assert.Equal(loginRequest.headImg, updatedUser.HeadImg);
Assert.NotNull(updatedUser.LastLoginAt);
}
[Fact]
public async Task GetUserBalance_ValidUser_ShouldReturnBalance()
{
// Arrange
var user = new User
{
OpenId = "test_openid_003",
Nickname = "余额测试用户",
HeadImg = "avatar.jpg",
Uid = "U002",
Money = 100.50m,
Money2 = 50.25m,
Integral = 200.75m,
Score = 300.00m
};
_context.Users.Add(user);
await _context.SaveChangesAsync();
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync($"/api/user/{user.Id}/balance");
// Assert
response.EnsureSuccessStatusCode();
var balance = await response.Content.ReadFromJsonAsync<dynamic>();
Assert.NotNull(balance);
}
}
}
2. 商品系统测试
// Tests/GoodsSystemTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using HoneyBox.Data;
using HoneyBox.Core.Entities;
using Xunit;
namespace HoneyBox.Tests.Integration
{
public class GoodsSystemTests : IClassFixture<TestWebApplicationFactory>
{
private readonly TestWebApplicationFactory _factory;
private readonly HoneyBoxDbContext _context;
public GoodsSystemTests(TestWebApplicationFactory factory)
{
_factory = factory;
var scope = factory.Services.CreateScope();
_context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
}
[Fact]
public async Task GetGoods_WithPagination_ShouldReturnCorrectPage()
{
// Arrange
await SeedGoodsData();
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync("/api/goods?page=1&pageSize=5");
// Assert
response.EnsureSuccessStatusCode();
var goods = await response.Content.ReadFromJsonAsync<List<Goods>>();
Assert.NotNull(goods);
Assert.True(goods.Count <= 5);
}
[Fact]
public async Task GetGoods_WithTypeFilter_ShouldReturnFilteredResults()
{
// Arrange
await SeedGoodsData();
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync("/api/goods?type=1");
// Assert
response.EnsureSuccessStatusCode();
var goods = await response.Content.ReadFromJsonAsync<List<Goods>>();
Assert.NotNull(goods);
Assert.All(goods, g => Assert.Equal((byte)1, g.Type));
}
[Fact]
public async Task GetGoodsById_ValidId_ShouldReturnGoodsWithItems()
{
// Arrange
var goods = await CreateTestGoods();
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync($"/api/goods/{goods.Id}");
// Assert
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<Goods>();
Assert.NotNull(result);
Assert.Equal(goods.Id, result.Id);
Assert.NotEmpty(result.Items);
}
private async Task SeedGoodsData()
{
var goods = new List<Goods>();
for (int i = 1; i <= 10; i++)
{
goods.Add(new Goods
{
Title = $"测试商品{i}",
ImgUrl = $"goods{i}.jpg",
ImgUrlDetail = $"goods{i}_detail.jpg",
Price = i * 10,
Type = (byte)(i % 3),
Status = 1,
IsShow = 1,
SortOrder = i
});
}
_context.Goods.AddRange(goods);
await _context.SaveChangesAsync();
}
private async Task<Goods> CreateTestGoods()
{
var goods = new Goods
{
Title = "测试盲盒",
ImgUrl = "test_box.jpg",
ImgUrlDetail = "test_box_detail.jpg",
Price = 29.9m,
Type = 1,
Status = 1,
IsShow = 1,
Items = new List<GoodsItem>
{
new GoodsItem
{
BoxNumber = 1,
PrizeId = 1,
Title = "普通奖品",
Stock = 100,
SurplusStock = 80,
ImgUrl = "prize1.jpg",
Price = 5.0m,
RealProbability = 0.6m
},
new GoodsItem
{
BoxNumber = 1,
PrizeId = 2,
Title = "稀有奖品",
Stock = 20,
SurplusStock = 15,
ImgUrl = "prize2.jpg",
Price = 50.0m,
RealProbability = 0.1m
}
}
};
_context.Goods.Add(goods);
await _context.SaveChangesAsync();
return goods;
}
}
}
3. 抽奖系统测试
// Tests/LotterySystemTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using HoneyBox.Data;
using HoneyBox.Core.Entities;
using HoneyBox.Core.Services;
using Xunit;
namespace HoneyBox.Tests.Integration
{
public class LotterySystemTests : IClassFixture<TestWebApplicationFactory>
{
private readonly TestWebApplicationFactory _factory;
private readonly HoneyBoxDbContext _context;
private readonly ILotteryService _lotteryService;
public LotterySystemTests(TestWebApplicationFactory factory)
{
_factory = factory;
var scope = factory.Services.CreateScope();
_context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
_lotteryService = scope.ServiceProvider.GetRequiredService<ILotteryService>();
}
[Fact]
public async Task DrawLottery_ValidOrder_ShouldReturnPrize()
{
// Arrange
var user = await CreateTestUser();
var goods = await CreateTestGoodsWithItems();
var order = await CreateTestOrder(user.Id, goods.Id);
// Act
var result = await _lotteryService.DrawAsync(order.Id);
// Assert
Assert.True(result.Success);
Assert.NotNull(result.Prize);
// 验证订单状态更新
var updatedOrder = await _context.Orders.FindAsync(order.Id);
Assert.Equal((byte)1, updatedOrder.Status);
// 验证订单详情创建
var orderItems = await _context.OrderItems
.Where(oi => oi.OrderId == order.OrderNo)
.ToListAsync();
Assert.NotEmpty(orderItems);
}
[Fact]
public async Task DrawLottery_InsufficientStock_ShouldFail()
{
// Arrange
var user = await CreateTestUser();
var goods = await CreateTestGoodsWithNoStock();
var order = await CreateTestOrder(user.Id, goods.Id);
// Act
var result = await _lotteryService.DrawAsync(order.Id);
// Assert
Assert.False(result.Success);
Assert.Contains("库存不足", result.Message);
}
[Fact]
public async Task DrawLottery_MultipleDraws_ShouldRespectProbability()
{
// Arrange
var user = await CreateTestUser();
var goods = await CreateTestGoodsWithItems();
var drawCount = 100;
var results = new List<string>();
// Act
for (int i = 0; i < drawCount; i++)
{
var order = await CreateTestOrder(user.Id, goods.Id);
var result = await _lotteryService.DrawAsync(order.Id);
if (result.Success)
{
results.Add(result.Prize.Title);
}
}
// Assert
Assert.NotEmpty(results);
// 验证概率分布(允许一定误差)
var commonPrizes = results.Count(r => r.Contains("普通"));
var rarePrizes = results.Count(r => r.Contains("稀有"));
Assert.True(commonPrizes > rarePrizes, "普通奖品应该比稀有奖品多");
}
private async Task<User> CreateTestUser()
{
var user = new User
{
OpenId = Guid.NewGuid().ToString(),
Nickname = "测试用户",
HeadImg = "avatar.jpg",
Uid = "U" + DateTime.Now.Ticks,
Money = 1000m
};
_context.Users.Add(user);
await _context.SaveChangesAsync();
return user;
}
private async Task<Goods> CreateTestGoodsWithItems()
{
var goods = new Goods
{
Title = "测试盲盒",
ImgUrl = "box.jpg",
ImgUrlDetail = "box_detail.jpg",
Price = 29.9m,
Status = 1,
Items = new List<GoodsItem>
{
new GoodsItem
{
BoxNumber = 1,
PrizeId = 1,
Title = "普通奖品",
Stock = 1000,
SurplusStock = 1000,
ImgUrl = "common.jpg",
RealProbability = 0.7m
},
new GoodsItem
{
BoxNumber = 1,
PrizeId = 2,
Title = "稀有奖品",
Stock = 100,
SurplusStock = 100,
ImgUrl = "rare.jpg",
RealProbability = 0.3m
}
}
};
_context.Goods.Add(goods);
await _context.SaveChangesAsync();
return goods;
}
private async Task<Order> CreateTestOrder(int userId, int goodsId)
{
var order = new Order
{
OrderNo = "TEST" + DateTime.Now.Ticks,
UserId = userId,
GoodsId = goodsId,
BoxNumber = 1,
GoodsTitle = "测试盲盒",
OrderTotal = 29.9m,
DiscountTotal = 29.9m,
FinalPrice = 29.9m,
DrawCount = 1,
Status = 0
};
_context.Orders.Add(order);
await _context.SaveChangesAsync();
return order;
}
}
}
5.2 性能测试 (0.3天)
任务描述
测试数据库查询性能,识别性能瓶颈
具体工作
- 查询性能基准测试
- 并发性能测试
- 内存使用测试
- 响应时间测试
性能测试脚本
1. 查询性能测试
// Tests/PerformanceTests.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using HoneyBox.Data;
using System.Diagnostics;
using Xunit;
using Xunit.Abstractions;
namespace HoneyBox.Tests.Performance
{
public class PerformanceTests : IClassFixture<TestWebApplicationFactory>
{
private readonly TestWebApplicationFactory _factory;
private readonly HoneyBoxDbContext _context;
private readonly ITestOutputHelper _output;
public PerformanceTests(TestWebApplicationFactory factory, ITestOutputHelper output)
{
_factory = factory;
_output = output;
var scope = factory.Services.CreateScope();
_context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
}
[Fact]
public async Task UserQuery_Performance_ShouldBeFast()
{
// Arrange
await SeedUsers(10000);
var stopwatch = Stopwatch.StartNew();
// Act
var users = await _context.Users
.Where(u => u.Status == 1)
.OrderByDescending(u => u.CreatedAt)
.Take(100)
.ToListAsync();
stopwatch.Stop();
// Assert
Assert.NotEmpty(users);
Assert.True(stopwatch.ElapsedMilliseconds < 100,
$"查询耗时 {stopwatch.ElapsedMilliseconds}ms,超过预期的100ms");
_output.WriteLine($"用户查询耗时: {stopwatch.ElapsedMilliseconds}ms");
}
[Fact]
public async Task GoodsQuery_WithJoins_ShouldBeFast()
{
// Arrange
await SeedGoodsWithItems(1000);
var stopwatch = Stopwatch.StartNew();
// Act
var goods = await _context.Goods
.Include(g => g.Items)
.Where(g => g.Status == 1)
.OrderByDescending(g => g.SortOrder)
.Take(50)
.ToListAsync();
stopwatch.Stop();
// Assert
Assert.NotEmpty(goods);
Assert.True(stopwatch.ElapsedMilliseconds < 200,
$"商品查询耗时 {stopwatch.ElapsedMilliseconds}ms,超过预期的200ms");
_output.WriteLine($"商品查询耗时: {stopwatch.ElapsedMilliseconds}ms");
}
[Fact]
public async Task OrderQuery_ComplexJoins_ShouldBeFast()
{
// Arrange
await SeedOrdersWithRelations(5000);
var stopwatch = Stopwatch.StartNew();
// Act
var orders = await _context.Orders
.Include(o => o.User)
.Include(o => o.Goods)
.Include(o => o.Items)
.Where(o => o.Status == 1)
.Where(o => o.CreatedAt >= DateTime.Now.AddDays(-30))
.OrderByDescending(o => o.CreatedAt)
.Take(100)
.ToListAsync();
stopwatch.Stop();
// Assert
Assert.NotEmpty(orders);
Assert.True(stopwatch.ElapsedMilliseconds < 300,
$"订单查询耗时 {stopwatch.ElapsedMilliseconds}ms,超过预期的300ms");
_output.WriteLine($"订单查询耗时: {stopwatch.ElapsedMilliseconds}ms");
}
[Fact]
public async Task ConcurrentUserLogin_ShouldHandleLoad()
{
// Arrange
var tasks = new List<Task>();
var userCount = 100;
var stopwatch = Stopwatch.StartNew();
// Act
for (int i = 0; i < userCount; i++)
{
var openId = $"concurrent_user_{i}";
tasks.Add(SimulateUserLogin(openId));
}
await Task.WhenAll(tasks);
stopwatch.Stop();
// Assert
var createdUsers = await _context.Users
.Where(u => u.OpenId.StartsWith("concurrent_user_"))
.CountAsync();
Assert.Equal(userCount, createdUsers);
Assert.True(stopwatch.ElapsedMilliseconds < 5000,
$"并发登录耗时 {stopwatch.ElapsedMilliseconds}ms,超过预期的5000ms");
_output.WriteLine($"并发登录({userCount}用户)耗时: {stopwatch.ElapsedMilliseconds}ms");
}
private async Task SimulateUserLogin(string openId)
{
using var scope = _factory.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<HoneyBoxDbContext>();
var user = new User
{
OpenId = openId,
Nickname = $"用户{openId}",
HeadImg = "avatar.jpg",
Uid = "U" + DateTime.Now.Ticks + Random.Shared.Next(1000, 9999)
};
context.Users.Add(user);
await context.SaveChangesAsync();
}
private async Task SeedUsers(int count)
{
var users = new List<User>();
for (int i = 0; i < count; i++)
{
users.Add(new User
{
OpenId = $"perf_user_{i}",
Nickname = $"性能测试用户{i}",
HeadImg = "avatar.jpg",
Uid = $"U{i:D6}",
Status = (byte)(i % 10 == 0 ? 0 : 1), // 10%的用户状态为0
CreatedAt = DateTime.Now.AddDays(-Random.Shared.Next(0, 365))
});
}
_context.Users.AddRange(users);
await _context.SaveChangesAsync();
}
private async Task SeedGoodsWithItems(int goodsCount)
{
var goods = new List<Goods>();
for (int i = 0; i < goodsCount; i++)
{
var goodsItem = new Goods
{
Title = $"性能测试商品{i}",
ImgUrl = $"goods{i}.jpg",
ImgUrlDetail = $"goods{i}_detail.jpg",
Price = Random.Shared.Next(10, 100),
Status = 1,
SortOrder = Random.Shared.Next(1, 1000),
Items = new List<GoodsItem>()
};
// 为每个商品添加3-5个奖品
var itemCount = Random.Shared.Next(3, 6);
for (int j = 0; j < itemCount; j++)
{
goodsItem.Items.Add(new GoodsItem
{
BoxNumber = 1,
PrizeId = j + 1,
Title = $"奖品{j + 1}",
Stock = Random.Shared.Next(50, 200),
SurplusStock = Random.Shared.Next(10, 50),
ImgUrl = $"prize{j + 1}.jpg",
RealProbability = Random.Shared.NextSingle()
});
}
goods.Add(goodsItem);
}
_context.Goods.AddRange(goods);
await _context.SaveChangesAsync();
}
}
}
5.3 性能优化 (0.3天)
任务描述
根据测试结果优化数据库性能
具体工作
- 分析查询执行计划
- 优化索引策略
- 调整数据库参数
- 实施缓存策略
性能优化措施
1. 索引优化
-- 分析查询执行计划
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
-- 用户查询优化
SELECT u.id, u.nickname, u.head_img, u.money, u.integral
FROM users u
WHERE u.status = 1
AND u.created_at >= DATEADD(DAY, -30, GETDATE())
ORDER BY u.created_at DESC;
-- 创建复合索引优化上述查询
CREATE INDEX ix_users_status_created_desc ON users(status, created_at DESC)
WHERE status = 1;
-- 商品查询优化
SELECT g.id, g.title, g.img_url, g.price, g.sort_order
FROM goods g
WHERE g.status = 1
AND g.is_show = 1
AND g.type = 1
ORDER BY g.sort_order DESC, g.created_at DESC;
-- 创建复合索引优化商品查询
CREATE INDEX ix_goods_status_show_type_sort ON goods(status, is_show, type, sort_order DESC, created_at DESC)
WHERE status = 1 AND is_show = 1;
-- 订单查询优化
SELECT o.id, o.order_no, o.user_id, o.goods_id, o.order_total, o.status, o.created_at
FROM orders o
WHERE o.user_id = @UserId
AND o.created_at >= DATEADD(DAY, -90, GETDATE())
ORDER BY o.created_at DESC;
-- 创建用户订单查询索引
CREATE INDEX ix_orders_user_created_desc ON orders(user_id, created_at DESC);
-- 抽奖库存查询优化
SELECT gi.id, gi.goods_id, gi.box_number, gi.surplus_stock, gi.real_probability
FROM goods_items gi
WHERE gi.goods_id = @GoodsId
AND gi.box_number = @BoxNumber
AND gi.surplus_stock > 0;
-- 创建抽奖查询索引
CREATE INDEX ix_goods_items_goods_box_stock ON goods_items(goods_id, box_number, surplus_stock)
WHERE surplus_stock > 0;
2. 查询优化
-- 启用查询存储分析慢查询
ALTER DATABASE honey_box SET QUERY_STORE = ON;
ALTER DATABASE honey_box SET QUERY_STORE (
OPERATION_MODE = READ_WRITE,
CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30),
DATA_FLUSH_INTERVAL_SECONDS = 900,
INTERVAL_LENGTH_MINUTES = 60,
MAX_STORAGE_SIZE_MB = 1000
);
-- 分析最耗时的查询
SELECT TOP 10
qst.query_sql_text,
qrs.avg_duration/1000.0 as avg_duration_ms,
qrs.avg_cpu_time/1000.0 as avg_cpu_time_ms,
qrs.count_executions,
qrs.avg_logical_io_reads,
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 > 10000 -- 超过10ms的查询
ORDER BY qrs.avg_duration DESC;
-- 更新表统计信息
UPDATE STATISTICS users;
UPDATE STATISTICS goods;
UPDATE STATISTICS orders;
UPDATE STATISTICS goods_items;
-- 重建索引碎片整理
ALTER INDEX ALL ON users REORGANIZE;
ALTER INDEX ALL ON goods REORGANIZE;
ALTER INDEX ALL ON orders REORGANIZE;
3. 内存优化配置
-- 为抽奖系统创建内存优化表
CREATE TABLE goods_inventory_memory (
goods_id INT NOT NULL,
box_number INT NOT NULL,
surplus_stock INT NOT NULL,
last_update_time DATETIME2 NOT NULL DEFAULT GETDATE(),
CONSTRAINT pk_goods_inventory_memory PRIMARY KEY NONCLUSTERED (goods_id, box_number)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);
-- 创建内存优化的库存扣减存储过程
CREATE PROCEDURE sp_deduct_inventory_memory
@goods_id INT,
@box_number INT,
@quantity INT = 1
WITH NATIVE_COMPILATION, SCHEMABINDING
AS BEGIN ATOMIC WITH (
TRANSACTION ISOLATION LEVEL = SNAPSHOT,
LANGUAGE = N'us_english'
)
DECLARE @current_stock INT;
SELECT @current_stock = surplus_stock
FROM dbo.goods_inventory_memory
WHERE goods_id = @goods_id AND box_number = @box_number;
IF @current_stock >= @quantity
BEGIN
UPDATE dbo.goods_inventory_memory
SET surplus_stock = surplus_stock - @quantity,
last_update_time = GETDATE()
WHERE goods_id = @goods_id AND box_number = @box_number;
RETURN 1; -- 成功
END
RETURN 0; -- 库存不足
END;
4. 缓存策略实施
// Services/CacheService.cs
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;
namespace HoneyBox.Core.Services
{
public interface ICacheService
{
Task<T?> GetAsync<T>(string key);
Task SetAsync<T>(string key, T value, TimeSpan? expiry = null);
Task RemoveAsync(string key);
Task RemovePatternAsync(string pattern);
}
public class CacheService : ICacheService
{
private readonly IMemoryCache _memoryCache;
private readonly IDistributedCache _distributedCache;
private readonly ILogger<CacheService> _logger;
public CacheService(
IMemoryCache memoryCache,
IDistributedCache distributedCache,
ILogger<CacheService> logger)
{
_memoryCache = memoryCache;
_distributedCache = distributedCache;
_logger = logger;
}
public async Task<T?> GetAsync<T>(string key)
{
try
{
// 先从内存缓存获取
if (_memoryCache.TryGetValue(key, out T? memoryValue))
{
return memoryValue;
}
// 再从分布式缓存获取
var distributedValue = await _distributedCache.GetStringAsync(key);
if (!string.IsNullOrEmpty(distributedValue))
{
var value = JsonSerializer.Deserialize<T>(distributedValue);
// 回写到内存缓存
_memoryCache.Set(key, value, TimeSpan.FromMinutes(5));
return value;
}
return default(T);
}
catch (Exception ex)
{
_logger.LogError(ex, "缓存获取失败: {Key}", key);
return default(T);
}
}
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
{
try
{
var defaultExpiry = expiry ?? TimeSpan.FromMinutes(30);
// 设置内存缓存
_memoryCache.Set(key, value, TimeSpan.FromMinutes(5));
// 设置分布式缓存
var serializedValue = JsonSerializer.Serialize(value);
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = defaultExpiry
};
await _distributedCache.SetStringAsync(key, serializedValue, options);
}
catch (Exception ex)
{
_logger.LogError(ex, "缓存设置失败: {Key}", key);
}
}
public async Task RemoveAsync(string key)
{
try
{
_memoryCache.Remove(key);
await _distributedCache.RemoveAsync(key);
}
catch (Exception ex)
{
_logger.LogError(ex, "缓存删除失败: {Key}", key);
}
}
public async Task RemovePatternAsync(string pattern)
{
// 实现模式匹配删除(需要根据具体缓存实现)
_logger.LogWarning("模式删除功能需要根据具体缓存实现");
}
}
}
// 缓存装饰器模式
public class CachedGoodsService : IGoodsService
{
private readonly IGoodsService _goodsService;
private readonly ICacheService _cacheService;
private readonly ILogger<CachedGoodsService> _logger;
public CachedGoodsService(
IGoodsService goodsService,
ICacheService cacheService,
ILogger<CachedGoodsService> logger)
{
_goodsService = goodsService;
_cacheService = cacheService;
_logger = logger;
}
public async Task<List<Goods>> GetGoodsAsync(int page, int pageSize, byte? type = null)
{
var cacheKey = $"goods:list:{page}:{pageSize}:{type}";
var cachedGoods = await _cacheService.GetAsync<List<Goods>>(cacheKey);
if (cachedGoods != null)
{
_logger.LogDebug("从缓存获取商品列表: {CacheKey}", cacheKey);
return cachedGoods;
}
var goods = await _goodsService.GetGoodsAsync(page, pageSize, type);
await _cacheService.SetAsync(cacheKey, goods, TimeSpan.FromMinutes(10));
_logger.LogDebug("商品列表已缓存: {CacheKey}", cacheKey);
return goods;
}
public async Task<Goods?> GetGoodsByIdAsync(int id)
{
var cacheKey = $"goods:detail:{id}";
var cachedGoods = await _cacheService.GetAsync<Goods>(cacheKey);
if (cachedGoods != null)
{
return cachedGoods;
}
var goods = await _goodsService.GetGoodsByIdAsync(id);
if (goods != null)
{
await _cacheService.SetAsync(cacheKey, goods, TimeSpan.FromMinutes(30));
}
return goods;
}
}
验收标准
功能测试验收
- 用户系统测试通过(登录、注册、信息更新)
- 商品系统测试通过(列表、详情、筛选)
- 抽奖系统测试通过(抽奖、概率、库存)
- 订单系统测试通过(创建、支付、查询)
- 财务系统测试通过(余额、积分、交易记录)
性能测试验收
- 用户查询响应时间 < 100ms
- 商品查询响应时间 < 200ms
- 订单查询响应时间 < 300ms
- 并发登录处理能力 > 100用户/秒
- 抽奖并发处理能力 > 50次/秒
性能优化验收
- 关键查询索引优化完成
- 查询执行计划分析完成
- 内存优化表配置完成
- 缓存策略实施完成
- 数据库参数调优完成
风险点和注意事项
测试风险
- 测试数据污染: 测试数据可能影响生产环境
- 并发测试: 可能导致数据不一致
- 性能测试: 可能影响其他系统运行
- 缓存一致性: 缓存更新可能不及时
性能风险
- 过度优化: 可能导致维护复杂度增加
- 索引过多: 可能影响写入性能
- 内存消耗: 内存优化表消耗更多内存
- 缓存雪崩: 缓存同时失效导致数据库压力
解决方案
- 隔离测试环境: 使用独立的测试数据库
- 渐进式优化: 逐步实施优化措施
- 监控指标: 持续监控性能指标
- 回滚准备: 准备优化措施的回滚方案
下一阶段准备
为阶段6准备的内容
- 生产环境部署脚本
- 监控和告警配置
- 备份和恢复策略
- 运维文档
交接文档
- 功能测试报告
- 性能测试报告
- 优化措施文档
- 缓存策略说明
阶段5完成标志: 所有功能测试通过,性能指标达到预期,优化措施实施完成,系统准备好进入生产环境。