mysql技巧(三)

覆盖索引

概念

如果索引包含所有满足查询需要的数据的索引成为覆盖索引(Covering Index),也就是平时所说的不需要回表操作

判断标准

使用explain,可以通过输出的extra列来判断,对于一个索引覆盖查询,显示为using index,MySQL查询优化器在执行查询前会决定是否有索引覆盖查询

案例

表结构如下,表数据总共485273条。

查询所有字段,没有中索引,使用文件排序。

查询type字段,也使用了文件排序。速度比全字段查询快30%。

覆盖索引,通过explain可以看到,extra中存在Using index表示,中了覆盖作用。rows扫描行数也是理论上最低的200002行。

延迟关联

延迟关联就是通过使用覆盖索引查询返回需要的主键,再根据主键关联原表获得需要的数据。

继续上面你的例子。这次我们查询200条数据。普通方法使用了0.338s。

使用延迟关联,如下,

及时查询了2次,外加一次关联,也比普通搜索快了很多。

Nginx 配置(一)

location

匹配优先级

1、 绝对匹配

2、部分匹配

分为前缀匹配正则匹配,他们两种匹配方式有分为是否区分大小写。

  • ^~ 前缀匹配区分大小写
  • 空修饰符前缀匹配不区分大小写
  • ~正则匹配区分大小写
  • ~*正则匹配不区分大小写

对于前缀匹配之间的匹配顺序比较是根据匹配到的字符数量来定的,匹配到的字符数量越多就越优先。而正则匹配之间的优先级则是以它们定义的先后顺序来决定的,越先定义的优先级越高。

当解析开始时,所有同类的匹配之间先做比较,最后可以得到一个前缀匹配和一个正则匹配的结果(可能只有其中一种),然后再对这两类匹配做一个比较。如果最终的前缀匹配是使用^~修饰符定义的,那么它将优先于「正则匹配」,否则反之。

访问/abcde会返回什么结果呢?

按照上面的逻辑,
前缀匹配有个2个/abc^~ /ab,按照匹配数量来说/abc胜出。
正则匹配只有一个~ a
最后拿~ a/abc比较,由于前缀匹配没有以^~修饰,所以最终结构是匹配到~ a

server_name

server_name 指令来匹配请求头中的 Host 以方便地建立站点。server_name 支持通配符和正则。

重复server_name只会生效第一个,后面无论多少个不会生效。

使用通配符的server_name

使用~开头表示使用正则匹配,~之后不能有空格。

注意:含有{}符号的表达式需要用""引号包含起来。否者会报错eload service nginx... nginx: [emerg] directive "server_name" is not terminated by ";" in xxxxxx

优先级;字符串 > 通配符 > 正则

当访问一个端口却没有任何 server_name 可以匹配的情况。这时候 nginx 会默认使用第一个 listen 这个端口的 server。当然这个默认值也是可以改的,只要在 listen 指令中加入 default 即可。

内部重定向

location 指定不仅可以定义匹配 path 的配置,还可以定义以@开头的内部重定向。
try_files 的本意是检测某个文件是否存在并访问,最终再 fallback 到一个地方。

访问nginx.cc.com/a或者nginx.cc.com/b会重定向到location @php去。

除了 try_files 之外,其实可以把内部重定向也视为一个普通的路由,使用 rewrite 来跳转。

但由于使用了 rewrite,所以丢失了 $uri

最适使用error_page重定向。

rewrite 与 return 的跳转

nginx 配置中如果想触发一个 3xx 重定向通常有两种方式。一种是使用 rewrite,后面接 redirect(302)permanent(301)。另一种是直接使用 return 返回一个 301302 的状态码,并在第二个参数中带上一个 URL,它将自动被设置为响应头中的 Location 参数。

然而这个配置是有问题的!$request_uri参数是请求的原始 URI,也就是包含 $args 的。而 rewrite 指令本身有自带 $args,于是 $args 就被重复加了一次。比如请求http://localhost/?a=1想被 301 到https://localhost/?a=1?a=1

 正确的写法应该是

  或者干脆使用 return

