我正在使用gpars并行处理250M行的MySQL数据库表。我创建了8个gpars线程,8个独立的数据库连接,并以这样的方式划分数据,即每个线程在不同的行范围内独立运行......这是一种廉价的MapReduce概念。核心是,逻辑是这样的:
withExistingPool(pool)
{
connection_array.collectParallel()
{
// Figure out which connection this thread can use.
// We use the index into the array to figure out
// which thread we are, and this tells us where to
// read data.
int i
for (i = 0; i < connection_array.size(); i++)
if (it == connection_array[i])
break
// Each thread runs the same query, with LIMIT controlling
// the position of rows it will read...if we have 8 threads
// reading 40000 rows per call to this routine, each thread
// reads 5000 rows (thread-0 reads rows 0-4999, thread-1 reads
// 5000-9999 and so forth).
def startrow = lastrow + (i * MAX_ROWS)
def rows = it.rows( "SELECT * ... LIMIT ($startrow, $MAX_ROWS)")
// Add our rows to the result set we will return to the caller
// (needs to be serialized since many threads can be here)
lock.lock()
if (!result)
result = rows
else
result += rows
lock.unlock()
}
}
代码最初运行良好,启动时每秒超过10,000行。但在几百万行之后,它开始减速。当我们有2500万行而不是每秒10,000行时,我们每秒只能获得1,000行。如果我们终止应用程序并从我们停止的位置重新启动它,它会再次返回到每秒10K行,但是随着处理的继续,它总是会变慢。
有足够的处理能力 - 这是一个8路系统,数据库是通过网络进行的,因此无论如何都有相当长的等待时间。在运行时,处理器通常运行不超过25-30%。也似乎没有任何内存泄漏 - 我们监视内存统计信息,并且一旦处理正在进行中就看不到任何更改。 MySQL服务器似乎没有受到压力(它最初运行大约30%,随着应用程序减慢而减少)。
是否有任何技巧可以帮助这类事情在大量迭代中更加一致地运行?
答案 0 :(得分:0)
好的,我们认为发现了这个问题 - 看起来它与在不同的线程上打开JDBC连接有关。通过最初在将要使用它的线程上打开连接,然后确保只有该线程访问该连接,性能问题就消失了。
我们还使用基于游标的方法重新编写逻辑,而不是使用LIMIT进行多次查询。有报道称,具有高start_row的LIMIT可能会很慢,但我们并没有看到通过进行此更改而产生的巨大差异(游标速度更快,但在处理行时性能仍然下降)。
但是,在这个和tim_yates建议的一些变化之间,我们运行速度比以前快了30% - 现在无论我们处理多少行,它都会一直很快。
答案 1 :(得分:0)
LIMIT
和OFFSET
效率不如大多数人想要的那么高。
执行LIMIT 1000,20
时,将读取1000行,但会跳过,然后将读取和传送20行。也就是说,随着OFFSET
的增长,查询变慢。
&#34;修复&#34;这是为了记住你离开的地方&#34;。使用AUTO_INCREMENT
主键尤其如此,但可以使用任何PRIMARY KEY
或UNIQUE
键完成此操作。
这是discussed further in my "Pagination" blog。它的目标是&#34; Next&#34;网页上的按钮,因此可以忽略一些讨论。