背景:性能应该是功能的一个重要参考,特别是在大数据的背景之下!写SQL语句时如果仅考虑业务逻辑,而不去考虑语句效率问题,有可能导致严重的效率问题,导致功能不可用或者资源消耗过大。其中的一种情况是,处理每日增量数据的程序,实际执行过程中可能会进行全表扫描,效率与全量程序并无二致。

案例:

mio_log数据量:134,092,418条记录

freph_a01_fromtask3数据量:176,581,388条记录

生产系统上按照业务处理逻辑编写的SQL语句核心代码如下:

  1. SELECT (CASE
  2. WHEN c.in_force_dateISNOT NULL
  3. THEN (CASE
  4. WHEN a.mio_date>=c.in_force_dateTHENa.mio_date
  5. ELSE c.in_force_date
  6. END )
  7. WHEN c.in_force_dateISNULL THEN (CASE
  8. WHEN a.mio_date>=a.plnmio_dateTHENa.mio_date
  9. ELSE a.plnmio_date
  10. END )
  11. ELSE a.mio_date
  12. END ) mio_date
  13. FROM   dbo.mio_loga
  14. INNER JOIN dbo.freph_a01_fromtask3c
  15. ON a.cntr_no = c.cntr_no
  16. AND a.pol_code=c.pol_code
  17. WHERE  ((c.in_force_dateISNOT NULL
  18. AND((CASE
  19. WHEN a.mio_date>=c.in_force_dateTHENa.mio_date
  20. ELSE c.in_force_date
  21. END ) BETWEEN @stat_begindateAND@stat_enddate))
  22. OR(c.in_force_dateISNULL
  23. AND((CASE
  24. WHEN a.mio_date>=a.plnmio_dateTHENa.mio_date
  25. ELSE a.plnmio_date
  26. END ) BETWEEN @stat_begindateAND@stat_enddate)) )

导致虽然mio_log表的mio_date、plnmio_date字段,以及freph_a01_fromtask3表的in_force_date字段上均有索引,但是由于两表不同字段进行CASE WHEN比较,执行计划为聚集索引扫描:

优化思路:

由于mio_log表的mio_date、plnmio_date字段,以及freph_a01_fromtask3表的in_force_date字段上均有索引,可先通过单个mio_date、in_force_date、plnmio_date索引取出增量时间段数据,在增量数据上进行不同表、字段的比对。

  1. SELECT (CASE
  2. WHEN in_force_date IS NOT NULL
  3. THEN ( CASE
  4. WHEN mio_date >= in_force_dateTHENmio_date
  5. ELSE in_force_date
  6. END )
  7. WHEN in_force_date IS NULL
  8. THEN ( CASE
  9. WHEN mio_date >= plnmio_dateTHENmio_date
  10. ELSE plnmio_date
  11. END )
  12. ELSE mio_date
  13. END )               mio_date
  14. from(
  15. SELECT a.mio_date,
  16. c.in_force_date,
  17. a.plnmio_date,
  18. a.MIO_LOG_ID
  19. FROM   dbo.mio_loga
  20. INNER JOIN dbo.freph_a01_fromtask3c
  21. ON a.cntr_no = c.cntr_no
  22. ANDa.pol_code=c.pol_code
  23. WHERE
  24. a.mio_dateBETWEEN@stat_begindateAND@stat_enddate
  25. union
  26. SELECT a.mio_date,
  27. c.in_force_date,
  28. a.plnmio_date,
  29. a.MIO_LOG_ID
  30. FROM   dbo.mio_loga
  31. INNER JOIN dbo.freph_a01_fromtask3c
  32. ON a.cntr_no = c.cntr_no
  33. ANDa.pol_code=c.pol_code
  34. WHERE
  35. c.in_force_dateBETWEEN@stat_begindateAND@stat_enddate
  36. union
  37. SELECT a.mio_date,
  38. c.in_force_date,
  39. a.plnmio_date,
  40. a.MIO_LOG_ID
  41. FROM   dbo.mio_loga
  42. INNER JOIN dbo.freph_a01_fromtask3c
  43. ON a.cntr_no = c.cntr_no
  44. ANDa.pol_code=c.pol_code
  45. WHERE
  46. a.plnmio_dateBETWEEN@stat_begindateAND@stat_enddate
  47. ) T
  48. WHERE  ((in_force_dateIS NOT NULL
  49. AND((CASE
  50. WHEN mio_date>= in_force_dateTHENmio_date
  51. ELSE in_force_date
  52. END ) BETWEEN @stat_begindateAND@stat_enddate))
  53. OR(in_force_dateIS NULL
  54. AND((CASE
  55. WHEN mio_date>= plnmio_dateTHENmio_date
  56. ELSE plnmio_date
  57. END ) BETWEEN @stat_begindateAND@stat_enddate)) )

