系统优化内部交流_第1页
系统优化内部交流_第2页
系统优化内部交流_第3页
系统优化内部交流_第4页
系统优化内部交流_第5页
已阅读5页,还剩15页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、应用系统优化(1)-、设计概念.索引分析索引给性能带来的好处有一定的代价,索引表需要更多的存储和内存空间。索引设计建议:使用窄索引可以在8k索引页容纳笔款索引更多的行,减少 I/O ,减少空间,缓存更有效;在索引中使用较少的歹U, 避免使用宽数据列席检查列的唯一性如性别、状态字段加索引对性能没有好处。检查数据类型短小容易计算的数据类型越有效考虑列的顺序复合索引的列属性是索引效率的重要因素。排列索引字段的考虑因素:列唯一性、列宽、列数据类型。.书签查找简单的说就是当你使用的 sql查询条件和select返回的列没有完全包含在索引列中时就会发生书签查 找。避免书签查找的方法:使用聚集索引,使用覆盖

2、索引(组合索引),使用索引连接.统计分析对于数据库的优化器来说,是基于数据分布的最新信息帮助优化器确定所使用的查询策略。在SQLServer中,这个信息一统计的形式维护。手动控制维护配置:无索引列桑新建统计,更新现有统计(自动更新),用于收集统计的采样度,现有统计的异步更新.碎片清理索引碎片的成因,包括 Insert,update语句引起的页面分割分析,碎片的解决:重建索引,自动维护二SQL性能优化.查询的模糊匹配尽量避免在一个复杂查询里面使用LIKE %parm1%,百分号会导致相关列的索引无法 使用,最好不要用.解决办法:其实只需要对该脚本略做改进,查询速度便会提高近百倍。改进方法如下:A

3、)修改前台程序一一把查询条件的供应商名称一栏由原来的文本输入改为下拉列表,用户模糊输入供应商名称时,直接在前台就帮忙定位到具体的供应商,这样在调用后台程序时,这列就可以直接用等于来关联了。B)直接修改后台一一根据输入条件,先查出符合条件的供应商,并把相关记录保存在一个临时表里头,然后再用临时表去做复杂关联.用=替代高效:SELECT * FROM EMPWHEREDEPTNO =4低效:SELECT * FROM EMP WHERE DEPTNO 3两者的区别在于,前者DBMSW直接跳到第一个 DEPT等于4的记录而后者将首先定位到 DEPTNO=3记录并且向前扫描到第一个 DEP砍于3的记录

4、.索引问题在做性能跟踪分析过程中,经常发现有不少后台程序的性能问题是因为缺少合适索引造成 的,有些表甚至一个索引都没有。这种情况往往都是因为在设计表时,没去定义索引,而开发 初期,由于表记录很少,索引创建与否,可能对性能没啥影响,开发人员因此也未多加重视。然一旦程序发布到生产环境,随着时间的推移,表记录越来越多这时缺少索引,对性能的影响便会越来越大了。这个问题需要数据库设计人员和开发人员共同关注 法则:不要在建立的索引的数据列上进行下列操作 : 避免对索引字段进行计算操作避免在索引字段上使用not, , !=避免在索引列上使用IS NULL和IS NOT NULL避免在索引列上出现数据类型转换

5、避免在索引字段上使用函数避免建立索引的列中使用空值。.复杂操作部分UPDATESELECT句写得很复杂(经常嵌套多级子查询)一一可以考虑适当拆成几 步,先生成一些临时数据表,再进行关联操作.在可以使用UNION ALU勺语句里,使用了 UNIONUNION因为会将各查询子集的记录做比较,故比起 UNION ALL,通常速度都会慢上许多。 一般来说,如果使用UNION ALLfg满足要求的tS,务必使用 UNION ALL还有一种情况大家可 能会忽略掉,就是虽然要求几个子集的并集需要过滤掉重复记录,但由于脚本的特殊性,不可能存在重复记录,这时便应该使用 UNION ALL.在WHER曲句中,尽量

