MySQL/MariaDB下索引基数cardinality的更新问题

起因与问题

使用MySQL做数据,有时会隐约感觉到一些语句执行速度极其慢,而理论上应该是很快的。通常使用phpMyAdmin作为客户端,在表结构页里可以方便的看到索引状态,对基数cardinalyty一知半解,隐约理解为惟一值个数。

但前两天写一条查询语句执行速度非常非常慢,看到一个索引的基数竟然是空的,而且明明应该有很多值。猜测MySQL出bug了,于是删除并重建了索引,基数正常了,语句也飞快跑完。于是稍多留意了一下索引基数。同一天,看到一个基数为1的索引,也是很多惟一值的字段,这也不正常。因为是MyISAM表,直接打包了对应的.frm, .MYD, MYI 三个文件,保留一个现场,有时间再做研究。

回顾了问题表生成语句,整理出一个简单化的重现过程,下面讨论。

问题重现

测试平台为 MySQL 5.5, MariaDB 10.3,其它版本应该也是类似。直接上语句

/* ************************************************* */
-- 一张数据表,并填充一些数据,稍微大一些
DROP  TABLE IF EXISTS `d_src` ;
CREATE TABLE `d_src` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `org` char(32) NOT NULL DEFAULT '',
 PRIMARY KEY (`id`),
 KEY `org` (`org`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

INSERT INTO `d_src`(org) VALUES (md5('abcdefg'));
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;
INSERT INTO `d_src`(org) SELECT md5(concat(org,id,rand()*id)) FROM `d_src`;

-- 新建一张表,这里先使用 MyISAM 存储引擎,用来重现问题。插入数据,加字段及索引,UPDATE
DROP TABLE IF EXISTS `d_making`;
CREATE TABLE `d_making` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `org` varchar(50) NOT NULL DEFAULT '',
 `cnt` int(10) unsigned NOT NULL DEFAULT 0,
 PRIMARY KEY (`id`),
 KEY `org` (`org`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

insert into `d_making`(`org`)
SELECT `org` FROM `d_src` 
ORDER BY id desc;

ALTER TABLE `d_making` ADD org_fix varchar(50) NOT NULL DEFAULT '', ADD INDEX (org_fix);

UPDATE `d_making` SET org_fix=left(org,2 );

-- watch the index status, the Cardinality of Key org_fix is 1
SHOW index FROM d_making;

此时查看索引状态,注意org_fix索引的基数,竟然是1 !  删除并重建索引,再看

-- rebuild index and show key again, the Cardinality NOT 1 
ALTER TABLE `d_making` DROP INDEX `org_fix`, ADD INDEX `org_fix` (`org_fix`); 
SHOW index FROM d_making;

org_fix的基数应该是256左右(因为是原始数据是随机数,大概在256的附近)。

按上脚本,只d_making表改用InnoDB存储引擎,结果也类似,也许不是1,但也明显不对。

不过,在MySQL 8.0 (CentOS 8默认配置)下,得到了 258,比真实值256要大。

原因探求

最早注意到索引基数时,搜索过相关资料,但仍是一知半解。这次通过 "mysql cardinality rebuild index" 查询,找到stackoverflow上有类似问题,最终找到mysql官方手册的一段说明

Cardinality

An estimate of the number of unique values in the index. To update this number, run ANALYZE TABLE or (for MyISAM tables) myisamchk -a.

Cardinality is counted based on statistics stored as integers, so the value is not necessarily exact even for small tables. The higher the cardinality, the greater the chance that MySQL uses the index when doing joins.

理解下来,说是索引基数只是个估算值,并不一定可靠,可以用 ANALYZE TABLE 表 更新。事实上,删除并重建索引并不一定奏效。SQL语句执行时,优化器会依据索引基数决定是否使用索引,于是,基数不正确(过小)的索引,就不能被选用,于是这个索引事实上无效了。

类似的基数明显偏离,其发生条件目前还不明确,后续有新发现将在本文持续更新。

结论

  1.  MySQL索引基数,可能不准确(过大过小都有可能),从而可能造成语句执行时忽略该索引(索引失效)。
  2.  要想让该值准确,得在表数据有改动后跑一遍 ANALYZE TABLE table_name

不知道这算不算缺陷,但对使用者来说,肯定是个坑,不确定什么时候就出现在你脚下。手工运行SQL语句前,可以事先关注一下索引基数是否正常(或先 explain一下)。如果是在线生产环境,可能就要自求多福了,或者搞一大群的计划任务,跑ANALYZE TABLE.

 

英文中连续字母频次统计

这只是个无聊的小把戏。

最近又看到关于qwerty键盘布局的讨论,有提到好的键盘布局标准之一:把连续的按键分散在左右两手上,这样第一只手击键的同时,第二只手可以提前做好准备,提高效率。

于是从网上找了一批古典英文小说的txt电子版,写了个傻傻的脚本,统计其中连续两个字母的频次(忽略大小写),其中频度最高的前30项如下表。这30项共计占总频次的43.3%.

如前述,“是否可以把连续击键分散在两只手上”,即表格最后一列。从结果上看,分散与否各15项,所以qwerty在这个标准上优势似乎并不明显。

当然,这并不能证明qwerty键盘是不合理的;毕竟评估因素非常多。

序号 字母组 频次 占比 双手分散*
1 he 390103 4.04% Y
2 th 366573 3.80% Y
3 in 234621 2.43%
4 er 221732 2.30%
5 an 205618 2.13% Y
6 re 163779 1.70%
7 nd 148125 1.53% Y
8 ha 135536 1.40% Y
9 ou 133357 1.38%
10 ed 132822 1.38%
11 on 132233 1.37%
12 at 128740 1.33%
13 en 124127 1.29% Y
14 ng 117178 1.21% Y
15 hi 116461 1.21%
16 to 112860 1.17% Y
17 it 108636 1.13% Y
18 is 103327 1.07% Y
19 as 102013 1.06%
20 ar 99665 1.03%
21 es 98518 1.02%
22 te 96810 1.00%
23 or 94932 0.98% Y
24 le 92578 0.96% Y
25 st 92218 0.96%
26 of 91493 0.95% Y
27 se 87755 0.91%
28 ve 84620 0.88%
29 me 80898 0.84% Y
30 ea 77540 0.80%

使用的小说素材有:傲慢与偏见, 安娜卡列尼娜, 巴黎圣母院, 悲惨世界, 格列佛游记, 白衣女人, 飘, 呼啸山庄, 尤利西斯, 德伯家的苔丝, 爱玛, 白鲸, 黑骏马。

傻傻的脚本。脚本本身区分大小写的,整理统计结果时在excel里合并了大小写。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

fp=open('english_novels_all.txt')
raw=fp.read()
fp.close()

ch='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
for i in range(len(raw)-2):
  if raw[i] not in ch or raw[i+1] not in ch:
    continue
  k=raw[i:i+2]    # k=raw[i:i+2].lower()
  if k in st:
    st[k]+=1
  else:
    st[k]=1

fp=open('st_out.txt','w+')
for k in st:
  fp.write('%s %s\r\n'%(k,st[k]))

fp.close()

Adblock Plus 的个人设置

FireFox 扩展Adblock Plus

使用原则:只阻止过于反感的广告,其他广告一律放行;亦即,自带的过滤列表一律不激活。

“Adblock Plus 设置 - 高级” 设置页

创建和编辑您的过滤列表

//pos.baidu.com/
//static.mediav.com/js/

算法收集

这是算法收集板

最大公共子序列(Longest CommonSubstring, LCS)Python版

def lcs(x, y):
    matrix = [''] * (len(x) + 1)
    for index_x in range(len(matrix)):
        matrix[index_x] = [''] * (len(y) + 1)
    for index_x in range(1,len(x) + 1):
        for index_y in range(1,len(y) + 1):
            if x[index_x - 1] == y[index_y - 1]:#这里利用属性一
                matrix[index_x][index_y] = matrix[index_x - 1][index_y - 1] + x[index_x - 1]
            elif len(matrix[index_x][index_y - 1]) > len(matrix[index_x -1][index_y]):#这里和下面利用属性二
                matrix[index_x][index_y] = matrix[index_x][index_y - 1]
            else:
                matrix[index_x][index_y] = matrix[index_x - 1][index_y]
    return matrix[len(x)][len(y)]

来源 https://www.jianshu.com/p/a1806f2fef52

娱乐一下:测试WinRAR与7-Zip压缩比(使用wordpress源码文件及相机照片)

测试环境

软件:采用当前(2019/03/28)的最新x64位版,软件都从官方下载。

  • WinRAR:  v5.70简体中文版      winrar-x64-570sc.exe
  • 7-Zip: 7-Zip 19.00 (2019-02-21) 7z1900-x64.msi

测试素材: 1) WordPress源码解压缩后的文件夹,是 wordpress-3.5.2-zh_CN.zip 比较老,没有特别原因,只是电脑上正好有这个zip包而已。  2) 一批数码相机照片,计400多张,370M.

