惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security Affairs
S
Schneier on Security
T
Tenable Blog
G
GRAHAM CLULEY
Latest news
Latest news
D
Darknet – Hacking Tools, Hacker News & Cyber Security
A
Arctic Wolf
I
Intezer
Cyberwarzone
Cyberwarzone
T
The Exploit Database - CXSecurity.com
T
Tailwind CSS Blog
K
Kaspersky official blog
Blog — PlanetScale
Blog — PlanetScale
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threat Research - Cisco Blogs
爱范儿
爱范儿
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园 - 叶小钗
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
P
Palo Alto Networks Blog
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 司徒正美
The Cloudflare Blog
Help Net Security
Help Net Security
罗磊的独立博客
博客园 - 聂微东
Jina AI
Jina AI
Project Zero
Project Zero
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LINUX DO - 最新话题
V
V2EX
人人都是产品经理
人人都是产品经理
美团技术团队
博客园 - 【当耐特】
Spread Privacy
Spread Privacy
J
Java Code Geeks
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Security Latest
Security Latest
The Last Watchdog
The Last Watchdog
Stack Overflow Blog
Stack Overflow Blog
雷峰网
雷峰网
S
Securelist
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
Microsoft Azure Blog
Microsoft Azure Blog
P
Privacy International News Feed
宝玉的分享
宝玉的分享
C
CERT Recently Published Vulnerability Notes

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第九章:分页查询与性能优化
我才是银古 · 2026-06-21 · via 博客园 - 我才是银古

第九章:分页查询与性能优化

目录


9.1 分页查询基础

9.1.1 ToPageList方法

SqlSugar提供了强大的分页功能,ToPageList是最常用的分页方法:

// 基础分页查询
int pageIndex = 1;  // 当前页码
int pageSize = 20;  // 每页数量
int totalCount = 0; // 总记录数

var pageData = db.Queryable<Student>()
    .Where(s => s.Status == 1)
    .OrderBy(s => s.CreateTime, OrderByType.Desc)
    .ToPageList(pageIndex, pageSize, ref totalCount);

Console.WriteLine($"总记录数: {totalCount}");
Console.WriteLine($"总页数: {Math.Ceiling(totalCount / (double)pageSize)}");

高级分页示例

// 多条件分页查询
public class PageRequest
{
    public int PageIndex { get; set; } = 1;
    public int PageSize { get; set; } = 20;
    public string Keyword { get; set; }
    public int? ClassId { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
}

public PageResult<Student> GetStudentsByPage(PageRequest request)
{
    int totalCount = 0;
    
    var query = db.Queryable<Student>()
        .WhereIF(!string.IsNullOrEmpty(request.Keyword), 
            s => s.Name.Contains(request.Keyword) || s.Email.Contains(request.Keyword))
        .WhereIF(request.ClassId.HasValue, s => s.ClassId == request.ClassId)
        .WhereIF(request.StartDate.HasValue, s => s.CreateTime >= request.StartDate)
        .WhereIF(request.EndDate.HasValue, s => s.CreateTime <= request.EndDate)
        .OrderBy(s => s.CreateTime, OrderByType.Desc);
    
    var list = query.ToPageList(request.PageIndex, request.PageSize, ref totalCount);
    
    return new PageResult<Student>
    {
        PageIndex = request.PageIndex,
        PageSize = request.PageSize,
        TotalCount = totalCount,
        TotalPages = (int)Math.Ceiling(totalCount / (double)request.PageSize),
        Items = list
    };
}

9.1.2 Skip和Take方法

除了ToPageList,还可以使用SkipTake进行分页:

// Skip/Take分页
int pageIndex = 1;
int pageSize = 20;

// 计算跳过的记录数
int skipCount = (pageIndex - 1) * pageSize;

var list = db.Queryable<Student>()
    .OrderBy(s => s.Id)
    .Skip(skipCount)
    .Take(pageSize)
    .ToList();

// 获取总数(需要单独查询)
var totalCount = db.Queryable<Student>().Count();

性能对比

// ToPageList:一次查询获取数据和总数(推荐)
int total = 0;
var data1 = db.Queryable<Student>()
    .ToPageList(1, 20, ref total);

// Skip/Take:需要两次查询
var data2 = db.Queryable<Student>()
    .Skip(0).Take(20).ToList();
var total2 = db.Queryable<Student>().Count();

9.1.3 分页参数封装

创建通用的分页结果类:

public class PageResult<T>
{
    public int PageIndex { get; set; }
    public int PageSize { get; set; }
    public int TotalCount { get; set; }
    public int TotalPages { get; set; }
    public bool HasPreviousPage => PageIndex > 1;
    public bool HasNextPage => PageIndex < TotalPages;
    public List<T> Items { get; set; }
}

// 扩展方法
public static class QueryableExtensions
{
    public static PageResult<T> ToPageResult<T>(
        this ISugarQueryable<T> query,
        int pageIndex,
        int pageSize)
    {
        int totalCount = 0;
        var items = query.ToPageList(pageIndex, pageSize, ref totalCount);
        
        return new PageResult<T>
        {
            PageIndex = pageIndex,
            PageSize = pageSize,
            TotalCount = totalCount,
            TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize),
            Items = items
        };
    }
}

