4

約 100,000 のキーと値のペア (両方の文字列、ほとんどがそれぞれ約 5 ~ 20 文字) のリストを使用して、特定のキーの値を効率的に見つける方法を探しています。

これは、php Web サイトで行う必要があります。私はJavaのハッシュテーブルに精通していますが(これはおそらくJavaで作業する場合に行うことです)、PHPは初めてです。

このリストを (テキスト ファイルまたはデータベースに) 保存し、このリストを検索する方法についてのヒントを探しています。

リストは時々更新する必要がありますが、私は主に検索時間に関心があります。

4

3 に答える 3

14

そのままの 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

これには Sqlite の代わりに MySQL を使用することもできますが、説明した特性を持つテーブルが 1 つだけの場合は、おそらくやり過ぎになるでしょう。上記の Sqlite の例は、MySQL サーバーを利用できる場合、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 秒!) というわけではありませんが、私のマシンでは約 4 倍高速です。また、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 秒かかります。これには、キャッシュ サイズに関係なく一定であるという利点もあります。

于 2010-12-23T05:24:29.637 に答える
1

アレイにアクセスする頻度によって異なります。このように考えて、同時にアクセスできるユーザーの数を考えてください。データベースに格納することには多くの利点があり、ここでは MySQL と SQLite の 2 つのオプションがあります。

SQLite は、SQL をサポートするテキスト ファイルのように機能します。アプリケーションの範囲内にあるため、クエリ中に数ミリ秒節約できます。一度に 1 つのレコードしか追加できないという主な欠点があります (テキスト ファイルと同じ)。GEO IP データや翻訳などの静的コンテンツを含む配列には、SQLite をお勧めします。

MySQL はより強力なソリューションですが、認証が必要であり、別のマシンに配置されています。

于 2010-12-23T05:29:20.633 に答える
0

PHP 配列は、必要なすべてを行います。しかし、それほど多くのデータをデータベースに保存するべきではないでしょうか?

http://php.net/array

于 2010-12-23T05:10:00.777 に答える