PHP的实现一个高效的数据库(文件存储,NOSQL)
用文件的方式读写,一个文件是索引文件,另一个文件是真实的数据文件。
索引文件分为2部分,第一部分是所有的指针,记录第二部分的位置;第二部分是索引记录。所有的索引指针:是记录所有相同Hash值的key的指针,它是一个链表结构,记录在数据文件的位置和同key的下一个值。
索引记录中:每条记录有四部分,第一部分4个字节,是下一条索引的偏移量;第二部分是该记录的key,128字节;第三部分是数据偏移量,4个字节;第四部分是数据记录长度,4个字节。
我们设定文件的存储上限为262144个。
查找流程如下:
1、根据key算出hash值,获取该hash值的链表在索引文件的第一部分(所有指针区)的位置。
2、根据步骤一的位置,获取值,时间复杂度O(1);
2、根据步骤一中的值,找到索引文件中第二部分(索引记录)的位置,也就是和key相同hash值的所有指针的链表。顺着链表查找该key,获取该key在链表中存放的数据,数据只包含该key在索引文件中的位置,时间复杂度为O(n);
3、根据步骤二所获取的key在索引文件位置,得到索引文件中存放该key的信息。信息包含在真实数据文件中存放真实数据的位置。
4、根据步骤三所获取的位置,在真实数据文件中获取数据,并返回给应用程序。
测试结果:插入10000条耗时:793ms。查找10000条耗时:149ms。虽然这效率只有Redis的十分之一。。。但是请不要在意这些细节。。。
代码做了注释,上述文字有些乱。代码只实现三个方法,一个插入(如果存在则跳过),一个是查找,一个是删除。
思路来源:《PHP核心技术与最佳实践》一书。尊重作者,转载请保留该书名。
代码如下 | 复制代码 |
<?php //成功-返回码 class DB{ /** return DB_SUCCESS; /** /** $idxoff = fstat($this->idx_fp); $datoff = fstat($this->dat_fp); $keylen = strlen($key); fseek($this->idx_fp, 0, SEEK_END); fseek($this->dat_fp, 0, SEEK_END); return DB_SUCCESS; if($found){ /** $found = false; if(!strncmp($key, $cpkey, strlen($key))){ $datalen = unpack('L', substr($block, DB_KEY_SIZE + 8, 4)); $found = true; /** $next = unpack('L', substr($block, 0, 4)); $cpkey = substr($block, 4, DB_KEY_SIZE); public function close(){ |
测试,测试添加一万条和查找一万条:
代码如下 | 复制代码 |
<?php $startTime = microtime(true); //插入测试...插入10000条:成功,耗时: 793.48206520081ms //查找测试...查找10000条:成功,耗时: 149.08313751221ms $endTime = microtime(true); |
Hash表作为最重要的数据结构之一,也叫做散列表。使用PHP实现Hash表的功能。PHP可以模拟实现Hash表的增删改查。通过对key的映射到数组中的一个位置来访问。映射函数叫做Hash函数,存放记录的数组称为Hash表。
Hash函数把任意长度的和类型的key转换成固定长度输出。不同的key可能拥有相同的hash。
Hash表的时间复杂度为O(1)
代码如下 | 复制代码 |
<?php class HashTable{ /** /** /** public function getList(){ public function editSize($size){ |
下面对我们的HashTable进行测试。
代码如下 | 复制代码 |
<?php //测试2
|
改变了值之后可以存放更多的元素。但是仍然存在不同的key可能产生相同的hash值,那么赋值的时候后操作会覆盖前操作的问题。这种冲突的问题我们来用拉链法解决。
拉链法解决冲突。拉链法解决冲突的做法是将所有的相同Hash值的key放在一个链表中,比如key3和key14在hash之后都是0,那么在数组的键为0的地方存储这两个值,形式是链表。如果不能理解我的文字,请看下面的示例,看一下打印信息就明白了。拉链法是什么,就是链表。
创建一个HashNode类,用来存储key和value的值,并且存储相同hash的另一个元素。在同一条链上,查找越后的元素越费时。时间复杂度为O(n).
代码如下 | 复制代码 |
<?php
|
对我们新的HashTable进行测试
代码如下 | 复制代码 |
<?php //测试1 $newArr = new NewHashTable(); for($i=0; $i<30; $i++){ $newArr->set('key'.$i, 'value'.$i); } print_r($newArr->getList()); var_dump($newArr->get('key3')); //SplFixedArray Object //( // [0] => HashNode Object //( // [key] => key23 // [value] => value23 // [nextNode] => HashNode Object //( // [key] => key14 // [value] => value14 // [nextNode] => HashNode Object //( // [key] => key3 // [value] => value3 // [nextNode] => // ) // // ) // // ) // // [1] => HashNode Object //( // [key] => key24 // [value] => value24 // [nextNode] => HashNode Object //( // [key] => key15 // [value] => value15 // [nextNode] => HashNode Object //( // [key] => key4 // [value] => value4 // [nextNode] => // ) // // ) // // ) // // [2] => HashNode Object //( // [key] => key25 // [value] => value25 // [nextNode] => HashNode Object //( // [key] => key16 // [value] => value16 // [nextNode] => HashNode Object //( // [key] => key5 // [value] => value5 // [nextNode] => // ) // // ) // // ) // // [3] => HashNode Object //( // [key] => key26 // [value] => value26 // [nextNode] => HashNode Object //( // [key] => key17 // [value] => value17 // [nextNode] => HashNode Object //( // [key] => key6 // [value] => value6 // [nextNode] => // ) // // ) // // ) // // [4] => HashNode Object //( // [key] => key27 // [value] => value27 // [nextNode] => HashNode Object //( // [key] => key18 // [value] => value18 // [nextNode] => HashNode Object //( // [key] => key7 // [value] => value7 // [nextNode] => // ) // // ) // // ) // // [5] => HashNode Object //( // [key] => key28 // [value] => value28 // [nextNode] => HashNode Object //( // [key] => key19 // [value] => value19 // [nextNode] => HashNode Object //( // [key] => key8 // [value] => value8 // [nextNode] => // ) // // ) // // ) // // [6] => HashNode Object //( // [key] => key29 // [value] => value29 // [nextNode] => HashNode Object //( // [key] => key10 // [value] => value10 // [nextNode] => HashNode Object //( // [key] => key9 // [value] => value9 // [nextNode] => // ) // // ) // // ) // // [7] => HashNode Object //( // [key] => key20 // [value] => value20 // [nextNode] => HashNode Object //( // [key] => key11 // [value] => value11 // [nextNode] => HashNode Object //( // [key] => key0 // [value] => value0 // [nextNode] => // ) // // ) // // ) // // [8] => HashNode Object //( // [key] => key21 // [value] => value21 // [nextNode] => HashNode Object //( // [key] => key12 // [value] => value12 // [nextNode] => HashNode Object //( // [key] => key1 // [value] => value1 // [nextNode] => // ) // // ) // // ) // // [9] => HashNode Object //( // [key] => key22 // [value] => value22 // [nextNode] => HashNode Object //( // [key] => key13 // [value] => value13 // [nextNode] => HashNode Object //( // [key] => key2 // [value] => value2 // [nextNode] => // ) // // ) // // ) // //) //string(6) "value3" ?> |
PHP命名空间是PHP5.3开始支持。本篇讲解PHP命名空间用法和PHP命名空间详解。它的诞生使的我们在一个文件中可以使用多个同名的类而不冲突。
好处:我们的项目有一个记录日志文件的类,叫做Log。然后我们又必须要引入另一个代码包,这个代码包里也有一个叫做Log的类。那么在一个文件中,我们记录日志的又需要给两个类都写一条日志。可以类同名了,怎么办?这个时候,命名空间应运而生。在Java等语言中命名空间是很早就已经提供了支持,而我大PHP一直到5.3才对命名空间提供了支持。
示例一:
文件index.php
<?php
include 'test.php';
class index{
public function a(){
echo basename(__FILE__);
echo '<br>';
echo __CLASS__ . ' : ' . __METHOD__;
}
}
$obj = new index();
$obj->a();
echo '<br>';
$obj1 = new test\index();
$obj1->a();
?>
文件test.php
<?php
namespace test;
class index{
public function a(){
echo basename(__FILE__);
echo '<br>';
echo __CLASS__ . ' : ' . __METHOD__;
}
}
?>
我们给index.php不设置命名空间,对test.php设置命名空间,名为test。运行index.php。
结果:
index.php
index : index::a
test.php
test\index : test\index::a
我们看到了,同名的类也可以运行而不冲突了。
示例二:
文件index.php
<?php
namespace index;
include 'test.php';
class index{
public function a(){
echo basename(__FILE__);
echo '<br>';
echo __CLASS__ . ' : ' . __METHOD__;
}
}
$obj = new index();
$obj->a();
echo '<br>';
$obj1 = new \test\index();
$obj1->a();
?>
文件test.php
<?php
namespace test;
class index{
public function a(){
echo basename(__FILE__);
echo '<br>';
echo __CLASS__ . ' : ' . __METHOD__;
}
}
?>
我们给index.php设置命名空间,名为index,对test.php设置命名空间,名为test。运行index.php。
结果:
index.php
index\index : index\index::a
test.php
test\index : test\index::a
比较示例一和二,不对index.php设置命名空间,即该文件是整个PHP全局命名空间下面的一个文件,那么使用test\index()即可,如果对index.php设置命名空间,即在其他的命名空间中使用命名空间,就要多一个“\”,就要使用\test\index()。
示例三:
文件index.php
<?php
namespace index;
include 'namespace.php';
use \test\test1\test2 as test2;
class index{
public function a(){
echo basename(__FILE__);
echo '<br>';
echo __CLASS__ . ' : ' . __METHOD__;
}
}
$obj = new index();
$obj->a();
echo '<br>';
$obj1 = new \test\test1\test2\index();
$obj1->a();
echo '<br>';
$obj1 = new test2\index();
$obj1->a();
文件test.php
<?php
namespace test\test1\test2;
class index{
public function a(){
echo basename(__FILE__);
echo '<br>';
echo __CLASS__ . ' : ' . __METHOD__;
}
}
结果:
index.php
index\index : index\index::a
test.php
test\test1\test2\index : test\test1\test2\index::a
test.php
test\test1\test2\index : test\test1\test2\index::a
这说明了什么?别名!用过SQL吧。
select COUNT(*) as `count` from `tebleName`
嗯,一个意思。\test\test1\test2这个名字太长了,就别名为test2就好了。使用了use之后呢,这个命名空间就想到于是在index这个命名空间下面了,而不是全局命名空间的一员了,所以使用test2\index(),而不是\test2\index()。
别名时在PHP代码编译的时候执行的,而变量的解析则要更晚。也就是说不能对变量运用use关键字。示例如下(摘自官方手册示例):
<?php
use My\Full\Classname as Another, My\Full\NSname;
$obj = new Another; // 实例化一个 My\Full\Classname 对象
$a = 'Another';
$obj = new $a; // 实际化一个 Another 对象
一个比较简单的php文件上传范例,可用于要求不高的时候,记录到这里,方便以后要用的时候直接过来复制。
$resume = $_FILES['resumefile'];
if($resume['name']!='')
{
$enableType = array('jpg','png','gif','doc','docx','rtf','pdf'); //支持格式
$maxSize = 1024*1024; //最大允许上传1M的文件
$filePath = 'upload/resume/';
$suffix = end(explode('.', $resume['name'])); //获取文件后缀名
$resumeName = 'resume_'.time().'.'.$suffix;
//大小限制
if($resume['size']>$maxSize)
{
die("<script> alert('请上传1M以下的文件!'); history.back(); </script>");
}
//格式限制
if(!in_array($suffix, $enableType))
{
die("<script> alert('格式不正确,请上传 ".implode(', ', $enableType)." 格式的文档!'); history.back(); </script>");
}
//上传
if(move_uploaded_file($resume['tmp_name'], $filePath.$resumeName))
{
$attachment = $filePath.$resumeName; //文件名,写入数据库
}
}
我们在一些应用中需要动态展示数据,比如当前在线人数,当前交易总额,当前汇率等等,前端页面需要实时刷新获取最新数据。本文将结合实例给大家介绍使用jQuery和PHP来实现动态数字展示效果。
本例假设要在页面上动态展示(无需刷新整个页面,只是局部刷新动态数字)当前在线用户数,常见在一些统计平台上应用。在HTML页面中只需定义以下结构:
代码如下 | 复制代码 |
<div class="count">当前在线:<span id="number"></span></div> |
首先我们要定义一个动画过程,使用jQuery的animate()函数实现从一个数字到另一个数字的变换过程,以下magic_number()自定义函数将代码整合如下:
代码如下 | 复制代码 |
function magic_number(value) { var num = $("#number"); num.animate({count: value}, { duration: 500, step: function() { num.text(String(parseInt(this.count))); } }); }; |
然后update()函数使用了jQuery的$.getJSON()向后台number.php发送了一个ajax请求,在得到PHP相应后,调用magic_number()展示最新的数字。为了能看到更好的效果,我们使用setInterval()设置代码执行的间隔时间。
代码如下 | 复制代码 |
function update() { $.getJSON("number.php?jsonp=?", function(data) { magic_number(data.n); }); }; setInterval(update, 5000); //5秒钟执行一次 update(); |
PHP
实际项目中,我们会使用PHP获取数据库中的最新数据,然后通过PHP返回给前端。本例为了更好的演示,使用随机数字,最后以json格式返回给前端js,number.php代码如下:
代码如下 | 复制代码 |
$total_data = array( |
原理其实非常的简单就是利用js settimeout实现过几秒加载一个php文件从而达到了实时显示在线人数的功能了。
相关文章
- 下面小编来给大家演示几个php操作zip文件的实例,我们可以读取zip包中指定文件与删除zip包中指定文件,下面来给大这介绍一下。 从zip压缩文件中提取文件 代...2016-11-25
- 操作类就是把一些常用的一系列的数据库或相关操作写在一个类中,这样调用时我们只要调用类文件,如果要执行相关操作就直接调用类文件中的方法函数就可以实现了,下面整理了...2016-11-25
- 本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
Jupyter Notebook读取csv文件出现的问题及解决
这篇文章主要介绍了JupyterNotebook读取csv文件出现的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2023-01-06- 这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
- php语言实现redis的客户端与服务端有一些区别了因为前面介绍过服务端了这里我们来介绍客户端吧,希望文章对各位有帮助。 为了更好的了解redis协议,我们用php来实现...2016-11-25
- 有时我们在页面上需要选择数值范围,如购物时选取价格区间,购买主机时自主选取CPU,内存大小配置等,使用直观的滑块条直接选取想要的数值大小即可,无需手动输入数值,操作简单又方便。HTML首先载入jQuery库文件以及jRange相关...2015-03-15
- 这篇文章主要介绍了Intellij IDEA连接Navicat数据库的方法,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借价值,需要的朋友可以参考下...2021-03-25
- 在开发过程中,我们经常会将日期时间的毫秒数存放到数据库,但是它对应的时间看起来就十分不方便,我们可以使用一些函数将毫秒转换成date格式。 一、 在MySQL中,有内置的函数from_unixtime()来做相应的转换,使用如下: 复制...2014-05-31
- 有时我们接受或下载到的PSD文件打开是空白的,那么我们要如何来解决这个 问题了,下面一聚教程小伙伴就为各位介绍Photoshop打开PSD文件空白解决办法。 1、如我们打开...2016-09-14
- C#使用System.IO中的文件操作方法在Windows系统中处理本地文件相当顺手,这里我们还总结了在Oracle中保存文件的方法,嗯,接下来就来看看整理的C#操作本地文件及保存文件到数据库的基本方法总结...2020-06-25
- 这篇文章主要介绍了解决python 使用openpyxl读写大文件的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-13
- 这篇文章主要介绍了C#实现HTTP下载文件的方法,包括了HTTP通信的创建、本地文件的写入等,非常具有实用价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要为大家详细介绍了SpringBoot实现excel文件生成和下载,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-02-09
- 通过内网连另外一台机器的mysql服务, 确发现速度N慢! 等了大约几十秒才等到提示输入密码。 但是ping mysql所在服务器却很快! 想到很久之前有过类似的经验, telnet等一些服务在连接请求的时候,会做一些反向域名解析(如果...2015-10-21
- 本文实例讲述了JS实现的简洁纵向滑动菜单(滑动门)效果。分享给大家供大家参考,具体如下:这是一款纵向布局的CSS+JavaScript滑动门代码,相当简洁的手法来实现,如果对颜色不满意,你可以试着自己修改CSS代码,这个滑动门将每一...2015-10-21
- 某些时候,例如为了搭建一个测试环境,或者克隆一个网站,需要复制一个已存在的mysql数据库。使用以下方法,可以非常简单地实现。假设已经存在的数据库名字叫db1,想要复制一份,命名为newdb。步骤如下:1. 首先创建新的数据库newd...2015-10-21
php无刷新利用iframe实现页面无刷新上传文件(1/2)
利用form表单的target属性和iframe 一、上传文件的一个php教程方法。 该方法接受一个$file参数,该参数为从客户端获取的$_files变量,返回重新命名后的文件名,如果上传失...2016-11-25- mysqldump命令的用法1、导出所有库系统命令行mysqldump -uusername -ppassword --all-databases > all.sql 2、导入所有库mysql命令行mysql>source all.sql; 3、导出某些库系统命令行mysqldump -uusername -ppassword...2015-10-21
- 1005:创建表失败1006:创建数据库失败1007:数据库已存在,创建数据库失败1008:数据库不存在,删除数据库失败1009:不能删除数据库文件导致删除数据库失败1010:不能删除数据目录导致删除数据库失败1011:删除数据库...2013-09-23