㈠ 如何利用索引提高sqlServer数据处理的效率
在良好的数据库设计基础上,能有效地使用索引是SQL Server取得高性能的基础,SQL Server采用基于代价的优化模型,它对每一个提交的有关表的查询,决定是否使用索引或用哪一个索引。因为查询执行的大部分开销是磁盘I/O,使用索引提高性能的一个主要目标是避免全表扫描,因为全表扫描需要从磁盘上读表的每一个数据页,如果有索引指向数据值,则查询只需读几次磁盘就可以了。
所以如果建立了合理的索引,优化器就能利用索引加速数据的查询过程。但是,索引并不总是提高系统的性能,在增、删、改操作中索引的存在会增加一定的工作量,因此,在适当的地方增加适当的索引并从不合理的地方删除次优的索引,将有助于优化那些性能较差的SQL Server应用。实践表明,合理的索引设计是建立在对各种查询的分析和预测上的,只有正确地使索引与程序结合起来,才能产生最佳的优化方案。本文就SQL Server索引的性能问题进行了一些分析和实践。
一、聚簇索引(clustered indexes)的使用
聚簇索引是一种对磁盘上实际数据重新组织以按指定的一个或多个列的值排序。由于聚簇索引的索引页面指针指向数据页面,所以使用聚簇索引查找数据几乎总是比使用非聚簇索引快。每张表只能建一个聚簇索引,并且建聚簇索引需要至少相当该表120%的附加空间,以存放该表的副本和索引中间页。建立聚簇索引的思想是:
1、大多数表都应该有聚簇索引或使用分区来降低对表尾页的竞争,在一个高事务的环境中,对最后一页的封锁严重影响系统的吞吐量。
2、在聚簇索引下,数据在物理上按顺序排在数据页上,重复值也排在一起,因而在那些包含范围检查(between、<、<=、>、>=)或使用group by或order by的查询时,一旦找到具有范围中第一个键值的行,具有后续索引值的行保证物理上毗连在一起而不必进一步搜索,避免了大范围扫描,可以大大提高查询速度。
3、在一个频繁发生插入操作的表上建立聚簇索引时,不要建在具有单调上升值的列(如IDENTITY)上,否则会经常引起封锁冲突。
4、在聚簇索引中不要包含经常修改的列,因为码值修改后,数据行必须移动到新的位置。
5、选择聚簇索引应基于where子句和连接操作的类型。
聚簇索引的侯选列是:
1、主键列,该列在where子句中使用并且插入是随机的。
2、按范围存取的列,如pri_order > 100 and pri_order < 200。
3、在group by或order by中使用的列。
4、不经常修改的列。
5、在连接操作中使用的列。
二、非聚簇索引(nonclustered indexes)的使用
SQL Server缺省情况下建立的索引是非聚簇索引,由于非聚簇索引不重新组织表中的数据,而是对每一行存储索引列值并用一个指针指向数据所在的页面。换句话说非聚簇索引具有在索引结构和数据本身之间的一个额外级。一个表如果没有聚簇索引时,可有250个非聚簇索引。每个非聚簇索引提供访问数据的不同排序顺序。在建立非聚簇索引时,要权衡索引对查询速度的加快与降低修改速度之间的利弊。另外,还要考虑这些问题:
1、索引需要使用多少空间。
2、合适的列是否稳定。
3、索引键是如何选择的,扫描效果是否更佳。
4、是否有许多重复值。
对更新频繁的表来说,表上的非聚簇索引比聚簇索引和根本没有索引需要更多的额外开销。对移到新页的每一行而言,指向该数据的每个非聚簇索引的页级行也必须更新,有时可能还需要索引页的分理。从一个页面删除数据的进程也会有类似的开销,另外,删除进程还必须把数据移到页面上部,以保证数据的连续性。所以,建立非聚簇索引要非常慎重。非聚簇索引常被用在以下情况:
1、某列常用于集合函数(如Sum,....)。
2、某列常用于join,order by,group by。
3、查寻出的数据不超过表中数据量的20%。
三、覆盖索引(covering indexes)的使用
覆盖索引是指那些索引项中包含查寻所需要的全部信息的非聚簇索引,这种索引之所以比较快也正是因为索引页中包含了查寻所必须的数据,不需去访问数据页。如果非聚簇索引中包含结果数据,那么它的查询速度将快于聚簇索引。
但是由于覆盖索引的索引项比较多,要占用比较大的空间。而且update操作会引起索引值改变。所以如果潜在的覆盖查询并不常用或不太关键,则覆盖索引的增加反而会降低性能。
四、索引的选择技术
p_detail是住房公积金管理系统中记录个人明细的表,有890000行,观察在不同索引下的查询运行效果,测试在C/S环境下进行,客户机是IBM PII350(内存64M),服务器是DEC Alpha1000A(内存128M),数据库为SYBASE11.0.3。
1、 select count(*) from p_detail where
op_date>’19990101’ and op_date<’
19991231’ and pri_surplus1>300
2、 select count(*),sum(pri_surplus1) from p_detail
where op_date>’19990101’ and
pay_month between‘199908’ and’199912’
不建任何索引查询1 1分15秒
查询2 1分7秒
在op_date上建非聚簇索引查询1 57秒
查询2 57秒
在op_date上建聚簇索引查询1 <1秒
查询2 52秒
在pay_month、op_date、pri_surplus1上建索引查询1 34秒
查询2 <1秒
在op_date、pay_month、pri_surplus1上建索引查询1 <1秒
查询2 <1秒
从以上查询效果分析,索引的有无,建立方式的不同将会导致不同的查询效果,选择什么样的索引基于用户对数据的查询条件,这些条件体现于where从句和join表达式中。一般来说建立索引的思路是:
(1)主键时常作为where子句的条件,应在表的主键列上建立聚簇索引,尤其当经常用它作为连接的时候。
(2)有大量重复值且经常有范围查询和排序、分组发生的列,或者非常频繁地被访问的列,可考虑建立聚簇索引。
(3)经常同时存取多列,且每列都含有重复值可考虑建立复合索引来覆盖一个或一组查询,并把查询引用最频繁的列作为前导列,如果可能尽量使关键查询形成覆盖查询。
(4)如果知道索引键的所有值都是唯一的,那么确保把索引定义成唯一索引。
(5)在一个经常做插入操作的表上建索引时,使用fillfactor(填充因子)来减少页分裂,同时提高并发度降低死锁的发生。如果在只读表上建索引,则可以把fillfactor置为100。
(6)在选择索引键时,设法选择那些采用小数据类型的列作为键以使每个索引页能够容纳尽可能多的索引键和指针,通过这种方式,可使一个查询必须遍历的索引页面降到最小。此外,尽可能地使用整数为键值,因为它能够提供比任何数据类型都快的访问速度。
五、索引的维护
上面讲到,某些不合适的索引影响到SQL Server的性能,随着应用系统的运行,数据不断地发生变化,当数据变化达到某一个程度时将会影响到索引的使用。这时需要用户自己来维护索引。索引的维护包括:
1、重建索引
随着数据行的插入、删除和数据页的分裂,有些索引页可能只包含几页数据,另外应用在执行大块I/O的时候,重建非聚簇索引可以降低分片,维护大块I/O的效率。重建索引实际上是重新组织B-树空间。在下面情况下需要重建索引:
(1)数据和使用模式大幅度变化。
(2)排序的顺序发生改变。
(3)要进行大量插入操作或已经完成。
(4)使用大块I/O的查询的磁盘读次数比预料的要多。
(5)由于大量数据修改,使得数据页和索引页没有充分使用而导致空间的使用超出估算。
(6)dbcc检查出索引有问题。
当重建聚簇索引时,这张表的所有非聚簇索引将被重建。
2、索引统计信息的更新
当在一个包含数据的表上创建索引的时候,SQL Server会创建分布数据页来存放有关索引的两种统计信息:分布表和密度表。优化器利用这个页来判断该索引对某个特定查询是否有用。但这个统计信息并不动态地重新计算。这意味着,当表的数据改变之后,统计信息有可能是过时的,从而影响优化器追求最有工作的目标。因此,在下面情况下应该运行update statistics命令:
(1)数据行的插入和删除修改了数据的分布。
(2)对用truncate table删除数据的表上增加数据行。
(3)修改索引列的值。
六、结束语
实践表明,不恰当的索引不但于事无补,反而会降低系统的执行性能。因为大量的索引在插入、修改和删除操作时比没有索引花费更多的系统时间。例如下面情况下建立的索引是不恰当的:
1、在查询中很少或从不引用的列不会受益于索引,因为索引很少或从来不必搜索基于这些列的行。
2、只有两个或三个值的列,如男性和女性(是或否),从不会从索引中得到好处。
另外,鉴于索引加快了查询速度,但减慢了数据更新速度的特点。可通过在一个段上建表,而在另一个段上建其非聚簇索引,而这两段分别在单独的物理设备上来改善操作性能。
㈡ hashjoinrightsemi如何优化
MySQL一直被人诟病没有实现HashJoin,最新发布的8.0.18已经带上了这个功能,令人欣喜。有时候在想,MySQL为什么一直不支持HashJoin呢?我想可能是因为MySQL多用于简单的OLTP场景,并且在互联网应用居多,需求没那么紧急。另一方面可能是因为以前完全靠社区,这种演进速度毕竟有限,Oracle收购MySQL后,MySQL的发版演进速度明显加快了很多。
HashJoin本身算法实现并不复杂,要说复杂,可能是优化器配套选择执行计划时,是否选择HashJoin,选择外表,内表可能更复杂一点。不管怎样现在已经有了HashJoin,优化器在选择Join算法时又多了一个选择。MySQL本着实用主义,相信这个功能增强也回应了一些质疑,有些功能不是没有能力做好,而是有它的优先级。
在8.0.18之前,MySQL只支持NestLoopJoin算法,最简单的就是Simple NestLoop Join,MySQL针对这个算法做了若干优化,实现了Block NestLoop Join,Index NestLoop Join和Batched Key Access等,有了这些优化,在一定程度上能缓解对HashJoin的迫切程度。下文会单独拿一个章节讲MySQL的这些Join优化,下面先讲HashJoin。
Hash Join算法
NestLoopJoin算法简单来说,就是双重循环,遍历外表(驱动表),对于外表的每一行记录,然后遍历内表,然后判断join条件是否符合,进而确定是否将记录吐出给上一个执行节点。从算法角度来说,这是一个M*N的复杂度。HashJoin是针对equal-join场景的优化,基本思想是,将外表数据load到内存,并建立hash表,这样只需要遍历一遍内表,就可以完成join操作,输出匹配的记录。如果数据能全部load到内存当然好,逻辑也简单,一般称这种join为CHJ(Classic Hash Join),之前MariaDB就已经实现了这种HashJoin算法。如果数据不能全部load到内存,就需要分批load进内存,然后分批join,下面具体介绍这几种join算法的实现。
In-Memory Join(CHJ)
HashJoin一般包括两个过程,创建hash表的build过程和探测hash表的probe过程。
1).build phase
遍历外表,以join条件为key,查询需要的列作为value创建hash表。这里涉及到一个选择外表的依据,主要是评估参与join的两个表(结果集)的大小来判断,谁小就选择谁,这样有限的内存更容易放下hash表。
2).probe phase
hash表build完成后,然后逐行遍历内表,对于内表的每个记录,对join条件计算hash值,并在hash表中查找,如果匹配,则输出,否则跳过。所有内表记录遍历完,则整个过程就结束了。过程参照下图,来源于MySQL官方博客
左侧是build过程,右侧是probe过程,country_id是equal_join条件,countries表是外表,persons表是内表。
On-Disk Hash Join
CHJ的限制条件在于,要求内存能装下整个外表。在MySQL中,Join可以使用的内存通过参数join_buffer_size控制。如果join需要的内存超出了join_buffer_size,那么CHJ将无能为力,只能对外表分成若干段,每个分段逐一进行build过程,然后遍历内表对每个分段再进行一次probe过程。假设外表分成了N片,那么将扫描内表N次。这种方式当然是比较弱的。在MySQL8.0中,如果join需要内存超过了join_buffer_size,build阶段会首先利用hash算将外表进行分区,并产生临时分片写到磁盘上;然后在probe阶段,对于内表使用同样的hash算法进行分区。由于使用分片hash函数相同,那么key相同(join条件相同)必然在同一个分片编号中。接下来,再对外表和内表中相同分片编号的数据进行CHJ的过程,所有分片的CHJ做完,整个join过程就结束了。这种算法的代价是,对外表和内表分别进行了两次读IO,一次写IO。相对于之之前需要N次扫描内表IO,现在的处理方式更好。
第一张图是外表的分片过程,第二张图是内表的分片过程,第三张图是对分片进行build+probe过程。
Grace Hash Join
主流的数据库Oracle,SQLServer,PostgreSQL早就支持了HashJoin。Join算法都类似,这里介绍下Oracle使用的Grace Hash Join算法。其实整个过程与MySQL的HashJoin类似,主要有一点区别。当出现join_buffer_size不足时,MySQL会对外表进行分片,然后再进行CHJ过程。但是,极端情况下,如果数据分布不均匀,导致大量的数据hash后都分布在一个分桶中,导致分片后,join_buffer_size仍然不够,MySQL的处理方式是一次读分片读若干记录构建hash表,然后probe对应的外表分片。处理完一批后,清理hash表,重复上述过程,直到这个分片的所有数据处理完为止。这个过程与CHJ在join_buffer_size不足时,处理逻辑相同。
GraceHash在遇到这种情况时,会继续分片进行二次Hash,直到内存足够放下一个hash表为止。但是,这里仍然有极端情况,如果输入join条件都相同,那么无论进行多少次Hash,都没法分开,那么这个时候GraceHashJoin也退化成和MySQL的处理方式一样。
hybrid hash join
与GraceHashJoin的区别在于,如果缓存能缓存足够多的分片数据,会尽量缓存,那么就不必像GraceHash那样,严格地将所有分片都先读进内存,然后写到外存,然后再读进内存去走build过程。这个是在内存相对于分片比较充裕的情况下的一种优化,目的是为了减少磁盘的读写IO。目前Oceanbase的HashJoin采用的是这种join方式。
MySQL-Join算法优化
在MySQL8.0.18之前,也就是在很长一段时间内,MySQL数据库并没有HashJoin,主要的Join算法是NestLoopJoin。SimpleNestLoopJoin显然是很低效的,对内表需要进行N次全表扫描,实际复杂度是N*M,N是外表的记录数目,M是记录数,代表一次扫描内表的代价。为此,MySQL针对SimpleNestLoopJoin做了若干优化,下面贴的图片均来自网络。
BlockNestLoopJoin(BNLJ)
MySQL采用了批量技术,即一次利用join_buffer_size缓存足够多的记录,每次遍历内表时,每条内表记录与这一批数据进行条件判断,这样就减少了扫描内表的次数,如果内表比较大,间接就缓解了IO的读压力。
IndexNestLoopJoin(INLJ)
如果我们能对内表的join条件建立索引,那么对于外表的每条记录,无需再进行全表扫描内表,只需要一次Btree-Lookup即可,整体时间复杂度降低为N*O(logM)。对比HashJoin,对于外表每条记录,HashJoin是一次HashTable的search,当然HashTable也有build时间,还需要处理内存不足的情况,不一定比INLJ好。
Batched Key Access
IndexNestLoopJoin利用join条件的索引,通过Btree-Lookup去匹配减少了遍历内表的代价。如果join条件是非主键列,那么意味着大量的回表和随机IO。BKA优化的做法是,将满足条件的一批数据按主键排序,这样回表时,从主键的角度来说就相对有序,缓解随机IO的代价。BKA实际上是利用了MRR特性(MultiRangeRead),访问数据之前,先将主键排序,然后再访问。主键排序的缓存大小通过参数read_rnd_buffer_size控制。
总结
MySQL8.0以后,Server层代码做了大量的重构,虽然优化器相对于Oracle还有很大差距,但一直在进步。HashJoin的支持使得MySQL优化器有更多选择,SQL的执行路径也能做到更优,尤其是对于等值join的场景。虽然MySQL之前对于Join做过若干优化,比如NBLJ,INLJ以及BKA等,但这些代替不了HashJoin的作用。一个好用的数据库就应该具备丰富的基础能力,利用优化器分析出合适场景,然后拿出对应的基础能力以最高效的方式响应请求。
㈢ websphere 分布式计算和架构是怎么实现的
介绍
分布式计算简单来说,是把一个大计算任务拆分成多个小计算任务分布到若干台机器上去计算,然后再进行结果汇总。 目的在于分析计算海量的数据,从雷达监测的海量历史信号中分析异常信号(外星文明),淘宝双十一实时计算各地区的消费习惯等。
海量计算最开始的方案是提高单机计算性能,如大型机,后来由于数据的爆发式增长、单机性能却跟不上,才有分布式计算这种妥协方案。 因为计算一旦拆分,问题会变得非常复杂,像一致性、数据完整、通信、容灾、任务调度等问题也都来了。
举个例子,产品要求从数据库中100G的用户购买数据,分析出各地域的消费习惯金额等。 如果没什么时间要求,程序员小明就写个对应的业务处理服务程序,部署到服务器上,让它慢慢跑就是了,小明预计10个小时能处理完。 后面产品嫌太慢,让小明想办法加快到3个小时。
平常开发中类似的需求也很多,总结出来就是,数据量大、单机计算慢。 如果上Hadoop、storm之类成本较高、而且有点大才小用。 当然让老板买更好的服务器配置也是一种办法。
利用分片算法
小明作为一个有追求有理想的程序员,决定用介于单机计算和成熟计算框架的过度解决方案,这样成本和需求都能满足了。 分布式计算的核心在于计算任务拆分,如果数据能以水平拆分的方式,分布到5台机器上,每台机器只计算自身的1/5数据,这样即能在3小时内完成产品需求了。
如上所述,小明需要把这些数据按照一定维度进行划分。 按需求来看以用户ID划分最好,由于用户之间没有状态上的关联,所以也不需要事务性及二次迭代计算。 小明用简单的hash取模对id进行划分。
f(memberid) % 5 = ServerN
这样程序可以分别部署到5台机器上,然后程序按照配置只取对应余数的用户id,计算出结果并入库。 这种方式多机之间毫无关联,不需要进行通信,可以避免很多问题。 机器上的程序本身也不具备分布式的特性,它和单机一样,只计算自身获取到的数据即可,所以如果某台机器上程序崩溃的话,处理方式和单机一样,比如记录下处理进度,下次从当前进度继续进行后续计算。
利用消息队列
使用分片方式相对比较简单,但有如下不足之处。
它不具有负载均衡的能力,如果某台机器配置稍好点,它可能最先计算完,然后空闲等待着。也有可能是某些用户行为数据比较少,导致计算比较快完成。
还有一个弊端就是每台机器上需要手动更改对应的配置, 这样的话多台机器上的程序不是完全一样的,这样可以用远程配置动态修改的办法来解决。
小明这种方式引入了个第三方,消息队列。 小明先用一个单独的程序把用户信息推送到消息队列里去,然后各台机器分别取消费这个队列。 于是就有了3个角色:
推送消息的,简称Master。
消息队列,这里以Rabbitmq为例。
各个处理程序,简称Worker或Slave都行。
虽然仅仅引入了个第三方,但它已经具备了分布式计算的很多特性。
计算任务分发。 Master把需要计算的用户数据,不断的推送消息队列。
程序一致性。 Worker订阅相同的消息队列即可,无需更改程序代码。
任意扩容。 由于程序完全一样,意味着如果想要加快速度,重复部署一份程序到新机器即可。 当然这是理论上的,实际当中会受限于消息队列、数据库存储等。
容灾性。 如果5台中某一台程序挂了也不影响,利用Rabbitmq的消息确认机制,机器崩溃时正在计算的那一条数据会在超时,在其他节点上进行消费处理。
Hadoop简介
Hadoop介绍已经相当多了,这里简述下比如:”Hadoop是一套海量数据计算存储的基础平台架构”,分析下这句话。
其中计算指的是MapRece,这是做分布式计算用的。
存储指的是HDFS,基于此上层的有HBase、Hive,用来做数据存储用的。
平台,指可以给多个用户使用,比如小明有一计算需求,他只需要按照对应的接口编写业务逻辑即可,然后把程序以包的形式发布到平台上,平台进行分配调度计算等。 而上面小明的分布式计算设计只能给自己使用,如果另外有小华要使用就需要重新写一份,然后单独部署,申请机器等。Hadoop最大的优势之一就在于提供了一套这样的完整解决方案。
下面找了介绍Hadoop的概览图,跟小明的设计做对比下:
图中“大数据计算任务” 对应小明的100G用户数据的计算任务。
”任务划分“ 对应Master和消息队列。
“子任务” 对应Worker的业务逻辑。
”结果合并“ 对应把每个worker的计算结果入库。
“计算结果” 对应入库的用户消费习惯数据。
PS:为了方便描述,把小明设计的分布式计算,叫做小和尚。
MapRece
由于MapRece计算输入和输出都是基于HDFS文件,所以大多数公司的做法是把mysql或sqlserver的数据导入到HDFS,计算完后再导出到常规的数据库中,这是MapRece不够灵活的地方之一。 MapRece优势在于提供了比较简单的分布式计算编程模型,使开发此类程序变得非常简单,像之前的MPI编程就相当复杂。
狭隘的来讲,MapRece是把计算任务给规范化了,它可以等同于小和尚中Worker的业务逻辑部分。 MapRece把业务逻辑给拆分成2个大部分,Map和Rece,可以先在Map部分把任务计算一半后,扔给Rece部分继续后面的计算。 当然在Map部分把计算任务全做完也是可以的。 关于Maprece实现细节部分不多解释,有兴趣的同学可以查相关资料或看下楼主之前的C#模拟实现的博客【探索C#之微型MapRece】。
如果把小明产品经理的需求放到Hadoop来做,其处理流程大致如下:
把100G数据导入到HDFS
按照Maprece的接口编写处理逻辑,分Map、Rece两部分。
把程序包提交到Maprece平台上,存储在HDFS里。
平台中有个叫Jobtracker进程的角色进行分发任务。 这个类似小和尚的Master负载调度管理。
如果有5台机器进行计算的话,就会提前运行5个叫TaskTracker的slave进程。 这类似小和尚worker的分离版,平台把程序和业务逻辑进行分离了, 简单来说就是在机器上运行个独立进程,它能动态加载、执行jar或dll的业务逻辑代码。
Jobtracker把任务分发到TaskTracker后,TaskTracker把开始动态加载jar包,创建个独立进程执行Map部分,然后把结果写入到HDFS上。
如果有Rece部分,TaskTracker会创建个独立进程把Map输出的HDFS文件,通过RPC方式远程拉取到本地,拉取成功后,Rece开始计算后续任务。
Rece再把结果写入到HDFS中
从HDFS中把结果导出。
这样一看好像是把简单的计算任务给复杂化了,其实如果只有几台计算任务的话,使用Maprece确实是杀鸡用牛刀了。 如果有TB、PB级别的数据、跑在成百上千台计算节点上,Maprece的优势才会体现出来。 其计算框架图架构如下:
离线计算
通常称Maprece及小和尚这种计算为离线计算,因为它对已经持久化的文件数据进行计算,不能实时响应。 还有个原因就是它的处理速度比较慢,它的输入和输出源都是基于HDFS设计,如果数据不是一开始就写入到HDFS上,就会涉及到数据导入导出,这部分相对耗费时间。 而且它的数据流动是基于文件系统的,Map部分输出的数据不是直接传送到Rece部分,而是先写入HDFS再进行传送。
处理速度慢也是Maprece的不足之处,促使了后面实时计算的诞生。
另外个缺点是Maprece的计算任务流比较单一,它只有Map、Rece两部分。 简单的可以只写一部分逻辑来解决,如果想拆分成多个部分,如逻辑A、逻辑B、逻辑C等, 而且一部分计算逻辑依赖上一次计算结果的话,MapRece处理起来就比较困难了。 像storm框架解决此类问题的方案,也称为流式计算,下一章继续补充。