gpt4 book ai didi

php - 您将如何在不重做数据库的情况下使用错误的数据库优化这个遗留 Webapp

转载 作者:搜寻专家 更新时间:2023-10-30 21:56:18 25 4
gpt4 key购买 nike

问题略有修改以提高可理解性

我的目标是优化一个 Web 应用程序,该应用程序的数据库设计非常糟糕,而且我无法访问该数据库(我无法更改表或引入新的数据库)。我可以对代码本身、文件系统或通过代理进行操作。通过“优化”我的意思是:减少发送到 webapp 的请求,而不是直接发送到文件系统的请求,将数据库查询保持在最低限度,减少不同 URL 调用的数量(记住缓存)。

让我试着构建一个虚构的例子,只是为了提供一些话题。让我们想象一下这个场景:

  • 我有一个 php webapp,它公开了一个包含一百万人的数据库。每个人都会在某个时候决定他们是快乐还是悲伤
  • 当我访问 person.php?id=x {x=1,..1000000} 时,页面会创建一个指向 show_picture_of_person.php?id=x 的链接. show_picture_of_person.php 将进入一百万行的数据库,这个数据库将通过返回图像告诉我这个人是悲伤还是快乐。我不知道这张图片是什么,除非我从数据库中提取它。如果我从数据库中提取它,我就可以分析它并了解它是一张悲伤的脸还是一张快乐的脸。 show_picture_of_person 函数实际输出图像。数据库还将图像本身存储在一个 blob 中。图片总是 sad.jpg 或 happy.jpg。

我想要的不是指向 show_picture_of_person.php?id=x 的一百万个链接,而是有 2 个链接,一个用于 sad.jpg,一个用于 happy.jpg .我想到的可能的解决方案:

  1. 我编写了一个脚本来调用所有可能的 show_picture_of_person 组合,保存所有图像,了解哪些是相同的,然后编写一个查找表。我将该查找表放在 php 函数 make_sensible_url("show_picture_of_person.php?id=x") -> happy.jpg 中。此函数将在 person.php 脚本中调用。我担心 php 引擎本身的性能(像这样的数组本身就是一个 50+MB 的文件!)
  2. 与上面相同,但我没有在 PHP 中构建数组,而是创建了一个包含一百万个文本文件的文件系统,并且在每个文本文件中我将拥有图像的实际静态文件的名称(避免重复)。函数 make_sensible_url("show_picture_of_person.php?id=x") 将简单地读取并输出文件的内容。 我喜欢这种方法,因为没有数据库调用和读取 fs 应该比解决方案 1 中的巨大 PHP 数组更快。
  3. 我更改了 person.php,这样就没有指向 show_picture_of_person.php 的链接,而是制作了 data:images。问题在于,如果我对 person.php 进行了 x 次调用,我仍然对数据库进行了 2 次调用(一次用于 person.php,一次用于 show_picture_of_person.php).我只想对数据库进行 x 次调用。这也增加了页面的大小(在我的真实情况下,我在一页中有大约 20 张图片,所以有很多查询和很多字节)
  4. 不知道还有什么..

你会如何解决这个问题?谢谢!


为了完整起见,这里是原始问题:

