Эффективный способ поиска значения на основе ключа в php

Со списком из примерно 100 000 пар ключ/значение (обе строки, в основном около 5-20 символов каждая) я ищу способ эффективно найти значение для данного ключа.

Это нужно сделать на сайте php. Я знаком с хеш-таблицами в java (вероятно, это то, что я бы сделал, если бы работал в java), но я новичок в php.

Я ищу советы о том, как мне хранить этот список (в текстовом файле или в базе данных?) и выполнять поиск по этому списку.

Список придется время от времени обновлять, но меня больше всего интересует время поиска.


person user552007    schedule 23.12.2010    source источник


Ответы (3)


Вы можете сделать это как обычный 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, но если это всего лишь одна таблица с описанными вами характеристиками, это, вероятно, будет излишним. Приведенный выше пример 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 секунды!), но на моей машине оно примерно в четыре раза быстрее. Также может быть недоступен 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 секунды для поиска на моей машине. Это также имеет то преимущество, что оно остается достаточно постоянным независимо от размера кеша.

person Shabbyrobe    schedule 23.12.2010

Это зависит от того, как часто вы будете обращаться к своему массиву, подумайте, сколько пользователей могут получить к нему доступ одновременно. Есть много преимуществ в хранении его в базе данных, и здесь у вас есть два варианта MySQL и SQLite.

SQLite работает больше как текстовый файл с поддержкой SQL, вы можете сэкономить несколько миллисекунд во время запросов, так как он находится в пределах досягаемости вашего приложения, основной недостаток заключается в том, что он может добавлять только одну запись за раз (так же, как текстовый файл). Я бы рекомендовал SQLite для массивов со статическим содержимым, таким как данные GEO IP, переводы и т. д.

MySQL является более мощным решением, но требует аутентификации и расположен на отдельной машине.

person Nazariy    schedule 23.12.2010

Массивы PHP сделают все, что вам нужно. Но разве столько данных не должно храниться в базе данных?

http://php.net/array

person Jonah    schedule 23.12.2010