平面文件数据库

在 PHP 中创建平面文件数据库结构的最佳实践是什么?

还有很多成熟的 PHP 平面文件框架,我试图实现类似 SQL 的查询语法,在大多数情况下,这种语法对于我的目的来说是过头了。(那时我只需要使用一个数据库)。

是否有一些优雅的技巧可以在代码开销很小的情况下获得良好的性能和特性?

68023 次浏览

我正在考虑的一个框架是博客平台。因为几乎所有可能的数据视图都是按日期排序的,所以我考虑了这个结构:

每个内容节点一个目录:

./content/YYYYMMDDHHMMSS/

每个节点的子目录,包括

/tags
/authors
/comments

以及节点目录中用于预处理和后处理内容等的简单文本文件。

这将允许一个简单的 PHP glob()调用(可能是结果数组的反转)查询内容结构中的任何内容:

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

将返回包括所有标记为“有趣”的文章的路径。

那么,平面数据库的本质是什么。它们是大还是小。是简单的数组吗?如果它的东西很简单,比如说用户资料就是这样建立的:

$user = array("name" => "bob",
"age" => 20,
"websites" => array("example.com","bob.example.com","bob2.example.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));
}

但是这个实现将根据您需要的应用程序和数据库的性质而有所不同。

你可以考虑 SQLite。它几乎和平面文件一样简单,但是您得到了一个用于查询的 SQL 引擎。也是 可以很好地使用 PHP

如果您打算使用平面文件来保存数据,那么可以使用 XML 来构造数据。PHP 有一个 内置的 XML 解析器

如果你想要一个人类可读的结果,你也可以使用这种类型的文件:

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

这样,您只有一个文件,您可以轻松地调试它(并手动修复) ,您可以稍后添加字段(在每一行的末尾)和 PHP 代码是简单的(对于每一行,按 | 分割)。

但是,缺点是您应该解析整个文件来搜索某些内容(如果有数百万个条目,那就不好了) ,并且应该处理数据中的分隔符(例如,如果缺口是 WaR | ordz)。

在我看来,使用“平面文件数据库”的意义(和你已经接受的答案)不一定是最好的方式去做事情。首先,如果有人进入并编辑文件,使用 serialize()unserialize()可能会导致 MAJOR 头疼(事实上,他们可以在每次运行的“数据库”中放入任意的代码)

我个人认为,为什么不着眼于未来呢?有很多次我遇到了问题,因为我一直在创建我自己的“专有”文件,并且这个项目已经发展到需要一个数据库的地步,我在想“你知道,我希望我已经写了一个数据库开始”-因为代码的重构需要太多的时间和精力。

从中我学到了将来对我的应用程序进行校验,这样当它变得更大时,我就不必花费数天时间重构,这才是前进的方向。我该怎么做?

SQLite.它作为一个数据库工作,使用 SQL,并且很容易转换为 MySQL (特别是如果您像我一样使用抽象类来进行数据库操作!)

事实上,特别是使用“接受答案”的方法,它可以大大减少应用程序的内存使用(您不必将所有“记录”加载到 PHP 中)

这是莉莉娜的密码:

<?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);
}
}


?>

它将每个条目存储为一个单独的文件,我们发现该文件的使用效率足够高(没有加载不需要的数据,而且保存速度更快)。

恕我直言,如果你想避免在家酿造某种东西,你有两个... ... 呃,三个选择:

  1. SQLite

如果您熟悉 PDO,您可以安装一个支持 SQLite 的 PDO 驱动程序。从来没有用过,但是我在 MySQL 中使用了大量的 PDO。我要在一个项目上试一试。

  1. XML

为了相对较少的数据,这样做了很多次。XMLReader是一个轻量级的、可读取的、游标样式的类。SimpleXML使得将 XML 文档读入对象变得非常简单,您可以像访问其他类实例一样访问这个对象。

  1. JSON (update)

适用于少量数据的好选项,只需读/写文件和 json _ decode/json _ encode。不确定 PHP 是否提供了一个结构来导航 JSON 树而不将其全部加载到内存中。

我编写了两个简单的函数,用于在文件中存储数据。你可以自己判断这个案子是否有用。 重点是将 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;
}

只是指出这种类型系统的平面文件数据库存在的一个潜在问题:

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行) ,但会占用大型数据库的大量内存。

这是一个鼓舞人心的实际解决方案:
Https://github.com/mhgolkar/flatfire
它使用多种策略来处理数据..。
[复制自述文件]

自由的,结构化的或混合的

- 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