《Troubleshooting SQL Server》读书笔记-CPU使用率过高(下)

第三章 High CPU Utilization.

CPU使用率过高的常见原因

查询优化器会尽量从CPU,IO和内存资源成本最小的角度,找到最高效的数据访问方式。如果没有正确的索引,或者写的语句本身就会忽略索引,

又或者不准确的统计信息等情况下,查询计划可能不是最优的。

有些查询计划可能对只对某种条件下的查询是高效,而不是所有条件下都是。

缺失索引

索引的缺失,会导致查询处理的行数大大超出必要的行数,从而加重CPU和IO的负载。简单的例子:

SELECT  per .FirstName  ,
per.LastName ,
p.Name ,
p.ProductNumber ,
OrderDate ,
LineTotal ,
soh.TotalDue
FROM Sales.SalesOrderHeader AS soh
INNER JOIN Sales.SalesOrderDetail sod
ON soh.SalesOrderID = sod.SalesOrderID
INNER JOIN Production.Product AS p ON sod.ProductID = p.ProductID
INNER JOIN Sales.Customer AS c ON soh.CustomerID = c.CustomerID
INNER JOIN Person.Person AS per
ON c.PersonID = per.BusinessEntityID
WHERE LineTotal > 25000 SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times:
CPU time = 452 ms, elapsed time = 458 ms.

上面的查询使用AdventureWorks2008数据库,字段LineTotal上没有索引,会导致SalesOrderDetail全表扫描。然后创建如下索引后,改善很明显:

CREATENONCLUSTEREDINDEX
idx_SalesOrde
ON
Sales.SalesOrderDetail (LineTotal)
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms. SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 8 ms.

过期的统计信息

查询优化器使用统计信息计算各种查询操作的基数(开销)。查询操作的成本(cost)又决定了查询计划的成本。过期的统计信息会导致生成非最优的查询计划,

如预估成本很低,但实际成本很高的计划。

最常见就是预估行数很少,并选择了那些适合少量数据的操作(如嵌套循环,LookUp),但当实际执行时要处理的行数却很多,查询效率就变得很低。

可以通过SSMS或者set statistics profile on为索引查找和扫描操作,返回实际行数与预估行数做比较。如果两者差异较大,就很有可能统计信息过期了。

过期时,可以使用update statistics tableName更新表上所有的统计信息,update statistics tableName statisticsName更新指定统计信息。

为了防止统计信息过期的问题,有如下三种方法:

a. 开启数据库的Auto_Update_Statistics选项或者用定时作业更新全库的统计信息。

b. 如果某些索引的自动更新统计信息被禁用,则需要指定STATISTICS_NORECOMPUTE=OFF重建索引开启。

c. 对于某些经常因为统计信息过期而导致性能问题的统计信息,可以创建定时作业频繁地更新它们。

非SAGR谓词

SAGR=Search Agrument.简单说就是能够使用索引查找的谓词。列应该直接与表达式进行比较则符合SAGR,如WHERE  SomeFunction(Column) = @Value就符合,

WHERE Column = SomeOtherFunction(@Value) 则符合。注意LIKE和BETWEEN也是SAGR谓词。

非SAGR会导致表或者索引扫描,它的影响跟缺失索引类似。使得CPU处理大量非必需的数据行。下面查询会导致索引扫描:

SELECT  soh .SalesOrderID ,
OrderDate ,
DueDate ,
ShipDate ,
Status ,
SubTotal ,
TaxAmt ,
Freight ,
TotalDue
FROM Sales.SalesOrderheader AS soh
INNER JOIN Sales.SalesOrderDetail AS sod
ON soh.SalesOrderID = sod.SalesOrderID
WHERE CONVERT(VARCHAR(10), sod.ModifiedDate , 101) = '01/01/2010'

改写成如下则会使用索引查找:

SELECT  soh .SalesOrderID ,
OrderDate ,
DueDate ,
ShipDate ,
Status ,
SubTotal ,
TaxAmt ,
Freight ,
TotalDue
FROM Sales.SalesOrderheader AS soh
INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID
WHERE sod.ModifiedDate >= '2010/01/01'
AND sod.ModifiedDate < '2010/01/02'

UPPER,LOWER,LTRIM,RTRIM,ISNULL这些经常会被滥用,甚至用于WHERE和JOIN条件中。

