Monthly Archives: April 2011

Mysql复杂查询时“表别名”的用处

比如说我有如下的一些数据:

解释一下:projectId是指某个项目的ID,qiantaiId是在这个项目中某个人的ID,但是一个人在某个项目中可能会做好几次,问题是如何用sql统计某个项目中究竟有多少人参与?

第一,这个sql肯定要有项目Id,where projectId=XX是肯定有的,qiantaiId只以类分,所以要group by qiantaiId,我第一想法是这样的:

select count(*)  from votes where projectId=5 group by qiantaiId

但是很遗憾,得到的结果是这样的:



虽然很遗憾,但是离我的目标不远了,至少的确是2行数据。那么就需要在查询结果中再计算,想到了如下sql:

SELECT count(*) from(select count(*)  from votes where projectId=5 group by qiantaiId)

但是mysql又报错了:

#1248 - Every derived table must have its own alias

原来mysql在复杂查询时需要表别名,这还真不知道,更改sql如下OK:

SELECT count(*) from(select count(*)  from votes where projectId=5 group by qiantaiId) as t1

使用rsync备份数据

首先要做的就是在把需要备份的服务器的公钥传到数据备份服务器上,参考《Linux创建密钥以及上传密钥至目标服务器》来实现。

首先很悲剧的说.并不是实时的备份.实时的备份研究了很久均没有成功.尽管网上教程一堆……
好了.实现的功能是手动利用rsync增量备份另外一个VPS的网站数据到本地的VPS上.实现了这个可以利用crontab来定时增量备份.
——-
rysnc 是一个数据镜像及备份工具.具有可使本地和远程两台主机的文件.目录之间.快速同步镜像.远程数据备份等功能.在同步过程中.rsync是根据自己独特的算法.只同步有变化的文件.甚至在一个文件里只同步有变化的部分.所以可以实现快速的同步数据的功能.
下面所讲的简单的rsync同步方法.依赖于SSH服务.一般rsync.ssh软件包都会默认安装的.
检查方法:rpm -qa|grep -i rsync
——-
假设A机器是网站服务器.B机器是备份服务器.
——-
首先安装rsync
yum -y install rsync
——-
两台机器做个ssh信任.省去每次输入密码.
两台机器都操作一次.
ssh-keygen -t rsa
创建证书
scp /root/.ssh/id_rsa.pub root@目标机器:/root/.ssh/authorized_keys
把公匙传到目标机器的/root/.ssh
然后双机互相验证一下用ssh登陆.是否不需要输入密码.如果不用则建立成功.
——-
第一种方法.在A机器上执行命令:
rsync -aSvH /home/* root@B机IP:/home/
上面这个表示将本机的/home目录下所有的文件.同步到B机器的/home目录下.
上面绿色标记是源文件位置.蓝色标记是目的位置.
——-
第二种方法.在B机器上执行命令:
rsync -aSvHroot@A机IP:/home/* /home/
上面这条表示.将远程机器A机器上的/home目录下所有的文件.同步到本地的/home目录下.同步的时候注意源和目的.
上面绿色标记是源文件位置.蓝色标记是目的位置.
——-
参数解释:
a 等同于-rlptgoD归档模式.就是保持文件所有属性.权限不变.
S 有效的处理零散文件.
v verbose模式.
H 保持hard links.
——-
如果想每次同步之后.两边的文件保持一致.需要添加参数–delete.添加这个参数时.一定要注意测试.否则会造成严重后果.删除了数据.
使用–delete参数后上面两条命令将变成:
第一种方法.在A机器上执行命令:
rsync -aSvH –delete /home/* root@B机IP:/home/
第二种方法.在B机器上执行命令:
rsync -aSvH –delete root@A机IP:/home/* /home/
——-
我们也可以利用crontab来定时执行这条命令
crontab -e
这个也不用解释……请自行Google.
——-
00 00 * * * rsync -aSvH –delete /home/*  root@B机IP:/home/
这个表示00:00执行增量备份.时间自己改.

Linux创建密钥以及上传密钥至目标服务器

1.创建自己的私钥和公钥

#ssh-keygen -t rsa

会在~/.ssh/目录下创建出”~/.ssh/id_rsa”文件和”~/.ssh/id_rsa.pub”文件。

2.上传id_rsa.pub至目标服务器内

#cat ~/.ssh/id_rsa.pub | ssh root@192.168.1.1 "cat - >> ~/.ssh/authorized_keys"

192.168.1.1表示服务器的IP,接下来会让你输入目标服务器的root用户密码,输入后OK。

3.目标服务器安全设置

编辑sshd_config,只允许密钥登陆,禁止密码登陆

PasswordAuthentication no

备份网站数据库和www目录

创建备份数据库和www目录shell的几个问题:

1. msyqldump输入密码的问题

默认使用mysqldump的时候,会让你你下一行输入密码然后回车,但是我需要能够自动化的shell,所以搜索到这个解决方案:

#mysqldump -u root --password=youpasswd 备份的数据库名 > back.sql

注意:password前面需要输入两个-号。

2. 备份脚本中tar压缩的问题

tar亚压缩的时候也有问题,比如在/tmp目录下,使用 tar czvf www.tag.gz /usr/share/nginx/html 命令来备份html目录的话,会在压缩文件中把/usr/share/nginx/目录信息都带进去,这就需要使用tar的临时目录参数了

#tar czvf www.tar.gz -C /usr/share/nginx(注意中间有空格) html

-C /usr/share/nginx的意思是把tar命令的当前目录设定为/usr/share/nginx,然后去压缩html目录。

3. 排除压缩

www目录中有些文件夹是不需要压缩备份的,需要排除掉,节省压缩时间

#tar czvf www.tar.gz -C /usr/share/nginx(注意中间有空格) html --(前面为双‘-’号)exclude=bbbb aaa

这样就能在压缩的时候把bbbb和aaa目录排除了。

4. 使用日期标示当前备份

#!/bin/bash
NOW=$(date +"%Y%m%d")
FILE="backup.$NOW.tar.gz"

参考:http://www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/

OpenVZ VPS节省内存之调整stack大小

OpenVZ VPS上每个进程默认的stack也不小,如果VPS开启的进程多的话,也会占用不少的内存,修改一下就能节省20%左右的内存,例如VPS跑的nginx和php-cgi都是以nginx为用户的,就可以修改/etc/security/limits.conf

nginx hard stack 256

把每个进程的stack改为256kb。

原创WordPress插件中的问题和解决方法

1. 如何在admin中显示我想要的菜单的问题

我希望自己的plugin在admin中有个top-level menu,参考了内容后一下代码实现:

add_action('admin_menu', 'mt_add_pages');
function mt_add_pages() {


    // 添加top-level菜单
    add_menu_page('待审核名单','待审核名单', 'manage_options', 'mt-top-level-handle', 'mt_toplevel_page' );

    //添加二级菜单
    add_submenu_page('mt-top-level-handle', __('已审核申请','menu-test'), __('已审核申请','menu-test'), 'manage_options', 'sub-page', 'mt_sublevel_page');

    add_submenu_page('mt-top-level-handle', __('未通过申请','menu-test'), __('未通过申请','menu-test'), 'manage_options', 'sub-page2', 'mt_sublevel_page2');
}

function mt_toplevel_page() {
	global $wpdb;
        echo "<h2>" . '已提交信息' . "</h2>";
	$qry = "SELECT * FROM trials";
	$states = $wpdb->get_results( $qry );
	echo "<table style='width:800px'>";
	echo "<td><strong>目前blog类型</strong></td><td><strong>姓名</strong></td><td><strong>电话</strong></td><td><strong>QQ</strong></td><td><strong>email</strong></td><td><strong>Blog</strong></td><td><strong>地址</strong></td>";
	foreach( $states as $row ) {
		$plan = $row->plan;
		$name = $row->name;
		$phone = $row->phone;
		$qq = $row->QQ;
		$email = $row->email;
		$blogUrl = $row->blogUrl;
		$address = $row->address;
		echo "<tr>";

		echo "<td>".$plan."</td>";
		echo "<td>".$name."</td>";
		echo "<td>".$phone."</td>";
		echo "<td>".$qq."</td>";
		echo "<td>".$email."</td>";
		echo "<td><a href=$blogUrl>".$blogUrl."</a></td>";
		echo "<td>".$address."</td>";
	
		echo "</tr>";
	}
	echo "</table>";
}


function mt_sublevel_page() {
    echo "<h2>" . __( '已审核申请', 'menu-test' ) . "</h2>";
}


function mt_sublevel_page2() {
    echo "<h2>" . __( '未通过申请', 'menu-test' ) . "</h2>";
}

我的plugin要实现的功能是在wordpress中有个page可以让网站用户提交信息,管理员可以在wordpress的后台看到所有人提交的信息,并对某些人提交的进行审核.

2. plugin在admin中如何接受更新信息

在plugin的开始输入以下信息来接受更新options信息:

if ( isset($_POST['action']) ){
	
	$value = $_POST['checkedbox'];
	$action = $_POST['action'];
	
	if ($action == 'binggo' && !empty($value)){
		echo "binggo".implode(",",$value);
		$ids = implode(",",$value);
		$wpdb->query("
			UPDATE trials SET checked='Y' , bingo='Y' WHERE id in ($ids)");
	}
	else if ($action == 'noBinggo' && !empty($value)) {
		echo "noBinggo".implode(",",$value);
		$ids = implode(",",$value);
		$wpdb->query("
			UPDATE trials SET checked='Y' , bingo='N' WHERE id in ($ids)");
	}
}

3. 如何在plugin的admin中使用js

我想在plugin中使用jquery js来控制一些信息,比如说有很多checkbox,需要有一个能看全选其他的checkbox,但是在plugin中无法直接写js,需要用wp_enqueue_script()导入js文件。

wp_enqueue_script('gatherInfo', home_url()  . '/wp-content/plugins/gatherInfo/gatherInfo.js',array('jquery'));

js文件:

jQuery(document).ready(function(){
	jQuery("#checkall").click(
	function(){
		if(this.checked){
			jQuery("input[name='checkedbox[]']").each(function(){this.checked=true;});
		}else{
			jQuery("input[name='checkedbox[]']").each(function(){this.checked=false;});
		}
	}
);
	
});

4. 用户如何提交信息

做法就是在theme中创建一个新的page template,在这个page template中添加from和各种需要输入的field。然后在wordpress中添加一个以刚才创建的page template为基础的page。

5. 如何接收到用户输入的信息

这个比较难办,参考其他的plugin都是需要在wp-admin/目录接受POST信息,参考wordpress中的wp-comments-post.php,同样复制了一个wp-userinput.php来接受用户输入信息,保存数据库,效果也很好。

if ( 'POST' != $_SERVER['REQUEST_METHOD'] ) {
	header('Allow: POST');
	header('HTTP/1.1 405 Method Not Allowed');
	header('Content-Type: text/plain');
	exit;
}

/** Sets up the WordPress Environment. */
require( dirname(__FILE__) . '/wp-load.php' );

