.Net+SQL Server企业应用性能优化笔记1——调研

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介:

在进行了一段时间的调研后,本周开始着手进行性能优化工作。现在在优化工作工作之前,我总结一下调研了的一些信息。

1.背景

客户这是一个03年的时候开发的系统了,所以使用的是.NET 1.1+SQL Server 2000,操作系统用的是Windows2003,使用了这么几年,只是对操作系统进行了升级(从当时的Windows2000升级到2003的)以及对系统进行维护,.Net环境和数据库并没有改变。由于系统中记录了几年的数据,有些表有几百万行的数据,当初没有建立索引和系统程序上考虑的不足造成目前系统运行十分缓慢,需要进行性能优化。我现在都开始用VS2008开发和使用SQL Server 2008数据库了,突然接收这样一个老项目,还真有点不适应,SQL2000我好久没有碰过了。

2.硬件

在硬件环境上,客户的服务器还是很不错的,Web服务器和数据库服务器各一台,都是4个双核CPU、8G内存、1G网络、300G的RAID5硬盘,总体来说感觉很好了。在得知客户是8G的内存时我第一反应就是客户肯定浪费内存了!结果实际一看,果然如此,系统是8G的内存,但是所有程序加起来用的内存才2G左右,而且一直上不去,为什么呢?因为客户使用的是32位的操作系统,所以默认只能支持4G的内存,有2G用于操作系统,另外2G用于应用程序,在任务管理器中可以看到SQL Server只占用了1.7G左右的内存,而不能再占用更多就是这样一个原因。SQL Server是一个做大量数据处理的程序,内存的速度比硬盘快很多,若要处理的数据如果都是在内存中将会比读取硬盘进行处理快的多,所以SQL Server占用的内存越多越好。要突破32位操作系统对应用程序2G的内存限制,可以打开3GB开关,将操作系统的内存使用改为1G,应用程序使用的内存改为3G。当然这里是8G的内存,所以打开3GB开关是不够的,这里就需要打开系统的APE开关,使用SQL Server的AWE功能。另外一种解决办法就是换成64位的操作系统和SQL Server。

3.数据访问

我简单的Review了一下程序代码,该系统是BS程序,三层架构,数据库访问主要是采用SqlHelper调用存储过程和SQL语句,然后使用DataReader最终返回一个对象或对象集合。由于系统是在不断的需求变化中完成了,所以也犯了很多项目的一个通病,为了赶时间,大家就不顾代码的规范和架构,有的是使用存储过程,有的是使用SQL语句,有的是直接在UI层写SQL语句,然后将SQL语句传到业务层,最后再传到数据层去执行。我已经不是第一次看到这样的程序了,我大学实习时开始做的一个项目中也出现过这样的代码,所以感觉可以理解。

在Review数据访问层的代码时,我看到了大量的读取一个DataReader用于填充一个对象的代码如下:

private void fillRegionLevel(RegionLevelMod level, SqlDataReader reader)
{
// 行政级别代码
if (!reader.IsDBNull(0))
level.LevelCode = reader.GetString(0);
// 行政级别名称
if (!reader.IsDBNull(1))
level.LevelName = reader.GetString(1);
// 行政级别描述
if (!reader.IsDBNull(2))
level.LevelDescribe = reader.GetString(2);
}

这样写程序也不会有什么错误,但是整个程序太依赖数据库返回对象的顺序了,如果修改了数据库,在最前面多提供了一列,那么reader.GetString(0)就会错位读取到其他的列,以后的读取也全部错位,所以扩展相对比较麻烦。我个人比较建议的写法是通过列名来读取DataReader中的内容,比如:

if (Convert.IsDBNull(reader["Code"]))
{
level.LevelCode = reader["Code"].ToString();
}

当然其实并不需要这么麻烦,在项目中如果通过DataReader填充一个对象的话,我一般采用反射的方式,在实体类定义的时候就为每个字段添加Attribute,然后统一使用一个方法通过反射方式就可以将类中的字段与DataReader返回的列进行映射了。具体参见示例代码: /Files/studyzy/LoadDbToObjectDemo.rar

还有一个问题就是对于字典表(比如地市区域、类型等),应该使用缓存,将数据保存在Web服务器的内存中,不需要每次都读取数据库。

4.图表

