Бази даних з плоскими файлами [закрито]


120

Які найкращі практики створення структури плоских файлів баз даних у PHP?

Набагато більше дозрілих рамкових файлів PHP там, де я намагаюся реалізувати синтаксис запитів у вигляді SQL, який у більшості випадків перебуває вгорі. (Я би просто використав базу даних у той момент).

Чи є якісь витончені хитрощі, щоб отримати хороші показники та функції з невеликим кодом накладних витрат?


1
Я хотів би додати, що тут є пакет для плоскої бази даних файлів github.com/tmarois/Filebase Я знаю, що це старе питання, але цей пакет є найновішою збіркою та обслуговуванням, а також рядом функцій, які найбільш нехтувати потрібно включити. .
tmarois

Я розробляю CMS і використовую текстову базу даних з текстовим файлом. На її виготовлення пішло багато годин, а на реструктуризацію - багато годин, але вона працює чудово. Запити будуть виконуватися набагато швидше за допомогою повністю індексованої та оптимізованої бази даних. Однак я уникаю потреби в запитах, зберігаючи метадані та ретельною організацією та структурою. Коли мені потрібні дані, я отримую їх без for loop(якщо я не використовую всі дані в папці), тому вона виконує набагато швидше, ніж це би база даних. Я б детально і детально дав відповідь, але, на жаль, це питання закрите.
Ден Брей

Відповіді:


75

Ну, яка природа плоских баз даних. Вони великі чи маленькі. Це прості масиви з масивами в них? якщо це щось просте, скажімо, користувацькі профілі побудовані як такі:

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

і зберегти або оновити запис db для цього користувача.

$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+ далі !!! Коли я писав це в липні 2012 року, SQLite за замовчуванням більше не буде працювати в сучасних системах. Офіційна заява тут
Sliq

Установка драйвера PDO для SQLite досить тривіальна, якщо у вас є доступ до сервера. На Ubuntu / Debian під керуванням Apache2 просто зробіть apt-get install php5-sqlite service apache2 restart
siliconrockstar

4
У відповідь на коментар від @Sliq, заявляючи, що "SQLite було ... припинено", це правда: розширення з назвою "SQLite" було припинено, а "SQLite3" тепер увімкнено за замовчуванням. php.net/manual/en/sqlite.installation.php "Оскільки PHP 5.0 це розширення було в комплекті з PHP. Починаючи з PHP 5.4, це розширення доступне лише через PECL." php.net/manual/en/sqlite3.installation.php "Розширення SQLite3 увімкнено за замовчуванням, як у PHP 5.3.0." "Це розширення коротко було розширенням PECL, але ця версія рекомендована лише для експериментального використання."
Пол ван Левен

Ви не відповіли на питання
JG Estiot

20

На мою думку, використання «Плоскої бази даних файлів» у тому сенсі, який ви маєте на увазі (і відповідь, яку ви прийняли), не обов'язково є найкращим способом вирішити справи. Перш за все, використання serialize()і unserialize()може спричинити великі головні болі, якщо хтось потрапляє і редагує файл (вони, власне, можуть поміщати арбітражний код у вашу "базу даних", який потрібно запускати кожен раз.)

Особисто я б сказав - чому б не заглянути в майбутнє? Було так багато разів, що у мене виникли проблеми, тому що я створював власні "власні" файли, і проект вибухнув до точки, коли йому потрібна база даних, і я думаю "ви знаєте, я хочу Я написав би це для того, щоб почати базу даних "- тому що рефакторинг коду вимагає занадто багато часу та зусиль.

З цього я дізнався, що майбутнє підтвердження моєї заявки так, що коли вона стає більшою, мені не доведеться їхати і витрачати дні на рефакторинг - це шлях іти вперед. Як це зробити?

SQLite. Він працює як база даних, використовує SQL, і досить легко перейти на mySQL (особливо, якщо ви використовуєте абстраговані класи для маніпулювання базами даних, як я!)

Насправді, особливо за допомогою методу "прийнятої відповіді", це може різко скоротити використання пам'яті вашого додатка (вам не доведеться завантажувати всі "ЗАПИСИ" в PHP)


Це правда. serialize()може бути дуже корисним і для цього. Я думаю, що фокус у створенні життєздатної системи - це знайти спосіб індексувати вузли даних, не вбиваючи себе складністю.
saint_groceon

12

Я вважаю, що одна платформа для блогівської платформи. Оскільки майже будь-який можливий перегляд даних, які ви хочете, були б відсортовані за датою, я думав про цю структуру:

Один каталог на вузол вмісту:

./content/YYYYMMDDHHMMSS/

Підкаталоги кожного вузла, включаючи

/tags  
/authors  
/comments  

А також прості текстові файли в каталозі вузлів для попередньо і після викладеного вмісту тощо.

Це дозволило б простому PHP- glob()дзвінку (і, можливо, змінити масив результатів) запитувати практично про все, що є в структурі вмісту:

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

Повертаються шляхи, включаючи всі статті, позначені "смішно".


9

Ось код, який ми використовуємо для Ліліни:

<?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 для читабельності людини, або ви також можете використовувати серіалізацію, json чи щось подібне.
Бен

Дуже погана порада. XML ніколи не слід використовувати. Це жирова аберація.
JG Estiot

@JGEstiot Хочете пояснити далі?
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;
}

Я виявив, що це цікаво, і це ЛИШЕ, оскільки ми просто скидаємо відформатований масив у файл. Нам це не потрібно будувати заново, просто читайте. Крім того, редагувати змінні трохи просто. Я ніколи не використовую це для зберігання великих даних, але я вважаю практичним зберігати модулі програми без бази даних. Дякую.
м3нда

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

ІМХО, у вас є два варіанти, якщо ви хочете уникнути чогось домашнього приготування:

  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. Вхід із екрана набагато простіший, тому вам не потрібно використовувати смішні комбінації рядків, щоб файл був більш читабельним.
Cypher
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.