该语句存在两个问题:

1.       如果子查询中mio_log、freph_a01_fromtask3没有主键,则需通过ROWID标识不同记录,即如果没有主键,可以通过ROWID进行替换。

ROWID这个概念在Oracle中非常重要,使用也非常广泛,其意义如下:

ROWIDPseudocolumn

Foreach row in the database, the ROWID pseudocolumn returns the address of therow. oracle Database rowid values contain information necessary to locate arow:

·         The dataobject number of the object

·         The datablock in the datafile in which the row resides

·         The positionof the row in the data block (first row is 0)

·         The datafilein which the row resides (first file is 1). The file number is relative to thetablespace.

SQLServer中并没有ROWID这个概念, SQL Server2008及以后版本中%%physloc%%虚拟列与ROWID最相近,信息如下:

The closest equivalent tothis in SQL Server is the rid which has three componentsFile:Page:Slot.

In SQL Server 2008 it ispossible to use the undocumented and unsupported %%physloc%% virtual column to see this. Thisreturns a binary(8) value with the Page ID in the firstfour bytes, then 2 bytes for File ID, followed by 2 bytes for the slot locationon the page.

The scalar function sys.fn_PhysLocFormatter or the sys.fn_PhysLocCracker TVF can be used to convert this into amore readable form.

  1. CREATE TABLET(XINT);
  2. INSERT INTOTVALUES(1),(2)
  3. SELECT %%physloc%%AS[%%physloc%%],
  4. sys.fn_PhysLocFormatter(%%physloc%%)AS[File:Page:Slot]
  5. FROM T

%%physloc%%

File:Page:Slot

0x7600000001000000

(1:118:0)

0x7600000001000100

(1:118:1)

Note that this is not leveraged by the queryprocessor. Whilst it is possible to use this in a WHERE clause

  1. SELECT *FROMT
  2. WHERE %%physloc%%=0x7600000001000000

SQL Server will not directly seek to thespecified row. Instead it will do a full table scan, evaluate %%physloc%% foreach row and return the one that matches (if any do).

2.     该语句有parameter sniffing问题:

当使用存储过程的时候,总是要使用到一些变量。变量有两种,一种是在存储过程的外面定义的,当调用存储过程的时候,必须要给它代入值,SQLServer在编译时知道它的值是多少。还有一种变量是在存储过程里面定义的。它的值是在存储过程的语句执行过程中得到的。对这种本地变量,SQLServer在编译时不知道它的值是多少。

SQLServer在处理存储过程时,为了节省编译时间,是一次编译多次使用的。那么计划重用就有两个潜在问题:

(1) 对于第一类变量,根据第一次运行时带入的值生成的执行计划,是不是就能够适合所有可能的变量值?

(2) 对于第二类本地变量,SQL Server在编译时并不知道它的值是多少,那怎么选择“合适”的执行计划?

parametersniffing”问题的定义:因为语句的执行计划对变量值很敏感,而导致重用执行计划会遇到性能问题。本地变量做出来的执行计划是一种比较“中庸”的方法,一般不会有parameter sniffing那么严重,很多时候,它还是解决parametersniffing的一个候选方案。

解决parameter sniffing问题的方法:

(1) 用exec()方式运行动态SQL语句:如果在存储过程里不是直接运行语句,而是把语句带上变量,生成一个字符串,再让exec()命令多动态语句运行,那SQL Server就会在运行到这个语句的时候,对动态语句进行编译。这时,SQLServer已经知道了变量的值,会根据值生成优化的执行计划,从而绕过parametersniffing问题。

(2) 使用本地变量:如果把变量值赋给一个本地变量,SQLServer在编译的时候是没有办法知道这个本地变量的值的。所以它会根据表格里数据的一般分布情况“猜测”一个返回值。不管用户在调用存储过程的时候带入的变量值是多少,做出来的执行计划都是一样的。而这样的执行计划一般比较“中庸”,不会是最优的执行计划,但是对大多数变量值来讲,也不会是一个很差的执行计划。该方法的好处是保持了存储过程的优点,缺点是要修改存储过程,而执行计划也不是最优的。