// 使用
var result = db.Queryable<Student>()
    .Where(s => s.Status == 1)
    .ToPageResult(1, 20);

9.2 性能优化技术

9.2.1 查询缓存

SqlSugar支持多种缓存策略:

// 简单缓存(缓存2小时)
var list = db.Queryable<Student>()
    .WithCache(7200) // 缓存时间(秒)
    .ToList();

// 带缓存键的缓存
var data = db.Queryable<Student>()
    .Where(s => s.ClassId == 1)
    .WithCache("student_class_1", 3600)
    .ToList();

// 移除缓存
db.RemoveDataCache("student_class_1");

// 移除所有缓存
db.RemoveDataCache();

自定义缓存服务

// 使用Redis缓存
public class RedisCacheService : ICacheService
{
    private readonly IConnectionMultiplexer _redis;
    
    public RedisCacheService(IConnectionMultiplexer redis)
    {
        _redis = redis;
    }
    
    public void Add<V>(string key, V value)
    {
        var db = _redis.GetDatabase();
        var json = JsonConvert.SerializeObject(value);
        db.StringSet(key, json);
    }
    
    public void Add<V>(string key, V value, int cacheDurationInSeconds)
    {
        var db = _redis.GetDatabase();
        var json = JsonConvert.SerializeObject(value);
        db.StringSet(key, json, TimeSpan.FromSeconds(cacheDurationInSeconds));
    }
    
    public bool ContainsKey<V>(string key)
    {
        var db = _redis.GetDatabase();
        return db.KeyExists(key);
    }
    
    public V Get<V>(string key)
    {
        var db = _redis.GetDatabase();
        var json = db.StringGet(key);
        return json.HasValue ? JsonConvert.DeserializeObject<V>(json) : default(V);
    }
    
    public IEnumerable<string> GetAllKey<V>()
    {
        var server = _redis.GetServer(_redis.GetEndPoints().First());
        return server.Keys().Select(k => k.ToString());
    }
    
    public V GetOrCreate<V>(string key, Func<V> create, int cacheDurationInSeconds = int.MaxValue)
    {
        if (ContainsKey<V>(key))
        {
            return Get<V>(key);
        }
        else
        {
            var value = create();
            Add(key, value, cacheDurationInSeconds);
            return value;
        }
    }
    
    public void Remove<V>(string key)
    {
        var db = _redis.GetDatabase();
        db.KeyDelete(key);
    }
}

// 配置使用Redis缓存
db.CurrentConnectionConfig.ConfigureExternalServices = new ConfigureExternalServices
{
    DataInfoCacheService = new RedisCacheService(redis)
};

9.2.2 索引优化

合理使用索引可以大幅提升查询性能:

// 实体类配置索引
[SugarTable("student")]
[SugarIndex("idx_name", nameof(Name), OrderByType.Asc)]
[SugarIndex("idx_class_status", nameof(ClassId) + "," + nameof(Status), OrderByType.Asc)]
public class Student
{
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    
    [SugarColumn(Length = 50, IsNullable = false)]
    public string Name { get; set; }
    
    public int ClassId { get; set; }
    public int Status { get; set; }
}

// 代码创建索引
db.DbMaintenance.CreateIndex("student", "idx_email", "Email");

// 创建唯一索引
db.DbMaintenance.CreateIndex("student", "uk_student_no", "StudentNo", true);

// 查看索引使用情况
var sql = "EXPLAIN SELECT * FROM student WHERE name = 'test'";
var dt = db.Ado.GetDataTable(sql);

9.2.3 批量操作

批量操作比循环单条操作性能高得多:

// 批量插入(推荐)
var students = new List<Student>();
for (int i = 0; i < 10000; i++)
{
    students.Add(new Student { Name = $"Student{i}" });
}
db.Insertable(students).ExecuteCommand();

// 使用分批插入(大数据量)
db.Insertable(students)
    .PageSize(1000) // 每次插入1000条
    .ExecuteCommand();

// 批量更新
db.Updateable(students).ExecuteCommand();

// 批量删除
var ids = new int[] { 1, 2, 3, 4, 5 };
db.Deleteable<Student>()
    .In(ids)
    .ExecuteCommand();

// 批量更新指定字段
db.Updateable<Student>()
    .SetColumns(s => new Student { Status = 1 })
    .Where(s => ids.Contains(s.Id))
    .ExecuteCommand();

性能对比

// 方式1:循环插入(慢)
var sw1 = Stopwatch.StartNew();
foreach (var student in students)
{
    db.Insertable(student).ExecuteCommand();
}
sw1.Stop();
Console.WriteLine($"循环插入耗时: {sw1.ElapsedMilliseconds}ms");

// 方式2:批量插入(快)
var sw2 = Stopwatch.StartNew();
db.Insertable(students).ExecuteCommand();
sw2.Stop();
Console.WriteLine($"批量插入耗时: {sw2.ElapsedMilliseconds}ms");

9.3 异步操作

9.3.1 异步查询

使用异步操作可以提高应用程序的响应性:

// 异步查询列表
public async Task<List<Student>> GetStudentsAsync()
{
    return await db.Queryable<Student>()
        .Where(s => s.Status == 1)
        .ToListAsync();
}

// 异步分页查询
public async Task<PageResult<Student>> GetPageAsync(int pageIndex, int pageSize)
{
    RefAsync<int> totalCount = 0;
    var list = await db.Queryable<Student>()
        .ToPageListAsync(pageIndex, pageSize, totalCount);
    
    return new PageResult<Student>
    {
        PageIndex = pageIndex,
        PageSize = pageSize,
        TotalCount = totalCount,
        Items = list
    };
}

// 异步获取单条
public async Task<Student> GetByIdAsync(int id)
{
    return await db.Queryable<Student>()
        .FirstAsync(s => s.Id == id);
}

// 异步聚合查询
public async Task<int> GetCountAsync()
{
    return await db.Queryable<Student>().CountAsync();
}

9.3.2 异步增删改

// 异步插入
public async Task<int> AddAsync(Student student)
{
    return await db.Insertable(student).ExecuteReturnIdentityAsync();
}

// 异步批量插入
public async Task<int> AddRangeAsync(List<Student> students)
{
    return await db.Insertable(students).ExecuteCommandAsync();
}

// 异步更新
public async Task<bool> UpdateAsync(Student student)
{
    return await db.Updateable(student).ExecuteCommandHasChangeAsync();
}

// 异步删除
public async Task<bool> DeleteAsync(int id)
{
    return await db.Deleteable<Student>()
        .Where(s => s.Id == id)
        .ExecuteCommandHasChangeAsync();
}

