master and slave have equal MySQL server UUIDs 解决办法0+

12,339 views / 2013.03.14 / 11:11 下午

使用rsync配置了大量mysql,省去了大量编译和配置的时间,随逐个修改master和slave服务器的my.cnf,后,发现数据不能同步,
在slave服务器show slave status:

Fatal error: The slave I/O thread stops because master and slave have equal MySQL server UUIDs; these UUIDs must be different for replication to work.

首先检查:
mysql> show variables like ‘server_id’;
+—————+——-+
| Variable_name | Value |
+—————+——-+
| server_id | 3 |
+—————+——-+

主从并不一样,排除该问题。

继续排查,找到原因在于,拷贝整个data目录,把auto.cnf文件也拷贝过来了,里面记录了数据库的uuid,每个库的uuid应该是不一样的。

[auto]
server-uuid=6dcee5be-8cdb-11e2-9408-90e2ba2e2ea6

解决办法,按照这个16进制格式,随便改下,重启mysql即可。

Categories: 感悟 Tags:

杜工在windows下定时备份mysql的脚本0+

3,588 views / 2011.12.12 / 8:08 下午

嘿嘿,大家是不是经常为找不到windows下的定时备份脚本而苦恼(哭闹?),这里杜工分享下bat脚本做自动备份的例子,使用前请先安装WinRAR。

rem
rem C:\Program Files (x86)\WinRAR 需要配置到系统环境变量 path 下,才能调用rar cli工具
rem
rem 跳转到工作目录下
e:
cd e:\DBBAK
rem 设置备份文件名
SET BAK_FILE=MY_DBBAK_%DATE:~0,-4%.sql
rem 设置日志文件名
SET LOG_FILE=MY_DBBAK.log
rem 记录日志
echo "%date%" >> %LOG_FILE%
rem 开始dump
mysqldump --default-character-set=utf8 -hlocalhost -uroot -R --triggers --single-transaction -B mydb > %BAK_FILE%
rem 压缩备份文件
rar a %BAK_FILE%.rar %BAK_FILE%
rem 删除临时文件
del /F %BAK_FILE%
echo "%date%" >> %LOG_FILE%
echo "" >> %LOG_FILE%
Categories: 感悟 Tags:

Mysql服务无法启动,错误1067最快解决办法2+

7,779 views / 2011.11.01 / 11:11 上午

mysql 1067错误一般出现在windows系统下,自定义data目录路径的时候。即使是最新的mysql5.5 安装包,在安装完成进行配置时,也会出现这个问题。

杜工解决方法:将data目录设置在mysql安装目录下,可正常启动服务。

过程:安装msi文件,到最后一步配置时,可跳过,然后进入安装目录 /bin/下,找到MysqlInstanceConfig.exe,运行后,可按照提示一步一步配置。

Categories: 感悟 Tags:

CentOS或者redhad下用yum或者rpm安装了 mysql 及 mysql-server , 在编译安装php时用 –with-mysql 选项出现错误

Note that the MySQL client library is not bundled anymore.

首先使用命令find / -name ‘mysql.h’ 查找路径,如果找到就给with-mysql=赋上路径,如果找不到,那就是缺少了 mysql-devel 安装包,用yum或者rpm安装上,即可解决问题

Categories: 感悟 Tags: ,

如何去管理php的用户名和密码(二)0+

6,867 views / 2010.06.17 / 2:02 下午