(3) 在语句里使用query hint指定执行计划:

在SELECT、INSERT、UPDATE、DELETE语句最后,可以加一个“Option(<query_hint>)”子句,对SQL Server将要生成的执行计划进行指导。目前的query_hint很强大,有十几种hint。完整的定义如下:

  1. <query_hint>::=
  2. { {HASH| ORDER } GROUP
  3. | {CONCAT| HASH | MERGE} UNION
  4. | {LOOP| MERGE | HASH} JOIN
  5. | FASTnumber_rows
  6. | FORCEORDER
  7. | MAXDOPnumber_of_processors
  8. | OPTIMIZEFOR( @vaariable_name= literal_constant[ , ...n ])
  9. | PARAMETERIZATION{SIMPLE | FORCED }
  10. | RECOMPILE
  11. | ROBUSTPLAN
  12. | KEEPPLAN
  13. | KEEPFIXEDPLAN
  14. | EXPANDVIEWS
  15. | MAXRECURSIONnumber
  16. | USEPLANN'xml_plan'
  17. }

这些hint的用途不一样。有些是引导执行计划使用什么样的运算的,例如{HASH| ORDER } GROUP、{CONCAT |HASH | MERGE} UNION、{LOOP| MERGE|HASH} JOIN。有些是防止重编译的,例如PARAMETERIZATION{SIMPLE | FORCED }、KEEPPLAN、KEEPFIXEDPLAN,有些是强制重编译的,如RECOMPILE。有些是影响执行计划的选择的,如FASTnumber_rows、FORCEORDER、MAXDOPnumber_of_processors、OPTIMIZEFOR( @vaariable_name= literal_constant[ , ...n ]),它们是和在不同的场合。具体定义参见SQL Server联机帮助。

为避免parameter sniffing问题,主要有以下几种常见query hint

(1)Recompile

Recompile这个查询提示告诉SQL Server,语句在每一次存储过程运行的时候,都要重新编译一下。这样就能够使SQL Server根据当前变量的值,选一个最好的执行计划。对前面的那个例子,我们可以这么改写。

  1. CREATE PROCNosniff_queryhint_recompile(@iINT)
  2. AS
  3. SELECT Count(b.SalesOrderID),
  4. Sum(p.Weight)
  5. FROM   dbo.SalesOrderHeader_testa
  6. INNER JOIN dbo.SalesOrderDetail_testb
  7. ON a.SalesOrderID=b.SalesOrderID
  8. INNER JOIN Production.Productp
  9. ON b.ProductID=p.ProductID
  10. WHERE  a.SalesOrderID=@i
  11. OPTION (recompile)
  12. go

和这种方法类似的,是在存储过程的定义里直接指定"recompile",也能达到避免parameter sniffing的效果。

  1. CREATE PROCNosniff_spcreate_recompile(@iINT)
  2. WITH recompile
  3. AS
  4. SELECT Count(b.SalesOrderID),
  5. Sum(p.Weight)
  6. FROM   dbo.SalesOrderHeader_testa
  7. INNER JOIN dbo.SalesOrderDetail_testb
  8. ON a.SalesOrderID=b.SalesOrderID
  9. INNER JOIN Production.Productp
  10. ON b.ProductID=p.ProductID
  11. WHERE  a.SalesOrderID=@i
  12. go

(2)   指定JOIN运算

  1. CREATE PROCNosniff_queryhint_joinhint(@iINT)
  2. AS
  3. SELECT Count(b.SalesOrderID),
  4. Sum(p.Weight)
  5. FROM   dbo.SalesOrderHeader_testa
  6. INNER JOIN dbo.SalesOrderDetail_testb
  7. ON a.SalesOrderID=b.SalesOrderID
  8. INNER hash JOIN Production.Productp
  9. ON b.ProductID=p.ProductID
  10. WHERE  a.SalesOrderID=@i
  11. go

(3)       OPTIMIZEFOR(@variable_name= literal_constant[ , …n] )