// 异步事务
public async Task<bool> TransferAsync(int fromId, int toId, decimal amount)
{
    try
    {
        db.BeginTran();
        
        await db.Updateable<Account>()
            .SetColumns(a => a.Balance == a.Balance - amount)
            .Where(a => a.Id == fromId)
            .ExecuteCommandAsync();
        
        await db.Updateable<Account>()
            .SetColumns(a => a.Balance == a.Balance + amount)
            .Where(a => a.Id == toId)
            .ExecuteCommandAsync();
        
        db.CommitTran();
        return true;
    }
    catch
    {
        db.RollbackTran();
        return false;
    }
}

9.4 SQL性能分析

9.4.1 SQL分析器

SqlSugar提供了强大的SQL分析功能:

// 启用SQL日志
db.Aop.OnLogExecuting = (sql, pars) =>
{
    Console.WriteLine($"SQL: {sql}");
    Console.WriteLine($"参数: {string.Join(",", pars?.Select(p => $"{p.ParameterName}={p.Value}"))}");
};

// 记录慢查询
db.Aop.OnLogExecuting = (sql, pars) =>
{
    Console.WriteLine($"开始执行: {DateTime.Now}");
};

db.Aop.OnLogExecuted = (sql, pars) =>
{
    Console.WriteLine($"执行完成: {DateTime.Now}");
};

// 监控慢查询
var slowQueryThreshold = 1000; // 1秒
db.Aop.OnLogExecuting = (sql, pars) =>
{
    db.Ado.SqlExecutionTime = Stopwatch.StartNew();
};

db.Aop.OnLogExecuted = (sql, pars) =>
{
    var sw = db.Ado.SqlExecutionTime;
    sw.Stop();
    if (sw.ElapsedMilliseconds > slowQueryThreshold)
    {
        Console.WriteLine($"慢查询警告: {sw.ElapsedMilliseconds}ms");
        Console.WriteLine($"SQL: {sql}");
    }
};

9.4.2 性能监控

实现完整的性能监控系统:

public class SqlPerformanceMonitor
{
    private static readonly ConcurrentDictionary<string, SqlStatistics> _statistics 
        = new ConcurrentDictionary<string, SqlStatistics>();
    
    public static void Initialize(SqlSugarClient db)
    {
        db.Aop.OnLogExecuting = (sql, pars) =>
        {
            var key = GetSqlKey(sql);
            var stats = _statistics.GetOrAdd(key, new SqlStatistics { Sql = sql });
            stats.StartExecution();
        };
        
        db.Aop.OnLogExecuted = (sql, pars) =>
        {
            var key = GetSqlKey(sql);
            if (_statistics.TryGetValue(key, out var stats))
            {
                stats.EndExecution();
            }
        };
        
        db.Aop.OnError = (exp) =>
        {
            var sql = exp.Sql;
            var key = GetSqlKey(sql);
            if (_statistics.TryGetValue(key, out var stats))
            {
                stats.RecordError();
            }
        };
    }
    
    private static string GetSqlKey(string sql)
    {
        // 提取SQL模板(去除参数值)
        return Regex.Replace(sql, @"'[^']*'|\d+", "?");
    }
    
    public static List<SqlStatistics> GetTopSlowQueries(int top = 10)
    {
        return _statistics.Values
            .OrderByDescending(s => s.AverageExecutionTime)
            .Take(top)
            .ToList();
    }
}

public class SqlStatistics
{
    public string Sql { get; set; }
    public int ExecutionCount { get; set; }
    public int ErrorCount { get; set; }
    public long TotalExecutionTime { get; set; }
    public long MaxExecutionTime { get; set; }
    public long MinExecutionTime { get; set; } = long.MaxValue;
    public double AverageExecutionTime => ExecutionCount > 0 
        ? TotalExecutionTime / (double)ExecutionCount : 0;
    
    private Stopwatch _currentExecution;
    
    public void StartExecution()
    {
        _currentExecution = Stopwatch.StartNew();
    }
    
