从细节出发 纯后端性能优化实战小结

最近2个月没做什么新项目 完全是对于旧的系统进行性能优化 避免超时 死锁 数据处理能力不够等常见的性能问题

这里不从架构方面出发 毕竟动大手脚成本比较高 那么我们以实例为前提 从细节开始



优化角度

一.业务逻辑优化

二.DB优化

三.数据处理优化

四.锁与性能

五.细节


业务逻辑优化

这一条不具有普遍性 不同的业务不同的场景 如果归纳起来 就是在不影响业务的前提下进行流程精简

1. 废弃冗余逻辑

常见于各种基于数据库的检查 很多同学在维护别人代码的时候 没有深入理解别人的逻辑 也许别人在取数据的时候已经在查询条件中已经过滤了相关逻辑 而后来维护的同学又来了一次check

当然如果处于数据安全的角度 double check无可厚非,但是如果连锁都没有的double check 其实不做也罢。

毕竟 省一次dbcall 可能效果胜于你做的N多优化

2. 合并业务请求

出发点和上述一致 节省dbcall 但是存在一个矛盾的点 如果业务包在事务里 这条需要慎重考虑 事务的设计原则里 当然能小则小


DB优化

这个其实是比较核心的点

1. 索引优化

这个点比较泛泛 但是做好的人不多 一个专攻于索引优化的人也可以在运维方面独当一面了

我们拿实例来看一个索引优化例子

首先利其器 选中你需要的调试信息

随便拿个典型sql来作示例 相关值为虚假值 仅供参考

SELECT DISTINCT TOP 1000 a.CustomerID FROM TravelTicket(nolock) a
WHERE  a.TicketChargeDate < GETDATE()
AND a.AvailableAmount > 999
AND a.[Status] <>999
AND a.IsInCome <>999
AND a.IsInCome <>998
AND NOT EXISTS
(SELECT TOP 1 1 FROM TicketCharge(NOLOCK) b
	WHERE b.chargetype = 999
		AND b.IsSuccessful = 999
		AND b.IsDeleted != 999
		AND b.FeeMonth = ‘999‘
		AND b.CustomerID = a.CustomerID
)

在完全没有任何索引的前提下我们查询一遍看下效果

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time:
   CPU time = 0 ms, elapsed time = 0 ms.
SQL Server parse and compile time:
   CPU time = 32 ms, elapsed time = 37 ms.

(1000 row(s) affected)
Table ‘TravelTicket‘. Scan count 1, logical reads 9069, physical reads 0, read-ahead reads 4248, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘TicketCharge‘. Scan count 1, logical reads 262, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 109 ms,  elapsed time = 591 ms.
SQL Server parse and compile time:
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

  执行计划如图

如图中所示 存在2个聚集索引扫描 我们优化的目标是将扫描(scan)变为查找(seek)

  在有聚集索引的表格上,数据是直接存放在索引的最底层的,所以要扫描整个表格里的数据,就要把整个聚集索引扫描一遍。在这里,聚集索引扫描就相当于一个表扫描。所要用的时间和资源与表扫描没有什么差别。并不是说这里有了“Index”这个字样,就说明执行计划比表扫描的有多大进步。当然反过来讲,如果看到“Table Scan”的字样,就说明这个表格上没有聚集索引。换句话说 上面那段sql存在2个表扫描。

  首先我们改变sql查找方式从not exists换为left join

SELECT TOP 1000 TravelTicket.CustomerID
FROM   TravelTicket(NOLOCK)
        LEFT JOIN TicketCharge(NOLOCK)
            ON  TravelTicket.CustomerID = TicketCharge.CustomerID      
            AND AvailableAmount > 999
            AND [Status] != 999
            AND TicketCharge.chargetype = 999
            AND TicketCharge.IsSuccessful = 999
            AND IsDeleted != 999
            AND travelTicket.IsInCome <> 999
            AND TravelTicket.IsInCome <> 998
            AND TicketCharge.FeeMonth =  ‘999‘