使用OPTIMIZE FOR 这个查询指导,就能够让SQL Server做到这一点。这是SQL 2005以后的一个新功能。

  1. create procNoSniff_QueryHint_OptimizeFor(@iint)as
  2. select count(b.SalesOrderID),sum(p.Weight)
  3. from dbo.SalesOrderHeader_testa
  4. inner joindbo.SalesOrderDetail_testb
  5. on a.SalesOrderID=b.SalesOrderID
  6. inner joinProduction.Productp
  7. on b.ProductID=p.ProductID
  8. where a.SalesOrderID=@i
  9. option (optimizefor(@i= 75124))
  10. go

(4)      Plan Guide

以上方法有个明显的局限性,就是徐要修改存储过程定义。有些时候没有应用开发组的许可,修改存储过程是不可以的。对用sp_executesql方式调用的指令,问题更大,因为这些指令可能是写在应用程序里面而不是SQLServer里。数据库管理员没有办法去修改应用程序。自SQLServer 2005以后,引入和完善了一种叫PlanGuide的功能,数据库管理员可以告诉SQLServer,当运行某个语句时,请数据库使用我制定的执行计划。这样就不许要修改存储过程或者应用。例如可以用下面的方法,在原来那个有parameter sniffing问题的存储过程”Sniff”上,解决sniffing问题。

  1. EXEC sp_create_plan_guide
  2. @name= N'Guide1',
  3. @stmt = N'select count(b.SalesOrderID),sum(p.Weight)
  4. from dbo.SalesOrderHeader_test a
  5. inner join dbo.SalesOrderDetail_test b
  6. on a.SalesOrderID = b.SalesOrderID
  7. inner join Production.Product p
  8. on b.ProductID = p.ProductID
  9. where a.SalesOrderID =@i',
  10. @type = N'OBJECT',
  11. @module_or_batch = N'Sniff',
  12. @params = NULL,
  13. @hints = N'OPTION (optimize for (@i = 75124))';
  14. go

由于以上两个问题,导致该方案在实际中并不是很好用

最优解决方案:

总体优化思路与上面的类似,只不过取增量范围是通过mio_log、in_force_date、plnmio_date字段上的索引取出mio_log_id范围,这三个索引取出的最大mio_log_id的最大值为@mio_log_id_max,最小的mio_log_id的最小值为@mio_log_id_min,那么增量数据范围可取出为mio_log_idbetween @mio_log_id_min and @mio_log_id_max。这是因为是瞬间完成的,同时通过mio_log_id取增量时能够确保走聚集索引。

具体解决方案如下:

  1. SELECT @mio_log_id_max3=Max(mio_log_id),
  2. @mio_log_id_min3 = Min(mio_log_id)
  3. FROM   dbo.freph_a01_fromtask3c(INDEX=i2_freph_a01_fromtask3)
  4. INNER loop JOIN mio_logaWITH(nolock)
  5. ON a.cntr_no = c.cntr_no
  6. AND a.pol_code=c.pol_code
  7. WHERE  c.in_force_dateBETWEEN@date_minAND @date_max
  8. SELECT @mio_log_id_max2=Max(mio_log_id),
  9. @mio_log_id_min2 = Min(mio_log_id)
  10. FROM   mio_log(INDEX=idx_mio_log_plnmio_date)
  11. WHERE  plnmio_dateBETWEEN@date_minAND @date_max
  12. SELECT @mio_log_id_max1=Max(mio_log_id),
  13. @mio_log_id_min1 = Min(mio_log_id)
  14. FROM   mio_log(INDEX=idx_mio_log_mio_date)
  15. WHERE  mio_dateBETWEEN@date_minAND @date_max
  16. SELECT @mio_log_id_max=dbo.F_find_max(@mio_log_id_max1,@mio_log_id_max2,@mio_log_id_max3)
  17. SELECT @mio_log_id_min=dbo.F_find_min(@mio_log_id_min1,@mio_log_id_min2,@mio_log_id_min3)
  18. SELECT (CASE
  19. WHEN in_force_date IS NOT NULL THEN
  20. (CASE
  21. WHEN mio_date>= in_force_dateTHENmio_date
  22. ELSE in_force_date
  23. END )
  24. WHEN in_force_date IS NULL THEN
  25. (CASE
  26. WHEN mio_date>= plnmio_dateTHENmio_date
  27. ELSE plnmio_date
  28. END )
  29. ELSE mio_date
  30. END ) mio_date
  31. FROM   (SELECTa.mio_date,
  32. a.plnmio_date,
  33. c.in_force_date
  34. FROM   dbo.mio_logaWITH(nolock)
  35. INNER JOIN dbo.freph_a01_fromtask3cWITH(nolock)
  36. ON a.cntr_no = c.cntr_no
  37. AND a.pol_code=c.pol_code
  38. WHERE  mio_log_id BETWEEN @mio_log_id_min AND @mio_log_id_max) T
  39. WHERE  ((t.in_force_dateISNOT NULL
  40. AND((CASE
  41. WHEN t.mio_date>=t.in_force_dateTHENt.mio_date
  42. ELSE t.in_force_date
  43. END ) BETWEEN @date_minAND@date_max ) )
  44. OR(t.in_force_dateISNULL
  45. AND((CASE
  46. WHEN t.mio_date>=t.plnmio_dateTHENt.mio_date
  47. ELSE t.plnmio_date
  48. END ) BETWEEN @date_minAND@date_max ) ) )