6、避免对索引字段进行计算操作这个常识相信绝大部分开发人员都应该知道,但仍有不少人这么使用,我想其中一个最主 要的原因可能是为了编写简单而损害了性能,那就不可取了.对Where语句的法则避免在 WHERE句中使用in , not in , or或者having。可以使用 exist 和not exist 代替in 和 not in 。可以使用表链接代替exist o Having可以用where代替,如果无法代替可以分两步处理。例子SELECT* FROMORDERSWHERECUSTOMER_NANMOETIN (SELECTCUSTOMER_NAFMREOMCUSTOMER) 优化SELECT

7、* FROMORDERSWHERECUSTOMER_NAME exist (SELECT CUSTOMER_NAFEOM CUSTOMER)不要以字符格式声明数字,要以数字格式声明字符值。(日期同样)否则会使索引无 效,产生全表扫描。例子使用:SELECT emp.ename, emp.job FROM emp WHERE emp.empno = 7369;不要使用:SELECT emp.ename, emp.job FROM emp WHERE emp.empno =7369.排序避免使用耗费资源的操作,带有 DISTINCT,UNION,MINUS,INTERSECT,ORDYl勺SQL语

8、句 会启动SQL引擎执行,耗费资源的排序(SORT功能.DISTINCT需要一次排序操作,而其他的 至少需要执行两次排序.临时表善于使用临时表可以极大的提高系统性能总结:所谓的优化就是 WHERE旬利用了索引,不可优化即发生了表扫描或额外开销。经 验显示,SQL Server性能的最大改进得益于逻辑的数据库设计、索引设计和查询设计方面。 反过来说,最大的性能问题常常是由其中这些相同方面中的不足引起的。其实SQLW化的实质就是在结果正确的前提下,用优化器可以识别的语句,充份利用索引,减少表扫描的I/O次数, 尽量避免表搜索的发生。三、EntityFramework 和 MyBatis.禁用延迟加

9、载尽量的减少数据库的访问次数。.优化操作 AsNoTracking()与Attach对于只读操作,强烈建议使用AsNoTracking进行数据获取,这样省去了访问 EF Context的时间, 会大大降低数据获取所需的时间。同时由于没有受到上下文的跟踪缓存,因此取得的数据也是及时最新的,更利于某些对数据及时性要求高的数据查询。db.Person.Where(a = a.IsDeleted = false).AsNoTracking().ToList();使用此方法比不使用而将其附加到上下文容器中,性能提升不是一点点。代码1 :EntityDB db = new EntityDB();var u

10、sers = db.User.AsNoTracking().ToList();foreach (var user in users)db.Set().Attach(user);foreach (var user in users)user.IsDeleted = true;/db.Entry(user).State=EntityState.Modified;db.SaveChanges();代码2:EntityDB db = new EntityDB();var users = db.User.AsNoTracking().ToList();/* foreach (var user in us

11、ers)db.Set().Attach(user);*/foreach (var user in users)user.IsDeleted = false;db.Entry(user).State=EntityState.Modified;db.SaveChanges();对比我们得出结论第一种采用Attach后赋值的方法是执行的按需更新,也就是说更新哪个字段就update它,而第二种则是不管更新了哪个字段,生成的 SQL语句都是更新全部。为什么第一种方法中我Attach后仅仅只是给对象赋值且没有修改State为Modified,但EF却能帮我修改数据值,那是因为当SaveChanges时,将

12、会自动调用 DetectChanges方法,此方法将扫描上下文中所有实体,并比较当前属性值和存储在快照中的原始属性值。如果被找到的属性值发生了改变,此日EF将会与数据库进行交互,进行数据更新,所以不用设置 State为Modified。对于删除操作则需要在Attach 后设置 db.Entry(user).State = EntityState.Deleted;. EF使用 SqlQuery对于某些特殊业务,我们也可以使用sql语句查询实体,以下只是一个简单的事例操作SqlParameter parameter = ;var user = db.Database.SqlQuery(select

13、 * from user, parameter).ToList();此方法获得的实体查询是在数据库( Database )上,实体不会被上下文跟踪。SqlParameter parameter = ;var user = db.Set().SqlQuery(select * from user, parameter).ToList();此方法获得的实体查询是被上下文跟踪,所以能直接赋值后SaveChanges()。var user = db.Set().SqlQuery(select * from user).ToList();user.Last().Name = makmong;db.Sav

14、eChanges();当然同样支持带参数的查询与存储过程操作。.关于 AsNonUnicodevar query = db.User.Where(a=a.Name=makmong).ToList();var query = db.User.Where(a=a.Name= DbFunctions.AsNonUnicode(makmong).ToList();其中生成的SQL语句区别了,一个加了N, 一个未加N, N是将字符串作为 Unicode格式进行存储。因为.Net字符串是Unicode格式,在上述SQL的Where子句中当一侧有 N型而另一侧没有 N型时,此时会进行数据转换,也就是说如果你

15、在表中建立了索引此时会失效代替的是造成全表扫描。用 DbFunctions.AsNonUnicode方法来告诉.Net将其作为一个非 Unicode来对待,此时生成的 SQL语句两侧都没有N型,就不会进行更多的数据转换,也就是说不会造成更多的全表扫描。所以当有大量数据时如果不进行转换会造成意想不到的结果。因此在进行字符串查找或者比较时建议用AsNonUnicode()方法来提高查询性能。. ViewModel替代实体 Entity大家可能都会碰到这种情况就是对应数据库的Entity实体拥有多个字段,但是查询数据到页面展示的时候可能只需要显示那么几个字段,这个时候建议使用ViewModel查询,

16、也就是说需要哪些字段就查询哪些,而不是 “select * ”将全部字段加载出来。此操作即出于安全考虑(不应该将实体Model直接传递到View上面),同时查询的字段减少(可能就几个)对查询性能也有所提升。例:var query = db.User.ToList();接着新建ViewModelpublic class UserViewModelpublic int Id get; set; public string Name get; set; 开始查询:var query = db.User.Select(a=new UserViewModel()Id = a.Id,Name = a.Na

17、me).ToList();.实体的枚举使用byte类型我们先来了解下 Sqlserver 中 tinyint, smallint, int, bigint 的区别bigint :从-263(-9223372036854775808) 到 263-1(9223372036854775807)的整型数据,存储大小为 8 个字节。一个字节就是 8位,那么bigint就有64位int:从-231(-2,147,483,648)到231-1(2,147,483,647)的整型数据,存储大小为 4个字节。int类型,最大可以存储32位的数据smallint :从-215(-32,768)到215-1(32

18、,767)的整数数据,存储大小为 2个字节。smallint就是有16位tinyint :从0至U 255的整数数据,存储大小为 1字节。tinyint就有8位。所以对于有些范围比较短的数值长度,例如枚举类型值,完全可以使用byte类型替换int类型,对应生成数据库tinyint类型以节省数据存储。如:public CouponType CouponType get; set; public enum CouponType : byteRedBag = 0,Experience = 1,Cash = 2,JiaXiQuan = 3. DateTime控制内容精度DateTime 字段类型对应的

19、时间格式是yyyy-MM-dd HH:mm:ss.fff , 3个f,精确到 1毫秒(ms),示例 2014-12-03 17:06:15.433 我们知道 EF Model的DateTime对应的SQL类型是 DateTime但是在业务操作中很多时间值我们仅仅只需要精确到秒就够了(特殊业务除外),那多余的毫秒数既无用又占数据库存储,既然是优化操作那么我们是否可以去除毫秒数而只存储到秒呢 例:2014-12-03 17:06:158.合理使用EF扩展EF 最有力的扩展库 EntityFramework.Extended指定字段更新db.User.Where(a = true).Update(a

20、 = new User() Name = ceshi);db.User.Where(a = true).Delete();批量查询功能EF做法:查询两次var q = db.User.Where(u = u.Name.StartsWith(a);var count = q.Count();var data = q.Skip(10).Take(10).ToList();EF扩展库的做法:一次查询var q = db.User.Where(t = t.Name.StartsWith(a);var q1 = q.FutureCount();var q2 = q.Skip(10).Take(10).F

21、uture();var data = q2.ToList();var count = ql.Value;查询缓存功能我们现在的后台项目权限管理模块,所有的菜单项都是写进数据库里,不同的角色用户所获取展示的菜单项各不相同。项目导航菜单就是频繁的访问数据库导致性能低下(一开始得到1级菜单,然后通过1级获取2级菜单,2级获取3级)解决方法就是第一次查询后把数据给缓存起来设定缓存时间,然后一段时间继续查询此数据(譬如整个页面刷新)则直接在缓存中获取,从而减少与数据库的交互。代码如下:var users = db.User.Where(u = u.Id = 5).FromCache(CachePolic

22、y.WithDurationExpiration(TimeSpan.FromSeconds(30);如果在30秒内重复查询,则会从缓存中读取,不会查询数据库我们再提出二个问题1、在第一次上下文中获得数据缓存,然后给某一个 User.Name赋值sss”,没有做aveChanges()操作情况下,接着从缓存中获取数据,缓存值是否相应的更改了 ?SaveChanges()操作之后呢?2、在不同的上下文中缓存获取结果是一样的吗?另外需要注意的是更改的时候可以根据操作结果选择是否继续缓存,例如数据更改失败但是缓存却改 动了,下次取值数据就会不一致,所以当我们在更新数据库失败时就可以选择移除缓存调用 R

23、emoveCache() 方法9. EF使用SQL分库操作当数据库的表及数据达到一定规模后我们想到的优化就有分库,分表之类的优化操作。对于之前的ADO.NET来说分库是一件很普通的操作。比如下面的非跨数据库查询语句:SELECT Name FROM dbo.User WHERE ID=1跨数据库查询语句:SELECT Name FROM MaiMangAdb.dbo.blog WHERE ID=1我们知道EF的DbContext中已经指定了连接字符串public EntityDB() : base(DefaultConnection)也就是说所有的上下文操作都是基于这个数据库来操作的,那我们就

24、不能用ADO.NET那套,多个查询配多个链接去操作数据库。当然大神们也给出了一套方法,而且也是简单明了。那我也就直接将其移植过来记录一下吧。方法就是给数据库添加SYNONYM 同义词,演示下创建2张Model表User和Rolepublic class UserKeypublic int Id get; set; public string Name get; set; public bool IsDeleted get; set; DateTime2Precisionpublic DateTime CreateDateTime get; set; public class RoleKeypu

25、blic int Id get; set; public string Name get; set; 并添加一条语句:EntityDB db = new EntityDB();db.User.Add(new User Id = 1, Name = ddd ,CreateDateTime = DateTime.Now);db.Role.Add(new Role() Id = 1, Name = admin);EF操作。db.SaveChanges();现在数据库表及内容都有了。然后我要把User表及内容移植到另一个数据库中,且不影响当前的创建新的数据库 EFSYNONYM 并添加User表,表结

26、构和 EFStudy中的User 一致。然后在EFStudy中删除表User且创建同义词CREATE SYNONYM dbo.Users FOR EFSYNONYM.dbo.Users此时的User和Role已经分别存在于不同的数据库里面,至此分库成功。当然此方法也有个缺点就是分库表和主表间由同义词关联而无法建立主外键关系(其实当数据量达到一定级别后联合 join查询反而不如分开多次查询来得快四、.NET程序性能优化的基本要领Bill Chiles (Roslyn编译器的程序经理)写了一篇文章Essential Performance Facts and .NET Framework Tips

27、 ,.不要进行过早优化一一在定位和发现问题之后再进行调优。编写代码比想象中的要复杂的多,代码需要维护,调试及优化性能。一个有经验的程序员,通常会对自然而然的提出解决问题的方法并编写高效的代码。但是有时候也可能会陷入过早优化代码的问题中。比如,有时候使用一个简单的数组就够了,非要优化成使用哈希表,有时候简单的重新计算一下可以,非要使用复杂的可能导致内存泄漏的缓存。发现问题时,应该首先测试性能问题然后再分析代码。.专业测试不会说谎一一没有评测,便是猜测。剖析和测量不会撒谎。测评可以显示CPU是否满负荷运转或者是存在磁盘I/O阻塞。测评会告诉你应用程序分配了什么样的以及多大的内存,以及是否CPU花费

28、了很多时间在垃圾回收上。应该为关键的用户体验或者场景设置性能目标,并且编写测试来测量性能。.好工具很重要。一一下载PerfView ,然后去看使用教程。好的工具能够让我们能够快速的定位到影响性能的最大因素(CPU ,内存,磁盘)并且能够帮助我们定位产生这些瓶颈的代码。PerfView 是一款免费且性能强大的工具,他主要关注影响性能的一些深层次的问题(磁盘I/O , GC事件,内存),后面会展示这方面的例子。我们能够抓取性能相关的Event Tracing for Windows (ETW)事件并能以应用程序,进程,堆栈,线程的尺度查看这些信息。PerfView 能够展示应用程序分配了多少,以及

29、分配了何种内存以及应用程序中的函数以及调用堆栈对内存分配的贡献。.内存分配决定app的响应性。一一也是新的编译器性能团队花的时间最多的地方你可能会想,编写响应及时的基于.NET的应用程序关键在于采用好的算法,比如使用快速排序替代冒泡排序,但是实际情况并不是这样。编写一个响应良好的app的最大因素在于内存分配,特别是当 app非常大或者处理大量数据的时候。常见的内存分配以及例子装箱装箱发生在当通常分配在线程栈上或者数据结构中的值类型,或者临时的值需要被包装到对象中的时候(比如分配一个对象来存放数据,活着返回一个指针给一个Object对象)。.NET框架由于方法的签名或者类型的分配位置,有些时候会

30、自动对值类型进行装箱。将值类型包装为引用类型会产生内存分配。.NET框架及语言会尽量避免不必要的装箱,但是有时候在我们没有注意到的时候会产生装箱操作。过多的装箱 操作会在应用程序中分配成 M上G的内存,这就意味着垃圾回收的更加频繁,也会花更长时间。字符串字符串操作是引起内存分配的最大元凶之一,通常在 PerfView中占到前五导致内存分配的原因。应用程序 使用字符串来进行序列化,表示JSON和REST。在不支持枚举类型的情况下,字符串可以用来与其他系统进行交互。当我们定位到是由于string操作导致对性能产生严重影响的时候,需要留意 string类的Format(),Concat(),Spli

31、t(),Join(),Substring()等这些方法。使用 StringBuilder 能够避免在拼接多个字符串时创建多个新字符串的开销,但是StringBuilder的创建也需要进行良好的控制以避免可能会产生的性能瓶颈。LINQ和Lambdas 表达式使用LINQ和Lambdas表达式是C#语言强大生产力的一个很好体现,但是如果代码需要执行很多次的时候,可能需要对 LINQ或者Lambdas表达式进行重写。class Syfbol public string Name getj private set; ) class Conpiler private List symbols;publi

32、c Symbol FindMatchingSymbcl(string name) ( return symbols,Fir5t0rDefault(s = s= naie); )第一行中,lambda表达式 s=s.Name=name ”是对本地变量 name的一个闭包。这就意味着需要分配额外的对象来为委托对象predict分配空间,需要一个分配一个静态类来保存环境从而保存name的值。(GetEnumerator(); )public Syrbol FindMatchingSymbol(string ndne) !foreach (Syitibol s in symbols) if (s.Na

33、ine is nam) return s; ) return null;码中并没有使用 LINQ扩展方法,lambdas表达式和迭代器,并且没有额外的内存分配开销。这是因为编译器看到symbol是List类型的集合,因为能够直接将返回的结构性的枚举器绑定到类型正确的本地变量 上,从而避免了对struct类型的装箱操作。原先的代码展示了 C#语言丰富的表现形式以及.NET Framework 强大的生产力。该着后的代码则更加高效简单,并没有添加复杂的代码而增加可维护性。Aync异步接下来的例子展示了当我们试图缓存异步方法返回值时的一个普遍问题:class Parser(/*/public Syn

34、tax 1 roe Syntaxget;public I ask ParseSourceCode()(class Compilation(public a$ync Task GetSyntaxTreeAsync() var parser = new Parser(); / allocation mwait parser-ParseSourceCcdeO; / expensive return parserSyntaxi由于Visual Studio的一些特性可能需要多次获取相同的语法树,所以通常可能会缓存解析结果来节省时间 和内存分配,但是下面的代码可能会导致内存分配所以通常可能会缓存解析结果

35、来节省时间由于Visual Studio的一些特性可能需要多次获取相同的语法树,和内存分配,但是下面的代码可能会导致内存分配解决办法class Compilation (private J ask cachedResult;public 1 isk GetSyntaxTreeAsync() (return this.cachedResult ? (this t cachedResjit = GetSyntaxTreeUncachedAsync();)private async Ia5k GetSynt43KTrecUnc3chedAsync() var parser = new Parser(); / allocation await parser,Pa reeSourceCode(); / expensivereturn parser.Syntax;) )注意 GetSyntaxTreeAsync 并没

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论