不管是日常业务数据处理中,还是数据库的导入导出,都可能遇到需要处理大量数据的插入。插入的方式和数据库引擎都会对插入速度造成影响,这篇文章旨在从理论和实践上对各种方法进行分析和比较,方便以后应用中插入方法的选择。
MySQL中插入一个记录需要的时间由下列因素组成,其中的数字表示大约比例:
如果我们每插入一条都执行一个SQL语句,那么我们需要执行除了连接和关闭之外的所有步骤N次,这样是非常耗时的,优化的方式有一下几种:
每种方式执行的性能如下。
InnoDB 给 MySQL 提供了具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。InnoDB 提供了行锁(locking on row level)以及外键约束(FOREIGN KEY constraints)。
InnoDB 的设计目标是处理大容量数据库系统,它的 CPU 利用率是其它基于磁盘的关系数据库引擎所不能比的。在技术上,InnoDB 是一套放在 MySQL 后台的完整数据库系统,InnoDB 在主内存中建立其专用的缓冲池用于高速缓冲数据和索引。
Macbook Air 12mid apache2.2.26 php5.5.10 mysql5.6.16
总数100W条数据
插入完后数据库大小38.6MB(无索引),46.8(有索引)
MyISAM 是MySQL缺省存贮引擎。设计简单,支持全文搜索。
Macbook Air 12mid apache2.2.26 php5.5.10 mysql5.6.16
总数100W条数据
插入完后数据库大小19.1MB(无索引),38.6(有索引)
我测试的数据量不是很大,不过可以大概了解这几种插入方式对于速度的影响,最快的必然是Load Data方式。这种方式相对比较麻烦,因为涉及到了写文件,但是可以兼顾内存和速度。
<?php
$dsn = 'mysql:host=localhost;dbname=test';
$db = new PDO($dsn,'root','',array(PDO::ATTR_PERSISTENT => true));
//删除上次的插入数据
$db->query('delete from `test`');
//开始计时
$start_time = time();
$sum = 1000000;
// 测试选项
$num = 1;
if ($num == 1){
// 单条插入
for($i = 0; $i < $sum; $i++){
$db->query("insert into `test` (`id`,`name`) values ($i,'tsetssdf')");
}
} elseif ($num == 2) {
// 批量插入,为了不超过max_allowed_packet,选择每10万插入一次
for ($i = 0; $i < $sum; $i++) {
if ($i == $sum - 1) { //最后一次
if ($i%100000 == 0){
$values = "($i, 'testtest')";
$db->query("insert into `test` (`id`, `name`) values $values");
} else {
$values .= ",($i, 'testtest')";
$db->query("insert into `test` (`id`, `name`) values $values");
}
break;
}
if ($i%100000 == 0) { //平常只有在这个情况下才插入
if ($i == 0){
$values = "($i, 'testtest')";
} else {
$db->query("insert into `test` (`id`, `name`) values $values");
$values = "($i, 'testtest')";
}
} else {
$values .= ",($i, 'testtest')";
}
}
} elseif ($num == 3) {
// 事务插入
$db->beginTransaction();
for($i = 0; $i < $sum; $i++){
$db->query("insert into `test` (`id`,`name`) values ($i,'tsetssdf')");
}
$db->commit();
} elseif ($num == 4) {
// 文件load data
$filename = dirname(__FILE__).'/test.sql';
$fp = fopen($filename, 'w');
for($i = 0; $i < $sum; $i++){
fputs($fp, "$i,'testtest'\r\n");
}
$db->exec("load data infile '$filename' into table test fields terminated by ','");
}
$end_time = time();
echo "总耗时", ($end_time - $start_time), "秒\n";
echo "峰值内存", round(memory_get_peak_usage()/1000), "KB\n";
?>
Vagrant是一款用来构建虚拟开发环境的工具,它其实算是一个跨平台的虚拟机管理工具。
Vagrant的旧版本是可以通过gem来安装的,但是由于依赖实在太多,官方放弃了这种安装方式,建议下载官方安装包来安装。
下载地址在http://www.vagrantup.com/downloads。下载好pkg包后,点击安装即可。
Vagrant依赖现有的虚拟机软件来管理虚拟机,如Virtualbox, Vmware Fusion, Parallel Desktop等,其中最方便的是VirtualBox,所以我选择了Virtualbox。
下载地址在https://www.virtualbox.org/wiki/Downloads。同样下载好后直接安装。
在Vagrant中,box是一种打包好的镜像,通过这个镜像,可以生成相应的虚拟机。box可以通过官方网站下载,也可以自己制作,在团队内分享。
官方的Box可以在创建时自动下载。例如以下步骤
cd ~/Documents/Vagrant/Ubuntu # 进入一个vagrant虚拟机目录,一个目录管理一个虚拟机
vagrant init hashicorp/precise32 # 创建一个ubuntu的虚拟机
vagrant up # 启动这个虚拟机
通过这个步骤,vagrant会去box列表中找hashicorp/precise32
这个镜像,如果没有就去官方下载。Box被保存在~/.vagrant
下。通过命令
vagrant box list
可以查看已经下载的box。如果想以这个box再建立一个虚拟机,只要再创建一个目录,例如~/Documents/Vagrant/Ubuntu32
,然后执行
vagrant init hashicorp/precise32
即可。
操作虚拟机时,必须进入刚刚建立的目录中去,这个目录中必须含有init
命令建立的Vagrantfile文件。常用命令有
$ vagrant init # 初始化
$ vagrant up # 启动虚拟机
$ vagrant halt # 关闭虚拟机
$ vagrant reload # 重启虚拟机
$ vagrant ssh # SSH 至虚拟机
$ vagrant status # 查看虚拟机运行状态
$ vagrant destroy # 销毁当前虚拟机
通过Vagrant建立的虚拟机和Mac共享文件非常容易,虚拟机中/vagrant
目录会映射到我们本地虚拟机目录中。例如
cd ~/Documents/Vagrant/Ubuntu
vagrant up
vagrant ssh
cd /vagrant
ls
这个时候,我们会看到,这里显示的文件和~/Documents/Vagrant/Ubuntu
下是一样的。
如果只有上述功能的话,那么Vagrant的作用就不是那么杀手级
了。通过命令
vagrant package
可以将一个虚拟机打包成Box,供别人使用。别人只要用打包的box来创建一个虚拟机即可,例如
vagrant box add myubuntu ~/Documents/Vagrant/Ubunutu/ubunut.box
Docker暂时并不支持原生的Mac系统,所以Mac下的Docker实际上是依赖一个很小的linux虚拟机来实现的。
Vagrant依赖现有的虚拟机软件来管理虚拟机,如Virtualbox, Vmware Fusion, Parallel Desktop等,其中最方便的是VirtualBox,所以我选择了Virtualbox。
下载地址在https://www.virtualbox.org/wiki/Downloads。下载好后直接安装。
Boot2Docker是帮助控制虚拟机中Docker的工具,它会下载一个安装好docker的虚拟机,并控制其实现docker功能。
在mac下安装boot2docker只要执行
brew install boot2docker
即可。
要想在mac下直接执行docker命令,需要安装一个适合mac的docker client,安装方法如下
# Get the docker client file
DIR=$(mktemp -d ${TMPDIR:-/tmp}/dockerdl.XXXXXXX) && \
curl -f -o $DIR/ld.tgz https://get.docker.io/builds/Darwin/x86_64/docker-latest.tgz && \
gunzip $DIR/ld.tgz && \
tar xvf $DIR/ld.tar -C $DIR/ && \
cp $DIR/usr/local/bin/docker ./docker
# Set the environment variable for the docker daemon
export DOCKER_HOST=tcp://127.0.0.1:4243
# Copy the executable file
sudo cp docker /usr/local/bin/
这样就有一个docker命令了
使用docker安装需要先启动boot2docker虚拟机
# Initiate the VM
boot2docker init
# Run the VM (the docker daemon)
boot2docker up
# To see all available commands:
boot2docker
之后就可以使用docker命令了
docker version
svn checkout path(path是服务器上的目录)
svn checkout svn://192.168.1.1/pro/domain
svn co
svn add file
svn add test.php(添加test.php)
svn add *.php(添加当前目录下所有的php文件)
svn add *.php --force(增加所有文件)
svn commit -m “LogMessage“ [-N] [--no-unlock] PATH(如果选择了保持锁,就使用–no-unlock开关)
svn commit -m “add test file for my test“ test.php
svn ci
svn lock -m “LockMessage“ [--force] PATH
svn lock -m “lock test file“ test.php
svn unlock PATH
svn update -r m path
svn update 如果后面没有目录,默认将当前目录以及子目录下的所有文件都更新到最新版本。
svn update -r 200 test.php(将版本库中的文件test.php还原到版本200)
svn update test.php(更新,于版本库同步。如果在提交的时候提示过期的话,是因为冲突,需要先update,修改文件,然后清除svn resolved,最后再提交commit)
svn up
svn status path(目录下的文件和子目录的状态,正常状态不显示)
【?:不在svn的控制中;M:内容被修改;C:发生冲突;A:预定加入到版本库;K:被锁定】
svn status -v path(显示文件和子目录状态)
第一列保持相同,第二列显示工作版本号,第三和第四列显示最后一次修改的版本号和修改人。
注:svn status、svn diff和 svn revert这三条命令在没有网络的情况下也可以执行的,原因是svn在本地的.svn中保留了本地版本的原始拷贝。
svn st
svn delete path -m “delete test fle“
svn delete svn://192.168.1.1/pro/domain/test.php -m “delete test file”
或者直接svn delete test.php 然后再svn ci -m ‘delete test file‘,推荐使用这种
svn (del, remove, rm)
svn log path
svn log test.php 显示这个文件的所有修改记录,及其版本号的变化
svn info path
svn info test.php
svn diff path(将修改的文件与基础版本比较)
svn diff test.php
svn diff -r m:n path(对版本m和版本n比较差异)
svn diff -r 200:201 test.php
svn di
svn merge -r m:n path
svn merge -r 200:205 test.php(将版本200与205之间的差异合并到当前文件,但是一般都会产生冲突,需要处理一下)
svn help
svn help ci
svn list path
svn ls
svn mkdir: 创建纳入版本控制下的新目录。
mkdir PATH…
mkdir URL…
创建版本控制的目录。
1、每一个以工作副本 PATH 指定的目录,都会创建在本地端,并且加入新增调度,以待下一次的提交。
2、每个以URL指定的目录,都会透过立即提交于仓库中创建.在这两个情况下,所有的中间目录都必须事先存在。
svn revert: 恢复原始未改变的工作副本文件 (恢复大部份的本地修改)。revert:
用法: revert PATH…
注意: 本子命令不会存取网络,并且会解除冲突的状况。但是它不会恢复被删除的目录
svn switch (sw): 更新工作副本至不同的URL。
1、switch URL [PATH]
2、switch –relocate FROM TO [PATH...]
1、更新你的工作副本,映射到一个新的URL,其行为跟“svn update”很像,也会将服务器上文件与本地文件合并。这是将工作副本对应到同一仓库中某个分支或者标记的方法。
2、改写工作副本的URL元数据,以反映单纯的URL上的改变。当仓库的根URL变动(比如方案名或是主机名称变动),但是工作副本仍旧对映到同一仓库的同一目录时使用这个命令更新工作副本与仓库的对应关系。
svn resolved: 移除工作副本的目录或文件的“冲突”状态。 java设计模式之——策略模式
用法: resolved PATH…
注意: 本子命令不会依语法来解决冲突或是移除冲突标记;它只是移除冲突的相关文件,然后让 PATH 可以再次提交。
svn cat 目标[@版本]…如果指定了版本,将从指定的版本开始查找。
svn cat -r PREV filename > filename (PREV 是上一版本,也可以写具体版本号,这样输出结果是可以提交的)
grep aaa *
查看文件夹大小 du -h --max-depth=1 /home/ys
查看驱动器空间 df -h
tar zxvf aaa.tar.gz
tar zcvf aaa.tar.gz aaa
login
lsof -i:8087 查看8087端口的使用
ps -aux|grep name|grep -v grep|cut -c 9-15|xargs kill -9
date 时间
date +%s 时间戳
date -d "2010-07-20 10:25:30" +%s 指定时间时间戳
date -d "@1279592730" 时间戳转时间
date -d "1970-01-01 14781 days" "+%Y/%m/%d %H:%M:%S"
top -d 1 -p pid [,pid ...]
pmap pid
ps aux|grep process_name
查看/proc/process_id/文件夹下的status文件
lsb_release -a
uname -a
python -m SimpleHTTPServer
lsof -i
curl ifconfig.me
wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com
screen -d -m -S some_ name ping my_router
ps -A -opid,stime,etime,args | grep python
nohup python /var/www/a.py &
du -h --max-depth=1 .
df -h
mtr
ping
traceroute
dig
netstat –tlnp
netstat -anop
ssh user@server bash < /path/to/local/script.sh
nc -z -v -n 127.0.0.1 20-100
siege -c20 www.google.co.uk -b -t30s
substr(start [, length ])
返回一个从指定位置开始的指定长度的子字符串
substring(start, end)
返回位于 String 对象中指定位置的子字符串。
window.navigate("top.jsp");
window.history.back(-1);
window.location.href="login.jsp?backurl="+window.location.href;
self.location='top.htm';
top.location='xx.jsp';
window.onload
必须等页面内包括图片的所有元素加载完成后才能执行。
不能同时编写多个,只执行一个
$(document).ready()
是DOM结构绘制完毕后就可以执行
可以编写多个
简写$(function(){});
$(window).load()等同与window.onload
history.go(0)
location.reload()
location=location
location.assign(location)
document.execCommand('Refresh')
window.navigate(location)
location.replace(location)
document.URL=location.href
JSON.parse("{a:'111',b:'ccc'}"); //解析
eval("("+""+")"); //解析
var day1 = parseInt(new Date().valueOf()/1000); //获得当前时间时间戳
day2 = new Date(day1*1000);
alert(day2.getFullYear()+"-"+(day2.getMonth()+1)+"-"+day2.getDate()+" "+day2.getHours()+":"+day2.getMinutes()+":"+day2.getSeconds())
d = new Date();
s = d.getFullYear() + "-";
s += ("0"+(d.getMonth()+1)).slice(-2) + "-";
s += ("0"+d.getDate()).slice(-2) + " ";
s += ("0"+d.getHours()).slice(-2) + ":";
s += ("0"+d.getMinutes()).slice(-2) + ":";
s += ("0"+d.getSeconds()).slice(-2) + ".";
s += ("00"+d.getMilliseconds()).slice(-3);
var a="':'";
en = encodeURI(a); //编码
a = decodeURI(en); //解码
function htmlEncode(value){
return $('<div/>').text(value).html();
}
function htmlDecode(value){
return $('<div/>').html(value).text();
}