ฐานข้อมูลไฟล์แบบแบน [ปิด]


120

แนวทางปฏิบัติที่ดีที่สุดเกี่ยวกับการสร้างโครงสร้างฐานข้อมูลไฟล์แบนใน PHP คืออะไร

เฟรมเวิร์กไฟล์แบน PHP ที่ครบกำหนดจำนวนมากขึ้นซึ่งฉันพยายามใช้ไวยากรณ์การสืบค้นแบบ SQL ซึ่งอยู่ด้านบนสำหรับวัตถุประสงค์ของฉันในกรณีส่วนใหญ่ (ฉันจะใช้ฐานข้อมูล ณ จุดนั้น)

มีเคล็ดลับที่สวยงามเพื่อให้ได้ประสิทธิภาพและคุณสมบัติที่ดีด้วยค่าใช้จ่ายโค้ดขนาดเล็กหรือไม่?


1
ฉันต้องการเพิ่มว่ามีแพ็คเกจสำหรับFlat File Database github.com/tmarois/Filebaseฉันรู้ว่านี่เป็นคำถามเก่า แต่แพ็คเกจนี้เป็นเวอร์ชันล่าสุดและได้รับการบำรุงรักษารวมถึงฟีเจอร์ที่ถูกละเลยมากที่สุด .
tmarois

ฉันกำลังพัฒนา CMS และฉันใช้ฐานข้อมูลไฟล์ข้อความแบบแบน ใช้เวลาหลายชั่วโมงในการทำและหลายชั่วโมงในการหักเหของแสง แต่ทำงานได้อย่างสมบูรณ์ การสืบค้นจะดำเนินการเร็วขึ้นมากด้วยฐานข้อมูลที่จัดทำดัชนีและปรับให้เหมาะสมอย่างสมบูรณ์ อย่างไรก็ตามฉันหลีกเลี่ยงความจำเป็นในการสืบค้นโดยการจัดเก็บข้อมูลเมตาและด้วยการจัดระเบียบและโครงสร้างที่รอบคอบ เมื่อฉันต้องการข้อมูลฉันจะได้รับโดยไม่มีfor loop(เว้นแต่ฉันจะใช้ข้อมูลทั้งหมดในโฟลเดอร์) ดังนั้นจึงทำงานได้เร็วกว่าฐานข้อมูลมาก ฉันจะลงรายละเอียดและให้คำตอบที่ดีมาก แต่น่าเสียดายที่คำถามนี้ถูกปิด
Dan Bray

คำตอบ:


75

ธรรมชาติของฐานข้อมูลแบบแบนคืออะไร มีขนาดใหญ่หรือเล็ก เป็นอาร์เรย์ธรรมดาที่มีอาร์เรย์อยู่หรือไม่? หากสิ่งที่เรียบง่ายบอกว่าโปรไฟล์ผู้ใช้สร้างขึ้นเช่นนี้:

$user = array("name" => "dubayou", 
              "age" => 20,
              "websites" => array("dubayou.com","willwharton.com","codecream.com"),
              "and_one" => "more");

และเพื่อบันทึกหรืออัปเดตบันทึกฐานข้อมูลสำหรับผู้ใช้นั้น

$dir = "../userdata/";  //make sure to put it bellow what the server can reach.
file_put_contents($dir.$user['name'],serialize($user));

และโหลดบันทึกสำหรับผู้ใช้

function &get_user($name){
    return unserialize(file_get_contents("../userdata/".$name));
}

แต่อีกครั้งการใช้งานนี้จะแตกต่างกันไปตามแอปพลิเคชันและลักษณะของฐานข้อมูลที่คุณต้องการ


48

คุณอาจพิจารณาSQLite เกือบจะง่ายเหมือนไฟล์แบบแบน แต่คุณจะได้รับเอ็นจิ้น SQL สำหรับการสืบค้น มันทำงานได้ดีกับ PHPด้วย


6
SQLite ถูกสร้างเป็น 5.0+ ตามค่าเริ่มต้น แต่มีส่วนลด (!) จาก PHP 5.4+ บน !!! ตามที่ฉันเขียนในเดือนกรกฎาคม 2555 SQLite จะไม่ทำงานบนระบบที่ทันสมัยอีกต่อไปตามค่าเริ่มต้น แถลงการณ์อย่างเป็นทางการที่นี่
Sliq

การติดตั้งไดรเวอร์ SQLite PDO เป็นเรื่องเล็กน้อยหากคุณมีการเข้าถึงเซิร์ฟเวอร์ บน Ubuntu / Debian ที่ใช้ Apache2 เพียงแค่ทำการ apt-get install php5-sqlite service apache2 restart
siliconrockstar