nocache_headers();

global $wpdb;

$plan = filter_input(INPUT_POST, 'plan');

$name = filter_input(INPUT_POST, 'Name');

$qq = filter_input(INPUT_POST, 'QQ');

$phone = filter_input(INPUT_POST, 'phone');

$email = filter_input(INPUT_POST, 'email');

$blogUrl = filter_input(INPUT_POST, 'blogUrl');

$address = filter_input(INPUT_POST, 'address');

$user_IP = ($_SERVER["HTTP_VIA"]) ? $_SERVER["HTTP_X_FORWARDED_FOR"] : $_SERVER["REMOTE_ADDR"];
$user_IP = ($user_IP) ? $user_IP : $_SERVER["REMOTE_ADDR"]; 

if(empty($plan) || empty($name) || empty($email) || empty($phone) || empty($blogUrl) || empty($address) || empty($user_IP)){
	echo "内容不能为空!";
	die();
}


//$qry_IP = "SELECT count( * ) as num FROM trials WHERE submitIP = '$user_IP'";
//$states = $wpdb->get_results( $qry_IP );
//$wpdb->show_errors();

$sql = $wpdb->prepare("SELECT count( * ) as num FROM trials WHERE submitIP = '%s'",array($user_IP));

$states = $wpdb->get_results( $sql);

