0%

居然提示不存在mysql扩展。那我就安装呗。

我通过查阅文档,说python3需要安装的包已经不是MySQLdb了,而是mysqlclient。那我们就安装这个包了。

执行下面的命令:

1
pip3 install mysqlclient

居然报错了:

1
OSError: mysql_config not found

因为我是使用Yum 安装 MySQL的方式来安装MySQL的。所有我需要安装开发包。如果你是编译安装的,就不会出现这个问题。

好了,现在安装开发包:

1
yum install mysql-devel -y

好了。安装成功了。那么就反过头来继续安装之前的mysql包:

1
pip3 install mysqlclient

好了,我们继续执行一下django,看看是否已经正常了:

1
python3 manage.py runserver 0.0.0.0:8001

显示信息如下:

1
2
3
Django version 1.10.5, using settings 'blog_python.settings'
Starting development server at http://0.0.0.0:8001/
Quit the server with CONTROL-C.

这样就表示已经正常了。我们可以继续往下开发了。

今天看到以前知乎上别人提问的关于模板和逻辑进行分离的问题。

一直都没有回答过。

今天终于回答了。我来记录以下:

首先是模板文件,我定义他为index.html,内容如下:

1
2
3
4
5
6
<h1><?=$title?></h1>
<ul>
<?php foreach($list as $value): ?>
<li><?=$value?></li>
<?php endforeach; ?>
</ul>

这里我们就不用自定义标签啊,之类的等等,我相信鸟哥的那句话,PHP本身就是一个非常好的模板引擎,我们没有必要再去造一个轮子。

所以,我们直接来写PHP的解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
/**
* 模板解析
*/
class View{
protected $path;
protected $vars;
public function __construct($path, $vars = []){
if (is_file($path)) {
$this->path = $path;
}
$this->vars = $vars;
}

public function fetch(){
ob_start();
ob_implicit_flush(0);
extract($this->vars, EXTR_OVERWRITE);
require_once $this->path;
return ob_get_clean();
}
}

$view = new View('./index.html', ['title' => 'test', 'list' => ['a', 'b', 'c']]);
echo $view->fetch();
?>

好了。直接运行一下,就能看到结果了。

不写了。太晚了。晚安。

这次作死,直接用默认配置安装了php。什么扩展都没有添加。结果一直在编译缺失的各种扩展。但是最后还是失败了,仅用做记录用。

在编译到pdo_mysql扩展的时候,就搞不定了。

在进行make操作的时候,出现了如下错误:

1
2
3
4
5
/usr/local/src/php-7.0.14/ext/pdo_mysql/php_pdo_mysql_int.h:27:34: 致命错误:ext/mysqlnd/mysqlnd.h:没有那个文件或目录
# include "ext/mysqlnd/mysqlnd.h"
^
编译中断。
make: *** [pdo_mysql.lo] 错误 1

这是走的弯路

我以为是mysqlnd没有编译,我接着去编译mysqlnd扩展了。

然后在./configure的时候,结果又提示我:

1
configure: error: Cannot find OpenSSL's <evp.h>

我以为是openssl-devel没有安装,我接着安装openssl-devel:

1
2
3
4
5
6
7
8
9
10
[root@bogon mysqlnd]# yum install openssl openssl-devel
已加载插件:fastestmirror
Loading mirror speeds from cached hostfile
* base: mirror.bit.edu.cn
* epel: mirrors.tuna.tsinghua.edu.cn
* extras: mirror.bit.edu.cn
* updates: mirror.bit.edu.cn
软件包 1:openssl-1.0.1e-60.el7.x86_64 已安装并且是最新版本
软件包 1:openssl-devel-1.0.1e-60.el7.x86_64 已安装并且是最新版本
无须任何处理

结果提示我已经安装了,那我强行指定路径吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[root@bogon mysqlnd]# ./configure --with-openssl=/usr/include/openssl
configure: WARNING: unrecognized options: --with-openssl
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for a sed that does not truncate output... /usr/bin/sed
checking for cc... cc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether cc accepts -g... yes
checking for cc option to accept ISO C89... none needed
checking how to run the C preprocessor... cc -E
checking for icc... no
checking for suncc... no
checking whether cc understands -c and -o together... yes
checking for system library directory... lib
checking if compiler supports -R... no
checking if compiler supports -Wl,-rpath,... yes
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking target system type... x86_64-unknown-linux-gnu
checking for PHP prefix... /usr/local/php
checking for PHP includes... -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib
checking for PHP extension directory... /usr/local/php/lib/php/extensions/no-debug-non-zts-20151012
checking for PHP installed headers prefix... /usr/local/php/include/php
checking if debug is enabled... no
checking if zts is enabled... no
checking for re2c... no
configure: WARNING: You will need re2c 0.13.4 or later if you want to regenerate PHP parsers.
checking for gawk... gawk
checking whether to enable mysqlnd... yes, shared
checking whether to disable compressed protocol support in mysqlnd... yes
checking for the location of libz... no
checking for DSA_get_default_method in -lssl... no
checking for X509_free in -lcrypto... yes
checking for pkg-config... /usr/bin/pkg-config
configure: error: Cannot find OpenSSL's <evp.h>

结果还是不行,这下两边路都走不通了,我就去网上查询了。结果还是查询不到什么有用的资料

转回正道

我后来想,我直接改源码试试看呢,把include加载文件写成绝对目录。

说干就干

我先看看这个报错的地方。

文件在**/usr/local/src/php-7.0.14/ext/pdo_mysql/php_pdo_mysql_int.h:27:34**

我打开该文件,修改第27行的内容:

1
2
#       include "ext/mysqlnd/mysqlnd.h"
# include "ext/mysqlnd/mysqlnd_libmysql_compat.h"

我的源代码的目录是**/usr/local/src/php-7.0.14/**,于是我就改成如下所示:

1
2
#       include "/usr/local/src/php-7.0.14/ext/mysqlnd/mysqlnd.h"
# include "/usr/local/src/php-7.0.14/ext/mysqlnd/mysqlnd_libmysql_compat.h"

我再进行编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@bogon pdo_mysql]# make clean
find . -name \*.gcno -o -name \*.gcda | xargs rm -f
find . -name \*.lo -o -name \*.o | xargs rm -f
find . -name \*.la -o -name \*.a | xargs rm -f
find . -name \*.so | xargs rm -f
find . -name .libs -a -type d|xargs rm -rf
rm -f libphp.la modules/* libs/*
[root@bogon pdo_mysql]# make
/bin/sh /usr/local/src/php-7.0.14/ext/pdo_mysql/libtool --mode=compile cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c -o pdo_mysql.lo
mkdir .libs
cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c -fPIC -DPIC -o .libs/pdo_mysql.o
In file included from /usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c:32:0:
/usr/local/src/php-7.0.14/ext/pdo_mysql/php_pdo_mysql_int.h:69:39: 致命错误:ext/mysqlnd/mysqlnd_debug.h:没有那个文件或目录
#include "ext/mysqlnd/mysqlnd_debug.h"
^
编译中断。
make: *** [pdo_mysql.lo] 错误 1

发现有效果,报错的地方变了,我接着改,这里提示报错的地方还是:**/usr/local/src/php-7.0.14/ext/pdo_mysql/php_pdo_mysql_int.h:69:39**,但是行数编程69了,我把69行改成如下所示:

1
#include "/usr/local/src/php-7.0.14/ext/mysqlnd/mysqlnd_debug.h"

继续,发现报错信息又变了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@bogon pdo_mysql]# make clean
find . -name \*.gcno -o -name \*.gcda | xargs rm -f
find . -name \*.lo -o -name \*.o | xargs rm -f
find . -name \*.la -o -name \*.a | xargs rm -f
find . -name \*.so | xargs rm -f
find . -name .libs -a -type d|xargs rm -rf
rm -f libphp.la modules/* libs/*
[root@bogon pdo_mysql]# make
/bin/sh /usr/local/src/php-7.0.14/ext/pdo_mysql/libtool --mode=compile cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c -o pdo_mysql.lo
mkdir .libs
cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c -fPIC -DPIC -o .libs/pdo_mysql.o
/usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c:64:45: 致命错误:ext/mysqlnd/mysqlnd_reverse_api.h:没有那个文件或目录
#include "ext/mysqlnd/mysqlnd_reverse_api.h"
^
编译中断。
make: *** [pdo_mysql.lo] 错误 1

我将文件**/usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c:64:45**改成如下所示:

1
#include "/usr/local/src/php-7.0.14/ext/mysqlnd/mysqlnd_reverse_api.h"