该方案在实施过程中有两个问题需要注意:

1.      通过非聚集索引取聚集索引键的最大最小值时,其自身生成的执行计划效率低下,需要通过query hint指导SQL Server优化器选择正确的执行计划:

  1. set statisticsioon
  2. set statisticstimeon
  3. declare @date_mindatetime
  4. declare @date_maxdatetime
  5. set @date_min='2013-07-15'
  6. set @date_max='2013-07-25'
  7. declare @mio_log_id_max1int
  8. declare @mio_log_id_min1int
  9. select @mio_log_id_max1=max(mio_log_id),@mio_log_id_min1=min(mio_log_id)
  10. from mio_log
  11. where mio_datebetween@date_minAND @date_max

执行计划如下为两个并行聚集索引扫描:

之所以通过聚集索引扫描来得到最大、最小mio_log_id,并不是进行完整的聚集索引扫描。SQL Server优化器以为从两头分别进行扫描,碰到第一个符合WHERE条件就返回的算法是最优的。而实验中通过参数得到的实际数据均分布在mio_log的最大端,得到最小的mio_log_id几乎就扫描了整个mio_log表,因而整个逻辑读为【到目前为止结果还没出来……,不等了】 。

该问题可以通过指导SQL Server优化器选择正确的执行计划解决:

  1. select @mio_log_id_max1=max(mio_log_id),@mio_log_id_min1=min(mio_log_id)
  2. from mio_log(index=idx_mio_log_mio_date)
  3. where mio_datebetween@date_minAND @date_max

执行计划如下:

逻辑读673,耗时215 ms。

2.      通过freph_a01_fromtask3表in_force_date字段获取mio_log表的mio_log_id时,其自身生成的执行计划效率低下,需要通过query hint指导SQL Server优化器选择正确的执行计划:

  1. SELECT @mio_log_id_max3=Max(mio_log_id),
  2. @mio_log_id_min3 = Min(mio_log_id)
  3. FROM   dbo.freph_a01_fromtask3c(INDEX=i2_freph_a01_fromtask3)
  4. INNER loop JOIN mio_logaWITH(nolock)
  5. ON a.cntr_no = c.cntr_no
  6. AND a.pol_code=c.pol_code
  7. WHERE  c.in_force_dateBETWEEN@date_minAND @date_max

另外,在逻辑优化过程中,还用到了索引覆盖、关联字段添加索引、脏读等技术。

参考资料:

1.      SQL Server ROWID: http://stackoverflow.com/questions/909155/equivalent-of-oracles-rowid-in-sql-server

2.      徐海蔚. Microsoft SQL Server企业级平台管理实践