该系统中要使用大量的Chart,为此开发人员自己写了一个服务器控件,通过设置各个属性,然后传入数据,最后执行DataBind()即可。这儿抽象出一个控件是相当不错的,实现了代码功能的复用。整个Chart的展现思路是这样的:

(1)系统将数据传入Chart控件,执行其DataBind方法,该控件将使用GDI+进行绘图。

(2)将绘出的图根据当前的时间等属性保存到服务器硬盘的某个文件夹中。

(3)将绘出的图的路径与<img>代码组合,将这段HTML代码Render出来。

这样功能是实现了,但是也存在几个问题:

  • Chart的绘制是在服务器上进行了,有大量的Chart需要展示时将增加服务器的负担。
  • Chart保存到了硬盘中,然后在img标签中指向该硬盘地址,为了这个图片就做了2次硬盘的IO操作。
  • 整个Chart数据读取和绘制与Page_Load是同一个线程,如果一个Chart在数据读取或绘制过程需要很长时间,则会导致整个页面响应被阻塞。

经过我实际跟踪测试,发现确实如此,如果绘制一个Chart要5秒,一个页面打开时间就必然超过5秒,只有等Chart绘制完成了,整个系统才会Response出来。对于以上提出的问题,我们可以采用以下的解决办法:

  • 将Chart的绘制从服务器转移到客户端,具体做法就是采用Flash或者SilverLight(其实使用JavaScript也可以绘制Chart)。客户端只需要下载了Flash后,系统将要绘制Chart的数据集通过XML、AMF等方式传送给Flash,然后由Flash负责将整个Chart绘制出来。使用Flash后一方面可以减小服务器压力,另一方面可以提供访问页面的速度,另外也可以做出很好的效果提高用户体验。
  • 不进行硬盘的IO操作,在服务器绘制了Chart后,就直接使用一个页面Response出来即可。
  • 将Chart的数据读取和绘制放在另外一个页面中进行,展现Chart的页面只需要输出<img src='Chart.aspx?type=1&data=1,3,5,7'/>这样的HTML即可。所有的数据读取和绘制操作将在Chart.aspx页面进行。这样如果绘制一个Chart要5秒钟,由于页面的线程中并没有执行绘图,所以可以很快返回,浏览器在载入了页面后才会去请求Chart.aspx页面,这个时候才进行绘图。

这里个人更推荐第一种方法,在新版系统中可以考虑采用。不过对于现有系统的优化,当然不能进行这么大的改动,采用第三点即可。

5.HTML

该系统的首页存在性能问题,每次打开都需要很长时间,如果网络情况不好的话就等的更久了。使用FireFox中的FireBug可以跟踪首页打开时请求的所有资源的大小、响应时间等,也可以使用网络抓包工具进行分析。经过我抓包分析发现,首页的HTML有180K左右,其实也不算大,引用的图片、CSS、JS也都不多,但是整个响应很慢除了系统进行大量数据库操作之外与HTML也存在很大关系。查看其HTML发现以下问题:

  • HTML中存在很大的ViewState,但是首页主要是只读的数据绑定,所以很多控件如DataGrid都可以关闭ViewState。
  • HTML中使用的是存储的Table拼接方式,这种方式将导致系统必须把整个大Table载入完成后才会呈现,可以改用div+css的方式,这样每获得了一块DIV就可以呈现一块内容。
  • 没有使用AJAX技术,页面上的很多内容特别耗时间,完全可以通过AJAX进行异步绑定的。

6.数据库

数据库是我本次优化的重点,由于是SQL Server 2000的数据库,所以没有DMV、没有SSMS用的性能监视器、没有包含索引……太多好用的功能没有啊,十分的不方便。对数据库的性能优化一般来说我觉得主要是3个方向:

  • 优化索引,建立该建的索引去掉没有用的索引。
  • 改写查询,使之符合SARG。
  • 减少阻塞和资源等待,避免死锁。

我接下来的工作就是围绕这3个方向展开。在SQL Server性能优化上必不可少的工具就是SQL Server Profiler,也就是SQL2000中的事件探测器。一种是使用Profiler抓取生产环境在业务高峰时的数据,一种是在测试环境中没有其他用户和程序干扰的情况下抓取打开某个页面或者执行某个操作时的SQL跟踪。跟踪的结果保存到数据库中,然后使用查询语句找到Reads和Duration很大的SQL语句,针对这些语句进行性能调优。