在不区分大小写排序规则中,大小写被视为相等的,像UPPER,LOWER这种拖累性能的函数就不必要用了。

SQL中字符串比较会忽略末尾空格,所以RTRIM也没必要用。

下面两个过滤条件,前者,字段NULL值转换成0从而被排除;后者中,其实NULL值与任何值比较操作都不会返回TURE,而被排除。

NULL值只在IS NULL或者IS NOT NULL检查时才可能返回TRUE。所以是等效的,但后者才能使用索引查找。

WHERE  ISNULL(SomeCol,0) > 0 
WHERE  SomeCol > 0

隐式转换

隐式转换发生在比较两个不同数据类型时。SQL不能对不同类型数据进行比较,所以查询优化器会在比较操作前把低优先级的数据类型转换成高优先级的数据类型再比较。

这跟非SARG谓词一样,将不能使用Index Seek,从而处理很多不必要的数据行,增加CPU开销。最常见例子是使用NVARCHAR类型的参数与VARCHAR类型的列进行比较。如:

 
SELECTp .FirstName , p.LastName , c.AccountNumber
FROM
Sales.Customer ASc
INNER JOINPerson.Person AS p
ON c.PersonID = p.BusinessEntityID
WHERE AccountNumber = N'AW00029594'
上面的查询导致一个非聚集索引扫描,在Filter操作中会看有一个COVERT_IMPLICIT。

为了避免隐式转换:

1. JOIN的列,数据类型尽量相同

2. 与列比较时,任何参数,变量和常量的类型要和列的类型相同

3. 当参数,变量或常量的类型与要比较的列不同时,斟酌地使用类型转换函数,使其与列类型相同

4. 有些数据访问组件和开发框架会把字符串类型默认地设置为NVARCHAR

参数探测(Parameter Sniffing)

参数探测是SQL Server为存储过程,函数和参数化查询创建查询计划时用到的处理方式。当首次编译查询计划时,SQL Server会检测或者探测输入参数的值并结合统计信

息,预估受影响的行数,

并以之估算查询计划成本。当根据传入的参数值创建查询计划,得到的受影响行数不是典型的情况时,就产生问题了。参数探测只出现在编译和重编译时,之后的存储过程,函数和

参数化查询,

会重用此查询计划。最初编译时只有输入参数的值会被探测到,本地变量是没有值的。如果批处理中的语句被重编译,则参数和变量将会被赋值并探测到。示例如下:


CREATEPROCEDUREuser_GetCustomerShipDates
( @ShipDateStart DATETIME
, @ShipDateEnd DATETIME
)
AS
SELECT CustomerID , SalesOrderNumber
FROM Sales.SalesOrderHeader
WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd
GO
Sales.SalesOrderHeader表的ShipDate字段范围是2004-08-07~2011-08-07,并创建非聚集索引: 
CREATENONCLUSTEREDINDEX IDX_ShipDate_ASC
ON Sales.SalesOrderHeader (ShipDate )
GO
首先我们执行两次SP,并用DBCC FREEPROCCACHE在运行前清空计划缓存:
DBCC FREEPROCCACHE
EXEC user_GetCustomerShipDates '2001/07/08', '2004/01/01'
EXEC user_GetCustomerShipDates '2001/07/10', '2001/07/20'
查询计划如图: 
查询并没有使用ShipDate列非聚集索引,因为它不是一个覆盖索引,并且被执行时,查询优化器根据参数值结合统计信息预估的行数很多,使用IndexSeek和LookUp的组合成本
 
太高。再观察STATISTICS IO&TIME:
==FIRST EXECUTION (LARGE DATE RANGE)===  