case when 性能优化的更多相关文章

  1. Android性能优化-内存泄漏的8个Case

    1为什么要做性能优化? 手机性能越来越好,不用纠结这些细微的性能? Android每一个应用都是运行的独立的Dalivk虚拟机,根据不同的手机分配的可用内存可能只有(32M.64M等),所谓的4GB. ...

  2. C#中那些[举手之劳]的性能优化

    隔了很久没写东西了,主要是最近比较忙,更主要的是最近比较懒...... 其实这篇很早就想写了 工作和生活中经常可以看到一些程序猿,写代码的时候只关注代码的逻辑性,而不考虑运行效率 其实这对大多数程序猿 ...

  3. 【腾讯Bugly干货分享】跨平台 ListView 性能优化

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/FbiSLPxFdGqJ00WgpJ94yw 导语 精 ...

  4. CSS3与页面布局学习总结(八)——浏览器兼容与前端性能优化

    一.浏览器兼容 1.1.概要 世界上没有任何一个浏览器是一样的,同样的代码在不一样的浏览器上运行就存在兼容性问题.不同浏览器其内核亦不尽相同,相同内核的版本不同,相同版本的内核浏览器品牌不一样,各种运 ...

  5. 记录一次bug解决过程:可维护性和性能优化

    一.总结 使用某些变量的地方在2次以上的,强烈建议使用枚举值来维护变量,日后方便扩展. 查数据库的方法调用,能合并就净量去合并. 二.Bug描述 枚举变量的维护以及方法使用: public class ...

  6. 转载:SqlServer数据库性能优化详解

    本文转载自:http://blog.csdn.net/andylaudotnet/article/details/1763573 性能调节的目的是通过将网络流通.磁盘 I/O 和 CPU 时间减到最小 ...

  7. 【转载】 Spark性能优化指南——基础篇

    转自:http://tech.meituan.com/spark-tuning-basic.html?from=timeline 前言 开发调优 调优概述 原则一:避免创建重复的RDD 原则二:尽可能 ...

  8. Android性能优化典范第一季

    2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关 ...

  9. Ceph性能优化总结(v0.94)

    优化方法论 做任何事情还是要有个方法论的,“授人以鱼不如授人以渔”的道理吧,方法通了,所有的问题就有了解决的途径.通过对公开资料的分析进行总结,对分布式存储系统的优化离不开以下几点: 1. 硬件层面 ...

随机推荐

  1. LG4213 【模板】杜教筛(Sum)和 BZOJ4916 神犇和蒟蒻

    P4213 [模板]杜教筛(Sum) 题目描述 给定一个正整数$N(N\le2^{31}-1)$ 求 $$ans_1=\sum_{i=1}^n\varphi(i)$$ $$ans_2=\sum_{i= ...

  2. drf框架 - 序列化组件 | Serializer

    序列化组件 知识点:Serializer(偏底层).ModelSerializer(重点).ListModelSerializer(辅助群改) 序列化与反序列化 序列化: 将对象序列化成字符串用户传输 ...

  3. Codeforces Global Round 6[A,B,C]

    题意:给一个字符串,对它重新排列使得它是60的倍数. 模拟一下,需要能整除60  字符串中需要 能整除2 3 10,所以需要字符串各位数之和能整除3 并且有 一个偶数和一个0  或者两个0也行[没考虑 ...

  4. 015——C#软件出现《未在本地计算机上注册"Microsoft.Jet.OLEDB.4.0"》

    (一)参考文献:https://zhidao.baidu.com/question/361243661437448812.html https://blog.csdn.net/yyx3214/arti ...

  5. web大文件分块上传断点续传demo

    一.概述 所谓断点续传,其实只是指下载,也就是要从文件已经下载的地方开始继续下载.在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了.一般断点下载时才用到Range和Content- ...

  6. AGC 030 B - Tree Burning 结论+枚举

    考试 T2,是一个脑筋急转弯. 最暴力的贪心是每次先选左,再选右,再选左..... 然而这么做在一些情况下是错的. 但是,我们发现我们的选法一定是 $LLLLRLRLRLRLR$ 或 $RRRRLRL ...

  7. web场景的监控

    在一台可以连外网的主机上创建web场景, 添加步骤,zabbix可以步骤化web页面,例如,登陆操作,正常操作是要先验证用户获取token,step1就可以是先访问token页面,获取到返回的toke ...

  8. linux 安装gcc 和 g++

    以CentOS为例,安装后是没有C语言和C++编译环境的,需要手动安装,最简单的是用yum的方式安装,过程如下: 1.安装gcc yum install gcc 询问是否,按y键回车即可,或者 yum ...

  9. github上项目的目录结构说明

    build 构建脚本 dist 编译出来的发布版 docs 文档 examples 示例文件 src 源码 test 测试脚本 .babelrc Babel 交叉编译的配置 .eslintrc ESL ...

  10. Neither shaken nor stirred(DFS理解+vector存图)

    题目链接:http://acm.timus.ru/problem.aspx?space=1&num=2013 题目理解: 给定n个点的有向图: 下面n行,第一个数字表示点权,后面一个数字m表示 ...