4
ในการตอบสนองต่อความคิดเห็นจาก @Sliq ที่ระบุว่า "SQLite was ... discontinued" นั้นเป็นจริง: ส่วนขยายชื่อ "SQLite" ถูกยกเลิกและ "SQLite3" ถูกเปิดใช้งานโดยค่าเริ่มต้น php.net/manual/en/sqlite.installation.php "เนื่องจาก PHP 5.0 ส่วนขยายนี้มาพร้อมกับ PHP เริ่มต้นด้วย PHP 5.4 ส่วนขยายนี้จะพร้อมใช้งานผ่าน PECL เท่านั้น" php.net/manual/th/sqlite3.installation.php "ส่วนขยาย SQLite3 ถูกเปิดใช้งานโดยค่าเริ่มต้นเป็น PHP 5.3.0" "ส่วนขยายนี้เป็นส่วนขยาย PECL ช่วงสั้น ๆ แต่แนะนำให้ใช้เวอร์ชันทดลองเท่านั้น"
Paul van Leeuwen

คุณไม่ได้ตอบคำถาม
JG Estiot

20

ในความคิดของฉันการใช้ "Flat File Database" ในความหมายที่คุณมีความหมาย (และคำตอบที่คุณยอมรับ) ไม่จำเป็นต้องเป็นวิธีที่ดีที่สุดในการทำสิ่งต่างๆ ก่อนอื่นการใช้serialize()และunserialize()อาจทำให้เกิดอาการปวดหัว MAJOR หากมีคนเข้ามาและแก้ไขไฟล์ (ในความเป็นจริงพวกเขาสามารถใส่รหัสที่กำหนดเองใน "ฐานข้อมูล" ของคุณเพื่อเรียกใช้ทุกครั้ง)

โดยส่วนตัวฉันจะบอกว่า - ทำไมไม่มองไปในอนาคต? มีหลายครั้งที่ฉันมีปัญหาเพราะฉันได้สร้างไฟล์ "ที่เป็นกรรมสิทธิ์" ของตัวเองและโครงการดังกล่าวได้ระเบิดไปถึงจุดที่ต้องการฐานข้อมูลและฉันคิดว่า "คุณรู้ไหมฉันต้องการ ฉันเขียนสิ่งนี้เพื่อให้ฐานข้อมูลเริ่มต้นด้วย "- เนื่องจากการปรับโครงสร้างโค้ดใหม่ต้องใช้เวลาและความพยายามมากเกินไป

จากสิ่งนี้ฉันได้เรียนรู้ว่าการพิสูจน์แอปพลิเคชันของฉันในอนาคตเพื่อที่เมื่อมันใหญ่ขึ้นฉันไม่ต้องไปและใช้เวลาหลายวันในการปรับโครงสร้างใหม่เป็นหนทางที่จะก้าวไปข้างหน้า ฉันต้องทำอย่างไร

SQLite มันทำงานเป็นฐานข้อมูลใช้ SQL และค่อนข้างง่ายที่จะเปลี่ยนเป็น MySQL (โดยเฉพาะอย่างยิ่งถ้าคุณใช้คลาสนามธรรมสำหรับการจัดการฐานข้อมูลเช่นฉัน!)

ในความเป็นจริงโดยเฉพาะอย่างยิ่งกับวิธีการของ "คำตอบที่ยอมรับ" มันสามารถลดการใช้หน่วยความจำของแอปของคุณได้อย่างมาก (คุณไม่จำเป็นต้องโหลด "RECORDS" ทั้งหมดลงใน PHP)


นั่นคือเรื่องจริง serialize()ก็มีประโยชน์เช่นกัน ฉันคิดว่าเคล็ดลับในการสร้างระบบที่ใช้งานได้คือการหาวิธีจัดทำดัชนีโหนดข้อมูลโดยไม่ต้องฆ่าตัวตายด้วยความซับซ้อน
saint_groceon

12

กรอบงานหนึ่งที่ฉันกำลังพิจารณาคือสำหรับแพลตฟอร์มบล็อก เนื่องจากมุมมองที่เป็นไปได้ของข้อมูลที่คุณต้องการจะถูกจัดเรียงตามวันที่ฉันจึงคิดถึงโครงสร้างนี้:

หนึ่งไดเร็กทอรีต่อโหนดเนื้อหา:

./content/YYYYMMDDHHMMSS/

ไดเรกทอรีย่อยของแต่ละโหนดรวมถึง

/tags  
/authors  
/comments  