rewrite 入门

使用 break 指令,它可以让 rewrite 不再进入匹配,直接从 root 查找文件。否则这条location将无穷循环,访问出现500错误

同样效果的try_files

if 指令

nginx中的if指令,只是 rewrite 模块的一部分而已,它的使用是很有局限性的。能在 if 块中使用的指令只是 rewrite 模块中的那些和一些 proxy_pass 之类的特殊指。

rewrite 重置 $uri
return 直接响应请求
set 设置变量
if 根据条件处理指令
break 阻止进入其他 location
rewrite_log 配置是否开启 rewrite 日志
uninitialized_variable_warn 配置是否开启变量初始化日志

上面这段配置会报错,因为deny all不属于rewrite模块,而是 access 模块中的指令。

这样写就可以了。

上面说了除了rewirte模块中的指令外,还有一些指令可以使用:
add_headerproxy_passfastcgi_pass

proxy_pass

proxy_pass 默认使用的是 http 1.0,可以通过 proxy_http_version 指令让它使用 http 1.1,以便开启 keepalive 之类的功能。

proxy_pass 不允许设置域名。

这样会报错

我们可以通过proxy_set_header来解决

参考:

https://www.web-tinker.com/
http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header
http://seanlook.com/2015/05/17/nginx-location-rewrite/
https://www.nginx.com/blog/creating-nginx-rewrite-rules/

laravel/lumen 数据库 和 Eloquent 浅析

一、 DatabaseServiceProvider 服务提供者

主要做了一下几件事:
– 初始化Model
– 注册Eloquent工厂
– 注册可队列化实体解析器实现
– 注册DB工厂
– 注册DB
– 注册DB连接

1、 初始化Model

主要清空初始化的类库和全局作用域。

2、 注册Eloquent工厂

作用暂未理解。
从代码中可以看到框架首先绑定了FakerGenerator::class数据伪造生成类,将其初始化到EloquentFactory中,并却传入了database_path('factories'),那么可以猜到大致作用为数据伪造器。

todo: 理解EloquentFactory

3、 注册可队列化实体解析器实现

用作暂时未理解。

todo: 理解QueueEntityResolver

4、 注册DB工厂

ConnectionFactory通过配置创建不同的的链接。
mysql为例,框架会通过MySqlConnector为我们创建一个数据库连接。MySqlConnector创建出来的其实就是PDO

另外,当我们开启主从分离的时候,ConnectionFactory还会根据相应的规则,创建读链接或者是写链接。

5、 注册DB

DatabaseManager负责管理ConnectionFactory创建出来的连接。
并且对外提供connection(创建)、purge(清除)、disconnect(断开)、reconnect(重连)方法,让外部进行统一调用。

6、注册DB连接

app('db.connection')后,返回一个有ConnectionFactory创建DatabaseManager负责管理的PDO连接。

Connection对外提供selectinsertupdatedeletecommitrollBackbeginTransaction等方法,提供对数据库的基本操作。

二、 Eloquent\ModelEloquent\BuilderQuery\BuilderSchema\Builder

Eloquent是框架提供的ORM

1、Eloquent\Model

Eloquent\Model是一个抽象类。模型必须继承它。Eloquent\Model类的主要作用,也是抽象类的主要作用是对子类所有功能的的一个抽象归纳。

主要抽象功能有:

  • 全局作用域管理,比如方法:addGlobalScopehasGlobalScopegetGlobalScopegetGlobalScopes等。
  • 事件管理,比如方法:observeflushEventListenersregisterModelEventgetObservableEventssetObservableEvents等。
  • 读写分离链接的主动选择,比如方法:ononWriteConnection
  • 关联关系管理,比如方法:hasOnemorphOnebelongsTomorphTohasManyhasManyThrough等。
  • 模型输入/输出数据管理,比如方法:guard系列方法(负责输入时过滤不想要的字段)、fill系列方法输入方法、getAttribute系列方法(负责输出时过滤敏感字段,格式化输出)
  • 数据操作方法,比如createupdatepushsavedestroydestroydelete等。这些方法提供了常用的数据库操作方法,并且在内部封装了事件机制。