测试项目

使用WinRAR与7-Zip分别压缩,含标准压缩、最大压缩,压缩成各家独特格式、zip格式,然后比较压缩包大小。

结果

$ ls -l --block-size=K
total 1566840K
drwxrwx---+ 1 feng None      0K Mar 28 17:50 photos

-rwxrwx---+ 1 feng None 377416K Mar 28 17:52 photos_7z19_stand.7z
-rwxrwx---+ 1 feng None 379106K Mar 28 17:51 photos_winrar57_stand.rar

-rwxrwx---+ 1 feng None 377081K Mar 28 17:55 photos_7z19_max.7z
-rwxrwx---+ 1 feng None 379106K Mar 28 17:53 photos_winrar57_max.rar

drwxrwx---+ 1 feng None      0K Jun 22  2013 wordpress

-rwxrwx---+ 1 feng None   6199K Mar 28 17:31 wordpress_winrar57_stand.rar
-rwxrwx---+ 1 feng None   4996K Mar 28 17:19 wordpress_7z19_stand.7z

-rwxrwx---+ 1 feng None   6196K Mar 28 17:45 wordpress_winrar57_max.rar
-rwxrwx---+ 1 feng None   6374K Mar 28 17:58 wordpress_winrar57_max.zip

-rwxrwx---+ 1 feng None   6381K Mar 28 17:58 wordpress_winrar57_stand.zip
-rwxrwx---+ 1 feng None   6337K Mar 28 18:00 wordpress_7z19_stand.zip