继续编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[root@bogon pdo_mysql]# make clean
find . -name \*.gcno -o -name \*.gcda | xargs rm -f
find . -name \*.lo -o -name \*.o | xargs rm -f
find . -name \*.la -o -name \*.a | xargs rm -f
find . -name \*.so | xargs rm -f
find . -name .libs -a -type d|xargs rm -rf
rm -f libphp.la modules/* libs/*
[root@bogon pdo_mysql]# make
/bin/sh /usr/local/src/php-7.0.14/ext/pdo_mysql/libtool --mode=compile cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c -o pdo_mysql.lo
mkdir .libs
cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/pdo_mysql.c -fPIC -DPIC -o .libs/pdo_mysql.o
/bin/sh /usr/local/src/php-7.0.14/ext/pdo_mysql/libtool --mode=compile cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/mysql_driver.c -o mysql_driver.lo
cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/mysql_driver.c -fPIC -DPIC -o .libs/mysql_driver.o
/bin/sh /usr/local/src/php-7.0.14/ext/pdo_mysql/libtool --mode=compile cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/mysql_statement.c -o mysql_statement.lo
cc -I/usr/local/php/include/php/ext -I -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -I. -I/usr/local/src/php-7.0.14/ext/pdo_mysql -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -c /usr/local/src/php-7.0.14/ext/pdo_mysql/mysql_statement.c -fPIC -DPIC -o .libs/mysql_statement.o
/bin/sh /usr/local/src/php-7.0.14/ext/pdo_mysql/libtool --mode=link cc -DPHP_ATOM_INC -I/usr/local/src/php-7.0.14/ext/pdo_mysql/include -I/usr/local/src/php-7.0.14/ext/pdo_mysql/main -I/usr/local/src/php-7.0.14/ext/pdo_mysql -I/usr/local/php/include/php -I/usr/local/php/include/php/main -I/usr/local/php/include/php/TSRM -I/usr/local/php/include/php/Zend -I/usr/local/php/include/php/ext -I/usr/local/php/include/php/ext/date/lib -DHAVE_CONFIG_H -g -O2 -o pdo_mysql.la -export-dynamic -avoid-version -prefer-pic -module -rpath /usr/local/src/php-7.0.14/ext/pdo_mysql/modules pdo_mysql.lo mysql_driver.lo mysql_statement.lo
cc -shared .libs/pdo_mysql.o .libs/mysql_driver.o .libs/mysql_statement.o -Wl,-soname -Wl,pdo_mysql.so -o .libs/pdo_mysql.so
creating pdo_mysql.la
(cd .libs && rm -f pdo_mysql.la && ln -s ../pdo_mysql.la pdo_mysql.la)
/bin/sh /usr/local/src/php-7.0.14/ext/pdo_mysql/libtool --mode=install cp ./pdo_mysql.la /usr/local/src/php-7.0.14/ext/pdo_mysql/modules
cp ./.libs/pdo_mysql.so /usr/local/src/php-7.0.14/ext/pdo_mysql/modules/pdo_mysql.so
cp ./.libs/pdo_mysql.lai /usr/local/src/php-7.0.14/ext/pdo_mysql/modules/pdo_mysql.la
PATH="$PATH:/sbin" ldconfig -n /usr/local/src/php-7.0.14/ext/pdo_mysql/modules
----------------------------------------------------------------------
Libraries have been installed in:
/usr/local/src/php-7.0.14/ext/pdo_mysql/modules

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the `LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the `LD_RUN_PATH' environment variable
during linking
- use the `-Wl,--rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to `/etc/ld.so.conf'

See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------

Build complete.
Don't forget to run 'make test'.

这样就成功了。接下来我们就可以继续make install进行安装了。

1
2
[root@bogon pdo_mysql]# make install
Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-20151012/

我们修改一下配置文件,添加下面的内容到配置文件中:

1
extension=pdo_mysql.so

然后我们测试扩展是否安装成功了:

1
2
3
4
5
6
7
[root@bogon pdo_mysql]# php -m
PHP Warning: Module 'PDO' already loaded in Unknown on line 0

Warning: Module 'PDO' already loaded in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/usr/local/php/lib/php/extensions/no-debug-non-zts-20151012/pdo_mysql.so' - /usr/local/php/lib/php/extensions/no-debug-non-zts-20151012/pdo_mysql.so: undefined symbol: mysqlnd_allocator in Unknown on line 0

Warning: PHP Startup: Unable to load dynamic library '/usr/local/php/lib/php/extensions/no-debug-non-zts-20151012/pdo_mysql.so' - /usr/local/php/lib/php/extensions/no-debug-non-zts-20151012/pdo_mysql.so: undefined symbol: mysqlnd_allocator in Unknown on line 0

报错了。看来我们还需要mysqlnd扩展:

然后还是提示找不到openssl的错误。

我接着去网上搜了一下。

还是找不到答案,我查了一下,这个好像是一个bug,一直没有修复。

解决办法只能是重装php来解决了。

nginx管理命令:

我不是生产者,我只是大自然的搬运工。
以下脚本来自LNMP一键安装包中军哥的一键安装脚本。感谢军哥的辛勤劳动。

首先我们先用vim或者vi打开/etc/init.d/nginx;

1
vim /etc/init.d/nginx

然后按i进入编辑模式,将以下内容复制到该文件里面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#! /bin/sh
# chkconfig: 2345 55 25
# Description: Startup script for nginx webserver on Debian. Place in /etc/init.d and
# run 'update-rc.d -f nginx defaults', or use the appropriate command on your
# distro. For CentOS/RedHat run: 'chkconfig --add nginx'

### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the nginx web server
# Description: starts nginx using start-stop-daemon
### END INIT INFO

# Author: licess
# website: http://lnmp.org

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
NAME=nginx
NGINX_BIN=/usr/local/nginx/sbin/$NAME
CONFIGFILE=/usr/local/nginx/conf/$NAME.conf
PIDFILE=/usr/local/nginx/logs/$NAME.pid

case "$1" in
start)
echo -n "Starting $NAME... "

if netstat -tnpl | grep -q nginx;then
echo "$NAME (pid `pidof $NAME`) already running."
exit 1
fi

$NGINX_BIN -c $CONFIGFILE

if [ "$?" != 0 ] ; then
echo " failed"
exit 1
else
echo " done"
fi
;;

stop)
echo -n "Stoping $NAME... "

if ! netstat -tnpl | grep -q nginx; then
echo "$NAME is not running."
exit 1
fi

$NGINX_BIN -s stop

if [ "$?" != 0 ] ; then
echo " failed. Use force-quit"
exit 1
else
echo " done"
fi
;;

status)
if netstat -tnpl | grep -q nginx; then
PID=`pidof nginx`
echo "$NAME (pid $PID) is running..."
else
echo "$NAME is stopped"
exit 0
fi
;;

force-quit)
echo -n "Terminating $NAME... "

if ! netstat -tnpl | grep -q nginx; then
echo "$NAME is not running."
exit 1
fi

kill `pidof $NAME`

if [ "$?" != 0 ] ; then
echo " failed"
exit 1
else
echo " done"
fi
;;

restart)
$0 stop
sleep 1
$0 start
;;

reload)
echo -n "Reload service $NAME... "

if netstat -tnpl | grep -q nginx; then
$NGINX_BIN -s reload
echo " done"
else
echo "$NAME is not running, can't reload."
exit 1
fi
;;

configtest)
echo -n "Test $NAME configure files... "

$NGINX_BIN -t
;;

*)
echo "Usage: $0 {start|stop|force-quit|restart|reload|status|configtest}"
exit 1
;;

esac

然后保存退出。接着执行赋予执行权限命令:

1
chmod +x /etc/init.d/nginx

这样我们就可以执行管理命令了。比如:

1
2
service nginx start
service nginx stop

具体我们可以执行service nginx查看。

我们还可以设置开机启动:

1
chkconfig nginx on

双向链表其实就是数据本身具备了左边和右边的双向指针。类似Redis的列表,它就是双向链表。

我们现在就学习一下SPL内置的SplDoublyLinkedList类。

首先介绍一下这个类都有哪些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 实例化方法
$dll = new SplDoublyLinkedList();

//尾部增加数据,类似array_push()方法
$dll->push($value);
//开头插入数据,类似array_unshift()方法
$dll->unshift($value);

//检测指定位置是否存在值,类似array_key_exists()方法
$dll->offsetExists($key);
//获取指定位置的值,类似$dll[$key]
$dll->offsetGet($key);
//修改指定位置的值,类似$dll[$key] = $value
$dll->offsetSet($key, $value);
//删除指定位置的值,类似unset($dll[$key])
$dll->offsetUnset($key);

//获取双向链表中值的数量
$dll->count();
//检测双向链表是否为空
$dll->isEmpty();

//序列化存储,类似serialize()
$dll->serialize();
//存储反序列化类似unserialize()
$dll->unserialize($str);

//设置迭代器模式
$dll->setIteratorMode($mode);
//获取迭代器模式
$dll->getIteratorMode();

//初始化链表,只有先执行此方法,才能使用操作游标的方法
$dll->rewind();
//获取当前游标的索引
$dll->key();
//获取当前游标的值
$dll->current();
//将游标移动到下一个节点
$dll->next();
//将游标移动到上一个节点
$dll->prev();
//检测当前游标后面是否还有节点
$dll->valid();

//返回第一个节点的值
$dll->bottom();
//返回最后一个节点的值
$dll->top();

//将双向链表中的最后一个节点弹出,类似array_pop()
$dll->pop();
//将双向链表中的第一个节点弹出,类似array_shift()
$dll->shift();

//在双向链表的指定位置插入值
$dll->add($index, $value);

Iterator 模式

  • 迭代的方向:
    • SplDoublyLinkedList::IT_MODE_LIFO (堆栈 style)
    • SplDoublyLinkedList::IT_MODE_FIFO (队列 style)
  • 迭代器的行为
    • SplDoublyLinkedList::IT_MODE_DELETE (元素由迭代器删除)
    • SplDoublyLinkedList::IT_MODE_KEEP (元素由迭代器遍历)

默认模式是:SplDoublyLinkedList::IT_MODE_FIFO | SplDoublyLinkedList::IT_MODE_KEEP

前置

使用 Composer 需要先安装 PHP。

可以参考如下几篇进行安装:

项目内

其实我们可以在项目根目录执行下列命令:

1
2
wget https://getcomposer.org/download/latest-stable/composer.phar
php composer.phar

全局手动安装

将文件下载到 /usr/local/bin 并修改名称为 composer

1
sudo wget -O /usr/local/bin/composer https://getcomposer.org/download/latest-stable/composer.phar

添加执行权限:

1
sudo chmod +x /usr/local/bin/composer

就可以直接通过如下方式执行了:

1
2
# composer -V
Composer version 2.4.0 2022-08-16 16:10:48

系统包管理安装

我个人不是很推荐,具体原因还是因为我使用 Ubuntu 的时候,会依赖一些 PHP 扩展,但是如果是使用第三方库安装的 PHP,那么安装 Composer 的扩展却不是自己能够控制的,以为很可能会依赖系统自带的 PHP 版本。

下面就列一下命令:

CentOS/Rocky/RedHat:

1
sudo dnf install composer

Debian/Ubuntu:

1
sudo apt install composer

升级

1
sudo composer self-update

此内容仅手动安装的可以使用,因为包管理没有该命令。

修改镜像

全局设置腾讯云镜像:

1
composer config -g repos.packagist composer https://mirrors.tencent.com/composer/

全局设置阿里云镜像:

1
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

全局取消镜像:

1
composer config -g --unset repos.packagist

项目内设置腾讯云镜像:

1
composer config repos.packagist composer https://mirrors.tencent.com/composer/

项目内设置阿里云镜像:

1
composer config repo.packagist composer https://mirrors.aliyun.com/composer/

项目内取消镜像:

1
composer config -g --unset repos.packagist

碰到的问题

  • phar "/usr/local/bin/composer" has a broken signature 错误:
1
2
3
4
5
PHP Fatal error:  Uncaught exception 'PharException' with message 'phar "/usr/local/bin/composer" has a broken signature' in /usr/local/bin/composer:23
Stack trace:
#0 /usr/local/bin/composer(23): Phar::mapPhar('composer.phar')
#1 {main}
thrown in /usr/local/bin/composer on line 23

解决办法:删除文件包,重新安装。

  • PHP 缺失 openssl 扩展:
1
2
3
4
5
Some settings on your machine make Composer unable to work properly.
Make sure that you fix the issues listed below and run this script again:

The openssl extension is missing, which means that secure HTTPS transfers are impossible.
If possible you should enable it or recompile php with --with-openssl

解决办法: PHP 安装 openssl 扩展。

  • PHP 缺失 zlib 扩展:
1
2
3
4
5
The zlib extension is not loaded, this can slow down Composer a lot.
If possible, install it or recompile php with --with-zlib

The php.ini used by your command-line PHP is: /usr/local/php/etc/php.ini
If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.

解决办法: PHP 安装 zlib 扩展。

安装完成composer之后。我们需要安装php的类库来对Elasticsearch进行连接。

我们现在需要使用的是elasticsearch/elasticsearch

比如我们的项目目录在 /data/project/elastic

我们就可以在这个目录下执行以下命令:

1
composer require elasticsearch/elasticsearch

如果出现如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
Using version ^5.0 for elasticsearch/elasticsearch
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 5 installs, 0 updates, 0 removals
- Installing react/promise (v2.5.0) Downloading: 100%
- Installing guzzlehttp/streams (3.0.0) Downloading: 100%
- Installing guzzlehttp/ringphp (1.1.0) Downloading: 100%
- Installing psr/log (1.0.2) Downloading: 100%
- Installing elasticsearch/elasticsearch (v5.0.0) Downloading: 100%
elasticsearch/elasticsearch suggests installing monolog/monolog (Allows for client-level logging and tracing)
Writing lock file
Generating autoload files

就表示已经安装成功了。

接下来我们编辑如下文件。

1
2
3
4
5
6
7
8
9
<?php
require_once './vendor/autoload.php';

$client = Elasticsearch\ClientBuilder::create();
$client->setHosts(['127.0.0.1']);
$client = $client->build();
$info = $client->info();
var_dump($info);
?>

然后保存为index.php。我们可以通过访问该文件来确认是否请求成功了。

然后我们执行一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@68 elastic]# php index.php 
array(5) {
["name"]=>
string(7) "ZQZ38Da"
["cluster_name"]=>
string(13) "elasticsearch"
["cluster_uuid"]=>
string(22) "FuPCyv2ZSimGYxAo_nLvpw"
["version"]=>
array(5) {
["number"]=>
string(5) "5.1.1"
["build_hash"]=>
string(7) "5395e21"
["build_date"]=>
string(24) "2016-12-06T12:36:15.409Z"
["build_snapshot"]=>
bool(false)
["lucene_version"]=>
string(5) "6.3.0"
}
["tagline"]=>
string(20) "You Know, for Search"
}

这样就表示我们的驱动已经安装并且连接完成了。

具体的文档我们可点击:https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html

今天朋友问我,js应该如何将时间字符串转为时间戳。

我下意识就去百度,结果找到的,写法却是复杂无比,比如下面这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
(function($) {
$.extend({
myTime: {
/**
* 当前时间戳
* @return <int> unix时间戳(秒)
*/
CurTime: function(){
return Date.parse(new Date())/1000;
},
/**
* 日期 转换为 UNIX时间戳
* @param <string> 2014-01-01 20:20:20 日期格式
* @return <int> unix时间戳(秒)
*/
DateToUnix: function(string) {
var f = string.split(' ', 2);
var d = (f[0] ? f[0] : '').split('-', 3);
var t = (f[1] ? f[1] : '').split(':', 3);
return (new Date(
parseInt(d[0], 10) || null,
(parseInt(d[1], 10) || 1) - 1,
parseInt(d[2], 10) || null,
parseInt(t[0], 10) || null,
parseInt(t[1], 10) || null,
parseInt(t[2], 10) || null
)).getTime() / 1000;
},
/**
* 时间戳转换日期
* @param <int> unixTime 待时间戳(秒)
* @param <bool> isFull 返回完整时间(Y-m-d 或者 Y-m-d H:i:s)
* @param <int> timeZone 时区
*/
UnixToDate: function(unixTime, isFull, timeZone) {
if (typeof (timeZone) == 'number')
{
unixTime = parseInt(unixTime) + parseInt(timeZone) * 60 * 60;
}
var time = new Date(unixTime * 1000);
var ymdhis = "";
ymdhis += time.getUTCFullYear() + "-";
ymdhis += (time.getUTCMonth()+1) + "-";
ymdhis += time.getUTCDate();
if (isFull === true)
{
ymdhis += " " + time.getUTCHours() + ":";
ymdhis += time.getUTCMinutes() + ":";
ymdhis += time.getUTCSeconds();
}
return ymdhis;
}
}
});
})(jQuery);

