为什么我的解决方案这么慢,我如何改进查询的性能?

时间:2021-08-30 04:15:21

Currently I was able to optimise performance quite a bit, but it is still somewhat slow :/

目前我能够优化性能相当多,但它仍然有点慢:/

LATEST EDIT:

最新编辑:

My current solution (the fastest atm (but still slow) and keeps order):

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

server

服务器

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);
        }
    });
});

client query

客户端查询

$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; 
                        });
                    })  
                });
            };

client HTML

客户端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>

4 个解决方案

#1


3  

Your solution is slow because you are downloading the images from your Cloud Storage and serving them on your own server. You get a delay on the download and upload, a ~33% overhead using base64-encoded data, plus your server is strained in delivering images instead of focusing on delivering your website content.

您的解决方案很慢,因为您正在从云存储下载映像并在自己的服务器上提供它们。下载和上传会有延迟,使用base64编码的数据会造成大约33%的开销,此外,您的服务器在交付图像时也会感到紧张,而不是专注于交付网站内容。

As pointed by many on the comments, the best-practice solution is to use the public URL for the images like so:

正如许多评论所指出的那样,最佳实践的解决方案是使用公共URL来获得这样的图像:

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

By using the public URL, you are directly serving from Cloud Storage leveraging Google’s global serving infrastructure. And the application does not have to respond to requests for images, freeing up CPU cycles for other requests.

通过使用公共URL,您可以利用谷歌的全局服务基础设施直接从云存储服务。而且应用程序不必响应图像请求,为其他请求释放CPU周期。

If you do not want bots to crawl your images using the above method, Google recommends using a robots.txt file to block access to your images.

如果您不希望机器人使用上述方法抓取图像,谷歌建议使用机器人。txt文件,以阻止访问您的图像。

#2


1  

  1. Before optimize, you'd better collect some data, I will show some in the following snippet
  2. 在优化之前,您最好收集一些数据,我将在下面的代码片段中显示一些数据
  3. Looks like base64_encode(contents) may cost a lot CPU, your logic seems to repeatedly doing this. This is guessing, the true bottleneck you have to find it by your self
  4. 看起来base64_encode(内容)可能会花费大量的CPU,您的逻辑似乎反复地这样做。这是猜测,真正的瓶颈是你必须自己找到它
  5. Other suggestions may make less improvement, but that will be much in total effect(gzip\CDN\http2\loadbalance...)
  6. 其他的建议可能不会有太大的改进,但是会有很大的效果(gzip\CDN\http2\loadbalance…)

Optimization Data Collect - Server Side, which operation took too much time

优化数据采集——服务器端,这一操作花费了太多的时间

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);
    }
  });
});

Optimization Data Collect - Client Side ()

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

为什么我的解决方案这么慢,我如何改进查询的性能?

Spare the use of base64_encode(contents)

避免使用base64_encode(内容)

$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)
  }
};

avoid download every time

避免每次下载

//------------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
}

#3


-1  

The easiest way to optimize your code is to send a single ajax request to the server-side, rather than performing an ajax request for each of the images.

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

Remove the ajax calls from the loop. Loop through the collection, gather the data you need to send to the server in an array and then send it via a single ajax request. This will speed things up.

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

Also, make sure to modify your server-side POST handler so that it can handle the array, you'll be passing from the client-side.

另外,请确保修改服务器端POST处理程序,使其能够处理数组,您将从客户端传递。

#4


-2  

I am unsure how to increase the speed of the delivered files, but the problem of them being out of order is because they are asynchronous. Basically what is happening is that you are telling the server to get you a bunch of files then you wait. once the server sends them to you, you handle them. but you dont know the order they are coming in at. What you need to do is keep track of the order they are arriving. The way to do this is to have some way of tracking information about each post. For example lets say you have

我不确定如何提高提交文件的速度,但是它们不正常的问题是它们是异步的。基本上你要做的就是告诉服务器给你一堆文件然后你再等。一旦服务器将它们发送给您,您就可以处理它们。但是你不知道他们来的顺序。你需要做的是跟踪他们到达的顺序。这样做的方法是有一些跟踪每个帖子信息的方法。比如说你有

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 should set the values correctly. The syntax might be a little off, but I believe this is the right idea for setting them in the correct order. The important part is setting the context, which will set what "this" is equal to in the success handler

