如何在C#

时间:2015-07-22 22:40:30

标签: google-bigquery

我正在使用C#.Net客户端库将数据从本地文件流插入Bigquery数据库。我使用我的代码成功插入所有数据,但性能(插入速度)非常慢。我的加载速度为20KB / sec。例如,我有一个大小为323MB的文件,程序需要大约20分钟才能加载。我测试了我的带宽,上传速度就像3MB /秒我想知道是否有办法提高性能?(我想它应该快得多)

这是我的代码:

namespace BigQuery_Test
{
    class StreamDatacs
    {


        private static ServiceAccountCredential credential;

        public StreamDatacs()
        {
            Credential2().Wait();


        }

        public async Task Credential2()
        {
            string certificateFile = "C:\\xxxxxx.p12";
            string serviceAccountEmail = "xxxxxxxxxxxx@developer.gserviceaccount.com";
            var certificate = new X509Certificate2(certificateFile, "notasecret", X509KeyStorageFlags.Exportable);
            credential = new ServiceAccountCredential(
               new ServiceAccountCredential.Initializer(serviceAccountEmail)
               {
                   Scopes = new[] { StorageService.Scope.DevstorageReadWrite,
                   BigqueryService.Scope.Bigquery }
               }.FromCertificate(certificate));

            Console.WriteLine(credential.Token);
            Console.WriteLine(credential.TokenServerUrl);
        }
        public void ReadData(string path)        
        {

            var logs = new List<TableDataInsertAllRequest.RowsData>();
            using (StreamReader sr = new StreamReader(path))
            {
                string line = "";
                string [] aLog = new string[1000];

                Log Loga = new Log();
                int count = 0;
                int i = 0;
                while((line = sr.ReadLine()) != null)
                {
                    var theLog = new TableDataInsertAllRequest.RowsData();              
                    string[] token = line.Split('\t');
                    theLog.Json = new Dictionary<string, object>();
                    theLog.Json.Add("Timestamp", token[0]);
                    theLog.Json.Add("ClientIpAddress", token[1]);
                    theLog.Json.Add("Username", token[2]);
                    theLog.Json.Add("GroupID", token[3]);
                    theLog.Json.Add("CompanyID", token[4]);
                    theLog.Json.Add("FullOrSiteLogging", token[5]);
                    theLog.Json.Add("PolicyFlags", token[6]);
                    theLog.Json.Add("ActionsTaken", token[7]);
                    theLog.Json.Add("ResponseStatus", token[8]);                       
                    logs.Add(theLog);
                    count++;

                    if (count > 20000)
                    {

                        BQStreamInsert(logs);
                        logs.Clear();
                        count = 0;

                    }
                }

            }

            Task.WaitAll(tasks.ToArray());
        }
        public void BQStreamInsert(List<TableDataInsertAllRequest.RowsData> rows)
        {
            string projectId= "ws-2015-logs";
            string datasetId = "CompanyGroup1";
            string tableId ="RawLogsTest7";
            var service = new BigqueryService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,

                ApplicationName = "BQ test"
            });
            try
            {
                var content = new TableDataInsertAllRequest();
                content.Rows = rows;
                content.Kind = "bigquery#tableDataInsertAllRequest";
                content.IgnoreUnknownValues = true;
                content.SkipInvalidRows = true;

                var insertTask = service.Tabledata.InsertAll(content, projectId, datasetId, tableId);
                TableDataInsertAllResponse response = insertTask.Execute();

            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.Message);
            }


        }
    }
}

在program.cs中:

 class Program
    {
        static void Main(string[] args)
        {

            var sw = Stopwatch.StartNew();
            StreamDatacs sd = new StreamDatacs();
            sd.ReadData(@"C:\2015071600.csv");   
            Console.WriteLine(sw.ElapsedMilliseconds);
            sw.Stop();
            Console.Read();

        }

在代码中,我逐行读取文件,每隔20,000行停止并插入BQ,直到插入所有行。我在这里尝试了不同的缓冲区大小(500行,1000行,50,000行),但没有看到很大的区别。 我感谢你的任何建议。非常感谢。

1 个答案:

答案 0 :(得分:2)

由于流媒体的有效载荷大小有限,请参阅Quota policy更容易谈论时间,因为有效载荷以同样的方式限制在我们两个人身上,但我也会提到其他副作用。

您需要正确实施限制:

  • 最大行数:1 MB
  • HTTP请求大小限制:10 MB
  • 每个请求的最大行数:500

否则你会收到错误。因此,它被称为流式插入,因此您可以运行许多并行流程而不是一项大工作。

我们为每个流媒体请求测量1200-2500毫秒,这在过去的一个月中是一致的,如图所示。

enter image description here

我们看到了几种副作用,但是:

  • 请求随机失败,类型为“后端错误”
  • 请求随机失败,类型为“连接错误”
  • 请求随机类型'timeout'随机失败(请注意此处,因为只有部分行失败而不是整个有效负载)
  • 其他一些错误消息是非描述性的,而且它们非常模糊,以至于它们无法帮助您,只需重试即可。
  • 我们每天都会看到数百起此类故障,因此它们几乎不变,与云健康状况无关。

对于所有这些,我们在付费Google Enterprise支持中打开了案例,但不幸的是他们没有解决它。它接缝推荐的选项,因为这是一个指数退避与重试,甚至支持告诉这样做。哪个人不会让我开心。

您选择的方法如果花费数小时就意味着it does not scale,并且无法扩展。您需要使用async processes重新考虑该方法。为了更快地完成,您需要并行运行多个工作程序,流式传输性能将是相同的。只有10名工人并行,这意味着时间将减少10倍。

后台处理IO绑定或cpu绑定任务现在是大多数Web应用程序中的常见做法。有很多软件可以帮助构建后台作业,其中一些基于Beanstalkd等消息传递系统。

基本上,您需要在封闭网络中分配插入作业,确定优先级,然后使用(运行)它们。嗯,这正是Beanstalkd提供的。

Beanstalkd提供了在管中组织作业的可能性,每个管对应于作业类型。

你需要一个API /生产者,它可以将作业放在一个管上,让我们说一下该行的json表示。这是我们用例的杀手级功能。所以我们有一个获取行的API,并将它们放在管上,这只需要几毫秒,因此您可以实现快速响应时间。

另一方面,你现在在一些管子上有一堆工作。你需要一个代理人。代理人/消费者可以预约工作。

它还可以帮助您进行作业管理和重试:成功处理作业后,消费者可以从管中删除作业。在失败的情况下,消费者可以埋葬这份工作。这项工作不会被推回管中,但可供进一步检查。

消费者可以释放一份工作,Beanstalkd会将这项工作推回管中,并将其提供给其他客户。

可以在大多数常见语言中找到Beanstalkd客户端,web interface可用于调试。