플랫 파일 데이터베이스 [닫힘]


120

PHP에서 플랫 파일 데이터베이스 구조를 만드는 모범 사례는 무엇입니까?

대부분의 경우 내 목적을 위해 상위에있는 SQL과 유사한 쿼리 구문을 구현하려고 시도하는 더 성숙한 PHP 플랫 파일 프레임 워크가 많이 있습니다. (그 시점에서 데이터베이스를 사용합니다).

작은 코드 오버 헤드로 좋은 성능과 기능을 얻을 수있는 우아한 트릭이 있습니까?


1
여기에 플랫 파일 데이터베이스에 대한 패키지가 있음을 추가하고 싶습니다. 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");

해당 사용자 의 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 년 7 월에이 글을 쓰면서 SQLite는 기본적으로 더 이상 최신 시스템에서 작동하지 않습니다. 공식 성명 여기
— SliQ의

서버 액세스 권한이 있으면 SQLite PDO 드라이버를 설치하는 것은 매우 간단합니다. Apache2를 실행하는 Ubuntu / Debian에서 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 확장 이었지만이 버전은 실험용으로 만 권장됩니다."
— Paul van Leeuwen

당신은 질문에 대답하지 않았습니다
— JG

20

제 생각에는 "플랫 파일 데이터베이스"를 의미하는 의미 (및 수락 한 답변)에서 사용하는 것이 문제를 해결하는 가장 좋은 방법은 아닙니다. 우선, 누군가가 파일에 들어 와서 파일을 편집하면 serialize()and unserialize()를 사용하면 심각한 골칫거리가 될 수 있습니다 (사실 매번 실행할 "데이터베이스"에 임의 코드를 넣을 수 있습니다).

개인적으로 말하고 싶습니다. 미래를 바라 보는 것은 어떨까요? 저만의 "독점"파일을 만들고 있고 프로젝트가 데이터베이스가 필요한 지점까지 폭발적으로 증가했기 때문에 여러 번 문제가 발생했습니다. 코드를 리팩토링하는 데 너무 많은 시간과 노력이 소요되기 때문에 데이터베이스가 시작되도록이 글을 작성했습니다.

이것으로부터 나는 애플리케이션이 커질 때 리팩토링에 며칠을 소비하지 않아도되도록 미래의 애플리케이션을 증명하는 것이 앞으로 나아가는 길이라는 것을 배웠다. 어떻게해야합니까?

SQLite. 데이터베이스로 작동하고 SQL을 사용하며 mySQL로 변경하기가 매우 쉽습니다 (특히 내가하는 것처럼 데이터베이스 조작을 위해 추상화 된 클래스를 사용하는 경우!)

사실, 특히 "수용된 답변"의 방법을 사용하면 앱의 메모리 사용량을 크게 줄일 수 있습니다 (모든 "기록"을 PHP로로드 할 필요가 없음).


사실입니다. serialize()그것도 꽤 유용 할 수 있습니다. 실행 가능한 시스템을 만드는 비결은 복잡성으로 인해 자신을 죽이지 않고 데이터 노드를 색인화하는 방법을 찾는 것입니다.
— saint_groceon 2008-08-01

12

제가 고려하고있는 하나의 프레임 워크는 블로깅 플랫폼을위한 것입니다. 원하는 데이터보기가 날짜별로 정렬되므로이 구조에 대해 생각했습니다.

컨텐츠 노드 당 하나의 디렉토리 :

./content/YYYYMMDDHHMMSS/

다음을 포함한 각 노드의 하위 디렉토리

/tags  
/authors  
/comments  

렌더링 전후 콘텐츠 등을위한 노드 디렉토리의 간단한 텍스트 파일.

이렇게하면 간단한 PHP glob()호출 (및 결과 배열의 반전)이 콘텐츠 구조 내의 거의 모든 항목을 쿼리 할 수 ​​있습니다.

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

"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 규칙을 따르거나 직렬화 또는 json 등을 사용하는 것이 좋습니다.
— Ben

매우 가난한 조언. 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;
}

나는 흥미롭고 이것은 더 나은 방법이라는 것을 발견했습니다. 형식화 된 배열을 파일에 덤프하기 때문입니다. 우리는 그것을 다시 구성 할 필요가 없습니다. 또한 변수를 편집하는 것은 조금 쉽습니다. 큰 데이터를 저장하는 데 절대 사용하지 않겠지 만 데이터베이스없이 프로그램 모듈을 저장하는 것이 실용적이라는 것을 알았습니다. 감사합니다.
— 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에 익숙하다면 SQLite를 지원하는 PDO 드라이버를 설치할 수 있습니다. 그것을 사용한 적이 없지만 MySQL과 함께 PDO를 많이 사용했습니다. 현재 프로젝트에 대해이 장면을 보여 드리겠습니다.

  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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.