เช่นเดียวกับไฟล์ข้อความธรรมดาในไดเร็กทอรีโหนดสำหรับเนื้อหาก่อนและหลังการแสดงผลและอื่น ๆ

สิ่งนี้จะช่วยให้การglob()เรียกPHP ง่ายๆ(และอาจเป็นการย้อนกลับของอาร์เรย์ผลลัพธ์) เพื่อค้นหาอะไรก็ได้ภายในโครงสร้างเนื้อหา:

glob("content/*/tags/funny");  

จะส่งคืนเส้นทางรวมถึงบทความทั้งหมดที่ติดแท็ก "ตลก"


9

นี่คือรหัสที่เราใช้สำหรับ Lilina:

<?php
/**
 * Handler for persistent data files
 *
 * @author Ryan McCue <cubegames@gmail.com>
 * @package Lilina
 * @version 1.0
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 */

/**
 * Handler for persistent data files
 *
 * @package Lilina
 */
class DataHandler {
    /**
     * Directory to store data.
     *
     * @since 1.0
     *
     * @var string
     */
    protected $directory;

    /**
     * Constructor, duh.
     *
     * @since 1.0
     * @uses $directory Holds the data directory, which the constructor sets.
     *
     * @param string $directory 
     */
    public function __construct($directory = null) {
        if ($directory === null)
            $directory = get_data_dir();

        if (substr($directory, -1) != '/')
            $directory .= '/';

        $this->directory = (string) $directory;
    }

    /**
     * Prepares filename and content for saving
     *
     * @since 1.0
     * @uses $directory
     * @uses put()
     *
     * @param string $filename Filename to save to
     * @param string $content Content to save to cache
     */
    public function save($filename, $content) {
        $file = $this->directory . $filename;

        if(!$this->put($file, $content)) {
            trigger_error(get_class($this) . " error: Couldn't write to $file", E_USER_WARNING);
            return false;
        }

        return true;
    }

    /**
     * Saves data to file
     *
     * @since 1.0
     * @uses $directory
     *
     * @param string $file Filename to save to
     * @param string $data Data to save into $file
     */
    protected function put($file, $data, $mode = false) {
        if(file_exists($file) && file_get_contents($file) === $data) {
            touch($file);
            return true;
        }

        if(!$fp = @fopen($file, 'wb')) {
            return false;
        }

        fwrite($fp, $data);
        fclose($fp);

        $this->chmod($file, $mode);
        return true;

    }

    /**
     * Change the file permissions
     *
     * @since 1.0
     *
     * @param string $file Absolute path to file
     * @param integer $mode Octal mode
     */
    protected function chmod($file, $mode = false){
        if(!$mode)
            $mode = 0644;
        return @chmod($file, $mode);
    }

    /**
     * Returns the content of the cached file if it is still valid
     *
     * @since 1.0
     * @uses $directory
     * @uses check() Check if cache file is still valid
     *
     * @param string $id Unique ID for content type, used to distinguish between different caches
     * @return null|string Content of the cached file if valid, otherwise null
     */
    public function load($filename) {
        return $this->get($this->directory . $filename);
    }

    /**
     * Returns the content of the file
     *
     * @since 1.0
     * @uses $directory
     * @uses check() Check if file is valid
     *
     * @param string $id Filename to load data from
     * @return bool|string Content of the file if valid, otherwise null
     */
    protected function get($filename) {
        if(!$this->check($filename))
            return null;

        return file_get_contents($filename);
    }

    /**
     * Check a file for validity
     *
     * Basically just a fancy alias for file_exists(), made primarily to be
     * overriden.
     *
     * @since 1.0
     * @uses $directory
     *
     * @param string $id Unique ID for content type, used to distinguish between different caches
     * @return bool False if the cache doesn't exist or is invalid, otherwise true
     */
    protected function check($filename){
        return file_exists($filename);
    }

    /**
     * Delete a file
     *
     * @param string $filename Unique ID
     */
    public function delete($filename) {
        return unlink($this->directory . $filename);
    }
}

?>

มันจัดเก็บแต่ละรายการเป็นไฟล์แยกกันซึ่งเราพบว่ามีประสิทธิภาพเพียงพอสำหรับการใช้งาน (ไม่มีการโหลดข้อมูลที่ไม่จำเป็นและบันทึกได้เร็วกว่า)


8

หากคุณจะใช้ไฟล์แบบแบนเพื่อคงข้อมูลให้ใช้ XML เพื่อจัดโครงสร้างข้อมูล PHP มีในตัวแยกวิเคราะห์ XML


และปฏิบัติตามกฎ xml ของความสามารถในการอ่านของมนุษย์หรือคุณอาจใช้ serialization หรือ json หรืออะไรก็ได้
เบ็น

