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


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.