看起来就非常的麻烦。于是我想到去官方文档去了一下,还真让我找到了。于是写了这么一个demo,这里我们并不需要将时间戳转化为字符串。

1
2
3
4
5
6
7
8
9
//时间字符串转化为时间戳
function str2time(data){
if (data.length == 10) {
data += ' 00:00:00';
}

var date = new Date(data);
return (date.getTime() / 1000);
}

这么一个简单的工作就完成了。

在这里要注意的一个坑就是,注意服务器的时区是否跟浏览器的时区相同,如果不同,那么显示的时间跟服务器就会有几个小时的误差。具体的误差就看差着几个时区了。

我这里对于这个也是抛砖引玉。

具体复杂的,可以看看我列出的参考连接。就是在new Date注意看看传入的值就好了。

参考文档:

我平常上传数据都是使用$(this).serialize(),但是这次我需要使用上传数据的对象。结果这个方法给我转成了字符串。特别不方便。于是找到一个插件。就可以实现,获取的时候,获取到的是对象而不是字符串了。

首先,我们先加载jquery;

1
<script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>

然后在后面写一个js方法就好了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script type="text/javascript">
$.fn.serializeObject = function() {
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name] !== undefined) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
</script>

这样我们直接就可以获取对象了:

1
$(form).serializeObject();

这样我们获取到的数据直接就是对象了。

参考资料:

我使用sublime,想要使用插件,还是得使用它的商店。

商店默认是不会安装的,于是我就是网上搜索,尝试安装一下。

我用的是这个:

1
import urllib.request,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

结果我执行之后,发现提示这个错误:

1
Error validating download (got df21e130d211cfc94d9b0905775a7c0f1e3d39e33b79698005270310898eea76 instead of 2915d1851351e5ee549c20394736b4428bc59f460fa1548d1514676163dafc88), please try manual install

顿时觉得不行了。我觉得我还是得找谷歌,于是我去外国网站,找了一个,嘿嘿,还真安装上了。现提供命令如下:

sublime3:

1
import urllib.request,os,hashlib; h = 'df21e130d211cfc94d9b0905775a7c0f' + '1e3d39e33b79698005270310898eea76'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

sublime2:

1
import urllib2,os,hashlib; h = 'df21e130d211cfc94d9b0905775a7c0f' + '1e3d39e33b79698005270310898eea76'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler()) ); by = urllib2.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); open( os.path.join( ipp, pf), 'wb' ).write(by) if dh == h else None; print('Error validating download (got %s instead of %s), please try manual install' % (dh, h) if dh != h else 'Please restart Sublime Text to finish installation')

完毕,收工。

参考网址:

当我们安装了mysql或者mariadb的时候,一不小心,就把密码给忘记了。这个时候,我们不管怎么尝试密码,都是错误的,都会显示如下错误:

1
2
3
[root@iZ28zkjw87oZ ~]# mysql -uroot -p
Enter password:
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

这个问题特别的头疼。
但是如果我们用如下方法就可以迎刃而解了。
首先我们先修改配置文件:

1
vim /etc/my.cnf

然后在mysqld节中增加下面一行代码:

1
2
[mysqld]
skip-grant-tables # 这个是要加入的内容

然后重启mysql/mariadb之后,就可以不用密码而直接进去了。
这个时候我们就可以执行命令:

