作者归档:admin

Mac 开发环境搭建

进入Mac的默认Shell终端,安装Homebrew工具:

#https://brew.sh/index_zh-cn.html
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Homebrew是一个Ruby工具,类似Ubuntu下的apt-get和CentOS下的YUM。后面安装的软件就依靠它来完成。

关于Homebrew,还需要知道的:
通过brew安装的软件,默认总是安装/usr/local/Cellar这个目录中,其它方式的组织是通过软链接的方式链接到对应的文件。
通过brew安装的软件,一般可以把/usr/local看做跟目录,比如工具对应的配置和可执行文件,分别位于或对应于/usr/local/etc/xxx,/usr/local/bin/xxx,/usr/local/sbin/xxx;其中的可执行文件均为符合链接。
搜索软件使用brew search xxx, 安装使用brew install xxx,卸载使用brew uninstall xxx。

另一个需要注意的是:Mac当前登录的用户,是普通用户,如果某个软件需要以root权限启动,那么就需要首先打入sudo来临时切换到以root身份运行某个命令。

一 安装PHP

#搜索,会出现几个分之,比如PHP56 PHP71
brew search php
#过滤,只要71分支,提供了非常多扩展包
brew search php71
#安装(选择需要的扩展包)
brew install homebrew/php/php71 homebrew/php/php71-apcu homebrew/php/php71-redis homebrew/php/php71-mongodb homebrew/php/php71-opcache omebrew/php/php71-swoole