个人感觉Eloquent\Model封装了比较多的东西,过于庞大,功能比较复杂,但是好处也是功能齐全,简便操作。可以更具自己要求是否开启Eloquent,为了简便开启Eloquent, 抑或是为了追求性能放弃Eloquent

2、Eloquent\Builder

Eloquent\Builder 在我看来才是真正的 Eloquent的核心, Eloquent\Model只是对其提供的方法进行封装和管理。

比如我们调用Model\User::first()方法,其实是通过Eloquent\Model__callStatic方法或者__call方法转发到了Eloquent\Builder上面。

我们需要注意到,通过Eloquent\Model每一次对数据的操作都会创建一个Eloquent\Builder。主要作用是一次请求封装在一个上下文中,我们可以很好的对其控制。好处有:

  1. 防止请求数据被污染
  2. 可以很好的管理内存

同理,http请求访问laravel\lumen框架,框架都会创建一个request也是为了如此。

还有一个细节需要注意的是Eloquent\Builder中的$passthru变量。Eloquent\Builder__call方法中,会判断如果请求放在存在于这个变量中的的时候,会返回Query\Builder,反之返回他自己Eloquent\Builder

3、 Query\Builder

Query\Builder 的作用就是存储本次数据库操作的基本操作。并且提供toSql方法,调用Query\Grammars\Grammar类来生成SQL语句。最后将SQL语句和绑定参数传给Connection类进行数据库操作。

Eloquent\Model在创建Eloquent\Builder的时候,会同时创建一个QueryBuilder,做一对一对于,所以QueryBuilder也可以很好的做好隔离工作。

4、 Schema\Builder

Schema\Builder是框架对数据库表操作的封装。

比如数据库的新增、修改、删除、索引、外键的管理等。

三、其他

1、 UML类图

PHPCon China 2016 概要

1、 信海龙·《PHP系统问题排查实践》

PHP系统常见问题形式

