在BigQuery中运行异步查询的速度并不明显

Rya*_*Val 1 python google-app-engine asynchronous google-bigquery google-api-python-client

我在App Engine上使用Google的python API客户端库在Big Query中运行大量查询以生成实时分析.调用大约需要两秒钟,并且有五个查询,这太长了,所以我研究了加快速度的方法,并认为异步运行查询将是一个可靠的改进.我的想法是,我可以同时插入五个查询,谷歌会做一些魔法同时运行它们,然后jobs.getQueryResults(jobId)用来获取每个作业的结果.我决定通过计算两个异步查询的执行时间并将其与同步运行的查询进行比较来验证理论.结果:

  • 同步:3.07秒(每个查询1.34秒和1.29秒)
  • 异步:2.39秒(每个插入0.52s和0.44s,再加上1.09s getQueryResults())

这只相差0.68秒.因此,尽管异步查询速度更快,他们没有实现的谷歌平行魔术的目标以减少总执行时间.所以第一个问题:并行魔术的期望是否正确?即使不是,我特别感兴趣的是谷歌的说法

异步查询通常在查询完成之前立即返回响应.

大约半秒插入查询不符合我的'立即'定义!我想乔丹或Big Query团队中的其他人将是唯一可以回答此事的人,但我欢迎任何答案!

编辑说明:

  1. 根据Mikhail Berlyant的建议,我收集了creationTime,startTimeendTime工作回复中发现:

    • creationTimestartTime:462ms,387ms(查询1和2的时间)
    • startTimeendTime:744毫秒,1005毫秒

虽然我不确定这是否会给故事增添任何内容,因为这是发布insert()和完成电话之间的时间,我很想知道.

  1. BQ的Jobs文档中,关于并行魔术的第一个问题的答案是肯定的:

您可以在BigQuery中同时运行多个作业

代码:

对于它的价值,我在本地和生产App Engine上进行了测试.本地速度减慢了约2-3倍,但复制了结果.在我的研究中,我也发现了分区表,我希望我之前知道(这可能最终成为我的解决方案),但这个问题依然存在.这是我的代码.我省略了实际的SQL,因为它们在这种情况下无关紧要:

    def test_sync(self, request):
    t0 = time.time()

    request = bigquery.jobs()
    data = { 'query': (sql) }
    response = request.query(projectId=project_id, body=data).execute()
    t1 = time.time()

    data = { 'query': (sql) }
    response = request.query(projectId=project_id, body=data).execute()
    t2 = time.time()

    print("0-1: " + str(t1 - t0))
    print("1-2: " + str(t2 - t1))
    print("elapsed: " + str(t2 - t0))

def test_async(self, request):
    job_ids = {}

    t0 = time.time()
    job_id = async_query(sql)
    job_ids['a'] = job_id
    print("job_id: " + job_id)
    t1 = time.time()

    job_id = async_query(sql)
    job_ids['b'] = job_id
    print("job_id: " + job_id)
    t2 = time.time()

    for key, value in job_ids.iteritems():

        response = bigquery.jobs().getQueryResults(
            jobId=value,
            projectId=project_id).execute()

    t3 = time.time()
    print("0-1: " + str(t1 - t0))
    print("1-2: " + str(t2 - t1))
    print("2-3: " + str(t3 - t2))
    print("elapsed: " + str(t3 - t0))

def async_query(sql):
    job_data = {
        'jobReference': {
            'projectId': project_id
        },
        'configuration': {
            'query': {
                'query': sql,
                'priority': 'INTERACTIVE'
            }
        }
    }

response = bigquery.jobs().insert(
    projectId=project_id,
    body=job_data).execute()
job_id = response['jobReference']['jobId']

return job_id
Run Code Online (Sandbox Code Playgroud)

Jor*_*ani 5

是否并行运行查询将加快结果的答案当然是"它取决于".

当您使用异步作业API时,大约有半秒的内置延迟会添加到每个查询中.这是因为API不是为短期运行的查询而设计的; 如果您的查询在一两秒内运行,则不需要异步处理.

未来半秒延迟可能会下降,但有一些固定成本不会变得更好.例如,您向谷歌而不是一个发送两个HTTP请求.这些需要多长时间取决于您发送请求的位置以及您正在使用的网络的特征.如果你在美国,这可能只是几毫秒的往返时间,但如果你在巴西,它可能是100毫秒.

此外,当您执行jobs.query()时,接收请求的BigQuery API服务器与启动查询的服务器相同.一旦查询完成,它就可以返回结果.但是当你使用异步api时,你的getQueryResults()请求将转到另一台服务器.该服务器必须轮询作业状态或找到运行请求以获取状态的服务器.这需要时间.

因此,如果您并行运行一堆查询,每个查询需要1-2秒,但是每个查询需要加上半秒,加上初始请求需要半秒钟,你不是可能会看到很多加速.另一方面,如果您的查询每次需要5或10秒,则固定开销将占总时间的百分比.

我的猜测是,如果你并行运行大量查询,你会看到更多的加速.另一种选择是使用API​​的同步版本,但在客户端上使用多个线程并行发送多个请求.

还有一个警告,那就是查询大小.除非您购买额外的容量,否则BigQuery默认会在您的所有查询中为您提供2000个"插槽".插槽是可以并行完成的工作单元.您可以使用这些2000个插槽来运行一个巨大的查询,或者使用20个较小的查询,每个查询一次使用100个插槽.如果您运行使2000个插槽饱和的并行查询,您将遇到速度减慢.

也就是说,2000个插槽很多.粗略估计,2000个插槽每秒可处理数百GB.因此,除非您通过BigQuery推送这种卷,否则添加并行查询不会减慢您的速度.