大部分PHP的模块,都包含在了homebrew/php/php71中,是编译到内核的(非动态模块),上面的apcu,redis,mogondb,swoole是动态模块,模块安装位置:/usr/local/opt/,比如:/usr/local/opt/php71-apcu/apcu.so。配置文件自然是/usr/local/etc/php/7.1/php.ini,扩展的配置放在/usr/local/etc/php/7.1/conf.d/*.ini。

php -v
php -m

编译到内核的模块确实是大而全,然后还需要调整一下php.ini的配置(才能符合开发环境要求):

#设置时区
date.timezone = Asia/Shanghai
 
#CGI相关参数,实际上建议修改的是force_redirect,其它均保留默认值
cgi.force_redirect = 0   #默认为1,改为0
cgi.fix_pathinfo = 1     #默认是1,保留
fastcgi.impersonate = 1  #默认是1,保留
cgi.rfc2616_headers = 0  #默认是0,保留

#其它参数调整,根据实际情况调整
upload_max_filesize = 64M
max_execution_time = 1200
max_input_time = 600
max_input_nesting_level = 128
max_input_vars = 2048
memory_limit = 1024M
 
post_max_size = 64M

如果要启动PHPFPM,FPM主配置文件/usr/local/etc/php/7.1/php-fpm.conf,池配置在/usr/local/etc/php/7.1/php-fpm.d中,需要注意的是,池配置中,默认的运行用户是和用户组均为_www,所以需要检查文件的权限,保证对_www具有读和执行(默认是符合的),如果要写入,那么还需要保证对应的文件夹有被写入的权限。

启动PHPFPM,由于php-fpm这个命令放入到了/usr/local/sbin中,默认shell并不搜索这个路径,所以要想添加环境变量:

#设置环境变量
export PATH="/usr/local/sbin:$PATH"  
echo 'export PATH="/usr/local/sbin:$PATH"' >> ~/.bash_profile

#确认命令能找到
which php-fpm
which php71-fpm

#手动启动,PHPFPM可以不使用root身份启动(user和group指令无用),会使用当前用户运行
sudo php71-fpm start
sudo php71-fpm stop

对于开发环境,PHPFPM可以不用启动,直接使用PHP内置的HTTP服务器也可以。

二 安装Nginx

brew install --with-http2 nginx  

如果要绑定到80端口,那么Nginx就必须以root身份运行。默认的server配置位于(可改):/usr/local/etc/nginx/servers。可以往里面方式配置:

server {
    listen 80;
    #listen 443 ssl http2;
    server_name test.app;
    root "/Users/xx/www/test/public";
 
    index index.html index.htm index.php;
 
    charset utf-8;
 
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
 
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
 
    access_log off;
 
    sendfile off;
 
    location ~ \.php$ {
        client_max_body_size 64M;
        fastcgi_intercept_errors off;
        fastcgi_connect_timeout 300;
        fastcgi_send_timeout 300;
        fastcgi_read_timeout 300;
        fastcgi_buffer_size 32k;
        fastcgi_buffers 64 32k;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
 
    location ~ /\.ht {
        deny all;
    }
 
    #ssl_certificate     /etc/nginx/ssl/test.app.crt;
    #ssl_certificate_key /etc/nginx/ssl/test.app.key;
}

Nginx的主配置文件设置的启动user一般应该和PHPFPM相同,或者需要保证Nginx对文件具备读和执行的权限。如果是文件上传,还需要确保Nginx对临时中间文件夹具备写入权限。

启动关闭等:

sudo nginx -t
sudo nginx -s start
sudo nginx -s stop

三 安装MySQL

#安装最新版本(5.7.xx)
brew install mysql

#确定搜索路径:
which mysqld
mysqld —verbose —help | grep -A 1 ‘Default options’

/etc/my.cnf  /etc/mysql/my.cnf  /usr/local/etc/my.cnf  ~/.my.cnf

#
mysql.server start
mysql_secure_installation

# 停止
mysql.server stop

MySQL不需要以root身份启动。

关于启动问题:
在Mac下,如果要开机启动,可以参考如下配置(一般不需要):

#Nginx
cp /usr/local/opt/nginx/homebrew.mxcl.nginx.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.nginx.plist  

# PHP-FPM
cp /usr/local/opt/php70/homebrew.mxcl.php71.plist ~/Library/LaunchAgents/  
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.php71.plist  

# MySQL
cp /usr/local/opt/mysql/homebrew.mxcl.mysql.plist ~/Library/LaunchAgents/  
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

## 卸载
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.nginx.plist  
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.php71.plist  
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist  
rm ~/Library/LaunchAgents/homebrew.mxcl.nginx.plist  
rm ~/Library/LaunchAgents/homebrew.mxcl.php71.plist  
rm ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist

更加实际的方法:进入操作系统后,启动Nginx和PHPFPM(因为要sudo,需要输入密码),MySQL则在需要时启动,比如本地一般链接远程数据库。所以可以这个别名:(往.bash_profile中写入)

alias servers.start='sudo nginx && php-fpm --fpm-config /usr/local/etc/php/7.1/php-fpm.conf -D'
alias servers.stop='sudo bash -c "killall -9 php-fpm && nginx -s stop"'                       
alias nginx.logs='tail -f /usr/local/opt/nginx/access.log'
alias nginx.errors='tail -f /usr/local/opt/nginx/error.log'

遇到问题:
1 Nginx启动提示

nginx: [emerg] getgrnam("

提示大体就是找不到用户组的意思。在Nginx配置中,user如果只指定了用户名,默认会去寻找同名的用户组,在Mac中,用户不一定对应一个同名的用户组,所以出现这种情况就是需要明确指定存在的用户组,可以通过如下方式来确定用户和用户组:

#当前登录的用户名
whoami
www

#确认用户组(可见www的uid是502,对应的组id是20,名称是staff)
id
uid=502(www) gid=20(staff) groups=20(staff),12(everyone)

把www和staff对应填入,错误提示消失。

Hack PHP: 黑你没商量

PHP7和它之前的版本比较,有了巨大的性能提升。对于一门具有20多年历史的语言,还能有如此大的性能提升,确实不容易。不过这也间接说明PHP7之前的版本有点烂吧。PHP7之后的PHP7.1和PHP7.2,都没有加入JIT,说好的JIT在PHP7中被跳票了。PHP的历史包袱是很重的,需要多方面兼顾。说PHP7接近HHVM运行PHP的性能,这其实是需要打问号的。从原理上来说,一个没有JIT的运行引擎会比一个具备JIT的运行引擎更快应该不可能,否则PHP还搞什么JIT。

PHP是弱类型的,这个对JIT来说不太友好。在运行时,需要类型推断,而且需要推断正确才能发挥JIT的作用。对于强类型语言,JIT就好做的多,于是出现了Hack(Hack PHP一把的意思),它引入了类型系统,用Hack写的PHP,HHVM的JIT可以充分发挥,从这个角度来说,HHVM是向JVM看齐的(它的多线程架构也和JAVA类似)。

所以,当前的PHP7和HHVM下的PHP相比,差的何止一点点。PHP7.0引入了类型系统(默认关闭),PHP7.1引入了类型推断优化opcode,PHP7.2还是看不到JIT的影子。这个大概就是PHP自身坚持弱类型,但是又要打造一个实用的JIT之间的矛盾。如果没有HHVM的出现,估计PHP压根没有打造自己的JIT的打算,这事本身就极具悲剧色彩,说HHVM拯救了PHP不为过。

从纯计算的角度,C/C++比Java快,Java比Node.js快,Node.js比PHP快。Java比PHP快一个数量级,不奇怪。Node.js携带的V8引擎自带JIT,可能已经到达极限,瓶颈在弱类型。目前Node.js在吞噬PHP的市场,它的生态虽然很火爆,但是工程化比PHP还是差很多。Java虽然运行很快,但是由于其臃肿的体积,在Web领域,无法撼动PHP的市场。多语言并存,相互协作已是常态。

PHP引入JIT还是非常值得期待的,从现有思路来看,官方希望在opcode上进行透明操作,或者提供一个开关也是一个不错的做法。比如对应新的项目,开启类型系统,开启JIT。

PHP 拟合函数计算趋势线

    // 拟合算法(最小二乘法)
    // 输入: $values = [2, 3, 5, 4, 7, 8, 3, 2, 6];
    // 输出: $values = [3.578, 3.794, 4.011, 4.228, 4.444, 4.661, 4.878, 5.094, 5.311];
    // 斜率(正切值): k = y / x;   =>   5.311 - 3.578 / 9 = 0.1925,最终结果是负无穷 -> 0 -> 正无穷
    // k=0 说明无斜率,与X轴平行, k=1说明斜度为45度,大于1说明斜度大于45度(分正负)
    // atan(k)获取弧度角,弧度换算为角度:atan(k) * (180 / M_PI);
    public function trendLine(array $values)
    {
        $n = count($values);
        $sumX = 0;
        $sumY = 0.0;
        $sumXX = 0;
        $sumXY = 0.0;
        for($i = 1; $i <= $n; $i++) {
            $sumX += $i;
            $sumY += $values[$i - 1];
            $sumXX += $i * $i;
            $sumXY += $i * $values[$i - 1];
        }
        // 求a,b
        $b = ($n * $sumXY - $sumX * $sumY) / ($n * $sumXX - $sumX * $sumX);
        $a = ($sumY - $b * $sumX) / $n;
        // 返回趋势线y值
        $ys = [];
        for($i = 1; $i <= $n; $i++) {
            $ys[$i - 1] = round($b * $i + $a, 3);
        }
        // 斜率(正切值)
        $slope = ($ys[$n -1] - $ys[0]) / $n;
        // 弧度
        $radians = atan($slope);
        // 角度(1度 = 180 / M_PI = 57.297弧度)
        $degree = $radians * 57.297;

        return [
            'slope' => $slope,
            'radians' => $radians,
            'degree' => $degree,
            'values' => $ys
        ];
    }

至于最小二乘法的公式和原理可以搜索一下。根据这个计算的结果,可以得到一条直线的趋势线,通常我们顺便计算一下斜率以及夹角大小(弧度角和角度)。

PostgreSQL 模糊查询

在MySQL中,只要字段对应建立B-Tree索引,那么前缀LIKE就可以走索引。不过在PostgreSQL中的设置就稍微复杂一点。

#建表和建索引
create table test(id int, name varchar(32));
create index idx on test(name);

#对应如下详细SQL语句
CREATE TABLE "public"."test" (
"id" int4,
"name" varchar(32) COLLATE "default"
)
WITH (OIDS=FALSE)
;

ALTER TABLE "public"."test" OWNER TO "postgres";
CREATE INDEX "idx" ON "public"."test" USING btree ("name" "pg_catalog"."text_ops");

注意,字段“name”的字符检验是“default”,表示继承自数据库的设置。创建的索引类型是btree(默认),未指定字符校验集,类型模式是text_ops(默认)。这个情况下(数据库的校验集不是“C”),直接like ‘xxx%’是无法走索引的。

如果需要走索引,有两种方式:
1 建立索引时,明确指定校验集为“C”,查询时也指定校验集为“C”(如果数据校验集是“C”,不需要指定)

#建索引时指定
create index idx on test(name collate "C"); 

#查询时指定
like 'xxx%' collate "C"

这个索引的搜索是二进制搜索,效率高效。

2 建立索引时,明确指定字段类型对应模式,比如varchar对应varchar_pattern_ops,text对应text_pattern_ops,其它类推

create index idx on test(name varchar_pattern_ops);

使用类型对应的 pattern ops 时,索引搜索不仅支持 LIKE 的写法,还支持规则表达式的写法(name ~ ‘^abcd’)。使用pattern ops将使用字符查询而非binary查询的搜索方式。

一般,第二种方式比较常用(因为默认字符校验一般都不会是“C”), 基于btree的后缀模糊搜索(建索引时调用reverse()函数),跟上面的讨论一致。前后模糊搜索,无法应用到btree索引。

在PostgreSQL中,内置了一般数据库都没有的pg_trgm插件,还内置了表达式索引和GIN索引的功能。PostgreSQL 支持使用 pg_trgm 索引来加速同时包含前模糊和后模糊的查询。为使索引过滤有较好的效果,前模糊查询至少需输入 1 个字符,后模糊查询至少需输入 2 个字符。若要高效支持多字节字符(如中文),数据库的 lc_ctype 不能为 “C”,只有 TOKEN 分割正确才能有较好的效果。

#创建gin索引
create index idx_test on test using gin (c1 gin_trgm_ops);

#查询
select * from test where c1 like 'x%';
select * from test where c1 like '%xx';

注意:对于后默认查询(2个字符以上),并不需要像btree索引那样另外调用翻转函数建立索引。

对于前后模糊搜索,需要至少3个字符:

select * from test where c1 like '%xxx%'

MySQL 模糊搜索测试

为了解决棘手的字符模糊匹配问题,于是对MySQL进行了一次大数据模糊匹配测试。测试环境:1台Linux虚拟机(CentOS 7.x), 单核,1G内存, MySQL 5.6,未做任何优化(默认安装)。

#key建了一个索引
mysql> show create table indexes \G;
*************************** 1. row ***************************
       Table: indexes
Create Table: CREATE TABLE `indexes` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `key` varchar(32) COLLATE utf8_bin NOT NULL DEFAULT '',
  `type` tinyint(4) NOT NULL DEFAULT '0',
  `refto` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `key_idx` (`key`)
) ENGINE=MyISAM AUTO_INCREMENT=91830001 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)

#总行数,约1亿条
mysql> select count(*) from indexes;
+----------+
| count(*) |
+----------+
| 91830000 |
+----------+
1 row in set (0.00 sec)

mysql> select count(id) from indexes;
+-----------+
| count(id) |
+-----------+
|  91830000 |
+-----------+
1 row in set (0.00 sec)

#前缀like,走索引
mysql> explain select * from indexes where `key` like 'aa%' \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: indexes
         type: range
possible_keys: key_idx
          key: key_idx
      key_len: 98
          ref: NULL
         rows: 44955
        Extra: Using index condition
1 row in set (0.01 sec)

#毫秒级完成
mysql> select * from indexes where `key` like 'bcd%' limit 100;
+----------+----------------------------------+------+-----------+
| id       | key                              | type | refto     |
+----------+----------------------------------+------+-----------+
|  9772263 | bcd0G5dNGJCE3ZLTvX4hTM8uOdvP     |    7 | 398601199 |
| 52244881 | bcd0zoLlD3cIgxoyAKHUQ1           |    1 | 397094852 |
| 50534282 | bcdGOh3euqx34nQUj                |    9 | 314711977 |
+----------+----------------------------------+------+-----------+
100 rows in set (0.00 sec)

#前后模糊(全表扫描) 几十秒完成
mysql> select * from indexes where `key` like '%bcd%' limit 100;
+---------+----------------------------------+------+-----------+
| id      | key                              | type | refto     |
+---------+----------------------------------+------+-----------+
|   22704 | 7jXbcd6Ryp3SUM1nCK9xqeszg2PV4    |    1 | 729091231 |
|   29434 | zlbcdN6dEK1TZW4mKvHjbGrQvD4b04   |    2 | 520555898 |
| 1465369 | g1bcdtfpW1NwcQl8uo               |    8 | 292527767 |
+---------+----------------------------------+------+-----------+
100 rows in set (0.60 sec)

mysql> select * from indexes where `key` like '%bdddcd%' limit 100;
Empty set (38.23 sec)

虽然有大约1亿条数据,但是前缀like是非常快速的,而且对于表有多少行数据并不影响查询效率(扫描索引)。但是前后like模糊搜索就比较不满意了。由于前缀like比较高效率,所以我们可以把类似%abcd%这样的查询,拆分成abcd, bcd, cd和d来存储,这样就可以把前面的%去掉。

不过,对于太长的字符串,这么做就会产生大量数据,查询效率上可能不会遇到问题,不过会占用大量存储空间,看看1亿条数据占用了多少空间:

-rw-rw---- 1 mysql mysql 8.5K May 23 20:02 indexes.frm
-rw-rw---- 1 mysql mysql 3.3G May 24 08:00 indexes.MYD
-rw-rw---- 1 mysql mysql 4.6G May 24 08:00 indexes.MYI

总共用8G空间,大概就是1000万占用800兆,10万占用8兆,1万条记录占用800K,100条占用8K, 1条数据占用0.08K(约82字节)。实际如果表字段多一些,会占用更大空间。

还好,我们可以用空间换时间。一个应用场景,需要从所有订单中搜索包含有指定字符串的所有订单,由于订单是不断增长的,加入1天有1万个订单,一年就有365万,3年就到达1000万,如果要从1000万中(订单号,SKU等)搜索包含有”abc”字符串的订单,那么必定要写成%abc%,虽然全局扫表也是可以出结果的,不过时间就比较难接受了。为了更好的体验,就需要建立“倒排”索引,比如订单号为AS-12345678(对应id为1),变成如下记录进行存储:

1 AS-12345678  1
2  S-12345678  1
3   -12345678  1
4    12345678  1
5     2345678  1
6      345678  1
7       45678  1
8        5678  1
9         678  1

一条记录拆成10条,如果还有其它字段,1000万记录建索引,轻松上亿。不过即使是亿级别的数据,查询依然很轻松。

PostgreSQL 入门

2010年9月20日发布了PostgreSQL 9.0
2011年9月12日发布了PostgreSQL 9.1(同步复制)
2012年9月10日发布了PostgreSQL 9.2(级联复制),CentOS 7.x默认YUM源会安装此版本。

大体应该是每年一个大版本:
2013 PostgreSQL 9.3
2014 PostgreSQL 9.4
2015 PostgreSQL 9.5
2016 PostgreSQL 9.6
2017 PostgreSQL 10.0

#CentOS 7中默认安装PostgreSQL 9.2,可以通过安装一个Yum包来获取最新的版本
yum install https://yum.postgresql.org/9.6/redhat/rhel-7-x86_64/pgdg-redhat96-9.6-3.noarch.rpm

#安装服务端和第三方贡献包和客户端
yum install postgresql96.x86_64 postgresql96-contrib.x86_64 postgresql96-server.x86_64 

#初始化数据库(默认数据目录/var/lib/pgsql/9.6/data/)
/usr/pgsql-9.6/bin/postgresql96-setup initdb

#设置开机启动和启动PostgresSQL
systemctl enable postgresql-9.6.service
systemctl start postgresql-9.6.service

#PostgresSQL安装后就可以启动,安装时自动创建了一个叫postgres的系统用户 和 一个同名的用户名和数据库(postgres)
#设置数据库需要切换到postgres用户,以postgres用户进入PostgreSQL不需要密码
[root@localhost ~]# su postgres
bash-4.2$ psql
could not change directory to "/root": Permission denied
psql (9.6.3)
Type "help" for help.

#PostgreSQL默认会创建一个叫postgres的数据库,还有两个模板数据库template0和template1。
#用户新建数据库时默认从模板数据库template1克隆出来(可以定制这个模板库)。
#而template0是一个最简化的模板库,创建数据库时,如果明确指定从此数据库中继承,则创建一个最简化的数据库。
postgres=# \l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+----------+----------+-------------+-------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 test      | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
(4 rows)

############################################################
#使用postgres用户,来生成其他用户和新数据库

\password postgres  #为postgres用户设置密码
CREATE USER dbuser WITH PASSWORD 'password'; #新建立用户和对应的密码
CREATE DATABASE exampledb OWNER dbuser;  # 创建用户的数据库
GRANT ALL PRIVILEGES ON DATABASE exampledb to dbuser;  #为用户授权

############################################################
#一般常用操作
#切换用户
su postgres
#运行psql终端
psql
#查看数据库
\l
#创建数据库
CREATE DATABASE test;
#切换数据库
\c test;
#创建数据库
CREATE TABLE t(id int primry key, name varchar(40));
#查看库中的数据表
\d
#查看具体的数据表结构
\d t
#查看更加详细的表结构
\d+ t

#显示SQL执行的时间
\timing on

postgres=# \q

登录,导入,命令:

#如果当前系统用户是dbuser,则-U dbuser可以省略
psql -U dbuser -d exampledb -h 127.0.0.1 -p 5432

#如果要导入数据库
psql exampledb < exampledb.sql

一般命令:

\h:查看SQL命令的解释,比如\h select。
\?:查看psql命令列表。
\l:列出所有数据库。
\c [database_name]:连接其他数据库。
\d:列出当前数据库的所有表格。
\d [table_name]:列出某一张表格的结构。
\du:列出所有用户。
\e:打开文本编辑器。
\conninfo:列出当前数据库和连接的信息。

基本配置:
1 监听地址和端口:

cd /var/lib/pgsql/9.6/data/
vi postgresql.conf

#listen_address = 'localhost'
#port = 5432

#############################
#如果修改了远程监听,还需要修改pg_hba.conf,添加一条记录
vi pg_hba.conf
host    all		all		all			trust

注:pg_hba.conf配置文件中的认证METHOD的ident修改为password,可以实现用账户和密码来访问数据库,其中这个认证标示有”trust”, “reject”, “md5”, “password”, “gss”, “sspi”

2 数据库日志

#日志收集
logging_collector = on

#日志目录(默认)
log_directory = 'pg_log'

#日志切换
# 每天生成一个新日志文件
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_truncate_on_ratation = off
log_rotation_age = 1d
log_roration_size = 0

#每当满一定大小则重建
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_truncate_on_ratation = off
log_rotation_age = 0
log_roration_size = 10M

#保留x天日志,循环覆盖
log_filename = 'postgresql-%a.log'
log_truncate_on_ratation = on
log_rotation_age = 1d
log_roration_size = 0

3 内存参数设置
shared_buffers: 共享内存的大小,主要用于共享数据块(缓存)
work_mem: 单个SQL执行时,排序、hash join所使用的内存
根据实际情况,可以适当调大。

————————————————————————————–
数据库逻辑结构
数据库:一个PostgreSQL实例下可以管理多个数据库。
表、索引:一个数据库中有很多表、索引。在PostgreSQL中表的术语为“Relation”(Table)。
数据行:每张表中有很多行数据。在PostgreSQL中行的术语为“Tuple”(Row)。

在PostgreSQL中,一个数据库包含一个或多个模式,模式中有包含了表、函数及操作符等数据库对象(模式可以理解为在数据库和表之间引入了一个命名空间,也即数据库-模式-表)。在PostgreSQL中不能同时访问不同数据库中的对象(需要重新连接到新库,MySQL可以切换库),而模式没有此限制(从这个概念上看,模式类似MySQL中的数据库级别的概念)。

要创建或访问模式中的对象,需要写上模式名(schema_name.table_name),通常创建和访问表时都不用指定模式,实际上这时访问的都是“public”的模式。当登录到该数据库是,如果没有特殊指定,都是以该模式(public)操作各种数据对象的。

默认,用户无法访问模式中不属于他们的对象。如要访问,模式的所有者必须在模式上赋予它们“USAGE”权限。用户也可以在别人的模式里创建对象,则需要被赋予在该模式上的”CREATE”权限。默认情况下每个人在public模式上都有CREATE和USAGE权限。

PostgreSQL支持两类临时表,一种是会话级的临时表,一种是事务级的临时表。

在PostgreSQL中,表空间实际上是为表指定一个存储的目录。在创建数据库时可以为数据库指定默认的表空间。创建表和索引时可以指定表空间,这样表、索引就可以存储到表空间对应的目录。

PostgreSQL使用多进程架构,每个连接会启动一个新的服务进程。不同于多线程方案,多进程架构通常使用共享内存来实现进程间通信,数据共享。PostgreSQL启动后,会生成一块共享内存用做数据库块的缓冲区。

数据目录结构:
使用环境变量PGDATA指向数据目录的根目录。目录的初始化是使用initdb来完成的。完成后这个数据目录下会生成三个配置文件(postgresql.conf实例配置文件,pg_hba.conf认证配置文件,pg_ident.conf认证方式ident的用户映射文件)

服务配置
1 连接配置项
listen_address
port
max_connections
superuser_reserved_connections 为超级用户连接保留的链接数
unix_socket_directory
unix_socket_group
unix_socket_permissions
bonjour
bonjour_name
tcp_keepalives_idle
tcp_keepalives_interval
tcp_keepalives_count

2 内存配置
shared_buffers 可以是专用内存的25%,比如1G,可以分250M(注意一个单位是8K,32M就是4000个8K,这里设置为4000)。
temp_buffers
work_mem
maintenance_work_mem
max_stack_depth

3 预写式日志配置
4 错误报告和日志项

访问控制配置文件
在PostgreSQL中,运行哪些IP的机器访问数据库服务器是由pg_hba.conf文件控制的。HBA的意思是host-based authentication,也就是基于主机的认证。

格式:

type	database	user	address		method

local	all		all			peer
host	all		all	127.0.0.1/32	ident
host	all		all	all		md5

第一字段是local,这个记录匹配通过UNIX域套接字的链接认证;是host时,这条记录匹配通过TCP/IP进行的连接(包括SSL和非SSL)。

认证方法有:trust、reject、md5和ident。
1 trust
无条件地允许连接。这个方法允许任何可以与PostgreSQL数据库服务器连接的用户以任意PostgresSQL数据库用户身份进行链接,不需要口令或其它任何认证
2 reject
无条件拒绝链接
3 md5
要求客户端提供一个MD5加密的口令进行认证
4 password
要求客户端提供一个未加密的口令进行认证
5 ident
运行客户端上的特定操作系统用户连接到数据库

查看系统信息的常用命令

#查看版本
select version();

#查看数据库启动时间
select pg_postmaster_start_time();

#查看加载配置文时间
select pg_conf_load_time();

#显示当前数据库时区
show timezone;

#查看当前实例有哪些数据库
psql -l #\l

#查看当前用户名
select user;

#查看Session用户
select session_user;

Laravel Scout使用实例

Laravel Scout是从Laravel 5.3引入的一个实现全文搜索的扩展包。

以下实例构建过程:

1 建立目录,安装框架
mkdir scout
cd scout
composer create-project --prefer-dist laravel/laravel .

vi app/Providers/AppServiceProvider.php
#对应添加
use Illuminate\Support\Facades\Schema;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Schema::defaultStringLength(191);
    }
}

#
vi config/database.php
'mysql' => [
    'strict' => false, // true改为false
],

2 安装扩展包
composer require laravel/scout
composer require algolia/algoliasearch-client-php

3 配置laravel/scout
vi config/app.php
#添加服务提供者
'providers' => [
    ....
    Laravel\Scout\ScoutServiceProvider::class,
]
#产生配置文件config/scout.php
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

4 配置algolia驱动
到https://www.algolia.com/api-keys获取秘钥(先注册一个账户,选择香港节点),获取Application ID对应ALGOLIA_APP_ID, Admin API Key对应ALGOLIA_SECRET,然后按照如下格式写入.env文件:
ALGOLIA_APP_ID=Application ID
ALGOLIA_SECRET=ALGOLIA_SECRET

5 建表和模型
php artisan make:migration create_items_table

#编辑database/migrations的迁移文件:
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateItemsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('items', function (Blueprint $table) {
           $table->increments('id');
           $table->string('title');
           $table->timestamps();
       });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop("items");
    }
}
#编辑.env,填写数据库相关信息

#运行迁移命令,开始建表
php artisan migrate

#建立模型
vi app/Item.php
<?php
namespace App;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Item extends Model
{

    use Searchable;

    public $fillable = ['title'];

    /**
     * 获取模型的索引名称
     *
     * @return string
     */
    public function searchableAs()
    {
        return 'items_index';
    }
}