WHERE  TicketChargeDate < GETDATE()
        AND TicketCharge.IsSuccessful IS NULL
GROUP BY
        TravelTicket.CustomerID

  再运行一次看看

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time:
   CPU time = 0 ms, elapsed time = 0 ms.

(1000 row(s) affected)
Table ‘Worktable‘. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Workfile‘. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘TravelTicket‘. Scan count 1, logical reads 604, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘TicketCharge‘. Scan count 1, logical reads 262, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 31 ms,  elapsed time = 151 ms.
SQL Server parse and compile time:
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

  travelticket表的逻辑读降低到了10分之一以内。执行计划类似

  

  那么下面我们再进行索引优化 先来针对travelticket表。

  首先去掉不等于逻辑 当你的sql语句存在不等于逻辑的时候 索引将不会被使用。同样的情况也存在于“like”,is null或者is not null

SELECT top 1000 a.CustomerID
FROM   TravelTicket(NOLOCK) a
        LEFT JOIN TicketCharge(NOLOCK)
            ON  a.CustomerID = TicketCharge.CustomerID
       AND a.AvailableAmount > 999
	    AND (a.[Status] = 991 OR a.[Status] = 992 OR a.[Status] = 993 )
	    AND (a.IsInCome = 991 OR a.IsInCome =993 OR a.IsInCome =994 OR a.IsInCome =995)
	    AND TicketCharge.IsDeleted != 999
            AND TicketCharge.chargetype = 999
            AND TicketCharge.IsSuccessful =999
            AND TicketCharge.FeeMonth =  ‘999‘
WHERE  TicketChargeDate < GETDATE()
			AND TicketCharge.IsSuccessful IS NULL
GROUP BY a.CustomerID

  这里有个细节我将distinct 换成了group by ,在数据量小的时候 这2者其实没什么区别,但是当数据量变得庞大后后者的效率是优于前者的。

  我们来继续添加索引

  

  

  再运行一次 看看

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time:
   CPU time = 0 ms, elapsed time = 0 ms.

(1000 row(s) affected)
Table ‘Worktable‘. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘Workfile‘. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘TravelTicket‘. Scan count 1, logical reads 60, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table ‘TicketCharge‘. Scan count 1, logical reads 262, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 15 ms,  elapsed time = 188 ms.
SQL Server parse and compile time:
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

  travelticket表的逻辑读再次降低到了10分之一以内。我们看看执行计划有哪些变化。

  聚集索引扫描变成为索引查找,KO!

  



除了上述人为的添加索引 还有一种取巧的办法

  打开sql server profiler

  

  按照自己的需求新建一个跟踪脚本

  

  写个脚本循环跑这个语句然后保存跟踪脚本。打开推荐sql优化器

  

  导入刚才的跟踪脚本,并且选择索引优化

  

  执行分析

  

  


数据处理优化

  对于db优化在程序员能力已经到瓶颈的前提下,可以着手从应用程序上的细节出发,例如并行。

  所谓并行也就是多线程针对同任务分区分块协同处理。多线程的技术大家都很了解,这里突出以下线程同步的问题。例如我有1000个人我分10组任务执行,如何正确的保证当前10组任务正确的完成互相不冲突并且等到所有任务完成后才开启下一轮1000.

  这里介绍一个比较通用的方法,首先申明一个信号量队列List<ManualResetEvent>();

  取出1000条后对于1000条进行添加顺序标识表示并且模余分组。标识从1开始递增就可以,模余分组方法如下

  testInfos.GroupBy(i => i.index % workTaskCount).Select(g => g.ToList()).ToList();

  其中index为刚才添加的顺序标识, workTaskCount为分组的任务数。

