使用大约 100,000 个键/值对的列表(两个字符串,每个大约 5-20 个字符),我正在寻找一种方法来有效地找到给定键的值。
这需要在 php 网站中完成。我熟悉 java 中的哈希表(如果在 java 中工作,这可能是我会做的),但对 php 很陌生。
我正在寻找有关如何存储此列表(在文本文件中还是在数据库中?)并搜索此列表的提示。
该列表必须偶尔更新,但我最感兴趣的是查找时间。
使用大约 100,000 个键/值对的列表(两个字符串,每个大约 5-20 个字符),我正在寻找一种方法来有效地找到给定键的值。
这需要在 php 网站中完成。我熟悉 java 中的哈希表(如果在 java 中工作,这可能是我会做的),但对 php 很陌生。
我正在寻找有关如何存储此列表(在文本文件中还是在数据库中?)并搜索此列表的提示。
该列表必须偶尔更新,但我最感兴趣的是查找时间。
您可以将其作为一个直接的 PHP 数组来执行,但如果可用,Sqlite 将是您最好的选择,以提高速度和便利性。
只需将所有内容存储在这样的 php 文件中:
<?php
return array(
'key1'=>'value1',
'key2'=>'value2',
// snip
'key100000'=>'value100000',
);
然后你可以像这样访问它:
<?php
$s = microtime(true); // gets the start time for benchmarking
$data = require('data.php');
echo $data['key2'];
var_dump(microtime(true)-$s); // dumps the execution time
不是世界上最有效的东西,但它会起作用。在我的机器上需要 0.1 秒。
PHP 应该启用 sqlite,这对于这种事情非常有用。
此脚本将从头到尾为您创建一个数据库,其特征与您在问题中描述的数据集相似:
<?php
// this will *create* data.sqlite if it does not exist. Make sure "/data"
// is writable and *not* publicly accessible.
// the ATTR_ERRMODE bit at the end is useful as it forces PDO to throw an
// exception when you make a mistake, rather than internally storing an
// error code and waiting for you to retrieve it.
$pdo = new PDO('sqlite:'.dirname(__FILE__).'/data/data.sqlite', null, null, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
// create the table if you need to
$pdo->exec("CREATE TABLE stuff(id TEXT PRIMARY KEY, value TEXT)");
// insert the data
$stmt = $pdo->prepare('INSERT INTO stuff(id, value) VALUES(:id, :value)');
$id = null;
$value = null;
// this binds the variables by reference so you can re-use the prepared statement
$stmt->bindParam(':id', $id);
$stmt->bindParam(':value', $value);
// insert some data (in this case it's just dummy data)
for ($i=0; $i<100000; $i++) {
$id = $i;
$value = 'value'.$i;
$stmt->execute();
}
然后使用这些值:
<?php
$s = microtime(true);
$pdo = new PDO('sqlite:'.dirname(__FILE__).'/data/data.sqlite', null, null, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
$stmt = $pdo->prepare("SELECT * FROM stuff WHERE id=:id");
$stmt->bindValue(':id', 5);
$stmt->execute();
$value = $stmt->fetchColumn(1);
var_dump($value);
// the number of seconds it took to do the lookup
var_dump(microtime(true)-$s);
这个速度更快。在我的机器上 0.0009 秒。
您也可以为此使用 MySQL 而不是 Sqlite,但如果它只是一张具有您描述的特征的表,那么它可能会有点矫枉过正。如果您有可用的 MySQL 服务器,则上面的 Sqlite 示例将在使用 MySQL 时正常工作。只需将实例化 PDO 的行更改为:
$pdo = new PDO('mysql:host=your.host;dbname=your_db', 'user', 'password', array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
sqlite 示例中的查询应该都可以在 MySQL 上正常工作,但请注意我没有对此进行测试。
并不是说 Sqlite 解决方案很慢(0.0009 秒!),但这在我的机器上快了大约四倍。此外,Sqlite 可能不可用,设置 MySQL 可能是不可能的,等等。
在这种情况下,您还可以使用文件系统:
<?php
$s = microtime(true); // more hack benchmarking
class FileCache
{
protected $basePath;
public function __construct($basePath)
{
$this->basePath = $basePath;
}
public function add($key, $value)
{
$path = $this->getPath($key);
file_put_contents($path, $value);
}
public function get($key)
{
$path = $this->getPath($key);
return file_get_contents($path);
}
public function getPath($key)
{
$split = 3;
$key = md5($key);
if (!is_writable($this->basePath)) {
throw new Exception("Base path '{$this->basePath}' was not writable");
}
$path = array();
for ($i=0; $i<$split; $i++) {
$path[] = $key[$i];
}
$dir = $this->basePath.'/'.implode('/', $path);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
return $dir.'/'.substr($key, $split);
}
}
$fc = new FileCache('/tmp/foo');
/*
// use this crap for generating a test example. it's slow to create though.
for ($i=0;$i<100000;$i++) {
$fc->add('key'.$i, 'value'.$i);
}
//*/
echo $fc->get('key1', 'value1');
var_dump(microtime(true)-$s);
这个在我的机器上查找需要 0.0002 秒。无论缓存大小如何,这还具有合理恒定的好处。
这取决于您访问数组的频率,这样想有多少用户可以同时访问它。将它存储在数据库中有很多优点,这里有 MySQL 和 SQLite 两个选项。
SQLite 的工作方式更像是支持 SQL 的文本文件,您可以在查询期间节省几毫秒,因为它位于应用程序的范围内,它的主要缺点是一次只能添加一条记录(与文本文件相同)。对于具有静态内容(如 GEO IP 数据、翻译等)的数组,我会推荐 SQLite。
MySQL 是更强大的解决方案,但需要身份验证并且位于单独的机器上。
PHP 数组将满足您的所有需求。但是这么多数据不应该存储在数据库中吗?