php - 基于 php 中的键查找值的有效方法

标签 php dictionary lookup-tables key-value

<分区>

有了大约 100,000 个键/值对的列表(都是字符串,每个大多数大约 5-20 个字符),我正在寻找一种方法来有效地找到给定键的值。

这需要在 php 网站中完成。我熟悉 java 中的哈希表(这可能是我在 java 中工作时会做的事情)但我是 php 的新手。

我正在寻找有关如何存储此列表(在文本文件或数据库中?)和搜索此列表的提示。

该列表必须偶尔更新,但我最感兴趣的是查找时间。

最佳答案

您可以将它作为一个直接的 PHP 数组来实现,但如果可用,Sqlite 将是您提高速度和便利性的最佳选择。

PHP 数组

只需像这样将所有内容存储在一个 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

您也可以为此使用 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 秒。这还有一个好处,即无论缓存大小如何都保持合理不变。

关于php - 基于 php 中的键查找值的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4515979/

相关文章:

php - php 从 mySql 获取行从某个点降序

php - 单个页眉和页脚文件

php - CodeIgniter 连接到数据库

php - 添加 Rest API 响应到 MySQL 数据库

python - a合并两个字典,在重复的键中添加项目,没有重复时添加键和项目

python - 使用 numpy.take 类型转换错误

c++ - 使用 map 检查 id 是否存在

c# - C# 有办法给我一个不可变的字典吗?

c# - 如何在 linq 中展平字典 <string,List<string>> 并将键保留在结果中

c - 以固定大小的位数组作为键查找表