    public void EndExecution()
    {
        if (_currentExecution != null)
        {
            _currentExecution.Stop();
            var elapsed = _currentExecution.ElapsedMilliseconds;
            
            ExecutionCount++;
            TotalExecutionTime += elapsed;
            MaxExecutionTime = Math.Max(MaxExecutionTime, elapsed);
            MinExecutionTime = Math.Min(MinExecutionTime, elapsed);
        }
    }
    
    public void RecordError()
    {
        ErrorCount++;
    }
}

9.5 最佳实践

性能优化清单

// 1. 只查询需要的字段
var list = db.Queryable<Student>()
    .Select(s => new { s.Id, s.Name, s.Email })
    .ToList();

// 2. 使用NoLock提示(SQL Server)
var data = db.Queryable<Student>()
    .With(SqlWith.NoLock)
    .ToList();

// 3. 避免N+1查询,使用Include
var students = db.Queryable<Student>()
    .Includes(s => s.Class)
    .ToList();

// 4. 使用分页避免查询大量数据
// ❌ 错误:查询所有数据
var allData = db.Queryable<Student>().ToList();

// ✅ 正确:使用分页
var pageData = db.Queryable<Student>().ToPageList(1, 100, ref total);

// 5. 合理使用缓存
var cachedData = db.Queryable<Dictionary>()
    .Where(d => d.Type == "Country")
    .WithCache(3600) // 字典数据缓存1小时
    .ToList();

// 6. 批量操作代替循环
// ❌ 错误
foreach (var id in ids)
{
    db.Deleteable<Student>().Where(s => s.Id == id).ExecuteCommand();
}

// ✅ 正确
db.Deleteable<Student>().In(ids).ExecuteCommand();

// 7. 使用异步操作
public async Task<List<Student>> GetDataAsync()
{
    return await db.Queryable<Student>().ToListAsync();
}

// 8. 关闭不必要的追踪
var list = db.Queryable<Student>()
    .AsNoTracking() // 只读查询
    .ToList();

常见性能陷阱

// 陷阱1:在循环中执行查询
// ❌ 错误
foreach (var classId in classIds)
{
    var students = db.Queryable<Student>()
        .Where(s => s.ClassId == classId)
        .ToList();
}

// ✅ 正确
var students = db.Queryable<Student>()
    .Where(s => classIds.Contains(s.ClassId))
    .ToList();

// 陷阱2:查询后在内存中过滤
// ❌ 错误
var allStudents = db.Queryable<Student>().ToList();
var filtered = allStudents.Where(s => s.Age > 18).ToList();

// ✅ 正确
var filtered = db.Queryable<Student>()
    .Where(s => s.Age > 18)
    .ToList();

// 陷阱3:不使用参数化查询
// ❌ 错误:SQL注入风险
var name = Request["name"];
var sql = $"SELECT * FROM student WHERE name = '{name}'";

// ✅ 正确
var students = db.Queryable<Student>()
    .Where(s => s.Name == name)
    .ToList();

9.6 本章小结

本章详细介绍了SqlSugar的分页查询和性能优化技术:

  1. 分页查询:掌握了ToPageList、Skip/Take等分页方法,以及如何封装通用的分页结果类
  2. 缓存优化:学会了使用查询缓存和自定义缓存服务来提升查询性能
  3. 索引优化:了解了如何创建和管理索引,以及索引对查询性能的影响
  4. 批量操作:掌握了批量插入、更新、删除等高效操作方法
  5. 异步编程:学会了使用异步方法提高应用程序的响应性和吞吐量
  6. 性能监控:实现了SQL分析和性能监控系统,能够识别和优化慢查询
  7. 最佳实践:总结了性能优化的各种技巧和需要避免的常见陷阱

通过本章的学习,您应该能够编写高性能的数据访问代码,并能够对现有系统进行性能分析和优化。在下一章中,我们将学习SqlSugar的分表分库与多租户功能,这对于构建大型分布式系统至关重要。