精华内容
下载资源
问答
  • MySQL 的存储引擎可能是所有关系数据库产品最具有特色的了,不仅可以同时使用多种存储引擎,而且每种存储引擎和MySQL之间使用插件方式这种非常松的耦合关系。 由于各存储引擎功能特性差异较大,这篇文章主要是...

    存储引擎

    MySQL 的存储引擎可能是所有关系型数据库产品中最具有特色的了,不仅可以同时使用多种存储引擎,而且每种存储引擎和MySQL之间使用插件方式这种非常松的耦合关系。

    由于各存储引擎功能特性差异较大,这篇文章主要是介绍如何来选择合适的存储引擎来应对不同的业务场景。

    MyISAM

    1.特性

    不支持事务:MyISAM存储引擎不支持事务,所以对事务有要求的业务场景不能使用

    表级锁定:其锁定机制是表级索引,这虽然可以让锁定的实现成本很小但是也同时大大降低了其并发性能

    读写互相阻塞:不仅会在写入的时候阻塞读取,MyISAM还会在读取的时候阻塞写入,但读本身并不会阻塞另外的读

    只会缓存索引:MyISAM可以通过key_buffer缓存以大大提高访问性能减少磁盘IO,但是这个缓存区只会缓存索引,而不会缓存数据

    2.适用场景

    不需要事务支持(不支持)

    并发相对较低(锁定机制问题)

    数据修改相对较少(阻塞问题)

    以读为主

    数据一致性要求不是非常高

    3.最佳实践

    尽量索引(缓存机制)

    调整读写优先级,根据实际需求确保重要操作更优先

    启用延迟插入改善大批量写入性能

    尽量顺序操作让insert数据都写入到尾部,减少阻塞

    分解大的操作,降低单个操作的阻塞时间

    降低并发数,某些高并发场景通过应用来进行排队机制

    对于相对静态的数据,充分利用Query Cache可以极大的提高访问效率

    MyISAM的Count只有在全表扫描的时候特别高效,带有其他条件的count都需要进行实际的数据访问

    InnoDB

    1.特性

    具有较好的事务支持:支持4个事务隔离级别,支持多版本读

    行级锁定:通过索引实现,全表扫描仍然会是表锁,注意间隙锁的影响

    读写阻塞与事务隔离级别相关

    具有非常高效的缓存特性:能缓存索引,也能缓存数据

    整个表和主键以Cluster方式存储,组成一颗平衡树

    所有Secondary Index都会保存主键信息

    2.适用场景

    需要事务支持(具有较好的事务特性)

    行级锁定对高并发有很好的适应能力,但需要确保查询是通过索引完成

    数据更新较为频繁的场景

    数据一致性要求较高

    硬件设备内存较大,可以利用InnoDB较好的缓存能力来提高内存利用率,尽可能减少磁盘 IO

    3.最佳实践

    主键尽可能小,避免给Secondary index带来过大的空间负担

    避免全表扫描,因为会使用表锁

    尽可能缓存所有的索引和数据,提高响应速度

    在大批量小插入的时候,尽量自己控制事务而不要使用autocommit自动提交

    合理设置innodb_flush_log_at_trx_commit参数值,不要过度追求安全性

    避免主键更新,因为这会带来大量的数据移动

    NDBCluster

    1.特性

    分布式:分布式存储引擎,可以由多个NDBCluster存储引擎组成集群分别存放整体数据的一部分

    支持事务:和Innodb一样,支持事务

    可与mysqld不在一台主机:可以和mysqld分开存在于独立的主机上,然后通过网络和mysqld通信交互

    内存需求量巨大:新版本索引以及被索引的数据必须存放在内存中,老版本所有数据和索引必须存在与内存中

    2.适用场景

    具有非常高的并发需求

    对单个请求的响应并不是非常的critical

    查询简单,过滤条件较为固定,每次请求数据量较少,又不希望自己进行水平Sharding

    3.最佳实践

    尽可能让查询简单,避免数据的跨节点传输

    尽可能满足SQL节点的计算性能,大一点的集群SQL节点会明显多余Data节点

    在各节点之间尽可能使用万兆网络环境互联,以减少数据在网络层传输过程中的延时

    注:以上三个存储引擎是目前相对主流的存储引擎,还有其他类似如:Memory,Merge,CSV,Archive等存储引擎的使用场景都相对较少,这里就不一一分析了,如果有朋友感兴趣,后面再补充吧。

    如今Bigtable型(列族)数据库应用越来越广,功能也很强大。但是很多人还是把它当做关系型数据库在使用,用原来关系型数据库的思维建表、存储、查询。本文以hbase举例讲述数据模式的变化。

    传统关系型数据库(mysql,oracle)数据存储方式主要如下:

    图一

    上图是个很典型的数据储存方式,我把每条记录分成3部分: 主键、记录属性、索引字段。我们会对索引字段建立索引,达到 二级索引的效果。

    但是随着业务的发展,查询条件越来越复杂,需要更多的索引字段,且很多值都不存在,如下图:

    图二

    上图是6个索引字段,实际情况可能是上百个甚至更多,并且还需要根据多个索引字段刷选。查询性能越来越低,甚至无法满足查询要求。关系型数据里的局限也开始显现,于是很多人开始接触NoSQL。

    列族数据库很强大,很多人就想把数据从mysql迁到hbase,存储的方式还是跟图一或者图二一样,主键为rowkey。其他各个字段的数据,存储一个列族下的不同列。但是想对索引字段查询就没有办法,目前还没有比较好的基于bigtable的二级索引方案,所以无法对索引字段做查询。

    这时候其实可以转换下思维,可以把数据倒过来,如下图:

    图三

    把各个索引字段的值作为rowkey,然后把记录的主键和属性值按照一定顺序存在对应rowkey的value里。上图只有一个列族,是最简单的方式。 Value里的记录可以设置成定长的byte[],多个记录集合通过移位快速查询到。

    但是上面只适合单个索引字段的查询。如果要同时对多个索引字段查询,图三的方式需要求取出所有value值,比如查询“浙江”and“手机”,需要取出两个value,再解析出各自的主键求交。如果每条记录的属性有上百个,对性能影响很大。

    接下来的变化是解决多索引字段查询的问题。我们将 主键字段和属性字段分开存储,储存在不同的列族下,多索引查询只需要取出列族1下的数据,再去最小集合的列族2里取得想要的值。储存如图四:

    图四

    为什么是不同列族,而不是一个列族下的两个列?

    列族数据库数据文件是按照列族分的。在取数据时,都会把一个列族的所有列数据都取出来,事实上我们并不需要把记录明细取出来,所以把这部分数据放到了另一个列族下。

    接下来是对列族2扩展,列族2储存更多的列,用来做各种刷选、计算处理。如下图:

    图五

    后来我感觉这玩样越来越像搜索了。。。

    索引设计

    为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引。本小节将向读者介绍一些索引的设计原则。

    1.选择唯一性索引

    唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。如果使用姓名的话,可能存在同名现象,从而降低查询速度。

    2.为经常需要排序、分组和联合操作的字段建立索引

    经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。

    3.为常作为查询条件的字段建立索引

    如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。因此,为这样的字段建立索引,可以提高整个表的查询速度。

    4.限制索引的数目

    索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。

    5.尽量使用数据量少的索引

    如果索引的值很长,那么查询的速度会受到影响。例如,对一个CHAR(100)类型的字段进行全文检索需要的时间肯定要比对CHAR(10)类型的字段需要的时间要多。

    6.尽量使用前缀来索引

    如果索引字段的值很长,最好使用值的前缀来索引。例如,TEXT和BLOG类型的字段,进行全文检索会很浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度。

    7.删除不再使用或者很少使用的索引

    表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。

    注意:选择索引的最终目的是为了使查询的速度变快。上面给出的原则是最基本的准则,但不能拘泥于上面的准则。读者要在以后的学习和工作中进行不断的实践。根据应用的实际情况进行分析和判断,选择最合适的索引方式。

    SQL 优化

    (一)深入浅出理解索引结构

    实际上,您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别:

    其实,我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。

    我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。

    如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。

    我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。

    通过以上例子,我们可以理解到什么是“聚集索引”和“非聚集索引”。

    进一步引申一下,我们可以很容易的理解:每个表只能有一个聚集索引,因为目录只能按照一种方法进行排序。

    (二)何时使用聚集索引或非聚集索引

    下面的表总结了何时使用聚集索引或非聚集索引(很重要)。

    动作描述

    使用聚集索引

    使用非聚集索引

    列经常被分组排序

    返回某范围内的数据

    不应

    一个或极少不同值

    不应

    不应

    小数目的不同值

    不应

    大数目的不同值

    不应

    频繁更新的列

    不应

    外键列

    主键列

    频繁修改索引列

    不应

    事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如:返回某范围内的数据一项。比如您的某个表有一个时间列,恰好您把聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到具体内容。

    (三)结合实际,谈索引使用的误区

    理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引,但在实践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区,以便于大家掌握索引建立的方法。

    1、主键就是聚集索引

    这种想法笔者认为是极端错误的,是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。

    通常,我们会在每个表中都建立一个ID列,以区分每条数据,并且这个ID列是自动增大的,步长一般为1。我们的这个办公自动化的实例中的列Gid就是如此。此时,如果我们将这个列设为主键,SQL SERVER会将此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但笔者认为这样做意义不大。

    显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。

    从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中,因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则;当然,这种情况只是针对用户经常修改记录内容,特别是索引项的时候会负作用,但对于查询速度并没有影响。

    在办公自动化系统中,无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。

    通常,办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的情况,但如果您的系统已建立了很长时间,并且数据量很大,那么,每次每个用户打开首页的时候都进行一次全表扫描,这样做意义是不大的,绝大多数的用户1个月前的文件都已经浏览过了,这样做只能徒增数据库的开销而已。事实上,我们完全可以让用户打开系统首页时,数据库仅仅查询这个用户近3个月来未阅览的文件,通过“日期”这个字段来限制表扫描,提高查询速度。如果您的办公自动化系统已经建立的2年,那么您的首页显示速度理论上将是原来速度8倍,甚至更快。

    在这里之所以提到“理论上”三字,是因为如果您的聚集索引还是盲目地建在ID这个主键上时,您的查询速度是没有这么高的,即使您在“日期”这个字段上建立的索引(非聚合索引)。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现(3个月内的数据为25万条):

    (1)仅在主键上建立聚集索引,并且不划分时间段:

    Select gid,fariqi,neibuyonghu,title from tgongwen

    用时:128470毫秒(即:128秒)

    (2)在主键上建立聚集索引,在fariq上建立非聚集索引:

    select gid,fariqi,neibuyonghu,title from Tgongwen

    where fariqi> dateadd(day,-90,getdate())

    用时:53763毫秒(54秒)

    (3)将聚合索引建立在日期列(fariqi)上:

    select gid,fariqi,neibuyonghu,title from Tgongwen

    where fariqi> dateadd(day,-90,getdate())

    用时:2423毫秒(2秒)

    虽然每条语句提取出来的都是25万条数据,各种情况的差异却是巨大的,特别是将聚集索引建立在日期列时的差异。事实上,如果您的数据库真的有1000万容量的话,把主键建立在ID列上,就像以上的第1、2种情况,在网页上的表现就是超时,根本就无法显示。这也是我摒弃ID列作为聚集索引的一个最重要的因素。

    得出以上速度的方法是:在各个select语句前加:declare @d datetime

    set @d=getdate()

    并在select语句后加:

    select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())

    2、只要建立索引就能显著提高查询速度

    事实上,我们可以发现上面的例子中,第2、3条语句完全相同,且建立索引的字段也相同;不同的仅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查询速度却有着天壤之别。所以,并非是在任何字段上简单地建立索引就能提高查询速度。

    从建表的语句中,我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在现实中,我们每天都会发几个文件,这几个文件的发文日期就相同,这完全符合建立聚集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。由此看来,我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。

    3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度

    上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,我们可以把他们合并起来,建立一个复合索引(compound index)。

    很多人认为只要把任何字段加进聚集索引,就能提高查询速度,也有人感到迷惑:如果把复合的聚集索引字段分开查询,那么查询速度会减慢吗?带着这个问题,我们来看一下以下的查询速度(结果集都是25万条数据):(日期列fariqi首先排在复合聚集索引的起始列,用户名neibuyonghu排在后列)

    (1)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>’2004-5-5′

    查询速度:2513毫秒

    (2)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>’2004-5-5′ and neibuyonghu=’办公室’

    查询速度:2516毫秒

    (3)select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu=’办公室’

    查询速度:60280毫秒

    从以上试验中,我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的,甚至比用上全部的复合索引列还要略快(在查询结果集数目一样的情况下);而如果仅用复合聚集索引的非起始列作为查询条件的话,这个索引是不起任何作用的。当然,语句1、2的查询速度一样是因为查询的条目数一样,如果复合索引的所有列都用上,而且查询结果少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记住:无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列。

    (四)其他书上没有的索引使用经验总结

    1、用聚合索引比用不是聚合索引的主键速度快

    下面是实例语句:(都是提取25万条数据)

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16′

    使用时间:3326毫秒

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000

    使用时间:4470毫秒

    这里,用聚合索引比用不是聚合索引的主键速度快了近1/4。

    2、用聚合索引比用一般的主键作order by时速度快,特别是在小数据量情况下

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi

    用时:12936

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid

    用时:18843

    这里,用聚合索引比用一般的主键作order by时,速度快了3/10。事实上,如果数据量很小的话,用聚集索引作为排序列要比使用非聚集索引速度快得明显的多;而数据量如果很大的话,如10万以上,则二者的速度差别不明显。

    3、使用聚合索引内的时间段,搜索时间会按数据占整个数据表的百分比成比例减少,而无论聚合索引使用了多少个

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>’2004-1-1′

    用时:6343毫秒(提取100万条)

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>’2004-6-6′

    用时:3170毫秒(提取50万条)

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16′

    用时:3326毫秒(和上句的结果一模一样。如果采集的数量一样,那么用大于号和等于号是一样的)

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>’2004-1-1′ and fariqi<‘2004-6-6′

    用时:3280毫秒

    4 、日期列不会因为有分秒的输入而减慢查询速度

    下面的例子中,共有100万条数据,2004年1月1日以后的数据有50万条,但只有两个不同的日期,日期精确到日;之前有数据50万条,有5000个不同的日期,日期精确到秒。

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>’2004-1-1′ order by fariqi

    用时:6390毫秒

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi<‘2004-1-1′ order by fariqi

    用时:6453毫秒

    (五)其他注意事项

    “水可载舟,亦可覆舟”,索引也一样。索引有助于提高检索性能,但过多或不当的索引也会导致系统低效。因为用户在表中每加进一个索引,数据库就要做更多的工作。过多的索引甚至会导致索引碎片。

    所以说,我们要建立一个“适当”的索引体系,特别是对聚合索引的创建,更应精益求精,以使您的数据库能得到高性能的发挥。

    当然,在实践中,作为一个尽职的数据库管理员,您还要多测试一些方案,找出哪种方案效率最高、最为有效。

    二、改善SQL语句

    很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解。比如:

    select * from table1 where name=’zhangsan’ and tID > 10000

    和执行:

    select * from table1 where tID > 10000 and name=’zhangsan’

    一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果tID是一个聚合索引,那么后一句仅仅从表的10000条以后的记录中查找就行了;而前一句则要先从全表中查找看有几个name=’zhangsan’的,而后再根据限制条件条件tID>10000来提出查询结果。

    事实上,这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”,它可以计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,也就是说,它能实现自动优化。

    虽然查询优化器可以根据where子句自动的进行查询优化,但大家仍然有必要了解一下“查询优化器”的工作原理,如非这样,有时查询优化器就会不按照您的本意进行快速查询。

    在查询分析阶段,查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。

    SARG的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值得范围内的匹配或者两个以上条件的AND连接。形式如下:

    列名 操作符 <常数 或 变量>

    <常数 或 变量> 操作符列名

    列名可以出现在操作符的一边,而常数或变量出现在操作符的另一边。如:

    Name=’张三’

    价格>5000

    5000<价格

    Name=’张三’ and 价格>5000

    如果一个表达式不能满足SARG的形式,那它就无法限制搜索的范围了,也就是SQL SERVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足SARG形式的表达式来说是无用的。

    介绍完SARG后,我们来总结一下使用SARG以及在实践中遇到的和某些资料上结论不同的经验:

    1、Like语句是否属于SARG取决于所使用的通配符的类型

    如:name like ‘张%’ ,这就属于SARG

    而:name like ‘%张’,就不属于SARG。

    原因是通配符%在字符串的开通使得索引无法使用。

    2、or 会引起全表扫描

    Name=’张三’ and 价格>5000 符号SARG,而:Name=’张三’ or 价格>5000 则不符合SARG。使用or会引起全表扫描。

    3、非操作符、函数引起的不满足SARG形式的语句

    不满足SARG形式的语句最典型的情况就是包括非操作符的语句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等,另外还有函数。下面就是几个不满足SARG形式的例子:

    ABS(价格)<5000

    Name like ‘%三’

    有些表达式,如:

    WHERE 价格*2>5000

    SQL SERVER也会认为是SARG,SQL SERVER会将此式转化为:

    WHERE 价格>2500/2

    但我们不推荐这样使用,因为有时SQL SERVER不能保证这种转化与原始表达式是完全等价的。

    4、IN 的作用相当与OR

    语句:

    Select * from table1 where tid in (2,3)

    Select * from table1 where tid=2 or tid=3

    是一样的,都会引起全表扫描,如果tid上有索引,其索引也会失效。

    5、尽量少用NOT

    6、exists 和 in 的执行效率是一样的

    很多资料上都显示说,exists要比in的执行效率要高,同时应尽可能的用not exists来代替not in。但事实上,我试验了一下,发现二者无论是前面带不带not,二者之间的执行效率都是一样的。因为涉及子查询,我们试验这次用SQL SERVER自带的pubs数据库。运行前我们可以把SQL SERVER的statistics I/O状态打开。

    (1)select title,price from titles where title_id in (select title_id from sales where qty>30)

    该句的执行结果为:

    表 ‘sales’。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。

    表 ‘titles’。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。

    (2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty>30)

    第二句的执行结果为:

    表 ‘sales’。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。

    表 ‘titles’。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。

    我们从此可以看到用exists和用in的执行效率是一样的。

    7、用函数charindex()和前面加通配符%的LIKE执行效率一样

    前面,我们谈到,如果在LIKE前面加上通配符%,那么将会引起全表扫描,所以其执行效率是低下的。但有的资料介绍说,用函数charindex()来代替LIKE速度会有大的提升,经我试验,发现这种说明也是错误的:

    select gid,title,fariqi,reader from tgongwen where charindex(‘刑侦支队’,reader)>0 and fariqi>’2004-5-5′

    用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。

    select gid,title,fariqi,reader from tgongwen where reader like ‘%’ + ‘刑侦支队’ + ‘%’ and fariqi>’2004-5-5′

    用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。

    8、union并不绝对比or的执行效率高

    我们前面已经谈到了在where子句中使用or会引起全表扫描,一般的,我所见过的资料都是推荐这里用union来代替or。事实证明,这种说法对于大部分都是适用的。

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16′ or gid>9990000

    用时:68秒。扫描计数 1,逻辑读 404008 次,物理读 283 次,预读 392163 次。

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16′

    union

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000

    用时:9秒。扫描计数 8,逻辑读 67489 次,物理读 216 次,预读 7499 次。

    看来,用union在通常情况下比用or的效率要高的多。

    但经过试验,笔者发现如果or两边的查询列是一样的话,那么用union则反倒和用or的执行速度差很多,虽然这里union扫描的是索引,而or扫描的是全表。

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16′ or fariqi=’2004-2-5′

    用时:6423毫秒。扫描计数 2,逻辑读 14726 次,物理读 1 次,预读 7176 次。

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-9-16′

    union

    select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=’2004-2-5′

    用时:11640毫秒。扫描计数 8,逻辑读 14806 次,物理读 108 次,预读 1144 次。

    9、字段提取要按照“需多少、提多少”的原则,避免“select *”

    我们来做一个试验:

    select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

    用时:4673毫秒

    select top 10000 gid,fariqi,title from tgongwen order by gid desc

    用时:1376毫秒

    select top 10000 gid,fariqi from tgongwen order by gid desc

    用时:80毫秒

    由此看来,我们每少提取一个字段,数据的提取速度就会有相应的提升。提升的速度还要看您舍弃的字段的大小来判断。

    10、count(*)不比count(字段)慢

    某些资料上说:用*会统计所有列,显然要比一个世界的列名效率低。这种说法其实是没有根据的。我们来看:

    select count(*) from Tgongwen

    用时:1500毫秒

    select count(gid) from Tgongwen

    用时:1483毫秒

    select count(fariqi) from Tgongwen

    用时:3140毫秒

    select count(title) from Tgongwen

    用时:52050毫秒

    从以上可以看出,如果用count(*)和用count(主键)的速度是相当的,而count(*)却比其他任何除主键以外的字段汇总速度要快,而且字段越长,汇总的速度就越慢。我想,如果用count(*), SQL SERVER可能会自动查找最小字段来汇总的。当然,如果您直接写count(主键)将会来的更直接些。

    11、order by按聚集索引列排序效率最高

    我们来看:(gid是主键,fariqi是聚合索引列)

    select top 10000 gid,fariqi,reader,title from tgongwen

    用时:196 毫秒。 扫描计数 1,逻辑读 289 次,物理读 1 次,预读 1527 次。

    select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc

    用时:4720毫秒。 扫描计数 1,逻辑读 41956 次,物理读 0 次,预读 1287 次。

    select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc

    用时:4736毫秒。 扫描计数 1,逻辑读 55350 次,物理读 10 次,预读 775 次。

    select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc

    用时:173毫秒。 扫描计数 1,逻辑读 290 次,物理读 0 次,预读 0 次。

    select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc

    用时:156毫秒。 扫描计数 1,逻辑读 289 次,物理读 0 次,预读 0 次。

    从以上我们可以看出,不排序的速度以及逻辑读次数都是和“order by 聚集索引列” 的速度是相当的,但这些都比“order by 非聚集索引列”的查询速度是快得多的。

    同时,按照某个字段进行排序的时候,无论是正序还是倒序,速度是基本相当的。

    12、高效的TOP

    事实上,在查询和提取超大容量的数据集时,影响数据库响应时间的最大因素不是数据查找,而是物理的I/0操作。如:

    select top 10 * from (

    select top 10000 gid,fariqi,title from tgongwen

    where neibuyonghu=’办公室’

    order by gid desc) as a

    order by gid asc

    这条语句,从理论上讲,整条语句的执行时间应该比子句的执行时间长,但事实相反。因为,子句执行后返回的是10000条记录,而整条语句仅返回10条语句,所以影响数据库响应时间最大的因素是物理I/O操作。而限制物理I/O操作此处的最有效方法之一就是使用TOP关键词了。TOP关键词是SQL SERVER中经过系统优化过的一个用来提取前几条或前几个百分比数据的词。经笔者在实践中的应用,发现TOP确实很好用,效率也很高。但这个词在另外一个大型数据库ORACLE中却没有,这不能说不是一个遗憾,虽然在ORACLE中可以用其他方法(如:rownumber)来解决。在以后的关于“实现千万级数据的分页显示存储过程”的讨论中,我们就将用到TOP这个关键词。

    到此为止,我们上面讨论了如何实现从大容量的数据库中快速地查询出您所需要的数据方法。当然,我们介绍的这些方法都是“软”方法,在实践中,我们还要考虑各种“硬”因素,如:网络性能、服务器的性能、操作系统的性能,甚至网卡、交换机等。

    三、实现小数据量和海量数据的通用分页显示存储过程

    建立一个web 应用,分页浏览功能必不可少。这个问题是数据库处理中十分常见的问题。经典的数据分页方法是:ADO 纪录集分页法,也就是利用ADO自带的分页功能(利用游标)来实现分页。但这种分页方法仅适用于较小数据量的情形,因为游标本身有缺点:游标是存放在内存中,很费内存。游标一建立,就将相关的记录锁住,直到取消游标。游标提供了对特定集合中逐行扫描的手段,一般使用游标来逐行遍历数据,根据取出数据条件的不同进行不同的操作。而对于多表和大表中定义的游标(大的数据集合)循环很容易使程序进入一个漫长的等待甚至死机。

    更重要的是,对于非常大的数据模型而言,分页检索时,如果按照传统的每次都加载整个数据源的方法是非常浪费资源的。现在流行的分页方法一般是检索页面大小的块区的数据,而非检索所有的数据,然后单步执行当前行。

    最早较好地实现这种根据页面大小和页码来提取数据的方法大概就是“俄罗斯存储过程”。这个存储过程用了游标,由于游标的局限性,所以这个方法并没有得到大家的普遍认可。

    后来,网上有人改造了此存储过程,下面的存储过程就是结合我们的办公自动化实例写的分页存储过程:

    CREATE procedure pagination1

    (@pagesize int, –页面大小,如每页存储20条记录

    @pageindex int –当前页码

    )

    as

    set nocount on

    begin

    declare @indextable table(id int identity(1,1),nid int) –定义表变量

    declare @PageLowerBound int –定义此页的底码

    declare @PageUpperBound int –定义此页的顶码

    set @PageLowerBound=(@pageindex-1)*@pagesize

    set @PageUpperBound=@PageLowerBound+@pagesize

    set rowcount @PageUpperBound

    insert into @indextable(nid) select gid from TGongwen where fariqi >dateadd(day,-365,getdate()) order by fariqi desc

    select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t where O.gid=t.nid

    and t.id>@PageLowerBound and t.id<=@PageUpperBound order by t.id

    end

    set nocount off

    以上存储过程运用了SQL SERVER的最新技术――表变量。应该说这个存储过程也是一个非常优秀的分页存储过程。当然,在这个过程中,您也可以把其中的表变量写成临时表:CREATE TABLE #Temp。但很明显,在SQL SERVER中,用临时表是没有用表变量快的。所以笔者刚开始使用这个存储过程时,感觉非常的不错,速度也比原来的ADO的好。但后来,我又发现了比此方法更好的方法。

    笔者曾在网上看到了一篇小短文《从数据表中取出第n条到第m条的记录的方法》,全文如下:

    从publish 表中取出第 n 条到第 m 条的记录:


    SELECT TOP m-n+1 *
    FROM publish
    WHERE (id NOT IN
    (SELECT TOP n-1 id
    FROM publish))

    id 为publish 表的关键字

    我当时看到这篇文章的时候,真的是精神为之一振,觉得思路非常得好。等到后来,我在作办公自动化系统(ASP.NET+ C#+SQL SERVER)的时候,忽然想起了这篇文章,我想如果把这个语句改造一下,这就可能是一个非常好的分页存储过程。于是我就满网上找这篇文章,没想到,文章还没找到,却找到了一篇根据此语句写的一个分页存储过程,这个存储过程也是目前较为流行的一种分页存储过程,我很后悔没有争先把这段文字改造成存储过程:

    CREATE PROCEDURE pagination2
    (
    @SQL nVARCHAR(4000), –不带排序语句的SQL语句
    @Page int, –页码
    @RecsPerPage int, –每页容纳的记录数
    @ID VARCHAR(255), –需要排序的不重复的ID号
    @Sort VARCHAR(255) –排序字段及规则
    )
    AS

    DECLARE @Str nVARCHAR(4000)

    SET @Str=’SELECT TOP ‘+CAST(@RecsPerPage AS VARCHAR(20))+’ * FROM (‘+@SQL+’) T WHERE T.’+@ID+’NOT IN
    (SELECT TOP ‘+CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+’ ‘+@ID+’ FROM (‘+@SQL+’) T9 ORDER BY ‘+@Sort+’) ORDER BY ‘+@Sort

    PRINT @Str

    EXEC sp_ExecuteSql @Str
    GO

    其实,以上语句可以简化为:

    SELECT TOP 页大小 *

    FROM Table1

    WHERE (ID NOT IN

    (SELECT TOP 页大小*页数 id

    FROM 表

    ORDER BY id))

    ORDER BY ID

    但这个存储过程有一个致命的缺点,就是它含有NOT IN字样。虽然我可以把它改造为:

    SELECT TOP 页大小 *

    FROM Table1

    WHERE not exists

    (select * from (select top (页大小*页数) * from table1 order by id) b where b.id=a.id )

    order by id

    即,用not exists来代替not in,但我们前面已经谈过了,二者的执行效率实际上是没有区别的。

    既便如此,用TOP 结合NOT IN的这个方法还是比用游标要来得快一些。

    虽然用not exists并不能挽救上个存储过程的效率,但使用SQL SERVER中的TOP关键字却是一个非常明智的选择。因为分页优化的最终目的就是避免产生过大的记录集,而我们在前面也已经提到了TOP的优势,通过TOP 即可实现对数据量的控制。

    在分页算法中,影响我们查询速度的关键因素有两点:TOP和NOT IN。TOP可以提高我们的查询速度,而NOT IN会减慢我们的查询速度,所以要提高我们整个分页算法的速度,就要彻底改造NOT IN,同其他方法来替代它。

    我们知道,几乎任何字段,我们都可以通过max(字段)或min(字段)来提取某个字段中的最大或最小值,所以如果这个字段不重复,那么就可以利用这些不重复的字段的max或min作为分水岭,使其成为分页算法中分开每页的参照物。在这里,我们可以用操作符“>”或“<”号来完成这个使命,使查询语句符合SARG形式。如:

    Select top 10 * from table1 where id>200

    于是就有了如下分页方案:

    select top 页大小 *

    from table1

    where id>

    (select max (id) from

    (select top ((页码-1)*页大小) id from table1 order by id) as T

    )

    order by id

    在选择即不重复值,又容易分辨大小的列时,我们通常会选择主键。下表列出了笔者用有着1000万数据的办公自动化系统中的表,在以GID(GID是主键,但并不是聚集索引。)为排序列、提取gid,fariqi,title字段,分别以第1、10、100、500、1000、1万、10万、25万、50万页为例,测试以上三种分页方案的执行速度:(单位:毫秒)

    页 码

    方案1

    方案2

    方案3

    1

    60

    30

    76

    10

    46

    16

    63

    100

    1076

    720

    130

    500

    540

    12943

    83

    1000

    17110

    470

    250

    1万

    24796

    4500

    140

    10万

    38326

    42283

    1553

    25万

    28140

    128720

    2330

    50万

    121686

    127846

    7168

    从上表中,我们可以看出,三种存储过程在执行100页以下的分页命令时,都是可以信任的,速度都很好。但第一种方案在执行分页1000页以上后,速度就降了下来。第二种方案大约是在执行分页1万页以上后速度开始降了下来。而第三种方案却始终没有大的降势,后劲仍然很足。

    在确定了第三种分页方案后,我们可以据此写一个存储过程。大家知道SQL SERVER的存储过程是事先编译好的SQL语句,它的执行效率要比通过WEB页面传来的SQL语句的执行效率要高。下面的存储过程不仅含有分页方案,还会根据页面传来的参数来确定是否进行数据总数统计。

    – 获取指定页的数据

    CREATE PROCEDURE pagination3

    @tblName varchar(255), — 表名

    @strGetFields varchar(1000) = ‘*’, — 需要返回的列

    @fldName varchar(255)=”, — 排序的字段名

    @PageSize int = 10, — 页尺寸

    @PageIndex int = 1, — 页码

    @doCount bit = 0, — 返回记录总数, 非 0 值则返回

    @OrderType bit = 0, — 设置排序类型, 非 0 值则降序

    @strWhere varchar(1500) = ” — 查询条件 (注意: 不要加 where)

    AS

    declare @strSQL varchar(5000) — 主语句

    declare @strTmp varchar(110) — 临时变量

    declare @strOrder varchar(400) — 排序类型

    if @doCount != 0

    begin

    if @strWhere !=”

    set @strSQL = “select count(*) as Total from [" + @tblName + "] where “+@strWhere

    else

    set @strSQL = “select count(*) as Total from [" + @tblName + "]“

    end

    –以上代码的意思是如果@doCount传递过来的不是0,就执行总数统计。以下的所有代码都是@doCount为0的情况

    else

    begin

    if @OrderType != 0

    begin

    set @strTmp = “<(select min”

    set @strOrder = ” order by [" + @fldName +"] desc”

    –如果@OrderType不是0,就执行降序,这句很重要!

    end

    else

    begin

    set @strTmp = “>(select max”

    set @strOrder = ” order by [" + @fldName +"] asc”

    end

    if @PageIndex = 1

    begin

    if @strWhere != ”

    set @strSQL = “select top ” + str(@PageSize) +” “+@strGetFields+ ” from [" + @tblName + "] where ” + @strWhere + ” ” + @strOrder

    else

    set @strSQL = “select top ” + str(@PageSize) +” “+@strGetFields+ ” from ["+ @tblName + "] “+ @strOrder

    –如果是第一页就执行以上代码,这样会加快执行速度

    end

    else

    begin

    –以下代码赋予了@strSQL以真正执行的SQL代码

    set @strSQL = “select top ” + str(@PageSize) +” “+@strGetFields+ ” from ["

    + @tblName + "] where [" + @fldName + "]” + @strTmp + “(["+ @fldName + "]) from (select top ” + str((@PageIndex-1)*@PageSize) + ” ["+ @fldName + "] from [" + @tblName + "]” + @strOrder + “) as tblTmp)”+ @strOrder

    if @strWhere != ”

    set @strSQL = “select top ” + str(@PageSize) +” “+@strGetFields+ ” from ["

    + @tblName + "] where [" + @fldName + "]” + @strTmp + “(["

    + @fldName + "]) from (select top ” + str((@PageIndex-1)*@PageSize) + ” ["

    + @fldName + "] from [" + @tblName + "] where ” + @strWhere + ” “

    + @strOrder + “) as tblTmp) and ” + @strWhere + ” ” + @strOrder

    end

    end

    exec (@strSQL)

    GO

    上面的这个存储过程是一个通用的存储过程,其注释已写在其中了。

    在大数据量的情况下,特别是在查询最后几页的时候,查询时间一般不会超过9秒;而用其他存储过程,在实践中就会导致超时,所以这个存储过程非常适用于大容量数据库的查询。

    笔者希望能够通过对以上存储过程的解析,能给大家带来一定的启示,并给工作带来一定的效率提升,同时希望同行提出更优秀的实时数据分页算法。

    四、聚集索引的重要性和如何选择聚集索引

    在上一节的标题中,笔者写的是:实现小数据量和海量数据的通用分页显示存储过程。这是因为在将本存储过程应用于“办公自动化”系统的实践中时,笔者发现这第三种存储过程在小数据量的情况下,有如下现象:

    1、分页速度一般维持在1秒和3秒之间。

    2、在查询最后一页时,速度一般为5秒至8秒,哪怕分页总数只有3页或30万页。

    虽然在超大容量情况下,这个分页的实现过程是很快的,但在分前几页时,这个1-3秒的速度比起第一种甚至没有经过优化的分页方法速度还要慢,借用户的话说就是“还没有ACCESS数据库速度快”,这个认识足以导致用户放弃使用您开发的系统。

    笔者就此分析了一下,原来产生这种现象的症结是如此的简单,但又如此的重要:排序的字段不是聚集索引!

    本篇文章的题目是:“查询优化及分页算法方案”。笔者只所以把“查询优化”和“分页算法”这两个联系不是很大的论题放在一起,就是因为二者都需要一个非常重要的东西――聚集索引。

    在前面的讨论中我们已经提到了,聚集索引有两个最大的优势:

    1、以最快的速度缩小查询范围。

    2、以最快的速度进行字段排序。

    第1条多用在查询优化时,而第2条多用在进行分页时的数据排序。

    而聚集索引在每个表内又只能建立一个,这使得聚集索引显得更加的重要。聚集索引的挑选可以说是实现“查询优化”和“高效分页”的最关键因素。

    但要既使聚集索引列既符合查询列的需要,又符合排序列的需要,这通常是一个矛盾。

    笔者前面“索引”的讨论中,将fariqi,即用户发文日期作为了聚集索引的起始列,日期的精确度为“日”。这种作法的优点,前面已经提到了,在进行划时间段的快速查询中,比用ID主键列有很大的优势。

    但在分页时,由于这个聚集索引列存在着重复记录,所以无法使用max或min来最为分页的参照物,进而无法实现更为高效的排序。而如果将ID主键列作为聚集索引,那么聚集索引除了用以排序之外,没有任何用处,实际上是浪费了聚集索引这个宝贵的资源。

    为解决这个矛盾,笔者后来又添加了一个日期列,其默认值为getdate()。用户在写入记录时,这个列自动写入当时的时间,时间精确到毫秒。即使这样,为了避免可能性很小的重合,还要在此列上创建UNIQUE约束。将此日期列作为聚集索引列。

    有了这个时间型聚集索引列之后,用户就既可以用这个列查找用户在插入数据时的某个时间段的查询,又可以作为唯一列来实现max或min,成为分页算法的参照物。

    经过这样的优化,笔者发现,无论是大数据量的情况下还是小数据量的情况下,分页速度一般都是几十毫秒,甚至0毫秒。而用日期段缩小范围的查询速度比原来也没有任何迟钝。

    聚集索引是如此的重要和珍贵,所以笔者总结了一下,一定要将聚集索引建立在:

    1、您最频繁使用的、用以缩小查询范围的字段上;

    2、您最频繁使用的、需要排序的字段上。

    结束语:

    本篇文章汇集了笔者近段在使用数据库方面的心得,是在做“办公自动化”系统时实践经验的积累。希望这篇文章不仅能够给大家的工作带来一定的帮助,也希望能让大家能够体会到分析问题的方法;最重要的是,希望这篇文章能够抛砖引玉,掀起大家的学习和讨论的兴趣,以共同促进,共同为公安科技强警事业和金盾工程做出自己最大的努力。

    最后需要说明的是,在试验中,我发现用户在进行大数据量查询的时候,对数据库速度影响最大的不是内存大小,而是CPU。在我的P4 2.4机器上试验的时候,查看“资源管理器”,CPU经常出现持续到100%的现象,而内存用量却并没有改变或者说没有大的改变。即使在我们的HP ML 350 G3服务器上试验时,CPU峰值也能达到90%,一般持续在70%左右。

    本文的试验数据都是来自我们的HP ML 350服务器。服务器配置:双Inter Xeon 超线程 CPU 2.4G,内存1G,操作系统Windows Server 2003 Enterprise Edition,数据库SQL Server 2000 SP3。

    展开全文
  • 关系数据库

    2017-05-23 18:54:02
    关系数据库系统是支持关系模型的数据库系统。本章主要是深入地介绍关系模型。 理解、掌握名词的意义是学习本章节的关键。 1、关系模式的三个组成部分 关系数据结构、关系操作集合、关系完整性约束。 (1)单一的...

    关系数据库系统是支持关系模型的数据库系统。本章主要是深入地介绍关系模型。
    理解、掌握名词的意义是学习本章节的关键。
    1、关系模式的三个组成部分
    关系数据结构、关系操作集合、关系完整性约束。
    (1)单一的数据结构—-关系:现实世界的实体以及实体间的各种联系均用关系来表示。
    (2)数据的逻辑结构—-二维表:从用户角度,关系模型中数据的逻辑结构是一张二维表。
    2、关系操作的特点
    特点:是集合操作方式,即操作的对象和结果都是集合。这种操作方式也称为一次一集合。
    3、关系的三类完整性
    实体完整性:通常由关系系统自动支持。
    参照完整性:早期系统不支持,目前大型系统能自己支持。
    用户定义的完整性:反映应用领域需要遵循的约束条件,体现了具体领域中的语义约束,用户定义后由系统支持。
    4、笛卡尔积
    给定一组与D1,D2,……,Dn, 这些域中可以用相同的。D1,D2,……Dn的笛卡尔积为:
    D1*D2*D3*……*Dn={(d1,d2,…,dn)|di∈Di,i=1,2,…,n}
    笛卡尔积可表示一个二维表。表中的每行对应一个元组,表中的每列对应一个域。
    5、主属性和非主属性
    若一个关系有多个候选码,则选定其中一个为主码。候选码的诸属性称为主属性。
    不包含在任何候选码中的属性称为非码属性。
    6、主键(码)和外键(码)
    主码上面已经说了。
    设F是基本关系R的一个或一组属性,但不是关系R的码。如果F与基本关系S的主码Ks相对应,则称F是基本挂系R的外码。
    7、关系代数
    关系代数是一种抽象的查询语言,是关系数据操纵语言的一种传统表达方式,它是用对关系的运算来表达查询的。
    8、传统的集合运算
    传统的集合运算是二目运算,包括并、差、交、广义笛卡尔积四种运算。
    9、专门的集合运算
    专门的集合运算包括选择、投影、连接、除等。

    展开全文
  • 关系数据库设计理论

    千次阅读 2018-07-11 18:32:27
    设计一个好的关系数据库系统,关键是要设计一个好的数据库模式(数据库逻辑设计问题) 数据库逻辑设计主要解决的问题 关系数据库应该组织成几个关系模式 关系模式包括哪些属性 “不好”的数据库设计 ...

    关系数据库设计理论

    设计一个好的关系数据库系统,关键是要设计一个好的数据库模式(数据库逻辑设计问题)

    数据库逻辑设计主要解决的问题

    关系数据库应该组织成几个关系模式

    关系模式中包括哪些属性

    “不好”的数据库设计

    举例:为学校设计一个关系数据库

    关系模式: UN(Sno,Cno,G,Sdept,MN)

    • Sno:描述学生
    • Sdept:描述系名
    • MN:描述系主任
    • Cno:描述课程
    • G:描述学习成绩
    • 根据对现实世界的分析,可得出:Sno,Cno是码
    • 按照关系模式UN装入部分数据

    对数据库操作时,会出现以下问题

      1. 数据冗余(系主任名的存储次数)
        • 数据重复存储:浪费存储空间,数据库维护困难(更新异常)
      1. 插入异常(一个系刚成立)
        • 主码为空的记录不能存在与数据库,导致不能进行插入操作
      1. 删除异常(一个系的学生全部毕业)
        • 删除操作后,一些相关信息无法保存在数据库中

    要消除以上的“弊病”,把上面的关系数据库模式分解为三个关系模式

    • S(Sno,Sdept)
    • SG(Sno,Cno,G)
    • Dept(Sdept,MN)

    函数依赖

    类似于变量之间的单值函数关系

    Y=F(X),其中自变量X的值,决定一个唯一的函数值Y

    在一个关系模式里的属性,由于它在不同元组里属性值可能不同,由此可以把关系中的属性看作变量

    一个属性与另一个属性在取值上可能存在制约关系

    函数依赖就是属性间的逻辑依赖关系

    定义1 设R(U)是一个关系模式,U是R的属性集合,X和Y是U的子集.对于R(U)的任何一个可能的关系r,如果r中不存在两个元组,它们在X上的属性值相同,而在Y上的属性值不同,则称X函数决定Y,或Y函数依赖于X,记作:X Y.

    X通常称为“决定因素”

    几点说明

      1. 函数依赖是语义范畴的概念.它反映了一种语义完整性约束,只能根据语义来确定一个函数依赖.
      1. 函数依赖是指关系R模式的所有关系元组均应满足的约束条件,而不是关系模式中的某个或某些元组满足的约束条件
      1. 函数依赖与属性间的联系类型有关
        • (1)若属性X和Y之间有“一对一”的联系,
        • (2)若属性X和Y之间有“多对一”的联系,
        • (3)若属性X和Y之间有“多对多”的联系,
      1. 如果X Y,并且Y不是X的子集,则称X Y是非平凡的函数依赖;如果Y是X的子集,则称X Y是平凡的函数依赖;

    完全函数依赖与部分函数依赖

    完全函数依赖

    部分函数依赖

    码的形式定义

    候选码的两个性质

      1. 标识的唯一性: 对于R(U)中的每一元组,K的值确定后,该元组就相应确定了.
      1. 无冗余性: K是属性组的情况下,K的任何一部分都不能唯一标识该元组(定义中的完全函数依赖的意义)

    规范化

    简介

    • 用几个简单的关系去取代原来结构复杂的关系的过程叫做关系规范化.
    • 规范化理论是研究如何把一个不好的关系模式转化为好的关系模式的理论
    • 规范化理论是E.E.Codd在1971年首先提出的
    • 规范化理论是数据库设计过程中的一个非常有用的辅助工具

    范式

    • 简介
      • 规范化理论是围绕着范式建立的.
      • 满足不同程度要求的约束集则称为不同的范式.
      • 如果一个关系满足某个指定的约束集,则称它属于某个特定的范式.
      • 较高层次的范式比较低层次的范式具有“更合乎要求的性质”
      • 一个低一级范式的关系模式,通过投影运算可以转化为若干个高一级范式的关系模式的集合,这个过程叫做规范化.
      • 如果一个关系满足某个范式要求,则它也会满足较其级别低的所有范式的要求
    • 范式层次
    • 第一范式(1NF)
      • 定义5: 在关系模式R中的每一个具体关系r中,如果每个属性值都是不可再分的最小数据单位,则称R是第一范式的关系,记作R∈1NF.
      • 数据库理论研究的是规范化关系.
      • 1NF规范化: 把非规范化关系规范提高到1NF关系模式的集合.
    • 第二范式(2NF)
      • 定义6: 若关系模式R∈1NF,且每个非主属性都完全依赖于R的任意候选码,则关系模式R属于第二范式,记作R ∈2NF.
      • 2NF规范化是把1NF关系模式规范提高到变成2NF关系模式的集合.
      • 从1NF中消除非主属性对候选码的部分函数依赖,则获得2NF关系.
      • 举例:UN(Sno,Cno,G,SDN,MN)
    • 第三范式(3NF)
      • 定义7: 若关系模式R∈2NF,且每个非主属性都不传递依赖于R的任意候选码,则R∈3NF.
      • 从2NF关系中,消除非主属性对码的传递依赖函数而获得3NF关系
      • R∈3NF,则每个非主属性既不部分依赖,也不传递依赖于R的任何候选码.
      • 3NF的规范化
    • BCNF范式
      • 3NF的不完善性
        • 定义8: 若R∈1NF,且R中每个决定因素都是候选码,则R ∈BCNF.
        • 满足BCNF的关系将消除任何属性对候选码的部分依赖与传递依赖
        • 应用BCNF定义时,可直接判断1NF是否属于BCNF
        • BCNF规范化
          这里写图片描述
    展开全文
  • 满足第一范式是关系模式规范化的最低要求,否则,将有很多基本操作在这样的关系模式实现不了。第二范式:如果关系模式R满足第一范式,并且R得所有非主属性都完全依赖于R的每一个候选关键属性,称R满足第二范式,...

    一.  数据库的三个设计范式,

    第一范式:当关系模式R的所有属性都不能在分解为更基本的数据单位时,称R是满足第一范式的,简记为1NF。满足第一范式是关系模式规范化的最低要

    求,否则,将有很多基本操作在这样的关系模式中实现不了。

    第二范式:如果关系模式R满足第一范式,并且R得所有非主属性都完全依赖于R的每一个候选关键属性,称R满足第二范式,简记为2NF。

    第三范式:设R是一个满足第一范式条件的关系模式,X是R的任意属性集,如果X非传递依赖于R的任意一个候选关键字,称R满足第三范式,简记为3NF.

    注:关系实质上是一张二维表,其中每一行是一个元组,每一列是一个属性

    二. 事物

    事物特性acid:

    1、原子性:原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

    2、一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如转账:转账前aaa+bbb=2000;转账后aaa+bbb=2000;

    3、隔离性:事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

    4、持久性:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的。

    事物的隔离级别:

    1、如果不考虑事务的隔离性,会出现什么问题?

    脏读:一个线程中的事务读到了另一个线程中事务未提交的数据

    不可重复读:一个线程中的事务读到了另一个线程中提交的update的数据,前后两次读到的内容不一致。

    虚读:一个线程中的事务读到了另一个线程中提交的insert或delete的数据,前后读到的记录条数不一致。

    2、事务的隔离级别:

    READ  UNCOMMITTED    脏读、不可重复读、虚读都有可能发生

    READ  COMMITTED  能避免脏读;不可重复读、虚读有可能发生(Oracle默认)

    REPEATABLE  READ  能避免脏读、不可重复读;虚读有可能发生(MySQL默认)

    SERIALIZABLE  能避免脏读、不可重复读、虚读的发生

    以上隔离级别:从上到下,级别越高,性能越低,数据也越安全。

    MySQL:

    查看当前数据库的隔离级别:

    select  @@tx_isolation;

    更改隔离级别:(开启事务之前更改)

    set  transaction  isolation  level  四个级别之一

    展开全文
  • 浅谈关系数据库理论

    2019-07-08 19:10:20
    ​ 在关系数据库中,关系模型包括一组关系模式,并且关系之间并不是完全孤立的。设计一个适合的关系型数据库的关键是设计关系型数据库的模式,具体包括,数据库中应该包含多少个关系模式,每个关系模式应该包含哪些...
  • https://docs.influxdata.com/influxdb/v0.9/concepts/key_concepts/#,measurement,就相当于关系数据库中的table,他就是tag,field,time的容器;#,对于influxDb的measurement来说,field是必须的,并且不能根据...
  • 查询优化在关系数据库系统有着非常重要的地位 关系查询优化是影响RDBMS性能的关键因素 由于关系表达式的语义级别很高,使关系系统可以从关系表达式分析查询语义,提供了执行查询优化的可能性 查询优化的优点...
  • 展开全部一、数据库系统的三636f707962616964757a686964616f...模式是数据库的中心与关键,它独立与其他层次。设计数据库模式结构时应首先确定数据库的逻辑模式。DBMS提供模式描述性语言来严格定义模式。2、外模...
  • 关系数据库范式

    2010-10-23 09:17:00
     注:只要是关系数据库都满足第一范式。 2NF:一个table的行是可以唯一标示的,(即table的行是不可以有重复的)  官方定义:第二范式(2NF):数据库表不存在非关键字段对任一候选关键字段...
  • 关系数据库理论-6

    2020-09-09 22:39:08
    关系数据库理论 6.1 问题提出 1. 好的数据库逻辑模式 什么是一个好的模式 好的模式不会发生插入,删除,更新异常,数据冗余度近可能的少 问题的原因 由于模式的某些数据依赖引起的 2. 数据依赖 完整性...
  • 关系数据库的查询优化

    千次阅读 2018-07-17 16:29:43
    关系查询优化是影响关系数据库管理系统性能的关键因素。 一、查询优化概述 查询优化的优点不仅在于用户不必考虑如何最好地表达查询以获得较高的效率,而且在于系统可以比用户程序的“优化”做得更好。 1. 优化...
  • 数据库学习总结 第一次真正写博客,以前...1.关系数据库和nosql数据库 2.数据库索引 3.事务 4.sql的优化规则 5.查看执行计划 一,关系型数据库和nosql数据库 关系型数据库也叫sql数据库,例如我们常用的Oracle,MySQL数
  • 关键语法 GROUP BY HAVING 统计相关:COUNT ,SUM ,MAX ,MIN ,AVG GROUP BY : 必须满足 “select 子句的列名必须为分组列或列函数” 列函数对于group by 子句定义的每个组各返回一个结果 HAVING: 通常与...
  • 关系数据库的末日是否已经来临

    千次阅读 2014-07-26 23:24:51
    这其中所释放出的一个关键信息是:“如果想获得丰富而随需应变的可伸缩性,你需要一个非关系数据库。” 如果这是真的,那么这是不是一个迹象,表明曾经强大的关系式数据库终于在它的盔甲上出现了裂缝?关系数据库...
  • 规范化问题的提出 在规范化理论出现以前,层次和网状数据库的设计只是遵循其模型本身固有的...如何设计一个适合的关系数据库系统,关键是关系数据库模式的设计,一个好的关系数据库模式应该包括多少关系模式,而...
  • 数据库管理软件承载着信息系统的... 本白皮书紧密围绕关系型云数据库应用过程关键问题,梳理了关系型云数据库的发展状况与技术体系,分析了关系型云数据库选型方法、业务影响评估、实施路径,并提出了相关建议。
  • 如何建立关系数据库中概念与知识图谱中相应概念之间的映射关系,是提高垂直领域知识图谱构建效率的关键。针对这一问题,本文提出了一种基于模型驱动体系结构(MDA)的从关系数据库中自动构造知识图谱的模型驱动方法。...
  • 所欲非平凡的多值依赖都是函数依赖5NF:连接依赖均由候选码所蕴含主属性:在一个关系中,如一个属性是构成某一个候选关键字的属性集中的一个属性,则称它为主属性。候选关键字:如果一个超关键...
  •      ...第一范式(1NF):数据库的字段都是单一属性的,不可再分。...第二范式(2NF):在满足第一范式的基础上,数据库不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依
  • 数据库关系

    2018-10-24 22:31:10
    什么是关联(association) 1.1 关联指的是类之间的引用关系。如果类A与类B关联,那么被引用的类B将被定义为类A的属性。例如: ...#关键点都在数据库中的外键上面,请好好理解下面这二句SQL和一对多及...
  • rqlite是一个轻量级的分布式关系数据库,它使用作为其存储引擎。 形成集群非常简单,可以很好地处理领导者选举,并容忍包括领导者在内的机器故障。 rqlite可用于Linux,macOS和Microsoft Windows。 查看。 为什么?...
  • 提供:ZStack云计算 内容介绍关系数据库的历史相当悠久,而其崛起要归功于关系模型的广泛普及,特别是在关键性数据处理领域的应用。在本教程,我们将探讨几种高人气关系数据库管理系统(简称RDBMS)间的异同,并...
  • 关系数据库的死期到了?

    千次阅读 2009-02-24 11:09:00
    国外的一文章。 最近,大量新的非关系式数据库如雨后春笋般出现在云里云外。这其中所释放出的一个关键信息是:“如果想获得...在本文,我们将检视当前这种在特定情况下摆脱关系数据库的趋势,并分析这对于关系数据库
  • 非阻塞算法思想在关系数据库应用程序开发的使用 非阻塞算法的关键思想就是CAS,CAS是compare and set的缩写,也常被称为lock-free或者wait-free,通过把compare和set两个操作原子化,使得不需要使用锁,但是...
  • 如何建立关系数据库中概念与知识图谱中相应概念之间的映射关系,是提高垂直领域知识图谱构建效率的关键。针对这一问题,本文提出了一种基于模型驱动体系结构(MDA)的从关系数据库中自动构造知识图谱的模型驱动方法。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,567
精华内容 626
关键字:

关系数据库中关键是