为啥我的解决方案这么慢,如何提高查询的性能?

Posted

技术标签:

【中文标题】为啥我的解决方案这么慢,如何提高查询的性能?【英文标题】:Why is my solution so slow and how can I improve performance of the query?为什么我的解决方案这么慢,如何提高查询的性能? 【发布时间】:2017-11-23 02:25:39 【问题描述】:

目前我能够对性能进行相当多的优化,但还是有点慢:/

最新编辑:

我目前的解决方案(最快的 atm(但仍然很慢)并保持秩序):

服务器

router.post('/images', function(req, res, next) 
    var image = bucket.file(req.body.image);
    image.download(function(err, contents) 
        if (err) 
            console.log(err);
         else 
            var resultImage = base64_encode(contents);
            var index = req.body.index;
            var returnObject = 
                image: resultImage,
                index: index
            
            res.send(returnObject);
        
    );
);

客户端查询

$scope.getDataset = function() 

                fb.orderByChild('id').startAt(_start).limitToFirst(_n).once("value", function(dataSnapshot) 

                    dataSnapshot.forEach(function(childDataSnapshot) 
                        _start = childDataSnapshot.child("id").val() + 1;

                        var post = childDataSnapshot.val();
                        var image = post.image;

                        var imageObject = 
                            image: image,
                            index: position
                        ;
                        position++;
                        $.ajax(
                            type: "POST",
                            url: "images",
                            data: imageObject,
                        ).done(function(result) 
                            post.image = result.image;
                            $scope.data[result.index] = post;
                            $scope.$apply();
                            firstElementsLoaded = true; 
                        );
                    )  
                );
            ;

客户端 HTML

<div ng-controller="ctrl">
        <div class="allcontent">
            <div id="pageContent" ng-repeat="d in data track by $index"><a href="details/d.key" target="_blank"><h3 class="text-left">d.title<a href="../users/d.author"><span class="authorLegend"><i> by d.username</i></span></a></h3>
                </a>
                <div class="postImgIndex" ng-show="d.upvotes - d.downvotes > -50">
                    <a href="details/d.key" target="_blank"><img class="imgIndex" ng-src="data:image/png;base64,d.image"></a>
                </div>
                <div class="postScore">d.upvotes - d.downvotes HP</div>
            </div>
        </div>
    </div>

【问题讨论】:

什么是bucket.file()?我不确定那是什么包裹。 @PatrickRoberts 忘记添加了。和上次一样。问题已编辑。 查看文档和代码后,这与上次相同。你push()将每个结果发送给$scope.data,就像你对here和fileArray所做的一样。鉴于此,你能弄清楚如何以类似的方式解决这个问题吗?你应该尝试学习如何识别和解决这个问题,这样你就不必每次都问。 @PatrickRoberts 我的主要问题是我不知道如何处理服务器和客户端之间的不同分段 你的服务器代码很好,只是你的firebase代码有点乱。我一直在寻找如何至少获取每个快照的索引,但显然我对快照的工作方式有一个基本的误解,但请查看***.com/questions/26471666/… 了解如何获取对每个孩子的引用。 【参考方案1】:

您的解决方案很慢,因为您从云存储下载图像并在自己的服务器上提供它们。您会在下载和上传时遇到延迟,使用 base64 编码的数据会产生约 33% 的开销,而且您的服务器在交付图像而不是专注于交付您的网站内容方面非常紧张。

正如许多 cmets 所指出的,最佳实践解决方案是使用公共 URL 来获取图像,如下所示:

function getPublicUrl (filename) 
  return "https://storage.googleapis.com/$CLOUD_BUCKET/$filename";

通过使用公共 URL,您可以直接从 Cloud Storage 提供服务利用 Google 的全球服务基础架构。并且应用程序不必响应图像请求,从而为其他请求释放 CPU 周期。

如果您不希望机器人使用上述方法抓取您的图片,Google 建议使用robots.txt 文件来阻止对您的图片的访问。

【讨论】:

恶意机器人没有观察到 robots.txt 文件。我以前使用过公共 URL,但这不是一个安全的解决方案:/ 签名 URL 也提供有限的保护以防止此类攻击。 我想没有比签名 URL atm 更好的解决方案了:/ 如果只有一种方法可以将文件的读取限制为仅来自我的域:(【参考方案2】:
    在优化之前,你最好收集一些数据,我会在下面的sn-p中展示一些 看起来base64_encode(contents) 可能会消耗大量CPU,您的逻辑似乎反复执行此操作。这是猜测,真正的瓶颈,你必须自己找到它 其他建议可能改进较少,但总体效果会很大(gzip\CDN\http2\loadbalance...)

优化数据收集 - 服务器端,哪个操作花费了太多时间

router.post('/images', function(req, res, next) 
  var d = new Date()
  var image = bucket.file(req.body.image);
  image.download(function(err, contents) 
    console.log('download:' + new Date() - d)
    if (err) 
      console.log(err);
     else 
      var resultImage = base64_encode(contents);
      console.log('base64_encode:' + new Date() - d)
      var index = req.body.index;
      var returnObject = 
        image: resultImage,
        index: index
      
      res.send(returnObject);
    
  );
);

优化数据收集 - 客户端 ()

备用使用base64_encode(contents)

$scope.getDataset = function() 

  fb.orderByChild('id').startAt(_start).limitToFirst(_n).once("value", function(dataSnapshot) 

    dataSnapshot.forEach(function(childDataSnapshot, index) 
      _start = childDataSnapshot.child("id").val() + 1;

      var post = childDataSnapshot.val();
      getImageBase64(post.image)
        .then((image) => 
          post.image = image;
          $scope.data[index] = post;
          $scope.$apply();
          firstElementsLoaded = true;
        )
    )
  );

  function getImageBase64(image1) 
    //without help of server, this will make your app faster
    //network reduced
    //server calculation reduced
    if (CanIUseBucktAndBase64Here) 
      return new Promise((reslove, reject) 
        var image = bucket.file(image1);
        image.download(function(err, contents) 
          if (err) 
            reject(err);
           else 
            //use worker thread might gain better performance
            var resultImage = base64_encode(contents);
            resolve(resultImage)
          
        );
      )
    
    //with help of server
    return $.ajax(
        type: "POST",
        url: "images",
        data: image1,
      )
      .then(result => result.image)
  
;

避免每次都下载

//------------load all to local suit for less images----------
// if you have many images and you can use cheaper cache like file cache

//--init.js download all images, run only once
downloadAll()

//--server.js
//when image updated, let server know and flush cache
server.get('/imageupdated', (req, res) => 
  downfile(req.params.imgid)
  res.send('got it')
)

//form cache first
server.post('/image', (req, res) => 
  memcache.get(req.body.imgid)
    .then((content) => 
      if (!content) return downfile(req.body.imgid)
      res.send(
        content
      )
      return true
    )
    .then((content) => 
      if (content === true) return
      res.send(
        content
      )
    )
)

server.listen()


//--common.js download file and cache to memcache
function downfile(imgid) 
  var base64 = ''
  return bucket.download(imgid)
    .then((file) => 
      base64 = base64(file)
      return memcache.set(imgid, base64)
    )
    .then(() => 
      return base64
    )

//downfileBatch
async function downfileBatch(skip, limit) 
  return cloudDrive.getImages(skip, limit)
    .then((list) => 
      return Promise.all(list.map())
    )

//down load all images
async function downloadAll() 
  var i = 0,
    limit = 5
  while (true) 
    var list = await downfileBatch(i, limit)
    if (list.length < limit)  else 
      i += limit
    
  
  return true

【讨论】:

问题是 image.download 是谷歌云存储 Node.js 库的一部分。据我所知,我无法在客户端上使用它。 无论如何,我不能在客户端公开我的 api 密钥,这违背了整个目的。 @TheProgrammer 那么也许某些解决方案可能效果较差,请使用更多服务器来完成任务,例如通过 nginx 进行负载均衡;如果image.download 花费大量时间,请快速将图像缓存在某个地方,例如本地文件,如果太多,缓存热门文件 @TheProgrammer 看看肯沃斯的回答,我想这应该是你想要的 不是。这正是我之前所做的:/【参考方案3】:

优化代码的最简单方法是向服务器端发送一个 ajax 请求,而不是对每个图像执行一个 ajax 请求。

从循环中删除 ajax 调用。遍历集合,收集需要发送到数组中的服务器的数据,然后通过单个 ajax 请求发送。这将加快速度。

此外,请确保修改您的服务器端 POST 处理程序,以便它可以处理数组,您将从客户端传递。

【讨论】:

遗憾的是,这意味着图像只有在全部下载后才会显示,这意味着用户的反馈时间要慢得多。我刚刚尝试过:使用您的方法每行加载 5 张图像需要 15 秒,而那段时间没有任何事情发生。我目前的方法是 5 秒。 我觉得奇怪的是,您对 ajax 请求的常见、众所周知的优化投了反对票。此优化适用于您将对所有图像执行单个请求。这将减少前端和后端之间的通信时间。这是否会影响用户的视觉反馈是不可能的,我在您的初始帖子中没有看到任何相关内容。 是什么让你认为我对你投了反对票?你知道我没有足够的代表这样做吗? (最少 125 代表反对)【参考方案4】:

我不确定如何提高传递文件的速度,但它们乱序的问题是因为它们是异步的。基本上发生的事情是你告诉服务器给你一堆文件然后你等待。一旦服务器将它们发送给您,您就可以处理它们。但你不知道他们进来的顺序。您需要做的是跟踪他们到达的顺序。做到这一点的方法是通过某种方式跟踪每个帖子的信息。 例如,假设您有

var arr = new Array(10);
for (var i = 0 ; i < arr.length; i++)
  $.ajax(
    url:____,
    type:"GET" (or whatever type you want),
    data: _____,
    context: index: i
    success : function(data)
                 arr[this.index] = data
              
  )

这应该正确设置值。语法可能有点偏离,但我相信这是按正确顺序设置它们的正确想法。重要的部分是设置上下文,它将在成功处理程序中设置“this”等于什么

【讨论】:

无论如何,我的问题是关于性能优化。我希望它加载得更快:我已经可以让它加载有序了。

以上是关于为啥我的解决方案这么慢,如何提高查询的性能?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 MongoDB 聚合查询这么慢

后端接口如何提高性能?从MySQLESHBASE等技术一起探讨下!

后端接口如何提高性能?

如何提高子查询的性能或 sql 中子查询的替代方案

为啥U盘运行速度很慢

为啥我的 MySQL 组这么慢?