if(!empty($states)){
	$num_ip = $states[0]->num;
	if($num_ip > 20){
		echo "你申请的次数太多了^_^";
		die();
	}
}

$qry_email = $wpdb->prepare("SELECT * FROM trials where email ='%s' or blogUrl ='%s'",array($email,$blogUrl));

$states = $wpdb->get_results( $qry_email );

if(!empty($states)){
	
	echo "你已经注册过了^_^";
	die();
}
else{
		
	$rows_affected  = $wpdb->insert('trials',array( 'plan' => $plan, 'name' => $name, 'qq' => $qq,'phone' => $phone,'email' => $email,'blogUrl' => $blogUrl,'address' => $address, 'submitIP' => $user_IP));
	
	echo "注册成功!,请耐心等待我们的邮件通知.";
}

主要注意:数据库查询需要防范sql注入攻击,SQL语句最好先用$wpdb->prepare函数生成,查询的时候更加保险一些;同理,插入的时候也最好用$wpdb->insert函数而别直接用sql语句插入。

4. 用户输入表单验证问题

表单js验证使用Jquery validationEngine来实现,效果很好,并且验证规则只要在input后面写上即可,不用单独在js中写明:

input class="phone validate[required,custom[phone],maxSize[11]]" maxlength="20" id="phone" name="phone" type="text"
input class="email validate[required,custom[email]]" maxlength="50" id="email" name="email" type="text"

然后使用jquery form来ajax提交用户输入信息:

jQuery(document).ready(function(){
	
	//jQuery("#submit_button").attr("disabled", true); 可以用来是按钮功能不可用
	//jQuery("#submit_button").css("display", "none"); 可以用来使按钮不可见

	jQuery("#trials_signup").validationEngine();
	
	var options = { 
        success:       showResponse  // post-submit callback 
	}
	
	jQuery('#trials_signup').submit(function() { 
		//如果验证field没有问题就ajax提交信息
		if(jQuery("#trials_signup").validationEngine('validate')){
			jQuery("#trials_signup").ajaxSubmit(options);
			return false; 			
		}
        return false; 
    }); 
	function showResponse(responseText, statusText, xhr, $form)  {  
		alert(responseText); 
	} 
});

PHP过滤输入内容与SQL注入

filter_input是PHP 5.2以后的版本新加入的函数,对于新手来说可以简单的处理一些input信息,过滤不安全输入。

http://php.net/manual/en/function.filter-input.php

不过PHP官方网站上倒是有一篇关于预防SQL注入的文章:

http://php.net/manual/en/security.database.sql-injection.php