(Table 'SalesOrderHeader'. Scan count 1, logical reads 686, physical reads 0.  

 SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 197 ms. SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 197 ms. ==SECOND EXECUTION (SMALL DATE RANGE)=== Table 'SalesOrderHeader'. Scan count 1, logical reads 686, physical reads 0. SQL Server Execution Times:
CPU time = 15 ms, elapsed time = 5 ms. SQL Server Execution Times:
CPU time = 15 ms, elapsed time = 5 ms.
两者的纯CPU时间和IO是基本一样,因为前者需要处理的数据量多很多,所以CPU消耗时间长一些。接下来,调换两个执行SP的顺序,再执行:
DBCC FREEPROCCACHE
EXEC user_GetCustomerShipDates '2001/07/10', '2001/07/20'
EXEC user_GetCustomerShipDates '2001/07/08', '2004/01/01'
==FIRST EXECUTION (SMALL DATE RANGE)===  

Table 'SalesOrderHeader'. Scan count 1, logica
ahead reads 0, lob logical reads 0, lob physic SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms. ==SECOND EXECUTION (LARGE DATE RANGE)=== Table 'SalesOrderHeader'. Scan count 1, logica SQL Server Execution Times:
CPU time = 47 ms, elapsed time = 182 ms.
这次两者性能差距就很明显了。参数探测导致优化器采用了适合少量数据的KeyLookUp操作,而第二次查询重用了此查询计划,但是实际它需要处理大量数据,
 
这时KeyLookUp就导致了明显的性能问题,需要额外的IO和CPU资源。 
 
根据具体的环境和SQL Server版本,有多种处理参数探测的方法:
1. 跟踪标志4136
      此跟踪标志使得SQLServer实例不再使用参数探测,而是使用列平均重复个数(=总行数/列的非重复值个数)来估算受影响行数。 这样的估算值是不精确的。
 
启用此标志将会使得那些正确的参数探测的情况,变得不准确,带来负面影响。所以应该做为最后的手段。
 
适用于SQL Server 2008 SP1 CU7,SQL Server 2008 R2 CU2,SQL Server 2005 in SP3 CU9。 
 
2. 使用OPTIMIZE FOR查询提示 SQLServer 2005及后续版本中,可以为查询优化器编译查询计划时指定参数的值。如:
CREATE PROCEDURE user_GetCustomerShipDates
(
@ShipDateStart DATETIME ,
@ShipDateEnd DATETIME
)
AS
SELECT CustomerID ,
SalesOrderNumber
FROM Sales.SalesOrderHeader
WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd
OPTION ( OPTIMIZE FOR ( @ShipDateStart = '2001/07/08' ,
@ShipDateEnd = '2004/01/01' ) )
GO
在2008中还能OPTIMIZE FOR UNKNOWN使得优化器不用参数探测,这个跟T-4136一样,只不过是语句级。
        
3. 重编译选项   

在创建存储可以指定WITH RECOMPILE重编译选项。指定后SP每次执行时会基于当前参数值重新编译,同时也不缓存执行计划。但是这样会增加执行处理时间。

CREATE PROCEDURE user_GetCustomerShipDates
(
@ShipDateStart DATETIME ,
@ShipDateEnd DATETIME
)
WITH RECOMPILE
AS
SELECT CustomerID ,SalesOrderNumber
FROM Sales.SalesOrderHeader
WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd

当SP中的多个语句,只是某个语句会产生参数探测问题,则可以对这个语句使用OPTION(RECOMPILE)查询提示。这样每次执行时只会对这个语句重编译,

而不像WITH RECOMPILE对整个SP重编译。如果可能尽量使用查询提示,减少重编译的影响范围和开销。

CREATE PROCEDURE user_GetCustomerShipDates
(
@ShipDateStart DATETIME ,
@ShipDateEnd DATETIME
)
AS
SELECT CustomerID ,
SalesOrderNumber
FROM Sales.SalesOrderHeader
WHERE ShipDate BETWEEN @ShipDateStart AND @ShipDateEnd
OPTION ( RECOMPILE )
 
即席非参数化查询
    即席查询不能重用执行计划,每次执行时都会被编译,消耗大量资源(特别是CPU)。像下面的查询,每次因为WHERE条件中参数值不同而产生不同的执行计划。
    虽然SQL Server有简单参数化(Simple Parameterization)的技术,但是此语句相对”太复杂”了。
SELECT  soh .SalesOrderNumber  ,
sod.ProductID
FROM Sales.SalesOrderHeader AS soh
INNER JOIN Sales.SalesOrderDetail AS sod
ON soh.SalesOrderID = sod.SalesOrderID
WHERE soh.SalesOrderNumber = 'SO43662'
    非参数化查询主要有两方面的影响:
        1. 即席查询产生的一次性的查询计划会填满计划缓存。由此带来的内存压力,会让那些本可以重用的计划迫于内存压力而被清除掉等等。
        2. 编译这些一次性的查询计划浪费了大量的CPU资源。
    可以用下面的计数器来判断即席非参数化查询的影响程度: 

SQLServer: SQL Statistics: SQL Compilations/Sec
SQLServer: SQL Statistics: Auto-Param Attempts/Sec
SQLServer: SQL Statistics: Failed Auto-Param/Sec

解决的方法有:

1. 修改应用程序代码,使发送到SQL Server语句尽量被参数化。

2. 在SQL Server 2005及以上版本中,能在数据库级别设定强制参数化。但可能会带来类型参数探测一样的问题。

ALTER  DATABASE  AdventureWorks SET  PARAMETERIZATION FORCED
 3. SQL Server 2008及以上版本中,启用实例级别的optimize for  ad hoc  workloads。
        启用后当即席查询第一次执行时只保存查询计划的一个“存根”,第二次执行时则缓存执行计划。“存根”使用很少的内存,
        这样就减少了那些本可以重用的执行计划,因内存压力而被清除掉的机率。
EXEC sp_configure  'show advanced options',1
RECONFIGURE
EXEC sp_configure 'optimize for ad hoc workloads',1
RECONFIGURE
不恰当的并行
   并行查询是把一个查询的工作分解成多个线程执行,每一个线程使用单独的计划程序。查询并行发生在操作符(Operator)级别。
查询优化器在编译执行计划时总是会让其尽可能的快。如果执行计划的预估成本连续超过cost threshold of prillelism,同时SQL Server可用
CPU个数多于1个,并且max degree of prallelism为0或大于1,则产生的执行计划将会包括并行。
并行查询通过水平分割输入数据,然后分布到多个逻辑CPU上同时执行,从而减少执行时间。对于数据仓库和报表系统会有好处,对于OLTP系统
则并行会占用过多的CPU资源,而其它的请求不得不等待CPU资源。
SQL Server有两个控制并行执行的sp_configure选项:
   cost threshold of prillelism:控制优化器为查询使用并行执行的阀值
   max degree of prallelism:避免单个查询用完所有可用的处理器内核
 cost threshold of prallelism的默认值为5秒。在较大的数据库上,默认阀值可能太低了,会导致并行执行的资源争用。
 可以通过下面的查询获取存于计划缓存中的并行执行计划,做为调整cost threshold of prillelism的重要参考依据:
SET  TRANSACTION ISOLATION LEVEL  READ UNCOMMITTED ;  

WITH XMLNAMESPACES
(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT query_plan AS CompleteQueryPlan ,
n.value ('(@StatementText)[1]', 'VARCHAR(4000)' ) AS StatementText ,
n.value ('(@StatementOptmLevel)[1]', 'VARCHAR(25)')
AS StatementOptimizationLevel ,
n.value ('(@StatementSubTreeCost)[1]', 'VARCHAR(128)')
AS StatementSubTreeCost ,
n.query ('.' ) AS ParallelSubTreeXML ,
ecp.usecounts ,
ecp.size_in_bytes
FROM sys .dm_exec_cached_plans AS ecp
CROSS APPLY sys .dm_exec_query_plan(plan_handle) AS eqp
CROSS APPLY query_plan.nodes
('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple')
AS qn ( n )
WHERE n.query ('.' ).exist ('//RelOp[@PhysicalOp="Parallelism"]' ) = 1
 
并行中使用到处理器数是取自后面三者中的最小值:max degree of prallelis的值,可用的处理器数和MAXDOP查询提示(会覆盖max degree of prallelis指定的值)。
合适的max degree of prallelis值取决于工作负载类型和硬件资源支撑并行开销的能力。很多砖家推荐将其设定为1,前提是你的系统是真正的单独的OLTP系统,
只有大量的并发的小事务。对于NUMA和SMP系统的设定是不同的。SQL Server 2008及以上版本利用resource governor能将max degree of prallelis绑定到特定的查询组。
 
当系统出现并行性能问题时,通常会CXPACKET等待会比较明显,其实它很冤,它只是个表面现象。根本原因还是要查看产生CXPACKET等待的子线程的等待类型。
如果伴随着如IO_COMPLETION,ASYNC_IO_COMPLETION,PAGEIOLATCH_*的等待,则要提高IO性能;
如果伴随着LATCH_*  and SOS_SCHEDULER_YIELD,则表示并行本身导致了性能问题,如果此时还有ACCESS_METHODS_DATASET_PARENT等待,则执行的并行度是根本原因。
出现这些问题时首先应该优化并行执行的语句,其次才是结合max degree of prallelis和cost threshold of prillelism两者进行限制,再次是提升硬件。
在硬件暂时无法提升时,只能对并行做一定限制,做折中和权衡考量。
 
在SQL Server 2005,有个关于TokenAndPermUserStore的CPU问题。当数据库用户很多,adhoc和动态查询很多且非AWE内存空间很多时,

可能会出现CMEMTHREAD大量等待和TokenAndPermUserStore使用过量并持续增长。微软已经推出的解决方案http://support.microsoft.com/kb/927396
长期的解决方案是修改程序架构,尽量减少可能导致此问题的adhoc和动态查询的使用。
短期解决方案是:
   1. 给应用程序账户提升为sysadmin,从而规避权限检查而极大的减少TokenAndPermUserStore的缓存使用量。这是有安全风险的
   2. 使用DBCC FREESYSTEMCACHE ('TokenAndPermUserStore'),定期清理这一部分缓存。
   3. SQL Server 2005 SP2及以上版本,可以使用Trace Flag 4618&4610.4610限制缓存条目为1024,两者都启用则限制缓存条目为8192.
   4. SQL Server 2005 SP3及以上版本,使用Trace Flag 4621,可以设定缓存配额。 http://support.microsoft.com/kb/959823。
   5. SQL Server 2008有access check cache bucket count & access check cache quota两个sp_configure项,
      用于设置TokenAndPermUserStore的hash bucket数量和缓存条目数。
 
关于超线程和BIOS节能控制选项
   很多砖家推荐SQL Server服务器不宜开启超线程,根据作者的研究,这种推荐做法在过去是对的,现在不一定是对的。
在过去,超线出来的CPU会一起共享板载缓存,而这个缓存是KB量级的。而且windows 2000本身不支持超线程,“超”出来的它会认为是物理CPU。“CPU”一多就会造成缓存命中率低下,进而影响性能。

而很多DBA管理的OLTP&DSS的混合环境,那么超线程对于那些DSS型查询的提升,在混合环境中很不明显,甚至有时会导致需要迅速响应OLTP弄查询等待。
现在,硬件和软件进步了,现在板载缓存量级都是MB级的了,CPU缓存命中率不再是问题。而windows 2003也支持超线程了。而且现在OLTP,OLAP,DW等系统一般会隔离使用。
当然是否开启还是需要经过测试才能最终决定。
 
绿色节能现在也是硬件和服务器一种标准了,自动降低系统中某些暂时未用到的硬件能耗和CPU频率。BioS中可以设定为硬件自己控制或者OS控制。
windows 2008&r2默认设定电源计划为“平衡”,以允许其切换到节能模式。有时在windows 2008 or R2的新服务器升级系统一段时间后,性能下降明显,这就是电源管理造成的CPU降频导致的。
windows的性能计数器 % Processor Usage 是已使用CPU频率除以可用CPU频率得到的。如果CPU被降频了,那么这个计算器会较高,会让人误会成工作负载很高。
可以使用CPU-Z来检测CPU的状态。
建议将windows的电源计划调定为“高性能”,并且检查BIOS的电源控制选项,设定为OS Control.
 
总结:
   1. SQL SERVER CPU调整的地方很少,很多时候还是因为语句性能低下导致CPU使用过高。
   2. 解决参数嗅探问题,还有拼动态语,使用plan_guide等方法。

-------------------------------------

作者:Joe.TJ

Joe's Blog:http://www.cnblogs.com/Joe-T/

 
 

《Troubleshooting SQL Server》读书笔记-CPU使用率过高(下)的更多相关文章

  1. 《Troubleshooting SQL Server》读书笔记-CPU使用率过高(上)

    第三章 High CPU Utilization. CPU使用率过高问题很容易被发现,但是诊断却不是很容易.CPU使用过高很多时候会成为其它问题的替罪羊,所以在确认和故障诊断时要抽丝剥茧. 调查CPU ...

  2. SQL SERVER读书笔记:内存

    系统先操作地址空间,真正要用的时候才申请物理内存,进行使用. Reserved Memory  保留内存,虚拟内存 Commited Memory 提交内存,物理内存 [如何判断SQL SERVER ...

  3. SQL SERVER读书笔记:阻塞与死锁

    阻塞是事务隔离带来的副作用,而并不是SQL SERVER的错. 死锁则是互相争用资源而引发.由于死锁会选择牺牲者,所以死锁的危害没有阻塞大.但有时为了解决死锁,会采取对资源加锁,导致阻塞的方式来避免.

  4. SQL SERVER读书笔记:TempDB

    每次SQL SERVER启动的时候,会重新创建. 用于 0.临时表 1.排序 2.连接(merge join,hash join) 3.行版本控制 临时表与表变量的区别: 1)表变量是存储在内存中的, ...

  5. SQL SERVER读书笔记:执行计划

    执行计划对性能影响甚大. 系统是怎么得出一个号的执行计划的?主要是依赖于准确的统计信息.统计信息准确的前提下,执行语句重用性高,可避免频繁编译,这也有助于提高性能. 但如果怀疑统计信息不够准确,可以强 ...

  6. SQL SERVER读书笔记:JOIN

    nested loop join:适用于小数据集,有索引的情况.不占用内存,不用tempdb. merge join:大数据,要排序,多对多,用tempdb: hash join:对大数据集,少用户使 ...

  7. SQL SERVER读书笔记:nolock

    让查询语句不去申请共享锁,从而消除死锁,效果立竿见影. 缺点: 1.脏读 2.只能解决共享锁(S锁)参与的问题 3.需要修改语句本身才能使用

  8. SQL SERVER 读书笔记:非聚集索引

    对于有聚集索引的表,数据存储在聚集索引的叶子节点,而非聚集索引则存储 索引键值 和 聚集索引键值.对于非聚集索引,如果查找的字段没有包含在索引键值,则还要根据聚集索引键值来查找详细数据,此谓 Book ...

  9. sql server 任务调度与CPU

    一. 概述 我们知道在操作系统看来, sql server产品与其它应用程序一样,没有特别对待.但内存,硬盘,cpu又是数据库系统最重要的核心资源,所以在sql server 2005及以后出现了SQ ...

随机推荐

  1. 归并排序 & 快速排序

    归并排序 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 将已有序的子序列合并,得到完全有序的序列:即先使每个子序列有 ...

  2. 将firebug安装在chrome浏览器上

    一直很喜欢火狐浏览器,原因是火狐的插件很喜欢,几天突然发现firebug这个插件能够安装在chrome浏览器上,震惊,更震惊的是这个好似已经很长时间了,而我猜发现. 具体的具体页面地址是 http:/ ...

  3. addEventListener 与attachEvent

    第一:简单的通用方法(IE && FF) window.onload = function(){ var oDiv = document.getElementById("J_ ...

  4. asp.net mvc上传头像加剪裁功能

    原文:asp.net mvc上传头像加剪裁功能 正好项目用到上传+剪裁功能,发上来便于以后使用. 我不能告诉你们其实是从博客园扒的前台代码,哈哈. 前端是jquery+fineuploader+jqu ...

  5. js 正则之检测素数

    原文:js 正则之检测素数 相信很多人应该看过这篇文章,我第一次看到的时候是11年的样子,那时候学vbs的时候看过这个问题.原文<检查素数的正则表达式>,在文章里已经解释了他是怎么判断的, ...

  6. C语言得到当前系统时间

    void getTime(){ //获取当前系统时间 time_t tTime;//距离1900年1月1日的秒数 char str[80]; struct tm* stTim;//时间结构 time( ...

  7. 第1章1节《MonkeyRunner源码剖析》概述:前言(原创)

    天地会珠海分舵注:本来这一系列是准备出一本书的,详情请见早前博文“寻求合作伙伴编写<深入理解 MonkeyRunner>书籍“.但因为诸多原因,没有如愿.所以这里把草稿分享出来,所以错误在 ...

  8. MySQL安装指南

    近期领导突然说要用MySQL,我立刻当天晚上就研究了一下. http://www.mysql.com/这是官网,还好能够訪问.好多年前已经被oracle收购.分为企业版和社区版: MySQL Ente ...

  9. 使用 C# 进行 Outlook 2003 编程

    原文:使用 C# 进行 Outlook 2003 编程 摘要: 本文介绍了 Microsoft Outlook 2003 对象模型介,并探讨了如何使用 C# 编程语言生成 Outlook 识别的应用程 ...

  10. JS中的模块规范(CommonJS,AMD,CMD)

    JS中的模块规范(CommonJS,AMD,CMD) 如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已. 现在就看看吧, ...