-rwxrwx---+ 1 feng None   4969K Mar 28 18:02 wordpress_7z19_max.7z
-rwxrwx---+ 1 feng None   6292K Mar 28 17:57 wordpress_7z19_max.zip

-rwxrwx---+ 1 feng None   6172K Mar 28 17:19 wordpress_winrar371_stand.rar

[注] 为方便对照,调整了条目的次序,并非ls原始次序。

简评

从结果上看,所有测试项目都是7-Zip胜出,这在意料之中。但同样压缩成zip格式,7-Zip都好于WinRAR.

甚至,winrar 3.71版比 5.70版压缩率还更高!实在让人大跌眼镜!

存储过程实现MySQL表数据变更记录追踪(含历史快照)

直接上代码及调用示例,调用示例:

假设有一张表user_status_in_playground,存储了用户在每个游戏场的统计数据,如总积分 、玩过次数、最近玩的时间、最近次得分。逻辑上 user_id + playground_id 是个惟一键,我们希望按照这个惟一键追踪其它字段的数据变更记录(建数据快照)。需要快照时执行一次存储过程,快照数据存到表archive_user_status_in_playground里。其中归档表只需要指定表名即可,将由存储过程自动创建。

SET @data_source='source_db.`user_status_in_playground`';
SET @archive_target='archive_db.archive_user_status_in_playground';
SET @identify_columns='user_id,playground_id';
SET @track_columns='score,paly_times,last_play_time,last_score';

call revision_archive(@data_source, @archive_target, @identify_columns, @track_columns, @dmesg);

如果我想按指定的时间点倒出当时的完整快照,其中revision是时间点(年月日时分秒),快照表也是自动创建的,指定表名即可。