//循环处理批次任务
foreach (var testGroup in testGroups)
{
    var mre = new ManualResetEvent(false);
    manualEvents.Add(mre);
    var testMethodParam = new TestMethodParam
    {
        mrEvent = mre,
        testGroup = TestGroup,
        testParam = testParam
    };
    //线程池处理计划任务
    ThreadPool.QueueUserWorkItem(DoTestMethod, testMethodParam);
}
if (manualEvents.Count != 0)
{
    //等待所有信号量完成 切记这里最大值为64
    WaitHandle.WaitAll(manualEvents.ToArray(), 30 * 60 * 1000);
}

  DoTestMethod就是旧的任务处理逻辑,testMethodParam负责涵盖你旧逻辑中所需要的参数并且包含一个完成信号量。

  这里需要牢记的是WaitHandle等待的信号量最大值为64.

  如果你需要的任务分组数超过64那么这里推荐在DoTestMethod方法中不适用信号量,而是使用原子操作的标识,例如Interlocked.Increment(taskCount)。当taskCount累加到1000(你设计的当前批次值)就结束一轮。不过比起WaitHandle性能上要慢一些。


死锁问题

  锁超时的问题大多是因为表锁产生。解决表锁的问题说难也不难,不过需要牺牲性能。mssql针对主键的更新不会产生表锁而是产生行锁。针对这个问题那么死锁的问题初步解决起来就简单了。

  这是比较通用的处理方法

DECLARE @step INT
DECLARE @id CHAR(12)
create table #tmpTest  --创建临时表
(
	rec_index INT ,
	id CHAR(12)
);
INSERT INTO #tmpTest  (rec_index,ID)
SELECT ROW_NUMBER() OVER(ORDER BY ID) AS rec_index,ID FROM TestTable(nolock) WHERE BID = @bID
SET @rowcount=0
SET @step=1
SELECT @rowcount=COUNT(*) FROM #tmpTest AS tt
WHILE(@step<[email protected])
BEGIN
	SELECT @id=Id
	FROM #tmpTest  AS tt
	WHERE @step=rec_index
	UPDATE TestTable
    SET
		UpdateUser = @UpdateUser,
		UpdateTime = @UpdateTime,
	WHERE  ID [email protected]
	SET @[email protected]+1
END


上篇先到此 优化无止境 下篇等空下来再写了

时间: 2024-08-06 07:45:35

从细节出发 纯后端性能优化实战小结的相关文章

扫‘雷’ 纯后端性能优化实战(已合下篇)

最近2个月没做什么新项目 完全是对于旧的系统进行性能优化 避免超时 死锁 数据处理能力不够等常见的性能问题 这里不从架构方面出发 毕竟动大手脚成本比较高 那么我们以实例为前提 从细节开始 优化角度 一.业务逻辑优化 二.DB优化 三.数据处理优化 四.锁与性能 五.cpu飙高小结 六.crash现象分析 业务逻辑优化 这一条不具有普遍性 不同的业务不同的场景 如果归纳起来 就是在不影响业务的前提下进行流程精简 1. 废弃冗余逻辑 常见于各种基于数据库的检查 很多同学在维护别人代码的时候 没有深入

面向.Net程序员的后端性能优化实战

最近2个月没做什么新项目 完全是对于旧的系统进行性能优化 避免超时 死锁 数据处理能力不够等常见的性能问题 这里不从架构方面出发 毕竟动大手脚成本比较高 那么我们以实例为前提 从细节开始 优化角度 一.业务逻辑优化 二.DB优化 三.数据处理优化 四.锁与性能 五.cpu飙高小结 六.crash现象分析 业务逻辑优化 这一条不具有普遍性 不同的业务不同的场景 如果归纳起来 就是在不影响业务的前提下进行流程精简 1. 废弃冗余逻辑 常见于各种基于数据库的检查 很多同学在维护别人代码的时候 没有深入

蚂蚁金服架构师带你深入性能优化一MySql性能优化实战

概要: Mysql的优化,大体可以分为三部分:索引的优化,sql语句的优化,表的优化.本文主要帮助自己整理思路,也可作为一个学习MySQL优化的提纲. 索引的优化 只要列中含有NULL值,就最好不要在此例设置索引,复合索引如果有NULL值,此列在使用时也不会使用索引 尽量使用短索引,如果可以,应该制定一个前缀长度 对于经常在where子句使用的列,最好设置索引,这样会加快查找速度 对于有多个列where或者order by子句的,应该建立复合索引 对于like语句,以%或者'-'开头的不会使用索