คำแนะนำที่แย่มาก ไม่ควรใช้ XML มันเป็นความผิดปกติของไขมัน
JG Estiot

@JGEstiot Care เพื่ออธิบายเพิ่มเติม?
UncaughtTypeError

7

หากคุณต้องการผลลัพธ์ที่มนุษย์สามารถอ่านได้คุณสามารถใช้ไฟล์ประเภทนี้:

ofaurax|27|male|something|
another|24|unknown||
...

ด้วยวิธีนี้คุณมีไฟล์เพียงไฟล์เดียวคุณสามารถแก้จุดบกพร่อง (และแก้ไขด้วยตนเอง) ได้อย่างง่ายดายคุณสามารถเพิ่มฟิลด์ในภายหลัง (ที่ท้ายแต่ละบรรทัด) และโค้ด PHP นั้นง่าย (สำหรับแต่ละบรรทัดแยกตาม |)

อย่างไรก็ตามข้อเสียคือคุณควรแยกวิเคราะห์ไฟล์ทั้งหมดเพื่อค้นหาบางสิ่งบางอย่าง (หากคุณมีหลายล้านรายการก็ไม่เป็นไร) และคุณควรจัดการตัวคั่นในข้อมูล (เช่นถ้าชื่อเล่นคือ WaR | ordz)


7

ฉันได้เขียนฟังก์ชันง่ายๆสองอย่างที่ออกแบบมาเพื่อจัดเก็บข้อมูลในไฟล์ คุณสามารถตัดสินด้วยตัวคุณเองว่ามีประโยชน์หรือไม่ในกรณีนี้ ประเด็นคือการบันทึกตัวแปร php (ถ้าเป็นอาร์เรย์สตริงหรืออ็อบเจกต์) ลงในไฟล์

<?php
function varname(&$var) {
    $oldvalue=$var;
    $var='AAAAB3NzaC1yc2EAAAABIwAAAQEAqytmUAQKMOj24lAjqKJC2Gyqhbhb+DmB9eDDb8+QcFI+QOySUpYDn884rgKB6EAtoFyOZVMA6HlNj0VxMKAGE+sLTJ40rLTcieGRCeHJ/TI37e66OrjxgB+7tngKdvoG5EF9hnoGc4eTMpVUDdpAK3ykqR1FIclgk0whV7cEn/6K4697zgwwb5R2yva/zuTX+xKRqcZvyaF3Ur0Q8T+gvrAX8ktmpE18MjnA5JuGuZFZGFzQbvzCVdN52nu8i003GEFmzp0Ny57pWClKkAy3Q5P5AR2BCUwk8V0iEX3iu7J+b9pv4LRZBQkDujaAtSiAaeG2cjfzL9xIgWPf+J05IQ==';
    foreach($GLOBALS as $var_name => $value) {
        if ($value === 'AAAAB3NzaC1yc2EAAAABIwAAAQEAqytmUAQKMOj24lAjqKJC2Gyqhbhb+DmB9eDDb8+QcFI+QOySUpYDn884rgKB6EAtoFyOZVMA6HlNj0VxMKAGE+sLTJ40rLTcieGRCeHJ/TI37e66OrjxgB+7tngKdvoG5EF9hnoGc4eTMpVUDdpAK3ykqR1FIclgk0whV7cEn/6K4697zgwwb5R2yva/zuTX+xKRqcZvyaF3Ur0Q8T+gvrAX8ktmpE18MjnA5JuGuZFZGFzQbvzCVdN52nu8i003GEFmzp0Ny57pWClKkAy3Q5P5AR2BCUwk8V0iEX3iu7J+b9pv4LRZBQkDujaAtSiAaeG2cjfzL9xIgWPf+J05IQ==')
        {
            $var=$oldvalue;
            return $var_name;
        }
    }
    $var=$oldvalue;
    return false;
}

function putphp(&$var, $file=false)
    {
    $varname=varname($var);
    if(!$file)
    {
        $file=$varname.'.php';
    }
    $pathinfo=pathinfo($file);
    if(file_exists($file))
    {
        if(is_dir($file))
        {
            $file=$pathinfo['dirname'].'/'.$pathinfo['basename'].'/'.$varname.'.php';
        }
    }
    file_put_contents($file,'<?php'."\n\$".$varname.'='.var_export($var, true).";\n");
    return true;
}