SET @archive_location='archive_db.archive_user_status_in_playground';
SET @dump_location='`tmp`.`user_status_in_playground__dump`';
SET @identify_columns='user_id,playground_id';
SET @track_columns='score,paly_times,last_play_time,last_score';
SET @revision=20190603100255;

call revision_dump(@archive_location, @dump_location, @identify_columns, @track_columns, @revision, @dmesg);

(原始存储过程见附件)

db_revision_archive.mysql.sql

MySQL语句实现字段拆分成多行

问题简述

如上图,拆分字段为多行,只使用MySQL本身功能,不借助其它工具。

下页结合实例详述原理,最后是完整的拆分语句。实际使用中,只需把最后一节的语句当作模板,修改字段名、表名即可。

核心知识点

    • 表连接关系 JOIN... ON 中使用小于号,而非通常用的等号
    • MySQL函数 SUBSTRING_INDEX() 详参MariaDB文档MySQL文档

示例数据表article_tags

文章标签表,文章编号aid,及文章标签`tags` ;即待拆分tags数据,分隔符为 " | ",竖线及前后空格共计三个字符。

CREATE TABLE `article_tags` (
  `aid` int(11) NOT NULL COMMENT 'article id',
  `tags` varchar(255) NOT NULL DEFAULT '',
  KEY `aid` (`aid`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

辅助表sp_idx

拆分过程需要一张辅助表,包含了正整数的序列的一列数据。可按下页语句构造1024行。

CREATE TABLE `sp_idx` (
 `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
 `x` bit(1) NOT NULL DEFAULT b'0',
 PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='拆分辅助表(正整数序列)';
INSERT into `sp_idx`(`id`,`x`) values(1,0);
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;
INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;

计算每条记录拆分条数

计算每行要拆分成的行数据,即有多少条数据通过分隔符拼接在一起。即计算字符串中删除分隔符后长度的减少量,即是 分隔符的个数*分隔符长度。分隔符个数+1 即是拆分条数。

这里使用char_length()计算长度;因为分隔符可能包含ASCII码,故不用length()。

SELECT r.`aid`, `tags`,
 (char_length(`tags`)-char_length(REPLACE(`tags`,' | ',''))) DIV char_length(' | ') + 1 as kw_count 
FROM `article_tags` r ;

联入入辅助表

按上步计算出的kw_count,联入相应条数的序列数据,从1 到 kw_count,序列数据从辅助表取。联表关系使用 < ,而不是通常使用的等于。

SELECT r.`aid`, `tags`
 ,x.`id`
FROM `article_tags` r INNER JOIN `sp_idx` x 
ON x.`id` <= (char_length(tags)-char_length(REPLACE(tags,' | ',''))) DIV char_length(' | ') + 1
;

按序号取子串

结果记录集最后一列id,事实上就是原始字符的排序号。每条记录按该序号截取子串即是结果了。这里使用 SUBSTRING_INDEX() 函数,截取前N段或后N段子串。

第一步,如下,截前N段子串的结果,再截最后一段即是每行所需结果。

SELECT r.`aid`, `tags`
 ,x.`id`, substring_index(`tags`,' | ',x.`id`) as step_1
 ,substring_index(substring_index(`tags`,' | ',x.`id`),' | ',-1) as step_2
FROM `article_tags` r
INNER JOIN `sp_idx` x 
on x.`id` <= (char_length(tags)-char_length(REPLACE(tags,' | ',''))) DIV char_length(' | ') + 1
;

总结整个过程

SELECT r.`aid`, `tags`
,x.`id` as idx, substring_index(substring_index(`tags`,' | ',x.`id`),' | ',-1) as piece
FROM `article_tags` r INNER JOIN `sp_idx` x 
ON x.`id` <= (char_length(`tags`)-char_length(REPLACE(`tags`,' | ',''))) DIV char_length(' | ') + 1 
WHERE r.`tags`!='' HAVING `piece` !='';

该语句,可以当作模板使用,其中:

  • `tags` 替换为待拆分字段(共4处),' | ' 为分隔字符串(共4处);
  • `piece`为拆分结果列,`idx` 为 `piece`在原始`tags`中的序号;
  • 比前面步骤增加了WHERE及HAVING子句,用来排除原表tags为空字符串的行、及拆分piece为空字符串的行。

辅助表`sp_idx`需要事先建好,事实上只要从是从1开始的整数序列即可;如果待拆分子串数超过1024,则要扩充本表,多跑几次该句即可:INSERT into `sp_idx`(`x`) SELECT `x` FROM `sp_idx`;

如果实在不方便建辅助表,可以使用`mysql`. `help_topic`表的help_topic_id字段(不推荐)。要能保证其完整性,而且最大拆分子串数不大于`help_topic`的总行数。而且还要多加个where条件 help_topic_id>0,否则会拆分出多余的空串行,因为help_topic_id字段是从0开始编号的。

附件:

示例数据及SQL脚本 split_field_to_lines_in_mysql.zip

CentOS 7的web环境等

原则:尽量使用yum repo安装

CentOS 官方对第三方repo的说明  https://wiki.centos.org/zh/AdditionalResources/Repositories

nginx

使用nginx 官方提供的repo,配置文件 /etc/yum.repos.d/nginx.repo 文件内容如下

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1

更多参看 http://nginx.org/en/linux_packages.html

php(fastcgi版)

因为要配合nginx,所以要选择fastcgi版的php-fpm, 这里选择remi源,这也是centos 官方推荐的php源之一。如下命令配置repo,将会配置好几个repo文件,但只有remi-safe.repo 是默认激活的,其他repo中的包有可能与系统文件冲突,所以在需要使用时在临时激活。

rpm -ivh https://rpms.remirepo.net/enterprise/remi-release-7.rpm

如下命令安装php-fpm

yum --enablerepo=remi-php73 install php-fpm

remi维护了多个php版本,选用目前最新的7.3,通过--enablerepo临时激活该源。注意包名带 "-fpm" 否则是apache的模块版。注意一下安装进程,没有自动安装httpd。

按需要安装其他php扩展,下面是常用扩展的参考

yum --enablerepo=remi-php73 install php-gd php-gmp php-igbinary \
  php-mbstring php-mcrypt php-memcached php-mysqli php-odbc \
  php-pdo php-xml php-zip php-zlib php-bcmath php-imagick

配置php-fpm。主要是文件  /etc/php-fpm.d/www.conf ,默认fastcgi进程数是动态管理的,根据机器内存、负载等控制进程数,下面是微小站点的配置,其中user/group 不改默认apache也行。(或许默认的apache与后面提到的session文件目录默认权限是相符的,待确定 TODO )

user = nobody
group = nobody
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 5

修改php默认的session文件存储目录权限,按前面php-fpm用户设置为

chown nobody:nobody /var/lib/php/session

在nginx启用php

nginx主配置文件为 /etc/nginx/nginx.conf,站点配置文件在 /etc/nginx/conf.d/*.conf ,默认参看主配置文件结尾处 include /etc/nginx/conf.d/*.conf 一行。

推荐习惯:把web站点文件放在 /var/www/html/ 目录下,每个站点一个子目录,下面以默认站点文件的配置为例,其他站点参考即可。更多参考nignx官方文档,尤其是其中core模块 ngx_http_core_module

为默认站点 default.conf 创建目录 mkdir -p /var/www/html/default ,备份一下自带的的站点文件 cp /etc/nginx/conf.d/default.conf{,__original} ,参考下面示例修改 /etc/nginx/conf.d/default.conf 文件。有更多站点,用default.conf 作模板修改。选项说明:

  • 其中 listen 80 default; 一行表示这是默认站点,其他站点就不能加了。
  • 原来在location / {...} 中的root 指令; 推荐直接放到server{...}节点中,目的是,在php 配置fastcgi_param中使用$document_root 变量,而不是像默认配置文件里的再写一遍绝对路径,其中也可以省略掉root指令了。
  • try_files $uri $uri/ /index.php; 一行是wordpress的伪静态rewrite规则;
  • rewrite ^/feed /license.txt; 是把url重写到指定文件上
  • 如果配置ssl/https,可以使用Let’s Encrypt免费证书,步骤及设置参数可参看这里
server {
    listen 80 default;
    server_name blog.path8.net;
    #server_name blog.path8.net www.path8.net; #多域名
    root /var/www/html/default;
    #autoindex on;
    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location /favicon.ico {
        log_not_found off;
        access_log off;
    }
    location /robots.txt {
        log_not_found off;
        access_log off;
    }

    location / {
    #    root   /usr/share/nginx/html;
        index  index.html index.htm;
        #try_files $uri $uri/ /index.php;
        #rewrite ^/feed /license.txt;
    }

    #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   /usr/share/nginx/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  $document_root$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 -t 测试配置文件,无误后启动(start)nginx及php-fpm,并设置自动启动(enable)

nginx -t
systemctl start nginx
systemctl status nginx
systemctl enable nginx
systemctl start php-fpm
systemctl status php-fpm
systemctl enable php-fpm

如果静态文件正常,php请求报错 File not found ,并且nginx error日志中有如下错误 FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream 可去检查web文件权限与php-fpm用户是否匹配,还有是不是开着SELinux且没配置好规则。

数据库MySQL/MariaDB安装

遵循CentOS7的改变,使用Mariadb替换MySQL,使用上差别不大,基本上完全沿用MySQL,除了改个名字。yum安装服务端及客户端

yum install mariadb mariadb-server

先不急着启动服务,修改一下配置文件,参考下面,放到[mysqld]节点后,大概第10行。这是适合微小型服务器的配置参数,可以按实际调整。的话添加下面几行。

#add by fengyqf start 161105
skip-name-resolve

innodb_log_buffer_size=32M
innodb_buffer_pool_size=64M
innodb_log_file_size=16M
innodb_additional_mem_pool_size=16M

server-id=301
log-bin=mysql-bin
log-error=mysql-bin.err
expire_logs_days=30
open_files_limit=10240
#add by fengyqf end

其中server-id起的4行,是配置复制功能用的,不要MySQL复制功能的话不要加,它们会生成一系列二进制文件,多占用磁盘。复制是自动同步把数据到其他服务上,还要其他配置才行,只这几行不够的。保存好启动服务。

systemctl start mariadb
systemctl status mariadb
systemctl enable mariad

如果在修改配置前启动过服务,启动会失败,删除日志文件再启动即可 rm /var/lib/mysql/ib_logfile*

new section

amazon lightsail使用记录及主要配置项(CentOS 7实例)

从朋友处得知amazon的lightsail,有东京机房而且网络质量还不错,价格还公道(512M最低配3.5刀),于是想换掉已经使用了7年的linode,正在用的早已绝版的东京1也4年了,网络抽风的频率有点高。试用过一段时间的东京2,网络质量确实有待改善。好几年前试用过aws的1年免费,啥都没干而浪费掉了,那时还跟linode一样是20刀1月。

英文国际版 https://aws.amazon.com/lightsail/

中国优化版 https://amazonaws-china.com/cn/lightsail/

网上有人说lightsail中文页面价格比英文高,可能是以前吧,现在并不是,所以跟随aws的提示进优化过的中文版,而且英文版可能还要科学上网(否则页面加载不完整)。

直接购买,目前最低配的首月免费,位置选择东京,平台选Linux/Unix,蓝图选仅操作系统-CentOS ,实例计划选最低的3.5刀版本,创建即可。关于系统版本,不选用预装应用(如Wordpress, Nginx等)的版本,是希望拿到干净的系统,按自己的需要定制。其实这点全看个人喜好了。

VPS实例初始化很快,搞一个静态IP地址,关联上去,免费的。

用户登录

默认不允许用户名/密码的登录方式,使用密钥对登录,按照提示进行即可。自带的用户名是centos,也可以先使用Web版SSH连接到服务器。关于用户方案推荐如下:

  • 先用web版ssh登录,添加一个新用户,设置密码,默认用户centos不去动它,似乎lightsail管理后台要用。useradd {yourname}; passwd {yourname}
  • 如果你使用linux/cygwin/maxosx,推荐把你的ssh公钥添加上去,以便直接ssh公钥登录,这比密码更安全,注意要 chmod 600 /home/{yourname}/.ssh/authorized_keys
  • 按后台提示创建适合putty用的密钥对,保存好不要泄漏。
  • 开启密码登录(只使用ssh密钥可略过):修改/etc/ssh/sshd_config 找到其中 PasswordAuthentication no 一行,删除或改成 PasswordAuthentication yes
  • (接上条)增强ssh,不允许root用户直接登录、只允许指定用户(默认用户centos及前面新建用户)登录,/etc/ssh/sshd_config 结尾追加如下两行
    AllowUsers {yourname} centos
    PermitRootLogin no

    记得systemctl restart sshd 重启sshd

防火墙/iptables

lightsail后台防火墙默认只开了几个端口,而iptables则完全开放(添加一条规则“所有TCP+UDP” 即可),推荐防火墙全开,而开启linux的iptables。如果不能掌握iptables基础使用,还是使用后台防火墙吧。

yum install iptables-services
systemctl start iptables
systemctl enable iptables

这样 iptables -L就显示默认规则了,按需要开启端口

iptables -I INPUT 5 -m state --state NEW -p tcp --dport 80 -j ACCEPT
iptables -I INPUT 6 -m state --state NEW -p tcp --dport 443 -j ACCEPT

[tip] lightsai防火墙没有开启ping或icmp的选项,但当允许了“所有TCP+UDP” 后,就可以ping了。

SELinux

lightsail的CentOS 7默认是开启SELinux的,玩不来还是关了的好. 临时关闭setenforce 0,修改配置文件/etc/selinux/config永久关闭。

 

python pip安装ConfigParser/MySQL-python的Hack方案

python3.x下pip报错No module named 'ConfigParser'

cygwin下virtual-env的python3.6下安装 MySQL-python报错,错误消息如后,提示找不到ConfigParser模块,而事实上该模块在python3.x后改名了。那就照旧名做个包,引用进去。

文件 /path-to-you-python/site-packages/ConfigParser.py

内容如下(事实上只要第二行就行),再运行正常了。(当然mysql-python的其他依赖包之前已经安装过。)

"""compatible configparser"""
from configparser import *
$ pip install MySQL-python
Looking in indexes: https://mirrors.ustc.edu.cn/pypi/web/simple
Collecting MySQL-python
  Downloading https://mirrors.ustc.edu.cn/pypi/web/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip (108kB)
    100% |████████████████████████████████| 112kB 799kB/s
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "", line 1, in 
      File "/tmp/pip-install-pasqnywc/MySQL-python/setup.py", line 13, in 
        from setup_posix import get_config
      File "/tmp/pip-install-pasqnywc/MySQL-python/setup_posix.py", line 2, in 
        from ConfigParser import SafeConfigParser
    ModuleNotFoundError: No module named 'ConfigParser'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-pasqnywc/MySQL-python/
(py36)

python3.x下pip安装 pymysql 代替MySQL-python 的方案

MySQL-python/MySQLdb 模块实在太久没有更新了,MariaDB发布后,其devel包的文件与mysql-devel有所不同,将造成编译失败。网上有hack方案,照着做补丁编译实在太麻烦,其实完全可以使用pymysql代替。

pip install pymysql

安装pymysql

新建个文件 /path-to-you-python/site-packages/MySQLdb.py 内容如下

"""compatible MySQLdb"""
from pymysql import *
install_as_MySQLdb()

完工。

前述的错误消息示例如后。如果还没安装mysql-devel包,报错将如下:

    _mysql.c:44:10: fatal error: my_config.h: No such file or directory
     #include "my_config.h"

装过MariaDB的devel包安装会报如下编译错误:

    In file included from _mysql.c:44:0:
    /usr/include/mysql/my_config.h:3:2: warning: #warning This file should not be included by clients, include only <mysql.h> [-Wcpp]
     #warning This file should not be included by clients, include only <mysql.h>
      ^~~~~~~

 

 

Pages: 1 2 3 4 5 6 7 8 9 10 ... 46 47 48 Next