对于系统中的死锁,使用如下命令可以打开死锁跟踪记录,一旦数据库中出现了死锁,则将会把死锁信息记录到数据库的日志中。

dbcc traceon (1204, 3605, -1)

dbcc tracestatus(-1)

DBCC TRACEON

数据库优化是一门很大的学问,接下来的日子我将主要进行数据库优化的工作,具体优化过程我也将尽量详细的记录下来,希望对大家也有所帮助。

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
8天前
|
SQL 数据库 C#
C# .NET面试系列十一:数据库SQL查询(附建表语句)
#### 第1题 用一条 SQL 语句 查询出每门课都大于80 分的学生姓名 建表语句: ```sql create table tableA ( name varchar(10), kecheng varchar(10), fenshu int(11) ) DEFAULT CHARSET = 'utf8'; ``` 插入数据 ```sql insert into tableA values ('张三', '语文', 81); insert into tableA values ('张三', '数学', 75); insert into tableA values ('李四',
82 2
C# .NET面试系列十一:数据库SQL查询(附建表语句)
|
8天前
|
SQL 关系型数据库 MySQL
《SQL必知必会》个人笔记(一)
《SQL必知必会》个人笔记(一)
44 0
|
8天前
|
SQL 关系型数据库 MySQL
【MySQL实战笔记】02.一条SQL更新语句是如何执行的-1
【4月更文挑战第4天】SQL更新语句执行涉及查询和日志模块,主要为`redo log`和`binlog`。`redo log`先写日志再写磁盘,保证`crash-safe`;`binlog`记录逻辑日志,支持所有引擎,且追加写入。执行过程分为执行器查找数据、更新内存和`redo log`(prepare状态)、写入`binlog`、提交事务(`redo log`转commit)。两阶段提交确保日志逻辑一致,支持数据库恢复至任意时间点。
22 0
|
8天前
|
SQL Windows
保姆级:Windows Server 2012上安装.NET Framework 3.5
保姆级:Windows Server 2012上安装.NET Framework 3.5
|
8天前
|
SQL 关系型数据库 MySQL
【MySQL系列笔记】常用SQL
常用SQL分为三种类型,分别为DDL,DML和DQL;这三种类型的SQL语句分别用于管理数据库结构、操作数据、以及查询数据,是数据库操作中最常用的语句类型。 在后面学习的多表联查中,SQL是分析业务后业务后能否实现的基础,以及后面如何书写动态SQL,以及完成级联查询的关键。
215 6
|
8天前
|
SQL 存储 关系型数据库
【MySQL系列笔记】SQL优化
SQL优化是通过调整数据库查询、索引、表结构和配置参数等方式,提高SQL查询性能和效率的过程。它旨在减少查询执行时间、减少系统资源消耗,从而提升数据库系统整体性能。优化方法包括索引优化、查询重写、表分区、适当选择和调整数据库引擎等。
237 3
|
8天前
|
SQL 存储 Oracle
《SQL必知必会》个人笔记
《SQL必知必会》个人笔记
16 1
|
8天前
|
SQL 存储 关系型数据库
【MySQL实战笔记】02.一条SQL更新语句是如何执行的-2
【4月更文挑战第5天】两阶段提交是为确保`redo log`和`binlog`逻辑一致,避免数据不一致。若先写`redo log`, crash后数据可能丢失,导致恢复后状态错误;若先写`binlog`,crash则可能导致重复事务,影响数据库一致性。一天一备相较于一周一备,能缩短“最长恢复时间”,但需权衡额外的存储成本。
21 1
|
8天前
|
SQL 存储 缓存
【MySQL实战笔记】01.一条SQL查询语句是如何执行的?
【4月更文挑战第3天】MySQL执行SQL的流程包括连接器、查询缓存、分析器、优化器和执行器。连接器负责建立连接、权限验证,查询缓存(MySQL 8.0已移除)存储查询结果,分析器解析SQL确保语法正确,优化器选择最佳索引和查询路径,执行器执行查询并管理权限。连接器使用长连接可能导致内存问题,可定期断开或使用`mysql_reset_connection`。注意,更新操作会导致查询缓存失效。
21 3
|
8天前
|
Windows
windows server 2019 安装NET Framework 3.5失败,提示:“安装一个或多个角色、角色服务或功能失败” 解决方案
windows server 2019 安装NET Framework 3.5失败,提示:“安装一个或多个角色、角色服务或功能失败” 解决方案
277 0