后端服务性能优化实战篇

本文简单介绍下后端服务开发中常用的一些性能优化策略. 1.代码 优化代码实现是第一位的,特别是一些不合理的复杂实现.如果结合需求能从代码实现的角度,使用更高效的算法或方案实现,进而解决问题,那是最简单有效的. 2.数据库 数据库的优化,总体上有3个方面: 1)  SQL调优:除了掌握SQL基本的优化手段,使用慢日志定位到具体问题SQL,使用explain.profile等工具来逐步调优. 2)  连接池调优:选择高效适用的连接池,结合当前使用连接池的原理.具体的连接池监控数据和当前的业务量作一个

Linux性能优化实战

你是否也曾跟我一样,看了很多书.学了很多 Linux 性能工具,但在面对 Linux 性能问题时,还是束手无策?实际上,性能分析和优化始终是大多数软件工程师的一个痛点.但是,面对难题,我们真的就无解了吗? 固然,性能问题的复杂性增加了学习难度,但这并不能成为我们进阶路上的“拦路虎”.在我看来,大多数人对性能问题“投降”,原因可能只有两个. 一个是你没找到有效的方法学原理,一听到“系统”.“底层”这些词就发怵,觉得东西太难自己一定学不会,自然也就无法深入学下去,从而不能建立起性能的全局观. 再一个

Android ListView性能优化实战方案

前言: 对于ListView,大家绝对都不会陌生,只要是做过Android开发的人,哪有不用ListView的呢? 只要是用过ListView的人,哪有不关心对它性能优化的呢? 关于如何对ListView进行性能优化,不仅是面试中常常会被问到的(我前段时间面试了几家公司,全部都问到了这个问题了),而且在实际项目中更是非常重要的一环,它甚至在某种程度上决定了用户是否喜欢接受你的APP.(如果你的列表滑起来很卡,我敢说很多人会直接卸载) 网上关于如何对ListView进行性能优化,提出了很多方案.但

android性能优化实战理论篇

本文地址:http://blog.csdn.net/iamws/article/details/51636175 第二篇:理论 通过之前前篇介绍的工具,我们知道了应该怎么样去获取要分析的数据,但是也仅仅局限在于怎么样获取数据,而没有深入数据分析,这一篇主要讲解的是UI刷新这块部分android理论知识,有了这些知识后,对于上面的数据该怎么分析,你就胸有成竹了. ps:本文只是个人理解后的总结,并不会深入源码层次分析,如有错误,还请麻烦各位帮忙指正~ 这篇文章要解决的理论问题如下: 1.什么是内存

Android UI性能优化实战 识别绘制中的性能问题

出自:[张鸿洋的博客]来源:http://blog.csdn.net/lmj623565791/article/details/45556391 1.概述 2015年初google发布了Android性能优化典范,发了16个小视频供大家欣赏,当时我也将其下载,通过微信公众号给大家推送了百度云的下载地址(地址在文末,ps:欢迎大家订阅公众号),那么近期google又在udacity上开了系列类的相关课程.有了上述的参考,那么本性能优化实战教程就有了坚实的基础,本系列将结合实例为大家展示如何去识别.

Linux性能优化实战: Linux 性能优化答疑(四)(32)

一.上节总结 专栏更新至今,四大基础模块的第三个模块——文件系统和磁盘 I/O 篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,并且热情地留言与讨论. 今天是性能优化的第四期.照例,我从 I/O 模块的留言中摘出了一些典型问题,作为今天的答疑内容,集中回复.同样的,为了便于你学习理解,它们并不是严格按照文章顺序排列的. 每个问题,我都附上了留言区提问的截屏.如果你需要回顾内容原文,可以扫描每个问题右下方的二维码查看. 二.问题 1:阻塞.非阻塞 I/O 与同步.异步 I/