这应该正确地设置值。语法可能有点错误,但我认为这是正确的想法,可以将它们设置为正确的顺序。重要的部分是设置上下文,它将设置成功处理程序中“this”等于什么。

#1


3  

Your solution is slow because you are downloading the images from your Cloud Storage and serving them on your own server. You get a delay on the download and upload, a ~33% overhead using base64-encoded data, plus your server is strained in delivering images instead of focusing on delivering your website content.

您的解决方案很慢,因为您正在从云存储下载映像并在自己的服务器上提供它们。下载和上传会有延迟,使用base64编码的数据会造成大约33%的开销,此外,您的服务器在交付图像时也会感到紧张,而不是专注于交付网站内容。

As pointed by many on the comments, the best-practice solution is to use the public URL for the images like so:

正如许多评论所指出的那样,最佳实践的解决方案是使用公共URL来获得这样的图像:

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

By using the public URL, you are directly serving from Cloud Storage leveraging Google’s global serving infrastructure. And the application does not have to respond to requests for images, freeing up CPU cycles for other requests.

通过使用公共URL,您可以利用谷歌的全局服务基础设施直接从云存储服务。而且应用程序不必响应图像请求,为其他请求释放CPU周期。

If you do not want bots to crawl your images using the above method, Google recommends using a robots.txt file to block access to your images.

如果您不希望机器人使用上述方法抓取图像,谷歌建议使用机器人。txt文件,以阻止访问您的图像。

#2


1  

  1. Before optimize, you'd better collect some data, I will show some in the following snippet
  2. 在优化之前,您最好收集一些数据,我将在下面的代码片段中显示一些数据
  3. Looks like base64_encode(contents) may cost a lot CPU, your logic seems to repeatedly doing this. This is guessing, the true bottleneck you have to find it by your self
  4. 看起来base64_encode(内容)可能会花费大量的CPU,您的逻辑似乎反复地这样做。这是猜测,真正的瓶颈是你必须自己找到它
  5. Other suggestions may make less improvement, but that will be much in total effect(gzip\CDN\http2\loadbalance...)
  6. 其他的建议可能不会有太大的改进,但是会有很大的效果(gzip\CDN\http2\loadbalance…)

Optimization Data Collect - Server Side, which operation took too much time

优化数据采集——服务器端,这一操作花费了太多的时间

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);
    }
  });
});

Optimization Data Collect - Client Side ()

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

为什么我的解决方案这么慢,我如何改进查询的性能?

Spare the use of base64_encode(contents)

避免使用base64_encode(内容)

$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)
  }
};

avoid download every time

避免每次下载

//------------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
}

#3


-1  

The easiest way to optimize your code is to send a single ajax request to the server-side, rather than performing an ajax request for each of the images.

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

Remove the ajax calls from the loop. Loop through the collection, gather the data you need to send to the server in an array and then send it via a single ajax request. This will speed things up.

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

Also, make sure to modify your server-side POST handler so that it can handle the array, you'll be passing from the client-side.

另外,请确保修改服务器端POST处理程序,使其能够处理数组,您将从客户端传递。

#4


-2  

I am unsure how to increase the speed of the delivered files, but the problem of them being out of order is because they are asynchronous. Basically what is happening is that you are telling the server to get you a bunch of files then you wait. once the server sends them to you, you handle them. but you dont know the order they are coming in at. What you need to do is keep track of the order they are arriving. The way to do this is to have some way of tracking information about each post. For example lets say you have

我不确定如何提高提交文件的速度,但是它们不正常的问题是它们是异步的。基本上你要做的就是告诉服务器给你一堆文件然后你再等。一旦服务器将它们发送给您,您就可以处理它们。但是你不知道他们来的顺序。你需要做的是跟踪他们到达的顺序。这样做的方法是有一些跟踪每个帖子信息的方法。比如说你有

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 should set the values correctly. The syntax might be a little off, but I believe this is the right idea for setting them in the correct order. The important part is setting the context, which will set what "this" is equal to in the success handler

这应该正确地设置值。语法可能有点错误,但我认为这是正确的想法,可以将它们设置为正确的顺序。重要的部分是设置上下文,它将设置成功处理程序中“this”等于什么。