(接上文:http://71j.cn/archives/173)

操练开始

在我们做出测试代码之前,首先要创建一个用户数据表。运行如下语句:

create database myapp;
use myapp;
create table users (user varchar(60), pass varchar(60));

其中user用来储存用户名,pass用来储存密码的hash值。目前,phpass生成的密码hash值最大长度为60。

创建新用户

首先,我们从phpass项目网站把PasswordHash.php下载到网站目录中,并设置能让php加载的权限(Unix系统下一般为600或者644)。然后在网站目录中创建两个文件:user-man.html (644权限), and user-man.php (权限同asswordHash.php)。

下面,把下面的内容写在user-man.html中:

<form action="user-man.php" method="POST">
用户名:<br>
<input size="60"><br>
密码:<br>
<input size="60"><br>
<input value="创建新用户">
</form>

这个文件获取用户名和密码,然后提交到user-man.php。下面是user-man.php的代码:

header('Content-Type: text/plain');
 
// 本例只是简单的输出文本的hash值,所以开头要声明下,不让浏览器当作html解析。
 
 
 
require '../PasswordHash.php';
 
// Base-2 logarithm of the iteration count used for password stretching
$hash_cost_log2 = 8;
// Do we require the hashes to be portable to older systems (less secure)?
$hash_portable = FALSE;
 
//在实际应用中,上面两行最好写在配置文件中,比如config.inc.php
 
//下面开始获取提交的用户名和密码,实际应用中需要验证有效性,不再赘述。
 
$user = $_POST['user'];
$pass = $_POST['pass'];
 
//下面开始计算密码hash值
 
$hasher = new PasswordHash($hash_cost_log2, $hash_portable);
$hash = $hasher->HashPassword($pass);
if (strlen($hash) < 20)//这里用的CRYPT_EXT_DES方法,其它加密算法得到结果会更长。
    fail('Failed to hash new password');
unset($hasher);
 
function fail($pub, $pvt = '')
{
    $msg = $pub;
    if ($pvt !== '')
        $msg .= ": $pvt";
    exit("An error occurred ($msg).\n");
}
 
//下面开始把用户信息存入到数据库中
 
$db_host = 'localhost';
$db_port = 3306;
$db_user = ‘dbuser’;
$db_pass = 'dbpass';
$db_name = 'dbname';
 
//数据库信息也最好存储在配置文件中。下面开始连接数据库,并注意弹出失败信息。
 
$db = new mysqli($db_host, $db_user, $db_pass, $db_name, $db_port);
if (mysqli_connect_errno())
    fail('MySQL connect', mysqli_connect_error());
 
//下面用预备语句插入用户信息
 
($stmt = $db->prepare('insert into users (user, pass) values (?, ?)'))
    || fail('MySQL prepare', $db->error);
$stmt->bind_param('ss', $user, $hash)
    || fail('MySQL bind_param', $db->error);
$stmt->execute()
    || fail('MySQL execute', $db->error);
 
//最后数据库连接
 
$stmt->close();
$db->close();

好了,把左右文件保存好,放在web server下测试下。输入用户名和密码,提交后,到数据库中看下:

mysql> select * from users;
+——–+————————————————————–+
| user   |pass                                                         |
+——–+————————————————————–+
| myuser | $3b$08$Lg5XF1Tr.X5TGyfb43vBBeEFZm4GTRQhKQ6SY6emkcnhAGT8KfxFS |
+——–+————————————————————–+
1 row in set (0.00 sec)

至此,用户插入成功。

用户已经存在

下面,我们用上面的方法插入一个相同的用户,同时,用相同的密码。然后查看数据库:

mysql> select * from users;
+——–+————————————————————–+
| user   |pass                                                         |
+——–+————————————————————–+
| myuser | $3b$08$Lg5XF1Tr.X5TGyfb43vBBeEFZm4GTRQhKQ6SY6emkcnhAGT8KfxFS |
| myuser | $1a$08$7lM07FwQMm5/C8G/urT4z..MudfsS227e8oUEu6T51bNWk/RGb/qe |
+——–+————————————————————–+
2 rows in set (0.00 sec)

我们得到了用户名相同的两条记录,但是密码hash值不相同,虽然我们使用了相同的密码。

为了解决这个问题,我们可以在执行插入前先执行一个select语句,查询下该用户名是否已经存在了。但是,这对程序的效率来说不是最优化的。好的做法是让为用户名建立唯一索引,禁止用户用户名的出现:

DROP TABLE users;
CREATE TABLE users (user varchar(60), pass varchar(60), UNIQUE (user));

当我们插入相同的用户名时,程序就会报错:

An error occurred (MySQL execute: Duplicate entry ‘myuser’ for key 1).

如此,系统效率会得到提高。虽然,这是纯技术性的错误提示, 我们将稍侯予以解决。

避免泄漏过多服务器细节

上面出现的报错多是mysql服务器报错,可能会泄漏一些敏感信息,如数据库名,数据库地址,甚至数据表文件的存储地址都会被显示,这是很危险的。因此,这些信息我们并不希望被显示,除非我们就是用户,或者是在调试。如此,我们可以修改fail()函数,把错误信息显示为用户可见的内容。

// 是否为debug模式,如果是,会显示敏感信息。
$debug = TRUE;
 
function fail($pub, $pvt = '')
{
    global $debug;
    $msg = $pub;
    if ($debug && $pvt !== '')
        $msg .= ": $pvt";
/* $pvt 可能会含有敏感信息,比如需要隐藏掉,或者需要编码才能被html正确显示的内容。*/
    exit("An error occurred ($msg).\n");
}

需要注意的,不管是apache还是php,默认情况下是会显示所有调试信息的。所以,作为一个程序员,我们的职责是防止这些信息被泄漏,就跟我们设置了debug模式一样,这对程序员或者服务器运维人员来说至关重要。默认情况下,要把$debug值设置为false,但我们的例子作为测试来说,将继续使用true.

如何区分mysql报错

我们需要去辨别mysql报错,以确定用户是否已经存在于数据库中,如果已经存在,需要输出一个友好的错误提示。因为当我们插入用户的时候,不只是会有一种错误,当出现其它错误的时候,我们不能傻不愣瞪的提示相同的错误(用户已经存在)吧?

一种解决方法是在出现报错后执行一个针对该用户名的select查询,如果能够返回一行数据,说明用户确实一定存在了。实现方法如下:

if (!$stmt->execute()) {
    $save_error = $db->error;
    $stmt->close();
 
// 用户已经存在了?
    ($stmt = $db->prepare('select user from users where user=?'))
        || fail('MySQL prepare', $db->error);
    $stmt->bind_param('s', $user)
        || fail('MySQL bind_param', $db->error);
    $stmt->execute()
        || fail('MySQL execute', $db->error);
    $stmt->store_result()
        || fail('MySQL store_result', $db->error);
 
    if ($stmt->num_rows === 1)
        fail('This username is already taken');
    else
        fail('MySQL execute', $save_error);
}

这个方法确实奏效,而且也很可靠。但是,我们还有更简捷的实现方法,那就是使用mysql错误码:

if (!$stmt->execute()) {
    if ($db->errno === 1062 /* ER_DUP_ENTRY */)
        fail('额滴神,该用户已经存在了');
    else
        fail('MySQL execute', $db->error);
}

在接下来的例子中,我们将使用这种简单的方法做演示。

魔法引号的处理

Magic quotes 开启后会自动转义输入的数据。其中,所有的单引号(’)、双引号(”)、反斜线、和 NULL 字符都会被转义(增加个反斜线),其实这操作本质上调用的是 addslashes 函数。

这对程序员来说固然是一个很好的事情,省却了我们过滤的麻烦。但是,当用户输入用户名和密码中含有这些字符时,我们从$_POST中获取到的内容是不是也会被addslashes了呢?

这就需要我们去做判断,示例如下:

function get_post_var($var)
{
    $val = $_POST[$var];
    if (get_magic_quotes_gpc())
        $val = stripslashes($val);
    return $val;
}

接下来,我们将用这个函数取post过来的数据,而不是单纯的$_POST数组。 

(待续)

Categories: 感悟 Tags: , , , ,