ฉันพบว่าน่าสนใจและนี่เป็นวิธีที่ดีกว่าเพราะเราแค่ถ่ายโอนอาร์เรย์ที่จัดรูปแบบไปยังไฟล์ เราไม่จำเป็นต้องสร้างมันอีกครั้งเพียงแค่อ่าน นอกจากนี้การแก้ไขตัวแปรก็ทำได้ง่ายเล็กน้อย ฉันจะไม่ใช้มันเพื่อจัดเก็บข้อมูลขนาดใหญ่ แต่ฉันพบว่ามันใช้งานได้จริงในการจัดเก็บโมดูลของโปรแกรมโดยไม่มีฐานข้อมูล ขอบคุณ.
m3nda

7

อันนี้สร้างแรงบันดาลใจในการใช้งานจริง:
https://github.com/mhgolkar/FlatFire
มันใช้หลายกลยุทธ์ในการจัดการข้อมูล ...
[คัดลอกจากไฟล์ Readme]

ฟรีหรือมีโครงสร้างหรือผสม

- STRUCTURED
Regular (table, row, column) format.
[DATABASE]
/   \
TX  TableY
    \_____________________________
    |ROW_0 Colum_0 Colum_1 Colum_2|
    |ROW_1 Colum_0 Colum_1 Colum_2|
    |_____________________________|
- FREE
More creative data storing. You can store data in any structure you want for each (free) element, its similar to storing an array with a unique "Id".
[DATABASE]
/   \
EX  ElementY (ID)
    \________________
    |Field_0 Value_0 |
    |Field_1 Value_1 |
    |Field_2 Value_2 |
    |________________|
recall [ID]: get_free("ElementY") --> array([Field_0]=>Value_0,[Field_1]=>Value_1...
- MIXD (Mixed)
Mixed databases can store both free elements and tables.If you add a table to a free db or a free element to a structured db, flat fire will automatically convert FREE or SRCT to MIXD database.
[DATABASE]
/   \
EX  TY

7

IMHO คุณมีสองทางเลือกหากคุณต้องการหลีกเลี่ยงการทำโฮมบรูว์บางสิ่ง:

  1. SQLite

    หากคุณคุ้นเคยกับ PDO คุณสามารถติดตั้งไดรเวอร์ PDO ที่รองรับ SQLite ได้ ไม่เคยใช้เลย แต่ฉันใช้ PDO กับ MySQL เป็นจำนวนมาก ผมจะลองดูโปรเจ็กต์ปัจจุบัน

  2. XML

    ทำหลาย ๆ ครั้งสำหรับข้อมูลจำนวนค่อนข้างน้อย XMLReaderเป็นคลาสสไตล์เคอร์เซอร์ที่มีน้ำหนักเบาอ่านไปข้างหน้า SimpleXMLทำให้ง่ายต่อการอ่านเอกสาร XML ลงในวัตถุที่คุณสามารถเข้าถึงได้เช่นเดียวกับอินสแตนซ์คลาสอื่น ๆ


5

เพียงแค่ชี้ให้เห็นปัญหาที่อาจเกิดขึ้นกับฐานข้อมูลไฟล์แบบแบนด้วยระบบประเภทนี้:

data|some text|more data

row 2 data|bla hbalh|more data

... ฯลฯ

ปัญหาคือข้อมูลในเซลล์มีเครื่องหมาย "|" หรือ "\ n" จากนั้นข้อมูลจะสูญหาย บางครั้งการแยกตัวอักษรที่คนส่วนใหญ่ไม่ได้ใช้ก็จะง่ายกว่า

ตัวอย่างเช่น:

ตัวแยกคอลัมน์: #$% (Shift+345)

ตัวแยกแถว: ^&* (Shift+678)

ไฟล์ข้อความ: test data#$%blah blah#$%^&*new row#$%new row data 2

จากนั้นใช้: explode("#$%", $data); use foreach, the explode again to separate columns

หรืออะไรก็ได้ตามแนวเหล่านี้ นอกจากนี้ฉันอาจเพิ่มว่าฐานข้อมูลไฟล์แบบแบนนั้นดีสำหรับระบบที่มีข้อมูลจำนวนน้อย (เช่นน้อยกว่า 20 แถว) แต่กลายเป็นหน่วยความจำขนาดใหญ่สำหรับฐานข้อมูลขนาดใหญ่


จุดที่ดี เมื่อก้าวไปอีกขั้น PHP สามารถทำให้อนุกรม JSON ได้อย่างง่ายดาย การหลีกเลี่ยงการป้อนข้อมูลนั้นง่ายกว่ามากดังนั้นคุณไม่จำเป็นต้องใช้การรวมสตริงตลก ๆ เพื่อให้ไฟล์อ่านง่ายขึ้น
ไซเฟอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.