This is the scenario:

  • a database with various tables with data which is not properly indexed (let's say for this argument's sake that we have 5000 unique objects represented in around 50.000 rows - so duplicates are present)
  • we are in a situation in which the database is non modifiable (this also mean that we can't insert another table)
  • we have a php app exposing these objects
  • there exists around 1 million php calls (all legitimate) which return one of those 5000 objects (e.g.: bad_url.php?id=bar, bad_url.php?id=foo, ..)
  • there is no easy way to programmatically decide which of the 5000 objects will be returned

Our goal is to somehow convert the million+ calls to calls which will be giveme.php?id=x, where x is one of the 5000 unique objects.

Just to give you an idea of a theoretical approach:

  • we could index all the millions calls and map them with which distinct object is returned
  • we can create a hash table or something and create a php function which would work as give_me_nice_url("bad_url.php?....").
  • my feeling is that creating an array with such solution would result in a 50-100MB array .. not sure how performant it would be running realtime under load.

My ask for this question is which approach would you use to solve this issue and handle the large data set? Does there exists a better way than a lookup table like in my solution? Remember I can't use a database in the final production setup.

最佳答案

为每条记录创建一个文件的选项很简单,但也非常臃肿。如果您知道图像是 2 个图像之一,则可以大幅减少开销。假设您使用具有 8 位字符编码的文件,这 8 位中的每一位都可以表示快乐(设置为 1)或悲伤(设置为 0)。这已经使您减少了 8。因此,对于一百万个条目 - 您的文件将达到 125K。

然后是读取和写入文件的各个位的情况。执行此操作的几个小函数和一些测试代码...

<?php
error_reporting ( E_ALL );
ini_set ( 'display_errors', 1 );

function getState ( int $user ) {
$fp = fopen ( "users.dat", "c+" );
// Find the character in the file (position is user >> 4,
// which is effectively / 8
fseek($fp, $user >> 4, SEEK_SET );
// Read the single char from the file
$flagByte = fread($fp,1);
// Extract the bit needed
// ord() converts a char to an ascii value ( 0-255)
// If the byte hasn't been read - use PHP 7 ?? to set it to a 0
// $user & 7 gets the bit position and shifts this bit to position 0
// & 1 extracts just this bit
$flag = (ord($flagByte[0]??chr(0)) >> ($user & 7 )) & 1;
fclose($fp);
return $flag;
}

function setState ( int $user, bool $status ) {
$fp = fopen ( "users.dat", "c+" );
fseek($fp, $user >> 4, SEEK_SET );
// Fetch the existing data
$flagByte = fread($fp,1);
$flagByte = ord($flagByte[0]??chr(0));
// Get position of flag
$flag = 1 << ($user & 7 );
// Either set or unset the appropriate bit
if ( $status ) {
$flagByte |= $flag;
}
else {
$flagByte &= ~$flag;
}
fseek($fp, $user >> 4, SEEK_SET );
fwrite($fp, chr($flagByte));
fclose($fp);
}

setState(1, false);
setState(2, true);
setState(3, true);
setState(4, false);
setState(71, true);
setState(600100, false);
setState(600102, true);

echo "User: 1:".getState(1).PHP_EOL;
echo "User: 71:".getState(71).PHP_EOL;
echo "User: 600100:".getState(600100).PHP_EOL;
echo "User: 3:".getState(3).PHP_EOL;
echo "User: 600102:".getState(600102).PHP_EOL;
echo "User: 4:".getState(4).PHP_EOL;
echo "User: 871:".getState(871).PHP_EOL;
echo "User: 3:".getState(3).PHP_EOL;

我确信您可以在代码上进行改进。特别是如果它被放在一个类中,您可以打开文件并关闭文件一次,而不是在每次调用时。但是,如果只测试一条记录,则不会有太大区别。

更新假设您想要针对用户跟踪图像,此方法的添加周期较慢(因为它会检查图像是否已经存在)但访问是更直接的途径。该概念使用 2 个文件,一个用于图像名称列表,一个用于与用户关联的图像。最主要的是在添加新图像时,它会检查图像是否已经存在,如果存在,则返回该图像在文件中的位置。如果没有找到,它只是将它添加到 EOF。所有名称都以 PHP_EOL 结尾,因此通过分配固定 block ,图像名称或浪费的空间没有限制。用户文件只有指向此图像文件的指针,但(为简单起见)这是一个 4 字节无符号整数,因此对于一百万用户来说,这是 4MB - 不算多。

function imageIndex ( string $addImage ): int {
$images = fopen ( "images.dat", "c+" );
while ( true ) {
$pos = ftell($images);
$image = fgets($images);
if ( $image === false || rtrim($image, PHP_EOL) == $addImage ) {
break;
}
}

if ( $image === false ) {
fseek($images, 0, SEEK_END);
$pos = ftell($images);
fwrite($images, $addImage.PHP_EOL);
}
fclose ( $images);
return $pos;
}

function addUserImage ( int $userID, string $image ) {
$users = fopen ( "users.dat", "c+" );
// Fetch image location
$image = imageIndex($image);
// Locate user indicator (4 bytes per user)
$loc = $userID << 2;
fseek($users, $loc);
// Write the location as an unsigned integer (4 bytes)
fwrite($users, pack("L", $image));
fclose ( $users);
}

function fetchUserImage ( int $userID ): string {
$users = fopen ( "users.dat", "c+" );
$images = fopen ( "images.dat", "c+" );
// Locate user indicator
$loc = $userID << 2;
fseek($users, $loc);
$imgRef = fread($users,4);
// Convert the 4 chars to a PHP integer
$imgLoc = unpack("Lloc", $imgRef);
fseek($images, $imgLoc["loc"]);
$image = fgets($images);
fclose ( $users);
fclose ( $images);

return rtrim($image,PHP_EOL);
}

// Create 4000 users with some image
// for ( $i=0; $i<2000; $i++ ) {
// addUserImage($i,"Image{$i}.jpg");
// }
// for ( $i=0; $i<2000; $i++ ) {
// $ino = 2000 - $i;
// addUserImage($i+2000,"Image{$ino}.jpg");
// }

// Fetch the image for 2000 users
for ( $i=0; $i < 4000; $i+=2) {
echo "User {$i} image=".fetchUserImage($i).PHP_EOL;
}

关于php - 您将如何在不重做数据库的情况下使用错误的数据库优化这个遗留 Webapp,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46119699/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com