6 添加路由
vi routes/web.php
Route::get('items-lists', ['as'=>'items-lists','uses'=>'ItemSearchController@index']);
Route::post('create-item', ['as'=>'create-item','uses'=>'ItemSearchController@create']);

7 添加控制器
vi app/Http/Controllers/ItemSearchController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Item;

class ItemSearchController extends Controller
{

    /**
     * items列表
     */
    public function index(Request $request)
    {
        if($request->has('titlesearch')){
            $items = Item::search($request->titlesearch)
                     ->paginate(6);
        }else{
            $items = Item::paginate(6);
        }
        return view('item-search',compact('items'));
    }


    /**
     * 创建新的item
     */
    public function create(Request $request)
    {
        $this->validate($request,['title'=>'required']);

        $items = Item::create($request->all());
        return back();
    }
}

8 添加视图
vi resources/views/item-search.blade.php

<!DOCTYPE html>
<html>
    <head>
        <title>Laravel 5.3 - laravel scout algolia search example</title>
        <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    </head>
    <body>
        <div class="container">
            <h2>Laravel Full Text Search using Scout and algolia</h2><br/>
            <form method="POST" action="{{ route('create-item') }}" autocomplete="off">
            @if(count($errors))
            <div class="alert alert-danger">
                <strong>Whoops!</strong> There were some problems with your input.
                <br/>
                <ul>
                @foreach($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
                </ul>
            </div>
            @endif

            <input type="hidden" name="_token" value="{{ csrf_token() }}">

            <div class="row">
                <div class="col-md-6">
                    <div class="form-group {{ $errors->has('title') ? 'has-error' : '' }}">
                        <input type="text" id="title" name="title" class="form-control" placeholder="Enter Title" value="{{ old('title') }}">
                        <span class="text-danger">{{ $errors->first('title') }}</span>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="form-group">
                        <button class="btn btn-success">Create New Item</button>
                    </div>
                </div>
            </div>
        </form>

        <div class="panel panel-primary">
            <div class="panel-heading">Item management</div>
                <div class="panel-body">
                    <form method="GET" action="{{ route('items-lists') }}">
                    <div class="row">
                        <div class="col-md-6">
                            <div class="form-group">
                                <input type="text" name="titlesearch" class="form-control" placeholder="Enter Title For Search" value="{{ old('titlesearch') }}">
                            </div>
                        </div>
                        <div class="col-md-6">
                            <div class="form-group">
                                <button class="btn btn-success">Search</button>
                            </div>
                        </div>
                    </div>
                    </form>

                    <table class="table table-bordered">
                        <thead>
                             <th>Id</th>
                             <th>Title</th>
                             <th>Creation Date</th>
                             <th>Updated Date</th>
                        </thead>
                        <tbody>
                        @if($items->count())
                            @foreach($items as $key => $item)
                            <tr>
                                <td>{{ ++$key }}</td>
                                <td>{{ $item->title }}</td>
                                <td>{{ $item->created_at }}</td>
                                <td>{{ $item->updated_at }}</td>
                            </tr>
                            @endforeach
                        @else
                            <tr>
                                 <td colspan="4">There are no data.</td>
                            </tr>
                        @endif
                        </tbody>
                    </table>
                    {{ $items->links() }}
                </div>
            </div>
        </div>
    </body>
</html>

启动HTTP服务器(可以直接使用php artisan serve),访问http://localhost:8000/items-lists:

如果item表已经存在数据,可以:php artisan scout:import “App\Item”,这样就会把数据推送到algolia,尤其里建索引。可以开启队列的方式让其进行异步推送。具体可参考文档。

这里的全文搜索方式的实现是利用了离线搜索服务商-algolia,由于没有在中国大陆部署节点(在香港有节点),所以访问会比较慢(涉及数据推送与查询)。这种离线搜索服务大多是收费的,不过相比自己搭建维护服务器集群,成本还是低很多的。

国内的用户可以试试阿里云的OpenSearch,也是离线搜索服务。

Laravel PhpStorm 使用

可以直接到官网下载:http://www.jetbrains.com/phpstorm/download/,默认提供30天的试用。建议购买正版,目前看到一年的标价是USD 159。也可以先下载官方免费的Toolbox,网址:http://www.jetbrains.com/toolbox/app,这个工具提供了非常便利的软件安装,启动,升级更新等操作,如果还有其它的系列的软件需要使用,比如WebStorm,这个Toolbox也可以很好的管理。

首先建议更换皮肤(File – Setting – Appearance):

这是一个黑色皮肤,非常护眼,建议使用。

第二就是知道可以安装插件,位置:File – Setting – Plugin,有大量第三方查看可用。

PhpStorm常用快捷键:

    全局搜索(ctrl + shift + F)
    显示类中的方法 (ctrl + 7)
    函数追踪 (ctrl +鼠标点击)
    单行注释/取消(ctrl + /)
    输入行号跳到某一行(ctrl + l)
    列出打开的文件(ctrl + e)
    删除当前行(ctrl + x)
    复制当前行(ctrl + d)
    跳到变量申明处(ctrl + b)

    格式化代码(command + alt + l)
    关闭当前窗口 (ctrl + w)
    项目刷新 (ctrl + alt + y)
    多行注释(ctrl + alt + /)
    查找//@todo标签(ctrl + 6)
    列出左侧文件(ctrl + 1)
    切换大小写(ctrl + shift + u)
    复制(ctrl + c)
    粘贴(ctrl + v)
    撤销(ctrl + z)

一般设置:
1 显示行号
File – Settings – Editor – General – Appearance – Show Line Number

2 换皮肤,设置字体大小
File – Settings – Editor – Colors & Fonts

针对Laravel项目,Laravel的IDE Helper工具是必须安装的,它是一个PHP包,参考:http://blog.ifeeline.com/2101.html

另外,针对PhpStrom的Laravel Plugin可以安装一下,这个插件主要针对Routes/Controllers/Views/Configuration/Services/Translations的代码补全。另外一个插件:Laravel Live Templates for PhpStorm,也可以安装一下,地址:https://github.com/koomai/phpstorm-laravel-live-templates#requests–input

Laravel 路线图

Laravel的发布路线图可以参考官方文档:https://laravel-news.com/laravel-release-process,最新的发货周期变更可参考:https://laravel-news.com/release-cycle-changes。

Laravel的版本发布是6个月发布一个版本,原来是6月和12月发布,最新变更为1月和7月(比原来推迟一个月)。

Laravel有两种发布类型:
LTS 版本 – 长期支持版本,英文 Long Term Support 的缩写,此类版本是 Laravel 能提供的最长时间维护版本。
一般发行版 – 只提供 6 个月的 Bug 修复支持,一年的安全修复支持。

解释(来自维基百科):
“长期支持 (英语:Long-term support,缩写:LTS)是一种软件的产品生命周期政策,特别是开源软件,它增加了软件开发过程及软件版本周期的可靠度。长期支持延长了软件维护的周期;它也改变了软件更新(补丁)的类型及频率以降低风险、费用及软件部署的中断时间,同时提升了软件的可靠性。但这并不必然包含技术支持。

在长期支持周期的开始,软件设计师会将软件特性冻结:他们制作补丁来修复程序错误及计算机安全隐患,但不会加入新的,可能会造成软件破坏的功能。软件维护者可能会单独发布补丁,或是将其置于维护版本、小数点版本或是服务包中发布。支持周期结束后,其称之为产品的生命周期结束。

“长期支持”这个术语通常是保留给特殊的软件版本,其他版本会有更短的生命周期。通常来说,长期支持版本至少会被维护两年。”

以下是一份版本计划,内容来自 – https://laravel-news.com/laravel-release-process
Laravel 5.1 LTS – 2015 年 6 月份
LTS 长久支持版本,Bug 修复直到 2017 年 6 月份,安全修复直到 2018 年 6 月份。

Laravel 5.2
– 2015 年 12 月份
一般发行版,提供 6 个月的 Bug 修复支持,一年的安全修复支持。

Laravel 5.3 – 2016 年 8 月份
一般发行版,提供 6 个月的 Bug 修复支持,一年的安全修复支持。

Laravel 5.4
– 2017 年 1 月份
一般发行版,提供 6 个月的 Bug 修复支持,一年的安全修复支持。

Laravel 5.5 – 2017 年 7 月份
下一个版本的 LTS 版本,会从这一刻开始停止 Laravel 5.1 的 Bug 修复,安全修复直到 2018 年 7 月份。

Laravel 5.x 核心概念

基于Laravel 5.4官方文档,重新梳理这部分内容。

一、请求的生命周期
1、简介

2、生命周期概览
第一件事
Laravel 应用的所有请求入口都是 public/index.php 文件,所有请求都会被 web 服务器(Apache/Nginx)导向这个文件。 index.php 文件包含的代码并不多,但是,这里是加载框架其它部分的起点。

index.php 文件载入 Composer 生成的自动加载设置,然后从 bootstrap/app.php 脚本获取 Laravel 应用实例,Laravel 的第一个动作就是创建服务容器实例。

HTTP/Console内核
接下来,请求被发送到 HTTP 内核或 Console 内核,这取决于进入应用的请求类型。这两个内核是所有请求都要经过的中央处理器,现在,就让我们聚焦在位于 app/Http/Kernel.php 的 HTTP 内核。

HTTP 内核继承自 Illuminate\Foundation\Http\Kernel 类,该类定义了一个 bootstrappers 数组,这个数组中的类在请求被执行前运行,这些 bootstrappers 配置了错误处理、日志、检测应用环境以及其它在请求被处理前需要执行的任务。

HTTP 内核还定义了一系列所有请求在处理前需要经过的 HTTP 中间件,这些中间件处理 HTTP 会话的读写、判断应用是否处于维护模式、验证 CSRF 令牌等等。

HTTP 内核的标志性方法 handle 处理的逻辑相当简单:获取一个 Request,返回一个 Response,把该内核想象作一个代表整个应用的大黑盒子,输入 HTTP 请求,返回 HTTP 响应。

服务提供者
内核启动过程中最重要的动作之一就是为应用载入服务提供者,应用的所有服务提供者都被配置在 config/app.php 配置文件的 providers 数组中。首先,所有提供者的 register 方法被调用,然后,所有提供者被注册之后,boot 方法被调用。

服务提供者负责启动框架的所有各种各样的组件,比如数据库、队列、验证器,以及路由组件等,正是因为他们启动并配置了框架提供的所有特性,服务提供者是整个 Laravel 启动过程中最重要的部分。

分发请求
一旦应用被启动并且所有的服务提供者被注册,Request 将会被交给路由器进行分发,路由器将会分发请求到路由或控制器,同时运行所有路由指定的中间件。

3、聚焦服务提供者
服务提供者是启动 Laravel 应用中最关键的部分,应用实例被创建后,服务提供者被注册,请求被交给启动后的应用进行处理,整个过程就是这么简单!

对 Laravel 应用如何通过服务提供者构建和启动有一个牢固的掌握非常有价值,当然,应用默认的服务提供者存放在 app/Providers 目录下。

默认情况下,AppServiceProvider 是空的,这里是添加自定义启动和服务容器绑定的最佳位置,当然,对大型应用,你可能希望创建多个服务提供者,每一个都有着更加细粒度的启动。

框架的启动流程参考:http://blog.ifeeline.com/2047.html

二、服务容器
三、服务提供者
服务容器与服务器提供者参考:http://blog.ifeeline.com/2507.html

四、Facades
Laravel 的门面作为服务容器中底层类的“静态代理”。Laravel 的所有门面都定义在 Illuminate\Support\Facades 命名空间。

在 Laravel 应用中,门面就是一个为容器中对象提供访问方式的类。该机制原理由 Facade 类实现。Laravel 自带的门面,以及我们创建的自定义门面,都会继承自 Illuminate\Support\Facades\Facade 基类。

门面类只需要实现一个方法:getFacadeAccessor。正是 getFacadeAccessor 方法定义了从容器中解析什么,然后 Facade 基类使用魔术方法 __callStatic() 从你的门面中调用解析对象。

门面 服务容器绑定
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Bus Illuminate\Contracts\Bus\Dispatcher
Cache Illuminate\Cache\Repository cache
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
DB Illuminate\Database\DatabaseManager db
DB(Instance) Illuminate\Database\Connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate
Hash Illuminate\Contracts\Hashing\Hasher hash
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\Writer log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Queue Illuminate\Queue\QueueManager queue
Queue(Instance) Illuminate\Contracts\Queue\Queue queue
Queue(Base Class) Illuminate\Queue\Queue
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\Database redis
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Blueprint
Session Illuminate\Session\SessionManager session
Session(Instance) Illuminate\Session\Store
Storage Illuminate\Contracts\Filesystem\Factory filesystem
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
Validator(Instance) Illuminate\Validation\Validator
View Illuminate\View\Factory view
View(Instance) Illuminate\View\View

五、Contracts

Contract References Facade
Illuminate\Contracts\Auth\Factory Auth
Illuminate\Contracts\Auth\PasswordBroker Password
Illuminate\Contracts\Bus\Dispatcher Bus
Illuminate\Contracts\Broadcasting\Broadcaster
Illuminate\Contracts\Cache\Repository Cache
Illuminate\Contracts\Cache\Factory Cache::driver()
Illuminate\Contracts\Config\Repository Config
Illuminate\Contracts\Container\Container App
Illuminate\Contracts\Cookie\Factory Cookie
Illuminate\Contracts\Cookie\QueueingFactory Cookie::queue()
Illuminate\Contracts\Encryption\Encrypter Crypt
Illuminate\Contracts\Events\Dispatcher Event
Illuminate\Contracts\Filesystem\Cloud
Illuminate\Contracts\Filesystem\Factory File
Illuminate\Contracts\Filesystem\Filesystem File
Illuminate\Contracts\Foundation\Application App
Illuminate\Contracts\Hashing\Hasher Hash
Illuminate\Contracts\Logging\Log Log
Illuminate\Contracts\Mail\MailQueue Mail::queue()
Illuminate\Contracts\Mail\Mailer Mail
Illuminate\Contracts\Queue\Factory Queue::driver()
Illuminate\Contracts\Queue\Queue Queue
Illuminate\Contracts\Redis\Database Redis
Illuminate\Contracts\Routing\Registrar Route
Illuminate\Contracts\Routing\ResponseFactory Response
Illuminate\Contracts\Routing\UrlGenerator URL
Illuminate\Contracts\Support\Arrayable
Illuminate\Contracts\Support\Jsonable
Illuminate\Contracts\Support\Renderable
Illuminate\Contracts\Validation\Factory Validator::make()
Illuminate\Contracts\Validation\Validator
Illuminate\Contracts\View\Factory View::make()
Illuminate\Contracts\View\View

跟踪框架的整个流程可以看到,容器中的很多绑定都是针对接口的,然后针对这些接口的绑定设置一个别名,Facade就根据这个别名取回绑定。