SPL:并行取数技术

JAVA 应用必须通过 JDBC 从数据库中取数,有时候我们会发现,数据库的负担并不重而且 SQL 很简单,但当数据量大到一定程度时取数的速度仍然很慢,这是因为JDBC本身性能不佳。SPL提供了并行运行功能,可以利用多CPU同时取数,提高了取数的效率。

 

1.   单表并行取数

ORACLE数据库中有一张CUSTOMER表有数据记录1500万行,共8个字段,其中C_CUSTKEY是从1开始的自然数。

并行取数的SPL脚本如下:


A

B

1

=now()

//记录开始时间

2

=connect("oracle").query@1x("SELECT   COUNT(*) FROM CUSTOMER")

3

>n=12

//设置并行数,根据电脑的物理CPU核数决定

4

=n.(range(1,A2+1,~:n))

//按总记录数和并行数分段,记录本段开头和下段开头数字

5

fork A4

=connect("oracle")

6


=B5.query@x("SELECT * FROM   CUSTOMER WHERE C_CUSTKEY>=? AND C_CUSTKEY<?",A5(1),A5(2))

7

=A5.conj()

//合并取数结果

8

=interval@s(A1,now())

//计算运行时间

并行取数时要把源数据分成相对平均的多个区间。本例中,C_CUSTKEY是从1开始的自然数,因此可以先求出总记录数(A2),再平均分成n段(A4)。然后 A5并行计算(注意:要每个线程各自连接数据库,否则数据库会串行执行同一连接的多个查询,速度并不能加快),再以分段区间为参数使C_CUSTKEY位于本段区间来执行SQL查询。最后合并各线程的取数结果,作为最终结果。 

有时不能这么分段,先计算count(*)有可能很慢(如果有WHERE条件的话),结果总时间反而更长了。

实际应用中,还可以采取其它合适的办法设置WHERE条件以获得相对平均的区间。假如C_CUSTKEY是自然数,不论是不是从1开始的,也不论是不是连续的,都可以采用计算它与并行数相除的余数来分段,余数为0,1,2……n-1各为一段,编写脚本如下:


A

B

1

=now()

//记录开始时间

2

>n=12

//设置并行数,根据电脑的物理CPU核数决定

3

fork to(n)

=connect("oracle")

4


=B3.query@x("SELECT * FROM   CUSTOMER WHERE MOD(C_CUSTKEY,?)=?", n, A3-1)

5

=A3.conj()

//合并取数结果

6

=interval@s(A1,now())

//计算运行时间

fork中的并行线程数和电脑物理CPU核数不一定相等,可以更大一点,运行时同时执行的线程数不会超过核数,超过的线程只能等待有的线程运行结束后有空闲的核时才执行。当分段方法分出的各段数据不太平均时,有的段因数据量少执行很快,就可以尽早让给等待的线程执行,从而实现负载均衡。

 

2.   多表并行取数

执行多个不同SQL语句取数时也可以使用并行技术,比如要从ORACLE数据库中读取五张表的数据,采用非并行取数的脚本如下:


A

B

1

SELECT * FROM SUPPLIER


2

SELECT * FROM PART


3

SELECT * FROM CUSTOMER


4

SELECT * FROM PARTSUPP


5

SELECT * FROM ORDERS


6

=now()

//记录开始时间

7

=connect("oracle")

//连接数据库

8

=[A1:A5].(A7.query(~))

//依次执行每条SQL

9

>A7.close()

//关闭数据库连接

10

=interval@s(A6,now())

//计算运行时间

改为并行取数,第7行开始脚本如下:


A

B

7

fork [A1:A5]

=connect("oracle")

8


=B7.query@x(A7)

9

=interval@s(A6,now())

//计算运行时间

尽管多表并行无法保证数据均匀分布,但并行取数也能让取数性能得到有效提升。