转载:SQL Server高效 -- 设计(ITPUT 讨论汇总

时间:2023-03-08 16:41:11
转载:SQL Server高效 -- 设计(ITPUT 讨论汇总
http://blog.csdn.net/zjcxc/article/details/8979756

认为在设计SQL Server对象时,主要会考虑哪些因素来避免出现性能问题?

讨论汇总——总体设计

l  应该根据系统架构类型或主要操作进行有针对性的设计: 如 OLTP 、OLAP 、ETL 等

l  逻辑清晰,使应用程序更便于开发。有良好的扩展性和维护性,减少数据冗余等

l  在DB管理上,得从CPU、HDD配置着手,在DB开发上,从业务流程、逻辑、功能、DB结构、SQL指令准确设计和完成目标

l  设计视图时应针对某个具体需求,不应过多考虑其重用性

l  触发器也应该避免使用,考虑将触发器完成的功能改写到触发语句中。对于调式,性能跟踪来说,不用触发器也会更直观些

l  先考虑会出现什么样的性能问题(访问性能?插入性能?);接着考虑数据的变化特点(是小表还是大表?数据的变化频率如何?里面数据重复性如何?),同时也考虑业务特点(是访问多,还是插入多,还是即访问多还有插入多);然后结合SQLServer的原理和特点进行设计,比如要不要使用索引,是使用聚集索引比较好,还是非聚集索引比较好?表的数据要怎么存储才能充分发挥SQL Server的特性(如从SQL Server数据存储、page、碎片等方面考虑。还有要不要使用读写分离?要不要从整个数据库级别也考虑?);最后,考虑对设计完成后的对象,进行测试,然后寻找最优的方案

l  逻辑数据库和表的设计。数据库的逻辑设计、包括表与表之间的关系是优化关系型数据库性能的核心。一个好的逻辑数据库设计可以为优化数据库和应用程序打下良好的基础。标准化的数据库逻辑设计包括用多的、有相互关系的窄表来代替很多列的长数据表

l  读写分离架构(如报告数据库和交易数据库部署分离等);关注物理存储;文件布局(数据文件和日志文件隔离,日志文件最好使用高速设备)

l  考虑数据库规模和重点表的规模,数据库文件组以及内部分区也需要考虑在内,这里可以跟磁盘一起考虑

l  硬件的二级缓存会影响,把数据、日志、索引放到不同的I/O设备上,增加读取速度。数据量(尺寸)越大,提高I/O越重要.

讨论汇总——表及索引设计

l  对表的应用场景进行分析,分为日志表、参数表和频繁更新删除表

l  合理的表数据量估算,分为大表、中表、小表,考虑大表分区/分片

l  清晰的逻辑和物理模型设计,要准从第三范式,必要时也需要反范式(参考下面的某些场景)

l  逻辑结构上可以适当冗余,避免太多表关联;物理结构上选择性能高的磁盘策略,表分区,读写分离

l  设计表对象时首先考虑功能,再考虑性能。如果表中字段过多,考虑拆分对多个小对象(小表);如果表中有访问频率不高,但有需较大存储空间的字段,考虑拆分另外存储

l  考虑好archive 机制,保证线上业务表数据量不能太大,其余的到历史表,不然SQL Server 单表到亿级别,怎么都不好使

l  根据业务需求,给各种数据选择恰当的数据类型,节约数据空间的同时提高效率

l  用占用空间尽量小的数据类型存储数据, 如date, smalldatetime、smallint、tinyint等

l  设计索引时先考虑查询频率和更新频率,切忌为偶尔执行的语句建立索引,建立复合索引时需考虑列的可选择性和使用频率来确定先后顺序

l  再次对表的索引进行评估(需要应用开发人员的参与,获取其SQL),初步构建索引,从索引列的选择性、索引列的倾斜度,索引列的使用频度、索引列的使用顺序、表索引的个数的综合衡量

l  索引的效率也很重要,具体要看业务会怎么样去用,设计合理的索引组合,同时需要定期跟踪

l  创建高效的索引, 使用索引的包含列、过滤索引等功能

讨论汇总——tempdb

l  ,tempdb在SQL Server停掉,重启时会自动的drop,re-create. 根据model数据库会默认建立一个新的8MB(mdf file:8MB;ldf file:1MB, ecoverymodel为simple

l  tempdb对IO的要求比较高,最好分配到高IO的磁盘上且与其他的数据文件分到不用的磁盘上,以提高读写效率

l  应该根据根据CPU个数来配置tempdb的文件数(有几个CPU建立几个文件)

个人补充:tempdb 的文件初始大小可以设置,通过ALTER DATABASE MODIFY FILE,或者界面操作直接设置文件的初始大小,这个设置在重启SQL服务时,重建tempdb会使用这个设置值

个人补充

l  合理划分数据库。设计开始的时候就考虑数据库隔离,不要把什么东西都放在一个数据库里面。数据库的划分可以综合考虑这些方面:业务功能、数据重要性、查询和读取比例、数据存储周期

l  为数据库选择合适的恢复模型(RECOVERY MODEL)。这个对数据库的写入和备份影响比较大

l  把控需求,明确使用数据库的必要性以及数据存储的详细程度

l  明确常规字段的定义(特别是在有多个开发团队的情况下),避免因为设计不同,导致在交互(包括相互查询)数据时带来的性能损失

l  确 定合理的对象使用期。有的人喜欢紧密结合当前需求来设计表,结果一段时间之后,可能因为某些变化,不得不修改表结构,修改包含数据的表结构,带来的影响是 比较大的;而某些人为了避免这种情况,干脆设计一个非常宽松的表,结果表结构的修改频率可能是非常低了,但是性能可能会受到比较大的影响(比如设计的一些 关键字段为了满足未来需要,设计的长度很宽,导致查询性能不好)

争议问题

l  主键使用自增列

支持使用的观点:

使用系统生成的主键,避免使用复合键,外键总是关联唯一的键字段,不要把社会保障号码(SSN)或身份证号码(ID)选作键

反对使用的观点:

如果一个表可以用自增列作主键,那么这个表的主键可有可无。例如记录网站用户登录信息的日志表。这种表可以不要主键,将登录时间设为聚集索引即可。

GUID生成时需要耗用cpu,也不在考虑范围。当表没有合适的列设主键时,我会考虑固定长度的流水号之类的字段当主键。

不管是自增列、GUID,还是流水号当主键,只要表本身字段有合适的字段作主键,哪怕是复合键,只要长度不太长(100字节以内可考虑,50以内最合适),以及字段数不太多(3个是能接受的范围,5个也可考虑),就应该使用这些字段来当主键。理由是:在表的join以及数据查询中,真正被查询的,还是这些字段。伪主键们除了占用空间,没有一点好处。

如果将一个自增列设为主键,它的好处在哪儿?这样看起来,就是为了给表安一个主键而去create的。你可能会说,自增列当主键,极大减少page spliting,减 少碎片;聚集索引包含在每个非聚集索引的尾部,极大减少索引size等等。我却觉得:1. 一个表的主键,最好能反映出这个表的逻辑设计,意思是,我一看这个表的主键,就大致知道这个表存的什么内容。2:就 算你使用了自增列当主键,但实际上这个表的逻辑主键,你还是必须得建立这样的索引。因为数据检索时,往往还是根据这个条件来查询的。自增列的主键,也不会 用来当外键,因为它的值极度不靠谱。这样你和别的表关联时,还是仍然需要使用表的逻辑主键字段来关联。所以这样的自增列主键,是多余的,没有必要的

个人的观点:

重点是根据应用场景合理选择,不是绝对的用或不用某种。

对于一个表来说,主键不是必须的,聚焦索引也不是必须的。

从功能上来讲,主键是对表中数据的一种约束(唯一、不允许NULL值),我们通过唯一索引(或约束),并且在字段上设置 NOT NULL属性可以达到同样的效果。所以玉键是可以被取代的,这也说明它不是必需的了(当然,就算没有等同的可取代的功能,从业务上来讲,也并不是每个表都要有一个能够确保数据唯一的东东)。

在提主键的时候,会提聚焦索引,主要是因为主键默认是聚焦索引(如果表中原来没有聚焦索引,并且没有指定NONCLUSTERED的情况下),这表明主键并不是聚焦索引,只是可以把它设置为聚焦索引

所以,单独讨论自增列做主键没有任何意义,我们增加一个自增列,并且把它确认它是唯一和非NULL值的,对于我们的业务数据而言,不存在任何意义(多余和没有必要)。

需 要考虑的,是主键作为聚焦索引的情况,当主键为聚焦索引时,我们要考虑的,除了主键的特性外,还要考虑聚焦索引的特性。聚焦索引是和数据存储在一起的,它 决定数据的存储顺序,表中数据存储的非叶子层是聚焦索引值(叶子层是数据);另外,聚焦索引键是非聚焦索引的行定位器(指向数据记录的指针,如果聚集索引 不是唯一的索引,SQL Server 将添加在内部生成的值(称为唯一值)以使所有重复键唯一。此四字节的值对于用户不可见。仅当需要使聚集键唯一以用于非聚集索引中时,才添加该值)。如果表中没有聚焦索引,那么非聚焦索引的行定位器是指向行的指针(由文件标识符 (ID)、页码和页上的行数生成的行 ID (RID))。很显然,非聚焦索引不依赖聚焦索引,而数据存储我们也并不一定需要保证按照某个顺序来存储,所以聚焦索引也不是必需的。但是从效率上来讲,顺序读取比行ID这种无序读取更有效率,所以对于频繁查询的表,聚焦索引是需要的。

结合前面的信息我们知道,对于可能涉及比较频繁查询的情况,是需要聚焦索引的(确保进行有效的顺序读取);同时我们也知道,聚焦索引键值是非聚焦索引的行定位器(存储在非聚焦索引的叶子层),所以聚焦索引的宽度应该要尽可能小(每个非聚焦索引中都要存一次,当然是越小空间占用越少,I/O效率越高),确保唯一(这样就没有必要为了保证唯一去加那个附加载的值了);另外,NULL值没有比较意义,所以最好是非NULL;数据的写入顺序最好是与聚焦索引键值生成顺序差不多,并且频繁的修改尽量不会破坏顺序(这样减少碎片的产生);最后,如果聚焦索引的定义发生修改,那就相当于表和索引都要重新组织存储一次,所以最好的情况是,聚焦索引列定义基本上不会被修改。结合评估这些所有的情况,如果业务数据列是没有合适的,那么自增列会是一个不错的选择。

当然,也有人可能会说,聚焦索引的检索效率是最高的,用自增列做主键,是不是浪费了这个最高效的机会。实际上,这个有点误区,聚焦索引的叶子结点是数据,非聚焦索引的叶子结点是行定位器,这意味着,如果表中的数据不只一列的话,聚焦索引Seek所要加载的页可能比非聚焦索引多(因为它的叶子层是数据,占用的空间比非聚焦索引的行定位器要多),它的好处是SEEK到键值的时候,也就可以直接取出对应的记录了(都在同一页上);而非聚焦索引还要通过行定位器去拿对应的记录;但是如果我们要的数据在索引中就全部包含的话,聚焦索引的效率就可能要低一些了。另外一个就是SCAN的情况,很显然,非聚焦索引涉及的PAGE比聚焦索引少,SCAN会更有优势。不是所有的查询都可以做SEEK,一般查询频繁的表也有多种常用的查询组合,所以总体来说,业务数据列做主键并不是在所有的场景下都能体现出优势。

讨论帖

后续讨论话题:

2、 您认为在T-SQL编写(包括存储过程、函数和视图)上,哪些因素会影响SQL Server效率?

3、 在设计数据库操作程序上,您认为应该注意哪些事项,以确保能够有效地使用数据库?

4、 在您的SQL Server使用过程中,有哪些令您非常困惑的性能问题 ?