502 Bad Gateway

  • php-fpm占满
  • 报错信息不应该出现nginx版本的信息,黑客会根据相应的版本号,找到漏洞攻击。比如说[nginx文件路径处理远程命令执行漏洞`](http://edu.cnzz.cn/201303/867622f9.shtml)。应该跳转到自定义的错误页面。

mysql_connect():xxxxxxxxxxxxx too many connections in /opt/wwwroot/xxxxxxx.php on line xx

  • 大并发占满mysql连接,出现报错信息
  • 报错信息出现:
    • 使用的mysql扩展,有被sql注入风险
    • 出现磁盘路径
  • 处理:
    • 增加mysql最大连接数
    • 使用连接池技术
    • 自定义错误页面

[20-Dec-2014 21:20:17] WARNING: [pool www] child 31401 said into stderr: "NOTICE: PHP message: PHP Warning: PDO::__construct(): MySQL server has gone away in ./abstract.class.php on line 26"

解决问题的基本思路

第一步: 恢复服务、保留现场

恢复服务,不能影响线上业务。

保留现场,方便排查问题。

  • 运行状态
    • 有问题时的状态数据
  • 外部监控
    • 第三方监控数据
  • 系统日志
    • 系统内部日志数据

第二步: 排查问题

排查问题,纠错,修BUG

知识 + 工具 + 数据

知识:
– 语言
– 基本语法,PHP运行机制
– 系统
– 操作系统,软件相关的知识
– 协议
– Tcp协议 Mysql通信协议

工具:
– 系统
– top
– vmstat
– 网络
– tcpdump 抓包
– wireshark 解包
– netstat
– 进程
– gdb
– pstack
– strace

第三步: 验证

验证,解决bug后,验证是否解决。

观后感: 提供了一个基本的解决方案的思路。但是能够解决什么样的问题,就是靠基本知识了。
依靠知识,会敏锐的发现错误的大致方向
依靠工具,可以全面定位错误
依靠数据,可以验证和证实自己的猜想,以及是否解决问题

2、 韩天峰·《PHP7+Swoole开发超高性能后台程序》

大并发最好的解决方案: 基于epoll实现异步IO处理

nginxswoole都是epoll模型

如何实现1W+ QPS

  • IO操作要足够快 或者 异步, 常见的IO操作:Redis、MySQL、CURL、磁盘读写
  • CPU消耗足够少:应用服务器、PHP框架、PHP应用程序

同步阻塞模型

  • 多开进程就能增加处理能力
  • 增加进程会带来额外的进程切换开销

QPS = 耗时ms * 进程数 / 每秒

CPU消耗

(内核耗时 + php-fpm耗时 + php框架耗时 + php程序耗时) * 内核数 / 每秒

比如
内核耗时: 可以忽略
php-fpm耗时: 2ms
PHP框架耗时:10ms
php程序耗时(业务代码): 20ms

那么单核QPS = (0 + 2 + 10 + 20) / 1000 约等于 33QPS
24核QPS = 33QPS * 24 约等于 800QPS

提升性能

  • 使用php7,基本可以提升100%的性能
  • 使用C扩展框架,比如Yaf/Phalcon
  • 使用swoole应用服务器
    • 将部分PHP对象常驻内存,减少传统LAMP架构每次请求创建销毁对象的开销
    • 纯C编写,网络通信引擎和协议解析性能非常强悍。

zend 引擎

  • APC/OpCache只能优化PHP代码编译生成 OpCode的开销
  • OpCode执行构建内存中可用HashTable需要消耗CPU资源(swoole可以解决这点)
  • LAMP每次请求结束会释放掉HashTable,下一次请求再次构建HashTable内存(swoole可以解决这点)

Swoole优势:

  • 大数组、对象、常量等常驻内存,节省大量重复创建销毁的CPU消耗
  • PHP框架的环境路径计算、常量定义、初始化框架、类载入、解析注释语法等操作仅启动时执行一次
  • 业务代码仅剩最干净的请求处理逻辑

Rango推荐

php7 + swoole + Yaf/Phalcon + Redis = 快如闪电

PHP7+Swoole超高性能程序开发实践

  • 短网址服务
    • 3位检验码 + 自增ID(62进制)
    • swoole_http_server + redis 单机性能高达 5W+ QPS
    • 统计设备号、UID、IP、UV、PV、地理位置等信息,为运营部门提供数据
    • 统计逻辑基于Swoole Task功能实现,不影响核心逻辑
  • mysql-proxy
    • 基于swoole_mysql实现,支持php-fpm长连接
    • 后端使用连接池可以有效减少MySQL服务器的连接数。
    • 支持MySQL后端服务路由,php-fpm到MySQL-Proxy只需要建立一个连接,即可向到多台MySQL服务器发送SQL
    • 不支持事务
  • 高性能统计程序
  • 超高性能统计运算程序
    • 使用PHP Array 全内存 存储、计算数据。超大规模读写Redis会成为瓶颈
    • 使用Worker/Task进程实现数据的Map-Reduce
    • 使用PHP的SPL数据结构,性能很好
    • 中间数据可以使用MySQL内存表,汇总计算后删除数据
    • PHP的GC非靠谱,及时unset掉不用的数据,连续运行无内存泄漏

swoole 2.0

支持协程

3、付超群·《PHP在金融股票项目中的应用》

数据准确性

问题:PHP动态语言,弱类型

“0”== 0
“1000”== 1000
false == 0
null == 0
null == false

解决方案:
配置化的validation

问题:浮点

原因: IEEE 754

解决方法:
1. 全部*1000000,bigint -2^63 ~ 2^63-1
2. 字符串化,bcmath/gmp运算

解耦:一切皆接口

问题:

  • 一个方法/类,做了所有事情,既处理数据又提供服务
  • 直接读写数据库

解决方案:
一切皆接口,包括所有后台服务,一个程序只干一件事情

好处:
a. 容灾/多数据源
b. 统一处理
c. 有损服务/降级服务
d. 便于重构

几个性能问题

1、 缓存

  • 高速缓存 VS 大量数据 → redis & ssdb
  • 被动缓存 VS 主动缓存
  • 特殊情况 dailyCache
  • 正向缓存 VS 反向缓存

2、 多个接口,一个功能页面需要请求N个不同数据接口

方案:
kql, 基于yar的自组织合并请求接口

例如:
一个页面请求,foo接口、bar接口、baz接口。三次请求,IO耗时严重。
合并成一个接口,
请求

响应

3、 实时计算

问题:
a. 数据项多 b. 格式复杂 c. 数据量大 d. 计算指标多

需求:
a. 准实时 b. 计算离数据近 c. 方便,尽量少写代码

方案:
选择mongodb

解决:
a.各种榜单
b.汇总/均值
c.map-reduce:基于mongodb服务器端js编程
d除复权:php7高性能运算

lxc容器部署

问题:
模块多,依赖多,环境复杂

容器:
docker:进程容器
lxc:环境容器,docker他爹

方案:
lxc容器部署,git代码发布

复制:
lxc-clone -o ubuntu -n foobar

备份:
tar –numeric-owner -czf ./foobar.tgz /path/to/lxc/foobar

迁移:
tar –numeric-owner -xzf ./foobar.tgz

4、 张凌·《PHP+TSF在起点中文网全新改版中的协程之路》

基础框架:

  • koa: 前后端分离框架
  • swoole:网络通信引擎
  • tsf:业务逻辑协程框架
  • taf:soa后台服务框架

架构:

  • 接入层 NGINX(页面请求koa)、(数据请求TSF)
  • 模板层 KOA
  • 逻辑层 TSF
  • soa服务层 TAF
  • 数据层 MYSQL、REDIS。。。

协程调度:

协程堆栈 需要研究利用splStack

协程 -> async io -> event loop -> …

Web灰度:

几种方式

  • 客户端模型 b/s架构,分发不同版本的客户端
  • 后台模型 接口版本化,调用调用端配合 App/接口架构
  • web模型

几种策略

  • 账号策略
  • 地域策略
  • 独立端策略

5、赖明星·Sys-Schema

mysql4.1 提供 information_schema 数据词典
mysql5.5 提供 performance_schema 性能词典
mysql5.6 默认开启 performance_schema
mysql5.7 提供 sys 系统数据库

sys schema 的组成

sys schema 包含了一些视图、函数和存储过程
sys schema 可以帮助DBA和开发分析定位问题

就好比,linux 中 performance_schema to /proc, and SYS to vmstat。

为什么要用 sys schema
1. performance_schema 数据量太大,mysql5.6有52张表,mysql5.7有87张表。
2. performance_schema 数据太专业
3. 用户需要的是解决问题的答案,而不是一堆数据。

安装
1. set performance_schema=ON
2. MySQL 5.6+
3. MySQL 5.7默认安装

检测是否安装完成

视图

有2种展示形式, 一种便于人们阅读,还有一种便于工具处理。

使用视图

从使用者角度看代价

  • 谁使用了最多资源

  • 大部分连接来自哪里

从资源角度看使用情况

  • 查看哪个文件IO最多

案例

  • 索引统计

  • 索引重复

  • 无用索引

  • 全表扫描

  • 记录排序

  • 冗余索引

总结

全表扫描 -> 添加索引避免全表扫描 -> 新的SQL语句需要排序 -> 新建索引避免排序 -> 发现冗余索引并修复

6、代维·《百万并发下PHP协程+异步非阻塞框架设计实践》

  • 为什么用协程,什么是协程

    1、连接数
    2、并发
    3、性能

  • web io 模型

    1、php-fpm: 请求 – 进程
    2、java: 请求 – 线程
    3、Golang: 请求 – 协程
    4、nodejs: 请求 – callback

7、卜赫·《从支持一个APP的后端到BaaS平台的探险》

8、范圣佑·《从学徒变大师:谈 Laravel 框架扩充与套件开发》

9、惠新宸·《PHP7高性能之源》

10、徐汉彬·《QQ会员活动平台的PHP7升级实践》

11、胡波·《手机微博升级PHP7那些事儿》