1700509263
数据科学家养成手册 13.2 进快还是出快
1700509264
1700509265
在第11章中我们学习了很多关于排序和检索的算法,其中有一个体会:由于数据的产生大部分是随机的,所以,如果想在某一次检索中很快地检索出一个数据,那么在检索之前准备工作的成本就比较高;相反,如果不想花费这个准备成本,就要忍受在检索时所花费的成本。这就是一对矛盾。
1700509266
1700509267
我们先来说说追求写入速度的情况。
1700509268
1700509270
13.2.1 最快写入
1700509271
1700509272
在选定一块SSD或者机械硬盘的时候,I/O峰值是有限定的。所以,这时写入的最高速度也不可能逾越硬盘自身的写入峰值。以我使用的测试机的硬盘为例,可以用“dd”命令进行写入操作,以测试连续写入速度。
1700509273
1700509274
root@henry-B85-HD3-A:/tmp# time dd if=/dev/zero of=/tmp/writetest bs=100M count=1010+0 records in10+0 records out1048576000 bytes(1.0 GB)copied, 0.689278 s, 1.5 GB/s real 0m0.690suser 0m0.000ssys 0m0.484s
1700509275
1700509276
通常第一次测试的速度都不太“稳定”,会显得写入速度非常快。例如,在以上测试中我们可以看到,写入速度大约为1.5GB/s,但实际上不可能达到这个速度,因为大部分数据都写到了缓冲区中。这种情况下,逐步提高写入的数据量就可以测出磁盘真实的写入速度了。
1700509277
1700509278
root@henry-B85-HD3-A:/tmp# time dd if=/dev/zero of=/tmp/writetest bs=100M count=2020+0 records in20+0 records out2097152000 bytes(2.1 GB)copied, 11.3685 s, 184 MB/s real 0m11.476suser 0m0.000ssys 0m1.240sroot@henry-B85-HD3-A:/tmp# time dd if=/dev/zero of=/tmp/writetest bs=100M count=3030+0 records in30+0 records out3145728000 bytes(3.1 GB)copied, 16.4094 s, 192 MB/s real 0m16.595suser 0m0.000ssys 0m1.884sroot@henry-B85-HD3-A:/tmp# time dd if=/dev/zero of=/tmp/writetest bs=100M count=4040+0 records in40+0 records out4194304000 bytes(4.2 GB)copied, 21.6509 s, 194 MB/s real 0m21.906suser 0m0.000ssys 0m2.552sroot@henry-B85-HD3-A:/tmp# time dd if=/dev/zero of=/tmp/writetest bs=100M count=5050+0 records in50+0 records out5242880000 bytes(5.2 GB)copied, 27.1801 s, 193 MB/s real 0m27.637suser 0m0.000ssys 0m3.360s
1700509279
1700509280
这个稳定在190MB/s的写入速度就是当前这块SSD磁盘的写入峰值速度。
1700509281
1700509282
机械硬盘(如图13-4所示)的测试方法与此类似。在盘片高速旋转的过程中,机械硬盘的磁头根据CPU传来的指令滑动,同时进行读写操作。对于机械硬盘,写入速度最快的方式就是在当前文件的尾部追加数据,这属于连续写入。因为所有的数据在磁盘上是连续的,也就意味着可以在高速旋转中不间断地写入数据,没有过多的寻址开销,所以对硬盘来说这种写入方式效率最高。如果这些数据是按照时间顺序直接来源于外界报送的话,这就是一种按照时间顺序连续写入的场景。
1700509283
1700509284
1700509285
1700509286
1700509287
图13-4 机械硬盘结构
1700509288
1700509289
但是,这种写入方式除了在需要连续读出整个文件的场景,在其他场景中效率都很低。例如,要在这样一个文件中找到包含“hello world”的信息,除了把整个文件全部读到内存里进行一次过滤,别无他法。这种查找的时间复杂度,与在一个单向链表中用穷举的方法找到一条数据相同。
1700509290
1700509291
1700509292
1700509293
1700509294
现在越来越多的数据收集场景中不再直接使用文本文件进行数据保存的原因也在这里。如果使用文本文件,那就纯粹是做压缩归档保存,以备未来进行审计或深度分析时使用。
1700509295
1700509297
13.2.2 读出最快
1700509298
1700509299
如果要让文件以最快的速度读出,就需要在文件写入磁盘之前进行排序处理。最常见的方式应该是在文件内容层面建立索引,并实时维护索引文件。这个过程非常复杂,非专业人士通常不会选择用这种方式来直接操作文件。更好的方式是使用数据库中的相应功能来实现。
1700509300
1700509301
追求“读出快”是一个比追求“写入快”更复杂的课题,因为这取决于读出时的场景。如果按照时间顺序连续写入,且按照时间顺序连续读出,这就是输入非常理想的、读写速度都非常快的情况。平心而论,这种情况太理想了,非常少见。在按照特定的顺序进行连续读取或随机读取的场景中,需要做强排序。Oracle中的索引组织表(Index Organized Table,IOT)就是其中的一种(如图13-5所示)。
1700509302
1700509303
1700509304
1700509305
1700509306
图13-5 堆组织表和索引组织表
1700509307
1700509308
这种表很特殊,在构建时要先声明表的类型为索引组织表,每次插入数据时,整条数据会被直接插入索引表。索引结构在这里既充当了索引结构,又充当了表数据载体。所以,其数据扫描与普通索引相比,不需要通过索引访问ROWID,再跳转到堆组织表。这种组织结构的好处是,只要是根据索引键连续扫描(索引键 BETWEEN a AND b),读出速度都非常快。当然,单条扫描的速度也非常快,只是与普通的索引项相比没有优势(除了在开始插入时需要不断调整这棵“索引树”以外,索引结构的其他劣势并不明显)。关于索引的组织方式,13.5.2节会专门讨论。
1700509309
1700509310
1700509311
[
上一页 ]
[ :1.700509262e+09 ]
[
下一页 ]