1
UPDATE mysql.user SET Password=PASSWORD(&#039;your password&#039;) WHERE User=&#039;root&#039;;

这样就把密码给修改了。修改之后。然后我们将my.cnf中的这行注释掉:

1
2
[mysqld]
#skip-grant-tables

然后重启就可以了。
这样就实现了我们mysql/mariadb的重置密码功能了。

关于副本集的理论我就不介绍了。下面说下如何实现副本集的操作。

前提

  1. 首先就是要安装MongoDB服务器了。我建议按照我之前写的这篇文章进行安装。
  2. 准备三台以上的服务器。然后同时安装MongoDB。我们三台服务器的IP分别是(10.55.160.91,10.55.160.92,10.55.160.93)

部署一个副本集

  1. 修改配置文件。
    首先我们打开配置文件。

    1
    vim /etc/mongod.conf

    找到bindIp: 127.0.0.1这行,我们需要对这行进行注释。变成如下这样:

    1
    #bindIp: 127.0.0.1  # Listen to local interface only, comment to listen on all interfaces.

    因为这个bindIp代表只允许本机进行连接,然后不允许其他服务器进行连接,那么,下面就没有办法继续执行了。

    找到#replication:这行我们将其注释去掉,然后下面增加一行,变成下面这样:

    1
    2
    replication:
    replSetName: test

    replSetName表示副本集组的名字(test是我自己写的,你们也可以写你们自己的喜欢的名字),比如,我们要将三台服务器进行关联,那么我们就要将三台服务器都设置为相同的名字。

    然后如果三台服务器的MongoDB都已经启动了,那么就执行以下命令:

    1
    service mongod restart

    如果没有启动就执行以下命令:

    1
    service mongod start
  2. 链接到任意一台服务器。
    我选择的是链接到10.55.160.91。也就是我现在正在操作的这一台服务器。然后登录:

    1
    mongo
  3. 初始化副本集。
    初始化副本集,我们需要使用rs.initiate()方法。
    我现在需要添加的是三台服务器,我们可以执行以下方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    rs.initiate({
    _id: "test",
    version: 1,
    members: [
    { _id: 0, host : "10.55.160.91:27017" },
    { _id: 1, host : "10.55.160.92:27017" },
    { _id: 2, host : "10.55.160.93:27017" }
    ]
    })

    当返回值显示为如下的时候,就代表成功了:

    1
    { "ok" : 1 }
  4. 这样就完成了,是不是很简单。哈哈。

然后你就发现你的输入命令的标题就变了。变成如下这样了:

1
test:OTHER>

不管它,我们继续执行操作,执行rs.status(),查看当前状态。

1
rs.status()

查看返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
{
"set" : "test",
"date" : ISODate("2016-09-22T08:08:36.607Z"),
"myState" : 1,
"term" : NumberLong(1),
"heartbeatIntervalMillis" : NumberLong(2000),
"members" : [
{
"_id" : 0,
"name" : "10.55.160.91:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 154,
"optime" : {
"ts" : Timestamp(1474531710, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2016-09-22T08:08:30Z"),
"infoMessage" : "could not find member to sync from",
"electionTime" : Timestamp(1474531709, 1),
"electionDate" : ISODate("2016-09-22T08:08:29Z"),
"configVersion" : 1,
"self" : true
},
{
"_id" : 1,
"name" : "10.55.160.92:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 17,
"optime" : {
"ts" : Timestamp(1474531710, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2016-09-22T08:08:30Z"),
"lastHeartbeat" : ISODate("2016-09-22T08:08:35.966Z"),
"lastHeartbeatRecv" : ISODate("2016-09-22T08:08:32.078Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.91:27017",
"configVersion" : 1
},
{
"_id" : 2,
"name" : "10.55.160.93:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 17,
"optime" : {
"ts" : Timestamp(1474531710, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2016-09-22T08:08:30Z"),
"lastHeartbeat" : ISODate("2016-09-22T08:08:35.966Z"),
"lastHeartbeatRecv" : ISODate("2016-09-22T08:08:32.078Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.91:27017",
"configVersion" : 1
}
],
"ok" : 1
}

我们主要是看members字段里面的stateStr的值。

很幸运,我当前执行命令的服务器被推举为主服务器。

如果你们现在服务器显示当前的服务器的状态是:SECONDARY。表示该服务器是从服务器,我们需要查看哪台服务器为主服务器,也就是"stateStr" : "PRIMARY"。我们需要在主服务器才进行进行操作,当然,也有强行在从服务器进行操作的办法。这里先不说。

这样我们就可以在主服务器进行操作了。比如存储数据。

添加成员

比如现在又有了一台服务器(10.55.160.94)需要加入到这个副本集中。我们可以使用rs.add()进行添加服务器。

首先我们还是别忘了修改配置文件的步骤。。。。。。修改完成配置文件之后重启Mongod,然后在主服务器执行如下命令:

1
rs.add("10.55.160.94:27017")

在执行该命令之后,可能会出现主服务器的选择,我们要注意主服务器是否已经变成了别的服务器。我们可以使用rs.status()查看新的主服务器是什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
{
"set" : "test",
"date" : ISODate("2016-09-22T09:28:53.913Z"),
"myState" : 1,
"term" : NumberLong(6),
"heartbeatIntervalMillis" : NumberLong(2000),
"members" : [
{
"_id" : 0,
"name" : "10.55.160.91:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 1202,
"optime" : {
"ts" : Timestamp(1474536460, 1),
"t" : NumberLong(6)
},
"optimeDate" : ISODate("2016-09-22T09:27:40Z"),
"lastHeartbeat" : ISODate("2016-09-22T09:28:52.089Z"),
"lastHeartbeatRecv" : ISODate("2016-09-22T09:28:52.095Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.93:27017",
"configVersion" : 6
},
{
"_id" : 1,
"name" : "10.55.160.92:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 1202,
"optime" : {
"ts" : Timestamp(1474536460, 1),
"t" : NumberLong(6)
},
"optimeDate" : ISODate("2016-09-22T09:27:40Z"),
"lastHeartbeat" : ISODate("2016-09-22T09:28:52.089Z"),
"lastHeartbeatRecv" : ISODate("2016-09-22T09:28:52.095Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.93:27017",
"configVersion" : 6
},
{
"_id" : 2,
"name" : "10.55.160.93:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1207,
"optime" : {
"ts" : Timestamp(1474536460, 1),
"t" : NumberLong(6)
},
"optimeDate" : ISODate("2016-09-22T09:27:40Z"),
"electionTime" : Timestamp(1474536383, 1),
"electionDate" : ISODate("2016-09-22T09:26:23Z"),
"configVersion" : 6,
"self" : true
},
{
"_id" : 3,
"name" : "10.55.160.94:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 71,
"optime" : {
"ts" : Timestamp(1474536460, 1),
"t" : NumberLong(6)
},
"optimeDate" : ISODate("2016-09-22T09:27:40Z"),
"lastHeartbeat" : ISODate("2016-09-22T09:28:52.089Z"),
"lastHeartbeatRecv" : ISODate("2016-09-22T09:28:52.059Z"),
"pingMs" : NumberLong(0),
"configVersion" : 6
}
],
"ok" : 1
}

修改服务器的优先级

比如我们新添加的服务器(10.55.160.94),这个服务器的配置较高。我希望优先让它成为主服务器。我们可以通过如下方式进行修改:

首先我们可以通过rs.conf来查看是第几个游标。

1
rs.conf()

返回值如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{
"_id" : "test",
"version" : 6,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 0,
"host" : "10.55.160.91:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {

},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "10.55.160.92:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {

},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "10.55.160.93:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {

},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 3,
"host" : "10.55.160.94:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1,
"tags" : {

},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"getLastErrorModes" : {

},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
},
"replicaSetId" : ObjectId("57e391736b7581c82cc75a83")
}
}

我们可以看到它的游标是第3个(从0开始计算)。
我们可以这样进行修改:

1
2
3
cfg = rs.conf()
cfg.members[3].priority = 10
rs.reconfig(cfg)

过了一会,我们就看到主服务器变成(10.55.160.94)了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
{
"set" : "test",
"date" : ISODate("2016-09-22T10:18:48.348Z"),
"myState" : 2,
"term" : NumberLong(7),
"syncingTo" : "10.55.160.94:27017",
"heartbeatIntervalMillis" : NumberLong(2000),
"members" : [
{
"_id" : 0,
"name" : "10.55.160.91:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 4197,
"optime" : {
"ts" : Timestamp(1474539241, 2),
"t" : NumberLong(7)
},
"optimeDate" : ISODate("2016-09-22T10:14:01Z"),
"lastHeartbeat" : ISODate("2016-09-22T10:18:46.676Z"),
"lastHeartbeatRecv" : ISODate("2016-09-22T10:18:46.535Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.94:27017",
"configVersion" : 7
},
{
"_id" : 1,
"name" : "10.55.160.92:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 4197,
"optime" : {
"ts" : Timestamp(1474539241, 2),
"t" : NumberLong(7)
},
"optimeDate" : ISODate("2016-09-22T10:14:01Z"),
"lastHeartbeat" : ISODate("2016-09-22T10:18:46.676Z"),
"lastHeartbeatRecv" : ISODate("2016-09-22T10:18:46.674Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.94:27017",
"configVersion" : 7
},
{
"_id" : 2,
"name" : "10.55.160.93:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 4202,
"optime" : {
"ts" : Timestamp(1474539241, 2),
"t" : NumberLong(7)
},
"optimeDate" : ISODate("2016-09-22T10:14:01Z"),
"syncingTo" : "10.55.160.94:27017",
"configVersion" : 7,
"self" : true
},
{
"_id" : 3,
"name" : "10.55.160.94:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 3066,
"optime" : {
"ts" : Timestamp(1474539241, 2),
"t" : NumberLong(7)
},
"optimeDate" : ISODate("2016-09-22T10:14:01Z"),
"lastHeartbeat" : ISODate("2016-09-22T10:18:46.676Z"),
"lastHeartbeatRecv" : ISODate("2016-09-22T10:18:47.546Z"),
"pingMs" : NumberLong(0),
"electionTime" : Timestamp(1474539241, 1),
"electionDate" : ISODate("2016-09-22T10:14:01Z"),
"configVersion" : 7
}
],
"ok" : 1
}

添加仲裁者服务器

仲裁者服务器只参与投票,而不是进行数据的存储。别忘了定义副本集名称。

我们可以使用rs.addArb()来进行添加一台新的服务器(10.55.160.95)为仲裁服务器。

命令如下:

1
rs.addArb('10.55.160.95')

返回{ "ok" : 1 }就正常执行了。这时我们通过rs.status()来看一下服务器的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
{
"set" : "test",
"date" : ISODate("2016-09-23T02:54:44.854Z"),
"myState" : 2,
"term" : NumberLong(9),
"syncingTo" : "10.55.160.94:27017",
"heartbeatIntervalMillis" : NumberLong(2000),
"members" : [
{
"_id" : 0,
"name" : "10.55.160.91:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 141,
"optime" : {
"ts" : Timestamp(1474599221, 1),
"t" : NumberLong(9)
},
"optimeDate" : ISODate("2016-09-23T02:53:41Z"),
"syncingTo" : "10.55.160.94:27017",
"configVersion" : 8,
"self" : true
},
{
"_id" : 1,
"name" : "10.55.160.92:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 95,
"optime" : {
"ts" : Timestamp(1474599221, 1),
"t" : NumberLong(9)
},
"optimeDate" : ISODate("2016-09-23T02:53:41Z"),
"lastHeartbeat" : ISODate("2016-09-23T02:54:43.113Z"),
"lastHeartbeatRecv" : ISODate("2016-09-23T02:54:44.091Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.94:27017",
"configVersion" : 8
},
{
"_id" : 2,
"name" : "10.55.160.93:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 86,
"optime" : {
"ts" : Timestamp(1474599221, 1),
"t" : NumberLong(9)
},
"optimeDate" : ISODate("2016-09-23T02:53:41Z"),
"lastHeartbeat" : ISODate("2016-09-23T02:54:43.113Z"),
"lastHeartbeatRecv" : ISODate("2016-09-23T02:54:43.121Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.94:27017",
"configVersion" : 8
},
{
"_id" : 3,
"name" : "10.55.160.94:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 83,
"optime" : {
"ts" : Timestamp(1474599221, 1),
"t" : NumberLong(9)
},
"optimeDate" : ISODate("2016-09-23T02:53:41Z"),
"lastHeartbeat" : ISODate("2016-09-23T02:54:43.113Z"),
"lastHeartbeatRecv" : ISODate("2016-09-23T02:54:43.134Z"),
"pingMs" : NumberLong(0),
"electionTime" : Timestamp(1474599220, 1),
"electionDate" : ISODate("2016-09-23T02:53:40Z"),
"configVersion" : 8
},
{
"_id" : 4,
"name" : "10.55.160.95:27017",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 79,
"lastHeartbeat" : ISODate("2016-09-23T02:54:43.113Z"),
"lastHeartbeatRecv" : ISODate("2016-09-23T02:54:42.775Z"),
"pingMs" : NumberLong(0),
"configVersion" : 8
}
],
"ok" : 1
}

我们可以看到IP为10.55.160.95这台服务器,它的stateStr的值是ARBITER。这个就代表是仲裁者的角色。

移除成员

移除成员我们使用rs.remove()方法就可以了。

比如我们要移除10.55.160.93。我们可以使用如下命令:

1
rs.remove('10.55.160.93:27017')

我们可以使用rs.status()查看一下返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
{
"set" : "test",
"date" : ISODate("2016-09-23T03:15:21Z"),
"myState" : 2,
"term" : NumberLong(9),
"syncingTo" : "10.55.160.94:27017",
"heartbeatIntervalMillis" : NumberLong(2000),
"members" : [
{
"_id" : 0,
"name" : "10.55.160.91:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 1377,
"optime" : {
"ts" : Timestamp(1474600512, 1),
"t" : NumberLong(9)
},
"optimeDate" : ISODate("2016-09-23T03:15:12Z"),
"syncingTo" : "10.55.160.94:27017",
"configVersion" : 9,
"self" : true
},
{
"_id" : 1,
"name" : "10.55.160.92:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 1331,
"optime" : {
"ts" : Timestamp(1474600512, 1),
"t" : NumberLong(9)
},
"optimeDate" : ISODate("2016-09-23T03:15:12Z"),
"lastHeartbeat" : ISODate("2016-09-23T03:15:20.593Z"),
"lastHeartbeatRecv" : ISODate("2016-09-23T03:15:20.592Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "10.55.160.94:27017",
"configVersion" : 9
},
{
"_id" : 3,
"name" : "10.55.160.94:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1319,
"optime" : {
"ts" : Timestamp(1474600512, 1),
"t" : NumberLong(9)
},
"optimeDate" : ISODate("2016-09-23T03:15:12Z"),
"lastHeartbeat" : ISODate("2016-09-23T03:15:20.595Z"),
"lastHeartbeatRecv" : ISODate("2016-09-23T03:15:20.582Z"),
"pingMs" : NumberLong(0),
"electionTime" : Timestamp(1474599220, 1),
"electionDate" : ISODate("2016-09-23T02:53:40Z"),
"configVersion" : 9
},
{
"_id" : 4,
"name" : "10.55.160.95:27017",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 1315,
"lastHeartbeat" : ISODate("2016-09-23T03:15:20.596Z"),
"lastHeartbeatRecv" : ISODate("2016-09-23T03:15:17.585Z"),
"pingMs" : NumberLong(0),
"configVersion" : 9
}
],
"ok" : 1
}

发现10.55.160.93这个服务器已经从副本集中消失了。

更换副本集成员

我们可以使用如下办法进行快速更换成员:

1
2
3
cfg = rs.conf()
cfg.members[0].host = "10.55.160.93"
rs.reconfig(cfg)

我们看之前被替换走的成员,它的状态已经变成这样了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"state" : 10,
"stateStr" : "REMOVED",
"uptime" : 3162,
"optime" : {
"ts" : Timestamp(1474602195, 1),
"t" : NumberLong(9)
},
"optimeDate" : ISODate("2016-09-23T03:43:15Z"),
"ok" : 0,
"errmsg" : "Our replica set config is invalid or we are not a member of it",
"code" : 93
}

最近在CentOS(7.2)上安装了Python3用来学习,在进入交互模式之后,发现无法使用后退键。这特别的不方便。可以使用以下方法来解决问题:

首先安装一个包:

1
yum install patch ncurses ncurses-devel

然后使用pip安装包:

1
pip3 install readline

安装完成以后,我们就可以正常使用python了。也可以使用后退键和方向键了。

概述

MySQL的复制功能是构建基于MySQL的大规模、高性能应用的基础。复制功能不仅有利于构建高性能的应用,同时也是高可用性、可扩展性、灾难恢复、备份以及数据仓库等工作的基础。

本文主要讲述复制如何工作,基本的复制如何搭建,复制的相关配置和优化复制服务器。

复制的工作原理:

简单来说,复制分为下面三个步骤:

  1. 在主库上把数据更改记录到二进制日志中(这些记录被称为二进制日志事件)。
  2. 备库将主库上的日志复制到自己的中继日志(relay log)中。
  3. 备库读取中继日志中的事件,将其重放到备库数据之上。

配置复制

为MySQL服务器配置复制非常简单。最基本的场景是新安装的主库和备库,总的来说分为以下几步:

  1. 在每台服务器上创建复制账号。
  2. 配置主库和备库。
  3. 通知备库连接到主库并从主库复制数据。

创建复制账号

MySQL 会赋予一个特殊的权限给复制线程。在备库运行的I/O线程会建立一个到主库的TCP/IP连接。这意味着必须在主库创建一个用户,并赋予一个合适的权限。

通过如下语句创建用户账号:

1
2
mysql> grant replication slave, replication client on *.* to repl@'10.55.160.%' identified by 'Pw12345.';
Query OK, 0 rows affected, 1 warning (0.00 sec)

请注意我们将这个账户限制在本地网络。因为这是一个特权账号。

复制账户只需要有主库上的 replication slave 权限,并不需要每一端都赋予 replication client 权限,为啥两端都要赋予呢:

  • 用来监控和管理复制的账号需要 replication client 权限,并且针对两个目的使用同一个账号更加容易。
  • 如果在主库上建立了账号,然后从主库将数据克隆到备库上时,备库也就设置了 ———— 变成主库所需要的角色,方便后续有需要进行转换角色。

配置文件

在主库的 my.cnf 文件中增加或者修改如下内容:

1
2
log_bin=mysql-bin
server_id=10

必须明确的指定一个唯一的服务器ID,默认服务器ID为1,使用默认值可能会导致和其它服务器的ID产生冲突,所以我们选择10来作为ID,一般的做法时使用服务器的末8位,但要保证它是不变且唯一的。最好选择一些有意义的约定并遵循。

如果之前没有在MySQL的配置中指定log_bin选项,需要重新启动MySQL。为了确认二进制日志文件是否已经创建,我们使用以下命令进行查看:

1
2
3
4
5
6
7
8
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000001
Position: 154
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)

需要在备库上的my.cnf文件中增加类似的配置,并且同样需要重启服务器:

1
2
3
4
5
log_bin=mysql-bin
server_id=11
relay_log=/var/lib/mysql/mysql-relay-bin
log_slave_updates=1
read_only=1

从技术上来说,这些选项都不是必须的。其中一些选项只是显式的列出了默认值。事实上只有server_id时必需的。

log_bin:默认情况下,它是根据机器名来命名的,但是如果机器名变化了可能会出现问题,为了简便起见,我们在主库和备库设置了同样的值。
relay_log:指定中继日志的位置和命名。
slave_updates:允许备库将其重放的事件也记录到自身的二进制日志中。
read_only:该选项会阻止没有任何特权权限的线程修改数据。所以最好不要给用户超出需要的权限。但是并不是很实用,特别是那些需要在备库建表的应用。

启动复制

下面我们告诉备库如何连接到主库并重放其二进制日志。这一步不要通过my.cnf来配置,而是使用 change master to 语句。因为该语句完全替代了my.cnf中相应的配置而且允许以后指向别的主库的时候无需重启备库。

1
2
mysql> change master to master_host='10.55.160.91', master_user='repl', master_password='Pw12345.', master_log_file='mysql-bin.000001', master_log_pos=0;
Query OK, 0 rows affected, 2 warnings (0.01 sec)

master_log_pos参数倍设置成0,因为要从日志的开头读起。我们可以通过show slave status语句来检查复制是否正确执行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: 10.55.160.91
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 4
Relay_Log_File: mysql-relay-bin.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: No
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 4
Relay_Log_Space: 154
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 0
Master_UUID:
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State:
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)

Slave_IO_StateSlave_IO_RunningSlave_SQL_Running这三列显式当前备库尚未运行。

运行下面的命令进行复制:

1
2
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)

执行该命令没有显示错误,我们再用show slave status命令检查一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State:
Master_Host: 10.55.160.91
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 4
Relay_Log_File: mysql-relay-bin.000001
Relay_Log_Pos: 4
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: No
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 4
Relay_Log_Space: 154
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 1593
Last_IO_Error: 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.
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 10
Master_UUID:
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp: 161114 15:28:07
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)

看到这里报出一个错误: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.,其实这是因为我是用的虚拟机,然后直接进行复制的,倒是UUID相同,解决办法就是删除auto.cnf就好了:

1
2
3
4
5
[root@localhost ~]# service mysqld stop
Redirecting to /bin/systemctl stop mysqld.service
[root@localhost ~]# \rm /var/lib/mysql/auto.cnf
[root@localhost ~]# service mysqld start
Redirecting to /bin/systemctl start mysqld.service

然后我们继续上面的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.55.160.91
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 472
Relay_Log_File: mysql-relay-bin.000003
Relay_Log_Pos: 685
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 472
Relay_Log_Space: 892
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 10
Master_UUID: a901199b-aa23-11e6-862e-000c2943e6a3
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)

这样就正常了。Slave_IO_State 显示正在等待从主库传递过来的事件,这意味着I/O线程已经读取了主库所有的事件。

到此,一个全新的复制就搞定了。

提示:以下命令都是在 root 账户下执行的命令。

每次安装mysql的时候都非常痛苦。因为至少要编译半个小时,在想有没有什么简单的办法,我一查官方文档,真让我看到一个简单的yum的安装办法。现在步骤如下:

删除已经存在的mysql

我们执行以下命令:

1
2
3
rpm -qa|grep mysql
rpm -e mysql mysql-libs
yum -y remove mysql-server mysql mysql-libs

配置源

首先我们需要获取分发源的地址(点击此处)。我现在提供 Red Hat/CentOS 连接如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#Red Hat7/CentOS7
http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm

#Red Hat6/CentOS6
http://dev.mysql.com/get/mysql57-community-release-el6-9.noarch.rpm

#Red Hat5/CentOS5
http://dev.mysql.com/get/mysql57-community-release-el5-7.noarch.rpm

#Fedora 24
http://dev.mysql.com/get/mysql57-community-release-fc24-9.noarch.rpm

#Fedora 23
http://dev.mysql.com/get/mysql57-community-release-fc23-9.noarch.rpm

#Fedora 22
http://dev.mysql.com/get/mysql57-community-release-fc22-8.noarch.rpm

比如说我现在的系统是CentOS7,那么我就使用如下方法进行安装:

1
2
3
4
5
6
[root@localhost ~]# rpm -ivh http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm
获取http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm
警告:/var/tmp/rpm-tmp.yIWWrJ: 头V3 DSA/SHA1 Signature, 密钥 ID 5072e1f5: NOKEY
准备中... ################################# [100%]
正在升级/安装...
1:mysql57-community-release-el7-9 ################################# [100%]

我们可以通过以下方法检测是否已经成功安装了rpm源:

1
2
3
4
[root@localhost ~]# yum repolist enabled | grep "mysql.*-community.*"
mysql-connectors-community/x86_64 MySQL Connectors Community 24
mysql-tools-community/x86_64 MySQL Tools Community 38
mysql57-community/x86_64 MySQL 5.7 Community Server 146

出现最后一行内容就表示已经安装成功了。

选择安装版本

注意,默认安装源之后自动开启5.7系列的安装。如果你要安装的MySQL低于5.7那么可以通过本节来进行修改源,否则跳过本节即可。

当你使用此方法进行安装MySQL的时候,会默认安装mysql的最新稳定版本(在我现在安装的时候,最新版本为5.7.16)。如果这就是你想要安装的,那么你就可以忽略这步了。如果想要安装以前的版本,比如5.6或者5.5,那么就可以用下面的方法来配置了。

首先我们先查看MySQL的那些源被禁用或者启用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost ~]# yum repolist all | grep mysql
mysql-connectors-community/x86_64 MySQL Connectors Community 启用: 24
mysql-connectors-community-source MySQL Connectors Community - Sourc 禁用
mysql-tools-community/x86_64 MySQL Tools Community 启用: 38
mysql-tools-community-source MySQL Tools Community - Source 禁用
mysql-tools-preview/x86_64 MySQL Tools Preview 禁用
mysql-tools-preview-source MySQL Tools Preview - Source 禁用
mysql55-community/x86_64 MySQL 5.5 Community Server 禁用
mysql55-community-source MySQL 5.5 Community Server - Sourc 禁用
mysql56-community/x86_64 MySQL 5.6 Community Server 禁用
mysql56-community-source MySQL 5.6 Community Server - Sourc 禁用
mysql57-community/x86_64 MySQL 5.7 Community Server 启用: 146
mysql57-community-source MySQL 5.7 Community Server - Sourc 禁用
mysql80-community/x86_64 MySQL 8.0 Community Server 禁用
mysql80-community-source MySQL 8.0 Community Server - Sourc 禁用

比如我们看到现在启用的是5.7版本系列的。我们需要安装的是5.6系列的。那么我们就可以执行以下命令:

1
2
[root@localhost ~]# yum-config-manager --disable mysql57-community
-bash: yum-config-manager: 未找到命令

提示没有找到命令,那么我们就需要安装执行以下命令来安装一个包:

1
yum install -y yum-utils 

执行成功之后就好了,那么我们继续执行上面的命令:

1
2
yum-config-manager --disable mysql57-community
yum-config-manager --enable mysql56-community

我们看看现在的系统配置:

1
2
3
4
[root@localhost ~]# yum repolist enabled | grep mysql
mysql-connectors-community/x86_64 MySQL Connectors Community 24
mysql-tools-community/x86_64 MySQL Tools Community 38
mysql56-community/x86_64 MySQL 5.6 Community Server 289

好了。下面就可以进行安装MySQL了。

安装MySQL

我这里也是使用MySQL5.7来安装的。

我们只需要执行以下命令就OK了:

1
yum install mysql-community-server

#MySQL 管理命令

使用下面的命令启动MySQL:

1
2
[root@localhost ~]# service mysqld start
Redirecting to /bin/systemctl start mysqld.service

只要没有错误信息就表示已经正常启动了。

现在我们看以下MySQL的状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost ~]# service mysqld status
Redirecting to /bin/systemctl status mysqld.service
● mysqld.service - MySQL Server
Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
Active: active (running) since 一 2016-11-14 12:34:44 CST; 5min ago
Process: 35494 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $MYSQLD_OPTS (code=exited, status=0/SUCCESS)
Process: 35421 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
Main PID: 35498 (mysqld)
CGroup: /system.slice/mysqld.service
└─35498 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid

11月 14 12:34:39 localhost.localdomain systemd[1]: Starting MySQL Server...
11月 14 12:34:44 localhost.localdomain systemd[1]: Started MySQL Server.

这样表示已经正常启动了。

停止:

1
service mysqld stop

重启:

1
service mysqld restart

修改MySQL密码

我们首先查找一下MySQL的密码:

1
2
[root@localhost ~]# grep 'temporary password' /var/log/mysqld.log
2016-11-14T04:34:41.742516Z 1 [Note] A temporary password is generated for root@localhost: sNKz9yEdzw%/

我们就可以看到我们的密码就是: **sNKz9yEdzw%/**。

首先启动MySQL client:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost ~]# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.16

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

首先我们先查看一下数据库:

1
2
mysql> show databases;
ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.

这里提示我们需要修改密码:

这里MySQL安装了 validate_password。这个插件要求密码至少包含一个大写字母,一个小写字母,一个数字和一个特殊字符,并且密码长度至少8个字符。

比如我们定义密码为 **Pw12345.**。

1
2
mysql> set password = password('Pw12345.');
Query OK, 0 rows affected, 1 warning (0.00 sec)

然后我们就可以正常的执行SQL命令了:

1
2
3
4
5
6
7
8
9
10
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)

使用Yum 安装其它的MySQL产品和组件

我们查看一下有什么可以安装的MySQL产品和组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[root@localhost ~]# yum --disablerepo=\* --enablerepo='mysql*-community*' list available
已加载插件:fastestmirror
Loading mirror speeds from cached hostfile
可安装的软件包
mysql-community-bench.x86_64 5.6.34-2.el7 mysql56-community
mysql-community-client.i686 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-client.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-common.i686 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-common.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-devel.i686 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-devel.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-embedded.i686 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-embedded.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-embedded-compat.i686 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-embedded-compat.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-embedded-devel.i686 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-embedded-devel.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-libs.i686 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-libs.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-libs-compat.i686 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-libs-compat.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-release.noarch el7-7 mysql57-community
mysql-community-server.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-community-test.x86_64 8.0.0-0.1.dmr.el7 mysql80-community
mysql-connector-odbc.x86_64 5.3.6-1.el7 mysql-connectors-community
mysql-connector-odbc-debuginfo.x86_64 5.3.6-1.el7 mysql-connectors-community
mysql-connector-odbc-setup.x86_64 5.3.6-1.el7 mysql-connectors-community
mysql-connector-python.noarch 2.0.4-1.el7 mysql-connectors-community
mysql-connector-python.x86_64 2.1.4-1.el7 mysql-connectors-community
mysql-connector-python-cext.x86_64 2.1.4-1.el7 mysql-connectors-community
mysql-connector-python-debuginfo.x86_64 2.1.4-1.el7 mysql-connectors-community
mysql-router.x86_64 2.0.3-1.el7 mysql-tools-community
mysql-router-debuginfo.x86_64 2.0.3-1.el7 mysql-tools-community
mysql-utilities.noarch 1.6.4-1.el7 mysql-tools-community
mysql-utilities-extra.noarch 1.5.6-1.el7 mysql-tools-community
mysql-workbench-community.x86_64 6.3.8-1.el7 mysql-tools-community
mysql-workbench-community-debuginfo.x86_64 6.3.8-1.el7 mysql-tools-community

我们可以使用以下命令来安装任何一个软件包,替换 package-name 为你要安装的软件包的名字:

1
yum install package-name

例如:我们要安装 mysql-community-libs

1
yum install mysql-community-libs

禁止更新

我们在安装之后,为了能够正常运行,我们会禁止MySQL进行更新。因为在yum更新了MySQL之后,MySQL会自动重启。这对于我们来说是没有必要的,所以我们可以屏蔽更新。我们可以这样,将下列指定放到你的/etc/yum.conf文件中:

1
exclude=mysql-community-client,mysql-community-common,mysql-community-libs,mysql-community-server

更新MySQL

一般在生产环境,我们都是禁用更新的。所以说,这里只是作为一个参考。
我们需要执行的命令就是:

1
yum update mysql-server

注意: 在使用 yum 更新之后,MySQL服务器会自动重启。

更新单个组件

我们也可以指定更新单个组件。首先我们先运行以下命令来查看MySQL的组件列表

1
2
3
4
5
6
[root@baoguoxiao ~]# yum list installed | grep "^mysql"
mysql-community-client.x86_64 5.7.17-1.el7 @mysql57-community
mysql-community-common.x86_64 5.7.17-1.el7 @mysql57-community
mysql-community-libs.x86_64 5.7.17-1.el7 @mysql57-community
mysql-community-server.x86_64 5.7.17-1.el7 @mysql57-community
mysql57-community-release.noarch el7-9 installed

我们可以使用以下命令来更新任何一个软件包,替换 package-name 为你要安装的软件包的名字:

1
yum update package-name

例如:我们要安装 mysql-community-client

1
yum update mysql-community-client

总结

上面实现了MySQL的默认安装,命令管理,修改密码,屏蔽更新以及更新。希望能对大家有所帮助。

今天看文档,无意中发现了 Redis 的一个新功能。
Redis 在 3.2 版本实现了一个地理位置计算的特性。

版本要求

Redis 3.2 或者更新

添加和获取位置

geoadd (添加位置)

这个命令对于经纬度是有要求的:

  • 有效的经度从-180度到180度。
  • 有效的纬度从-85.05112878度到85.05112878度。

如果超出返回,那么命令就会返回一个错误.

添加命令如下:

1
GEOADD location-set longitude latitude name [longitude latitude name ...]

这个可以同时添加多个位置。其中 location-set 是存储地理位置的集合名称,longitudelatitudename 则是地理位置的精度、纬度、名字。

下面添加北京的地铁站的坐标:

添加单个位置如下:

1
2
127.0.0.1:6379> geoadd subways 116.404269 39.906543 qianmen
(integer) 1

添加多个位置

1
2
127.0.0.1:6379> geoadd subways 116.409465 39.939578 nanluoguxiang 116.402549 39.944163 shichahai 116.315934 40.005471 yuanmingyuan 116.399279 40.007208 aolinpikegongyuan
(integer) 4

将坐标记录到位置集合之后,我们使用 geopos 命令来获取位置的名字和具体经纬度

获取命令如下:

1
GEOPOS location-set name [name ...]

比如说,如果我们想要获取圆明园、前门的经纬度,那么我们就可以执行以下代码

1
2
3
4
5
127.0.0.1:6379> geopos subways yuanmingyuan qianmen
1) 1) "116.31593316793441772"
2) "40.00546983911101506"
2) 1) "116.40426903963088989"
2) "39.90654220698316834"

计算两个点之前的距离

命令如下:

1
GEODIST location-set location-x location-y [unit]

可选参数 unit 用于指定计算距离时的单位,它的值为下面的一个:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

比如我们要计算奥林匹克公园和圆明园的距离:

1
2
127.0.0.1:6379> geodist subways yuanmingyuan aolinpikegongyuan
"7103.4924"

如果我们指定单位为 km

1
2
127.0.0.1:6379> geodist subways yuanmingyuan aolinpikegongyuan km
"7.1035"

获取指定范围内的元素

Redis 提供了两种方式:

1
2
3
GEORADIUS location-set longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

GEORADIUSBYMEMBER location-set location radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

这两个命令都是用来查找特定范围地点的功能,但是指定中心点的方式不同:georadius 使用的是用户给定的经纬度,而 georadiusbymember 则使用存储在位置集合里面的某个地点作为中心点。

  • m|km|ft|mi 指定的是计算范围时的单位;
  • 如果给定了可选的 WITHCOORD , 那么命令在返回匹配的位置时会将位置的经纬度一并返回;
  • 如果给定了可选的 WITHDIST , 那么命令在返回匹配的位置时会将位置与中心点之间的距离一并返回;
  • 在默认情况下, GEORADIUS 和 GEORADIUSBYMEMBER 的结果是未排序的, ASC 可以让查找结果根据距离从近到远排序, 而 DESC 则可以让查找结果根据从远到近排序;
  • COUNT 参数指定要返回的结果数量。

命令默认返回未排序的位置元素。 通过以下两个参数, 用户可以指定被返回位置元素的排序方式:

  • ASC: 根据中心的位置, 按照从近到远的方式返回位置元素。
  • DESC: 根据中心的位置, 按照从远到近的方式返回位置元素。

比如说我们要返回指定坐标的10km内的位置:

1
2
3
4
127.0.0.1:6379> georadius subways 116.403878 39.914942 10 km
1) "qianmen"
2) "nanluoguxiang"
3) "shichahai"

我们还需要获取位置与中心点的距离:

1
2
3
4
5
6
7
127.0.0.1:6379> georadius subways 116.403878 39.914942 10 km withdist
1) 1) "qianmen"
2) "0.9349"
2) 1) "nanluoguxiang"
2) "2.7812"
3) 1) "shichahai"
2) "3.2521"

我们需要返回按照最远距离排序:

1
2
3
4
5
6
7
127.0.0.1:6379> georadius subways 116.403878 39.914942 10 km withdist desc
1) 1) "shichahai"
2) "3.2521"
2) 1) "nanluoguxiang"
2) "2.7812"
3) 1) "qianmen"
2) "0.9349"

我们只需要两个就够了,三个太多了:

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> georadius subways 116.403878 39.914942 10 km withdist desc count 2
1) 1) "shichahai"
2) "3.2521"
2) 1) "nanluoguxiang"
2) "2.7812"
127.0.0.1:6379> georadius subways 116.403878 39.914942 10 km withdist count 2
1) 1) "qianmen"
2) "0.9349"
2) 1) "nanluoguxiang"
2) "2.7812"

获取前门10km附近的位置:

1
2
3
4
127.0.0.1:6379> georadiusbymember subways qianmen 10 km
1) "qianmen"
2) "nanluoguxiang"
3) "shichahai"

我们只是需要将其坐标换成集合名称中的会员名称就好了,用法跟之前的georadius相同。

georadiusgeoradiusbymember执行的代价并不低,因此强烈建议为查询结果创建缓存。

geohash

这个会返回一个11个字符的geohash字符串,使用内部52位表示。返回的geohashes具有以下特性:

  • 他们可以缩短从右边的字符。它将失去精度,但仍将指向同一地区。
  • 它可以在 geohash.org 网站使用,网址http://geohash.org/。查询例子:http://geohash.org/sqdtr74hyu0.
  • 与类似的前缀字符串是附近,但相反的是不正确的,这是可能的,用不同的前缀字符串附近。

命令格式如下:

1
GEOHASH key member [member ...]

例如我要查询前门的geohash:

1
2
127.0.0.1:6379> geohash subways qianmen
1) "wx4g0bg02n0"

完了。

hell
sudo apt-get install openssh-server

1
2
3
4
5

然后执行启动命令:

````shell
sudo systemctl start ssh

开机启动

我们打开/etc/rc.local,加入如下命令:

1
sudo systemctl enable ssh

注意:如果文件中如果有exit 0,一定要在其之前加入。

这样我们的ubuntu就能正常远程访问了。

本次教程使用的版本

  • Rocky-9.0
  • Nginx-1.23.1
  • PHP-8.1.9
  • MySQL-8.0.30

添加指定用户

添加用户(添加用户和用户组 wwwmysql,并且禁止登录)。

www 用户主要是用来赋予 NginxPHP 执行权限,mysql 主要是赋予给 MySQL 权限,禁止登录是为了防止用户有权限去操作 wwwmysql,一切为了安全。

1
2
useradd -s /sbin/nologin www
useradd -s /sbin/nologin mysql

添加预安装包

1
dnf install -y wget tar gcc gcc-c++ cmake pcre-devel openssl-devel zlib-devel libxml2-devel sqlite-devel libcurl-devel libpng-devel libwebp-devel libjpeg-devel freetype-devel libxslt-devel ncurses-devel libtirpc libudev-devel rpcgen

下载源码包

我一般都会将安装包放到 /usr/local/src 目录中,所以先执行下面的命令

1
cd /usr/local/src

首先我们先下载必需的安装包:

1
2
3
wget -c http://nginx.org/download/nginx-1.23.1.tar.gz
wget -c http://www.php.net/distributions/php-8.1.9.tar.gz
wget -c https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.30-linux-glibc2.17-x86_64-minimal.tar.xz

安装依赖

  • configure: error: Package requirements (oniguruma) were not met:
1
2
3
4
5
6
wget -c https://github.com/kkos/oniguruma/releases/download/v6.9.8/onig-6.9.8.tar.gz
tar zxf onig-6.9.8.tar.gz
cd onig-6.9.8
./configure --libdir=/lib64
make && make install
cd ..
  • error: Package requirements (libsodium >= 1.0.8) were not met:
1
2
3
4
5
6
wget -c https://github.com/jedisct1/libsodium/releases/download/1.0.18-RELEASE/libsodium-1.0.18.tar.gz
tar zxf libsodium-1.0.18.tar.gz
cd libsodium-1.0.18
./configure --libdir=/lib64
make && make install
cd ..
  • error: Package requirements (libzip >= 0.11 libzip != 1.3.1 libzip != 1.7.0) were not met:
1
2
3
4
5
6
7
8
9
wget -c https://libzip.org/download/libzip-1.9.2.tar.gz
tar zxf libzip-1.9.2.tar.gz
cd libzip-1.9.2
mkdir build && cd build
cmake ..
make && make install
cd ../../
ln -sf /usr/local/lib64/libzip.so.5 /usr/local/lib/
ldconfig /usr/local/lib
  • Package 'libtirpc', required by 'virtual:world', not found
1
rpm -hvi https://dl.rockylinux.org/pub/rocky/9/CRB/x86_64/os/Packages/l/libtirpc-devel-1.3.2-1.el9.x86_64.rpm

安装 PHP

1
2
3
4
5
tar zxf php-8.1.9.tar.gz
cd php-8.1.9
./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-config-file-scan-dir=/usr/local/php/conf.d --enable-fpm --with-fpm-user=www --with-fpm-group=www --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-iconv --with-freetype --with-jpeg --with-zlib --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --with-curl --enable-mbregex --enable-mbstring --enable-intl --enable-pcntl --enable-ftp --enable-gd --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-zip --enable-soap --with-gettext --enable-opcache --with-xsl --with-pear --with-webp --enable-exif --with-sodium
make && make install
cd ../

配置 PHP

复制 php.ini 配置文件。下面的命令默认就是复制的开发模式的配置文件:

1
cp /usr/local/src/php-8.1.9/php.ini-development /usr/local/php/etc/php.ini

如果是复制生产模式的配置文件,则命令如下:

1
cp /usr/local/src/php-8.1.9/php.ini-production /usr/local/php/etc/php.ini

修改 FPM 的配置文件:

1
2
cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf
cp /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf

接下来就是配置 PHP-FPM 服务并启动。

首先以下内容保存在 /etc/systemd/system/php-fpm.service 文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=The PHP FastCGI Process Manager
After=network.target

[Service]
Type=simple
PIDFile=/usr/local/php/var/run/php-fpm.pid
ExecStart=/usr/local/php/sbin/php-fpm --nodaemonize --fpm-config /usr/local/php/etc/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=false

[Install]
WantedBy=multi-user.target

这样就可以通过如下命令来控制 FPM:

  • 查看当前运行状态
1
systemctl status php-fpm
  • 启动
1
systemctl start php-fpm
  • 停止
1
systemctl stop php-fpm
  • 重启
1
systemctl restart php-fpm
  • 重载
1
systemctl reload php-fpm

安装 Nginx

1
2
3
4
5
tar zxf nginx-1.23.1.tar.gz
cd nginx-1.23.1
./configure --user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-http_sub_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-pcre --with-pcre-jit
make && make install
cd ../

配置 Nginx

首先以下内容保存在 /etc/systemd/system/nginx.service 文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=false

[Install]
WantedBy=multi-user.target

这样就可以通过如下命令来控制 FPM:

  • 查看当前运行状态
1
systemctl status nginx
  • 启动
1
systemctl start nginx
  • 停止
1
systemctl stop nginx
  • 重启
1
systemctl restart nginx
  • 重载
1
systemctl reload nginx

安装完成后,编译形成的配置文件,都在 nginx.conf 里,会显得杂乱与臃肿,扩展起来也不方便,所以这里我建议对其进行分拆。

Nginx 分拆

主要是将 http 中的每个 server 都作为一个单独文件进行保存。

修改如下,首先是 nginx.conf 的修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

#user nobody;
worker_processes 1;

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;


events {
worker_connections 1024;
}


http {
include mime.types;
default_type application/octet-stream;

#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';

#access_log logs/access.log main;

sendfile on;
#tcp_nopush on;

#keepalive_timeout 0;
keepalive_timeout 65;

#gzip on;

# 注意这里 `server` 已经移动到其他的文件,后面会进行介绍

# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;

# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;

# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;

# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;

# location / {
# root html;
# index index.html index.htm;
# }
#}

include vhost/*.conf; # 注意添加了这行
}

这里对拆分出来的 server 保存到 /usr/local/nginx/conf/vhost/default.conf 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {
root html;
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

配置 Nginx 与 PHP-FPM 进行网络通信

这里需要指定项目地址。我已经默认访问的目录地址是 /home/www/default

1
sudo -u www mkdir /home/www/default

然后再里面填充以下内容:

1
echo -e "<?php\nphpinfo();" | sudo -u www tee /home/www/default/index.php

这里还是要进行修改 /usr/local/nginx/conf/vhost/default.conf。修改后的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

root /home/www/default; # 这里设置访问的目录
index index.html index.php; # 这里设置默认入口文件

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
# 这里这一段都开发
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 这里注意替换 $document_root
include fastcgi_params;
}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

修改完成之后,先执行 Nginx 配置检测命令,如果没有错误就执行重新加载配置文件的命令:

1
2
/usr/local/nginx/sbin/nginx -t # 配置检测命令
/usr/local/nginx/sbin/nginx -s reload # 动态加载配置命令

在访问的时候你就会发现,如果你使用非本机操作(例如虚拟机),则会出现访问无响应的情况,这是因为 Rocky 更换了防火墙工具,所以需要使用下列命令进行放开端口:

1
2
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --reload

如果不出问题,访问 http://localhost 就可以看到经典的 phpinfo 相关信息了。

如果有任何问题,可以联系我。

安装 MySQL

MySQL 5.7.5 之后版本都要安装 boost 包。这里选择的是已自带 boost 安装包的 MySQL 安装包:

1
2
3
4
5
6
7
tar zxvf mysql-boost-8.0.30.tar.gz
mkdir mysql-8.0.30/build
cd mysql-8.0.30/build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DSYSCONFDIR=/etc -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_general_ci -DWITH_EMBEDDED_SERVER=1 -DENABLED_LOCAL_INFILE=1 -DWITH_BOOST=/usr/local/src -DDOWNLOAD_BOOST=1
make && make install
chown -R mysql:mysql /usr/local/mysql #对mysql目录进行赋予权限
cd ../../

在国内下载 bootst 通常都会失败,比如说看我这里的安装错误日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- Downloading boost_1_77_0.tar.bz2 to /usr/local/src
-- [download 0% complete]
-- [download 1% complete]
-- [download 2% complete]
-- Download failed, error: 28;"Timeout was reached"
CMake Error at cmake/boost.cmake:226 (MESSAGE):
You can try downloading
https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.bz2
manually using curl/wget or a similar tool, or increase the value of
DOWNLOAD_BOOST_TIMEOUT (which is now 600 seconds)
Call Stack (most recent call first):
CMakeLists.txt:1543 (INCLUDE)


-- Configuring incomplete, errors occurred!
See also "/usr/local/src/mysql-8.0.30/build/CMakeFiles/CMakeOutput.log".
See also "/usr/local/src/mysql-8.0.30/build/CMakeFiles/CMakeError.log".

可以看到这里已经提示了下载地址。那么可以通过开启 VPN 下载完成后,就可以通过 FTP 软件或者 scp 命令将其同步到指定位置。这里就提供一个 scp 同步命令:

1
scp D:\迅雷下载\boost_1_77_0.tar.bz2 root@192.168.1.8:/usr/local/src

MySQL configure 安装参数解释:

配置项 注释
-DCMAKE_INSTALL_PREFIX /usr/local/mysql 指定安装路径
-DSYSCONFDIR /etc 默认 my.cnf 选项文件的目录
-DWITH_MYISAM_STORAGE_ENGINE 1 开启 MyISAM 存储引擎
DWITH_INNOBASE_STORAGE_ENGINE 1 开启 innobase 存储引擎
DWITH_PARTITION_STORAGE_ENGINE 1 开启分区存储引擎
DWITH_FEDERATED_STORAGE_ENGINE 1 开启 Federated 存储引擎
DEXTRA_CHARSETS all 安装所有扩展字符集
DDEFAULT_CHARSET utf8mb4 设置字符集
DDEFAULT_COLLATION utf8mb4_general_ci 设置字符排序规则
DWITH_EMBEDDED_SERVER 1 是否构建 libmysqld 嵌入式服务器库
DENABLED_LOCAL_INFILE 1 是否允许使用 LOAD DATA LOCAL INFILE 命令导入存放于客户端的数据文件
DWITH_BOOST /usr/local/src 设置下载 boost 要存放的位置。可以提前使用其它方式下载该文件,然后安装程序就会直接使用,而不会再次下载
DDOWNLOAD_BOOST 1 是否要下载 boost

MySQL 配置

创建 MySQL 配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cat > /etc/my.cnf<<EOF
[client]
port=3306
socket=/tmp/mysql.sock

[mysqld]
port=3306
socket=/tmp/mysql.sock
key_buffer_size=16M
max_allowed_packet=128M
pid-file=/usr/local/mysql/var/mysqld.pid

[mysqldump]
quick
EOF

更改数据库相关目录权限

1
chown -R mysql:mysql /usr/local/mysql

对数据库进行初始化:

1
/usr/local/mysql/bin/mysqld --initialize --user=mysql

执行初始化命令时,如果日志提示:

1
2
2022-08-13T13:04:11.920018Z 0 [ERROR] [MY-010457] [Server] --initialize specified but the data directory has files in it. Aborting.
2022-08-13T13:04:11.920038Z 0 [ERROR] [MY-013236] [Server] The designated data directory /usr/local/mysql/var/ is unusable. You can remove all files that the server added to it.

原因是存在空目录 /usr/local/mysql/var/lib/mysqlrouter/,原因暂时未知。可以使用如下方式解决:

1
rm -rf /usr/local/mysql/var/*

执行完成后注意看最后一条日志信息:

1
2022-08-13T15:39:52.858029Z 6 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: r?u3kx1jr+!E

这里已经出现了生成的密码。

接下来就是配置 MySQL 服务并启动。

  • mysql_safe 服务管理
1
2
# /usr/local/mysql/support-files/mysql.server
Usage: mysql.server {start|stop|restart|reload|force-reload|status} [ MySQL server options ]
  • Systemctl 服务管理

首先以下内容保存在 /etc/systemd/system/mysql.service 文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[Unit]
Description=MySQL Community Server
After=network.target syslog.target
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
Documentation=man:mysqld(8)

[Service]
User=mysql
Group=mysql

# Have mysqld write its state to the systemd notify socket
Type=notify

# Disable service start and stop timeout logic of systemd for mysqld service.
TimeoutSec=0

# Start main service
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf $MYSQLD_OPTS

# Use this to switch malloc implementation
EnvironmentFile=-/etc/sysconfig/mysql

# Sets open_files_limit
LimitNOFILE = 10000

Restart=on-failure

RestartPreventExitStatus=1

# Set environment variable MYSQLD_PARENT_PID. This is required for restart.
Environment=MYSQLD_PARENT_PID=1

PrivateTmp=false

[Install]
WantedBy=multi-user.target

这样就可以通过如下命令来控制 MySQL:

  • 查看当前运行状态
1
systemctl status mysql
  • 启动
1
systemctl start mysql
  • 停止
1
systemctl stop mysql
  • 重启
1
systemctl restart mysql
  • 重载
1
systemctl reload mysql

修改数据库密码

在创建数据库时生成的密码实际上已经标记为过期。需要先登录数据库修改密码。

首先使用 mysql 命令登录账户:

1
/usr/local/mysql/bin/mysql -uroot -p

然后执行以下 SQL 命令修改密码(这里我图省事,就设置为 root):

1
SET PASSWORD = 'root';

其实在生产环境,最好使用随机密码,该 SQL 会生成随机密码设置后并返回明文密码:

1
2
3
4
5
6
7
mysql> SET PASSWORD TO RANDOM;
+------+-----------+----------------------+-------------+
| user | host | generated password | auth_factor |
+------+-----------+----------------------+-------------+
| root | localhost | nOS5+yD:zoPQBXp,mN++ | 1 |
+------+-----------+----------------------+-------------+
1 row in set (0.00 sec)

PHP 测试连接 MySQL

编辑 PHP 脚本,测试是否能否正常连上 MySQL:

1
2
3
4
<?php
$pdo = new PDO("mysql:host=127.0.0.1;dbname=sys;port=3306", 'root', 'nOS5+yD:zoPQBXp,mN++', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8mb4', PDO::ATTR_TIMEOUT => 1));
var_dump($pdo);
?>

如果没有任何错误。那就表示 PHP 能够正常连接 MySQL。

感想

如果不是为了更新这篇文章,打死我也不想编译环境了。太麻烦了。强烈推荐使用第三方别人预编译的包进行安装使用。

后续我也会对相关内容进行更新。

参考