วิธีที่มีประสิทธิภาพในการค้นหาค่าตามคีย์ใน 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