紫悦博客

不进则退,退一步万丈悬崖!

0%

下面的程序是用来计算产品价格范围程序的程序

<?php
/**
 * 计算产品价格范围有bug的程序
 * @author http://www.phpff.com
 **/
$price=0;
switch ($price) {

case $price &lt;= 100:
    $price_between = "100以下";
    break;
case $price &lt;= 300:
    $price_between = "100-300";
    break;
case $price &lt;= 500:
    $price_between = "300-500";
    break;
case $price &lt;= 800:
    $price_between = "500-800";
    break;
case $price &lt; 1000:
    $price_between = "800-1000";
    break;
default:
    $price_between = "1000以上";

}

echo $price_between;//1000以上
?>

当$price大于0时程序没有任何问题,当$price等于0时最终的结果不是预期的"100以下",而是"1000以上"。

问题的原因就是switch case在执行的流程是 switch 位置的条件和 case 位置的条件做比较,再继续执行程序。

a.当$price大于0时,switch位置的条件为“真” true,然后用true 和 case 位置的条件作比较。这时程序是没有问题的,因为只要case位置的条件为true语句就跳出了。

b.当$price等于0时,就需要主要了。这个时候switch位置的条件为“假” false,语句继续执行的时候,只有当 case 位置的条件也为false 程序才会执行 case 冒号位置后的程序。而事实是 case 位置的所有调教都为“真”,所以最终的结果是执行default冒号后的语句。

下面的程序是如何处理这个问题的正确方法。

<?php
/**
 * 计算产品价格范围程序
 * @author http://www.phpff.com
 **/
$price=0;
switch (true) {

    case $price <= 100:
        $price_between = "100以下";
        break;
    case $price <= 300:
        $price_between = "100-300";
        break;
    case $price <= 500:
        $price_between = "300-500";
        break;
    case $price <= 800:
        $price_between = "500-800";
        break;
    case $price < 1000:
        $price_between = "800-1000";
        break;
    default:
        $price_between = "1000以上";
}

echo $price_between;//1000以上
?>

变化是把 switch 位置的值由 $price 改成了 true ,这样问题就解决了。

来源:https://mp.weixin.qq.com…

译者:孙薇
小象科技原创作品,欢迎大家疯狂转发;

机构、自媒体平台转载务必至后台留言,申请版权。

MySQL5.7

MySQL5.7的有趣变化

MySQL的GA版本仍是5.6版,不过MySQL5.7开发版本的发布确实为数据库管理系统的世界引入了一些令人兴奋的变化。它是否值得尝试呢?本文将会对它的几个崭新功能进行更进一步的探究,你可以借以决定是否要尝试。

1

原生支持JSON

JSON(Java Script Object Notation的缩写)是一种存储信息的格式,可以很好地替代XML。如果还没用过JSON,可以看下这个非常简单的例子:

直至MySQL5.7.8才对JSON提供支持,不过毫无疑问这个功能是MySQL用户极为热衷的。在之前的版本中,只能以strings之类的通用形式来存储JSON文件,这种做法当然有其缺陷:必须自行确认/解析数据、解决更新中的困难、或在执行插入操作时忍受较慢的速度。

从MySQL5.7.8之后,由于原生支持JSON,处理JSON文件就非常简单。现在执行插入与更新操作时可以自动确认了,而且效率很高;使用新定制的一系列功能访问对象与数组成员的速度也更快了。尝试用JSON列来创建一个简单的user表格:

下一步,用新的列类型来创建几条命令行,得到的表格可能会像下面这样:

现在我们可以运用新的JSON功能来轻松处理JSON列了。举个例子,如果想要列出用户名,可以使用下列query命令:

可以用< !=这些符号来比较JSON值,还能将它们转为其他类型。这些还不够吗?简单来说,MySQL现在已经全面支持JSON文档了。

2

在ONLY_FULL_GROUP_BY中

具有更高的自由度

如果你学过SQL教程或教材,也许还记得GROUP BY中的这条黄金定律:SELECT列表中的任何非聚合字段都必须包括在GROUP BY表达式中。这条定律遵守SQL92标准,而且是很安全的假设——非聚合列可以有多个值,因此如果不进行分组,数据库引擎在判断取哪个值的时候就会有困难。

在SQL99标准中这条定律有修改,变成了在SELECT列表中的任何非聚合列都必须在功能上依赖GROUP BY列表。 这代表着如果在一个列有固定值,这个值与该列所属的GROUP BY表达式相同,则只要它的依赖性是通过主键或唯一键显示,就无需再进行分组排列。

举个简单的例子:如果有一张book表,其book_id列是主键:

我们可以写出下列查询语句(不过没太大用):

这个查询命令并不符合SQL92标准,该列的标题未列在GROUP BY表达式中,它也不是聚合列。但由于其book_id是主键,同时每个book只能有一个标题,因此这个标题在功能上依赖于该book_id。因此,在SQL99中这条查询命令完全清晰易读。

之前,MySQL中的ONLY_FULL_GROUP_BY模式遵守SQL92标准的规定,在默认情况下是关闭的。MySQL并未强求严格按照SQL92标准编写查询命令。在MySQL5.7.5中,ONLY_FULL_GROUP_BY的含义改变了,由于遵守SQL99,现在将实现更为复杂的功能。也就是说,有时候要写的代码少了,却仍旧能实现查询功能。现在的 ONLY_FULL_GROUP_BY默认更加宽泛。在这里能找到关于该问题更详细的解释。

3

在InnoDB中

支持空间数据类型

空间数据类型常用于处理地理空间信息,它们描述了几何对象的真实坐标与形状。在MySQL中,可以使用像Point、LineString或Polygon之类的几何对象代表,还有一些很有用的空间函数。例如,我们可以创建一个简单的表格来保存邮编,其中会包含一个描述特定邮编区域的area列:

每个区域可以用一个Polygon来代表:

现在我们可以通过st_contains函数找到地图上指定Point的邮编,来自动确定指定Point是否包含在该Polygon内:

在MySQL5.7之前的版本中也能使用空间数据类型,不过是以BLOBs(Binary Large OBjects)类型存储在InnoDB中,这种类型可以保存不等量的数据。在新的MySQL中,空间数据类型被映射到一个单独的内部数据类型DATA_GEOMETRY上。现在这些数据类型可以与其他存储为BLOB类型的信息分开处理了。从MySQL5.7.5以后,空间数据类型甚至可以使用空间索引了:

4

更好地支持

对亚洲语言的处理

一般出现在MySQL中的全文解析器将全文分解为词汇,使用空格作为导向。这种做法在拉丁语系的语言中(如英语)是个有用的解决方案,但是对于亚洲的表意文字——中文、日语、韩语(通常缩写为CJK)则没什么用。这些语言不使用任何文字分隔符,因此词汇的开始和结束就不甚明了。
现在MySQL使用n-gram分析程序将n个字符的连续序列分割成为n个字符长度的token。假设n=3,词汇“ABCDE”就能产生“ABC”,“BCD”和“CDE”。在处理表意文字时这种做法十分有效。例如,中文表述“工作人员”由以下两个词组成:“工作”和“人员”。我们知道在这个表述中的词组至多有2个字符长,因此可以使用n-gram分析程序,设定n=2,将这个表述解析成:工作、作人和人员。第二个token由中间的两个字符(作人)组成,实际上是另一个中文词,因此我们可能需要进一步分析这些token,以确认哪些才是正确的词组。
还有另一个更为复杂的插件名叫MeCab,专为日语设计,从形态上分析日语句子并自动断出词。比如这句话:

ワルシャワはポーランドの首都です (华沙是波兰的首都)

可以转成:

ワルシャワ(华沙)

は(标出话题)

ポーランド(波兰)

の(的)

首都(首都)

です(礼貌的连系动词)

如果处理的是中文,也可以从较小的全球化进程中获益——MySQL5.7.4设有gb18030字符功能,对应着中国国家标准GB18030。多亏了这些转变,处理亚洲语言容易多了。

5

还有很多其他的功能!

MySQL5.7中当然还有其他的新增功能与功能改善。如果想要了解更多内容,就戳【 阅读原文 】吧,其中简单列出了所有改动。

一、扩展安装

1、安装zlib

1
2
3
wget ​http://zlib.net/zlib-1.2.8.tar.gz
tar -zxf zlib-1.2.8.tar.gz
cd zlib-1.2.8
1
./configure --prefix=/usr/local/zlib
1
2
make
make install​

 

2、安装​freetype

说明:FreeType库是一个完全免费(开源)的、高质量的且可移植的字体引擎​

地址:http://sourceforge.net/projects/freetype/files/freetype2/2.5.5/​

1
2
3
wget http://jaist.dl.sourceforge.net/project/freetype/freetype2/2.5.5/freetype-2.5.5.tar.gz
tar -zxf freetype-2.5.5.tar.gz
cd freetype-2.5.5
1
2
3
./configure --prefix=/usr/local/freetype --enable-shared
make
make install​

3、安装libpng​

说明:gd库需要​,最新版是1.6.16,但是和zlib1.2.8不兼容,就选择了1.5.21

地址:http://jaist.dl.sourceforge.net/project/libpng/libpng15/older-releases/1.5.23/​

http://jaist.dl.sourceforge.net/project/libpng/libpng15/1.5.21/libpng-1.5.21.tar.gz

1
./configure --prefix=/usr/local/libpng/ --enable-shared --with-zlib-prefix=/usr/local/zlib/
1
2
make
make install

小提示:如果有错误,可以执行make clean,然后再make试试​

4、安装libtiff

地址:http://jaist.dl.sourceforge.net/project/libpng/libpng15/older-releases/1.5.23/​

地址:http://download.osgeo.org/libtiff/tiff-4.0.3.tar.gz​

1
./configure --prefix=/usr/local/libtiff --enable-shared
1
2
make
make install​

5、安装jpeg

地址:http://jaist.dl.sourceforge.net/project/libpng/libpng15/older-releases/1.5.23/​

地址:​http://www.ijg.org/files/jpegsrc.v9a.tar.gz

1
./configure --prefix=/usr/local/jpeg --enable-shared
1
2
make
make install​

 

6、安装libgd

1
wget https://bitbucket.org/libgd/gd-libgd/downloads/libgd-2.1.1.tar.gz
1
./configure --prefix=/usr/local/libgd --enable-shared --with-jpeg=/usr/local/jpeg --with-png=/usr/local/libpng --with-freetype=/usr/local/freetype --with-fontconfig=/usr/local/freetype --with-xpm=/usr/ --with-tiff=/usr/local/libtiff
1
2
make
make install​

7、安装PHP​

1
2
3
​wget http://cn2.php.net/distributions/php-5.6.5.tar.gz
tar -zxf php-5.6.5.tar.gz
cd php-5.6.5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
./configure  --prefix=/usr/local/php \
--with-apxs2=/usr/local/apache/bin/apxs \
--with-config-file-path=/usr/local/php/etc \
--with-pic --enable-mbstring \
--enable-inline-optimization \
--disable-static --with-regex=system \
--with-gettext --with-gd --with-zlib --with-gdbm --disable-debug \
--enable-safe-mode --enable-sysvmsg --enable-sysvsem \
--enable-sysvshm --enable-ftp --enable-xml \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-freetype-dir=/usr \
--with-png-dir=/usr \
--enable-gd-native-ttf --with-gmp --with-iconv \
--with-jpeg-dir=/usr/local/jpeg --with-libexpat-dir=/usr \
--enable-bcmath --enable-exif --enable-sockets --enable-wddx \
--enable-calendar --enable-shmop --enable-soap --enable-zend-multibyte \
--enable-magic-quotes --with-pear --with-curl \
--with-mcrypt=/usr/local/libmcrytp \
--enable-pcntl \
--enable-zip --with-openssl \
--with-tidy
1
2
3
make
make test
make install

的企业做好基本的优化工作,至于具体的SQL优化,大部分通过加适当的索引即可达到效果,更复杂的就需要具体分析了,可以参考本站的一些优化案例或者联系我,下方有我的联系方式。这是上篇。

1、硬件层相关优化

1.1、CPU相关

在服务器的BIOS设置中,可调整下面的几个配置,目的是发挥CPU最大性能,或者避免经典的NUMA问题:

1、选择Performance Per Watt Optimized(DAPC)模式,发挥CPU最大性能,跑DB这种通常需要高运算量的服务就不要考虑节电了;
2、关闭C1E和C States等选项,目的也是为了提升CPU效率;
3、Memory Frequency(内存频率)选择Maximum Performance(最佳性能);

4、内存设置菜单中,启用Node Interleaving,避免NUMA问题;

1.2、磁盘I/O相关

下面几个是按照IOPS性能提升的幅度排序,对于磁盘I/O可优化的一些措施:

1、使用SSD或者PCIe SSD设备,至少获得数百倍甚至万倍的IOPS提升;
2、购置阵列卡同时配备CACHE及BBU模块,可明显提升IOPS(主要是指机械盘,SSD或PCIe SSD除外。同时需要定期检查CACHE及BBU模块的健康状况,确保意外时不至于丢失数据);

3、有阵列卡时,设置阵列写策略为WB,甚至FORCE WB(若有双电保护,或对数据安全性要求不是特别高的话),严禁使用WT策略。并且闭阵列预读策略,基本上是鸡肋,用处不大;

4、尽可能选用RAID-10,而非RAID-5;

5、使用机械盘的话,尽可能选择高转速的,例如选用15KRPM,而不是7.2KRPM的盘,不差几个钱的;

2、系统层相关优化

2.1、文件系统层优化

在文件系统层,下面几个措施可明显提升IOPS性能:

1、使用deadline/noop这两种I/O调度器,千万别用cfq(它不适合跑DB类服务);
2、使用xfs文件系统,千万别用ext3;ext4勉强可用,但业务量很大的话,则一定要用xfs;

3、文件系统mount参数中增加:noatime, nodiratime, nobarrier几个选项(nobarrier是xfs文件系统特有的);

2.2、其他内核参数优化

针对关键内核参数设定合适的值,目的是为了减少swap的倾向,并且让内存和磁盘I/O不会出现大幅波动,导致瞬间波峰负载:

1、将vm.swappiness设置为5-10左右即可,甚至设置为0(RHEL 7以上则慎重设置为0,除非你允许OOM kill发生),以降低使用SWAP的机会;
2、将vm.dirty_background_ratio设置为5-10,将vm.dirty_ratio设置为它的两倍左右,以确保能持续将脏数据刷新到磁盘,避免瞬间I/O写,产生严重等待(和MySQL中的innodb_max_dirty_pages_pct类似);

3、将net.ipv4.tcp_tw_recycle、net.ipv4.tcp_tw_reuse都设置为1,减少TIME_WAIT,提高TCP效率;

4、至于网传的read_ahead_kb、nr_requests这两个参数,我经过测试后,发现对读写混合为主的OLTP环境影响并不大(应该是对读敏感的场景更有效果),不过没准是我测试方法有问题,可自行斟酌是否调整;

3、MySQL层相关优化

3.1、关于版本选择

官方版本我们称为ORACLE MySQL,这个没什么好说的,相信绝大多数人会选择它。

我个人强烈建议选择Percona分支版本,它是一个相对比较成熟的、优秀的MySQL分支版本,在性能提升、可靠性、管理型方面做了不少改善。它和官方ORACLE MySQL版本基本完全兼容,并且性能大约有20%以上的提升,因此我优先推荐它,我自己也从2008年一直以它为主。

另一个重要的分支版本是MariaDB,说MariaDB是分支版本其实已经不太合适了,因为它的目标是取代ORACLE MySQL。它主要在原来的MySQL Server层做了大量的源码级改进,也是一个非常可靠的、优秀的分支版本。但也由此产生了以GTID为代表的和官方版本无法兼容的新特性(MySQL 5.7开始,也支持GTID模式在线动态开启或关闭了),也考虑到绝大多数人还是会跟着官方版本走,因此没优先推荐MariaDB。

3.2、关于最重要的参数选项调整建议

建议调整下面几个关键参数以获得较好的性能(可使用本站提供的my.cnf生成器生成配置文件模板):

1、选择Percona或MariaDB版本的话,强烈建议启用thread pool特性,可使得在高并发的情况下,性能不会发生大幅下降。此外,还有extra_port功能,非常实用, 关键时刻能救命的。还有另外一个重要特色是 QUERY_RESPONSE_TIME 功能,也能使我们对整体的SQL响应时间分布有直观感受;

2、设置default-storage-engine=InnoDB,也就是默认采用InnoDB引擎,强烈建议不要再使用MyISAM引擎了,InnoDB引擎绝对可以满足99%以上的业务场景;

3、调整innodb_buffer_pool_size大小,如果是单实例且绝大多数是InnoDB引擎表的话,可考虑设置为物理内存的50% ~ 70%左右;

4、根据实际需要设置innodb_flush_log_at_trx_commit、sync_binlog的值。如果要求数据不能丢失,那么两个都设为1。如果允许丢失一点数据,则可分别设为2和10。而如果完全不用care数据是否丢失的话(例如在slave上,反正大不了重做一次),则可都设为0。这三种设置值导致数据库的性能受到影响程度分别是:高、中、低,也就是第一个会另数据库最慢,最后一个则相反;

5、设置innodb_file_per_table = 1,使用独立表空间,我实在是想不出来用共享表空间有什么好处了;

6、设置innodb_data_file_path = ibdata1:1G:autoextend,千万不要用默认的10M,否则在有高并发事务时,会受到不小的影响;

7、设置innodb_log_file_size=256M,设置innodb_log_files_in_group=2,基本可满足90%以上的场景;

8、设置long_query_time = 1,而在5.5版本以上,已经可以设置为小于1了,建议设置为0.05(50毫秒),记录那些执行较慢的SQL,用于后续的分析排查;

9、根据业务实际需要,适当调整max_connection(最大连接数)、max_connection_error(最大错误数,建议设置为10万以上,而open_files_limit、innodb_open_files、table_open_cache、table_definition_cache这几个参数则可设为约10倍于max_connection的大小;

10、常见的误区是把tmp_table_size和max_heap_table_size设置的比较大,曾经见过设置为1G的,这2个选项是每个连接会话都会分配的,因此不要设置过大,否则容易导致OOM发生;其他的一些连接会话级选项例如:sort_buffer_size、join_buffer_size、read_buffer_size、read_rnd_buffer_size等,也需要注意不能设置过大;

11、由于已经建议不再使用MyISAM引擎了,因此可以把key_buffer_size设置为32M左右,并且强烈建议关闭query cache功能;

3.3、关于Schema设计规范及SQL使用建议

下面列举了几个常见有助于提升MySQL效率的Schema设计规范及SQL使用建议:

1、所有的InnoDB表都设计一个无业务用途的自增列做主键,对于绝大多数场景都是如此,真正纯只读用InnoDB表的并不多,真如此的话还不如用TokuDB来得划算;

2、字段长度满足需求前提下,尽可能选择长度小的。此外,字段属性尽量都加上NOT NULL约束,可一定程度提高性能;

3、尽可能不使用TEXT/BLOB类型,确实需要的话,建议拆分到子表中,不要和主表放在一起,避免SELECT * 的时候读性能太差。

4、读取数据时,只选取所需要的列,不要每次都SELECT *,避免产生严重的随机读问题,尤其是读到一些TEXT/BLOB列;

5、对一个VARCHAR(N)列创建索引时,通常取其50%(甚至更小)左右长度创建前缀索引就足以满足80%以上的查询需求了,没必要创建整列的全长度索引;

6、通常情况下,子查询的性能比较差,建议改造成JOIN写法;

7、多表联接查询时,关联字段类型尽量一致,并且都要有索引;

8、多表连接查询时,把结果集小的表(注意,这里是指过滤后的结果集,不一定是全表数据量小的)作为驱动表;

9、多表联接并且有排序时,排序字段必须是驱动表里的,否则排序列无法用到索引;

10、多用复合索引,少用多个独立索引,尤其是一些基数(Cardinality)太小(比如说,该列的唯一值总数少于255)的列就不要创建独立索引了;

11、类似分页功能的SQL,建议先用主键关联,然后返回结果集,效率会高很多;

3.3、其他建议

关于MySQL的管理维护的其他建议有:

1、通常地,单表物理大小不超过10GB,单表行数不超过1亿条,行平均长度不超过8KB,如果机器性能足够,这些数据量MySQL是完全能处理的过来的,不用担心性能问题,这么建议主要是考虑ONLINE DDL的代价较高;

2、不用太担心mysqld进程占用太多内存,只要不发生OOM kill和用到大量的SWAP都还好;

3、在以往,单机上跑多实例的目的是能最大化利用计算资源,如果单实例已经能耗尽大部分计算资源的话,就没必要再跑多实例了;

4、定期使用pt-duplicate-key-checker检查并删除重复的索引。定期使用pt-index-usage工具检查并删除使用频率很低的索引;

5、定期采集slow query log,用pt-query-digest工具进行分析,可结合Anemometer系统进行slow query管理以便分析slow query并进行后续优化工作;

6、可使用pt-kill杀掉超长时间的SQL请求,Percona版本中有个选项 innodb_kill_idle_transaction 也可实现该功能;

7、使用pt-online-schema-change来完成大表的ONLINE DDL需求;

8、定期使用pt-table-checksum、pt-table-sync来检查并修复mysql主从复制的数据差异;

1.查看TCP连接状态

netstat -nat |awk ‘{print $6}’|sort|uniq -c|sort -rn

netstat -n | awk ‘/^tcp/ {++S[$NF]};END {for(a in S) print a, S[a]}’

netstat -n | awk ‘/^tcp/ {++state[$NF]}; END {for(key in state) print key,”t”,state[key]}’

netstat -n | awk ‘/^tcp/ {++arr[$NF]};END {for(k in arr) print k,”t”,arr[k]}’

netstat -n |awk ‘/^tcp/ {print $NF}’|sort|uniq -c|sort -rn

netstat -ant | awk ‘{print $NF}’ | grep -v ‘[a-z]’ | sort | uniq -c

netstat -ant|awk ‘/ip:80/{split($5,ip,”:”);++S[ip[1]]}END{for (a in S) print S[a],a}’ |sort -n

netstat -ant|awk ‘/:80/{split($5,ip,”:”);++S[ip[1]]}END{for (a in S) print S[a],a}’ |sort -rn|head -n 10

awk ‘BEGIN{printf (“http_codetcount_numn”)}{COUNT[$10]++}END{for (a in COUNT) printf a”tt”COUNT[a]”n”}’

2.查找请求数请20个IP(常用于查找攻来源):

netstat -anlp|grep 80|grep tcp|awk ‘{print $5}’|awk -F: ‘{print $1}’|sort|uniq -c|sort -nr|head -n20

netstat -ant |awk ‘/:80/{split($5,ip,”:”);++A[ip[1]]}END{for(i in A) print A[i],i}’ |sort -rn|head -n20

3.用tcpdump嗅探80端口的访问看看谁最高

tcpdump -i eth0 -tnn dst port 80 -c 1000 | awk -F”.” ‘{print $1″.”$2″.”$3″.”$4}’ | sort | uniq -c | sort -nr |head -20

4.查找较多time_wait连接

netstat -n|grep TIME_WAIT|awk ‘{print $5}’|sort|uniq -c|sort -rn|head -n20

5.找查较多的SYN连接

netstat -an | grep SYN | awk ‘{print $5}’ | awk -F: ‘{print $1}’ | sort | uniq -c | sort -nr | more

6.根据端口列进程

netstat -ntlp | grep 80 | awk ‘{print $7}’ | cut -d/ -f1

网站日志分析(Apache):
1.获得访问前10位的ip地址

cat access.log|awk ‘{print $1}’|sort|uniq -c|sort -nr|head -10

cat access.log|awk ‘{counts[$(11)]+=1}; END {for(url in counts) print counts[url], url}’

2.访问次数最多的文件或页面,取前20及统计所有访问IP

cat access.log|awk ‘{print $11}’|sort|uniq -c|sort -nr|head -20

awk ‘{ print $1}’ access.log |sort -n -r |uniq -c|wc -l

3.列出传输最大的几个exe文件(分析下载站的时候常用)

cat access.log |awk ‘($7~/.exe/){print $10 “ “ $1 “ “ $4 “ “ $7}’|sort -nr|head -20

4.列出输出大于200000byte(约200kb)的exe文件以及对应文件发生次数

cat access.log |awk ‘($10 > 200000 && $7~/.exe/){print $7}’|sort -n|uniq -c|sort -nr|head -100

5.如果日志最后一列记录的是页面文件传输时间,则有列出到客户端最耗时的页面

cat access.log |awk ‘($7~/.php/){print $NF “ “ $1 “ “ $4 “ “ $7}’|sort -nr|head -100

6.列出最最耗时的页面(超过60秒的)的以及对应页面发生次数

cat access.log |awk ‘($NF > 60 && $7~/.php/){print $7}’|sort -n|uniq -c|sort -nr|head -100

7.列出传输时间超过 30 秒的文件

cat access.log |awk ‘($NF > 30){print $7}’|sort -n|uniq -c|sort -nr|head -20

8.统计网站流量(G)

cat access.log |awk ‘{sum+=$10} END {print sum/1024/1024/1024}’

9.统计404的连接

awk ‘($9 ~/404/)’ access.log | awk ‘{print $9,$7}’ | sort

10. 统计http status.

cat access.log |awk ‘{counts[$(9)]+=1}; END {for(code in counts) print code, counts}’

cat access.log |awk ‘{print $9}’|sort|uniq -c|sort -rn

11.每秒并发:

awk ‘{if($9~/200|30|404/)COUNT[$4]++}END{for( a in COUNT) print a,COUNT[a]}’|sort -k 2 -nr|head -n10

12.带宽统计

cat apache.log |awk ‘{if($7~/GET/) count++}END{print “client_request=”count}’

cat apache.log |awk ‘{BYTE+=$11}END{print “client_kbyte_out=”BYTE/1024″KB”}’

13.统计对象数量及对象平均大小

cat access.log |awk ‘{byte+=$10}END{ print byte/NR/1024,NR}’

cat access.log |awk ‘{if($9~/200|30/)COUNT[$NF]++}END{for( a in COUNT) print a,COUNT

[a],NR,COUNT[a]/NR*100″%”}

14.取5分钟日志

if [ $DATE_MINUTE != $DATE_END_MINUTE ] ;then #则判断开始时间戳与结束时间戳是否相等START_LINE=sed -n “/$DATE_MINUTE/=" $APACHE_LOG|head -n1 #如果不相等,则取出开始时间戳的行号,与结束时间戳的行号

#END_LINE=sed -n “/$DATE_END_MINUTE/=" $APACHE_LOG|tail -n1

END_LINE=sed -n “/$DATE_END_MINUTE/=" $APACHE_LOG|head -n1sed -n “${START_LINE},${END_LINE}p” $APACHE_LOG > $MINUTE_LOG ##通过行号,取出5分钟内的日志内容 存放到 临时文件中

GET_START_TIME=`sed -n “${START_LINE}p” $APACHE_LOG|awk -F ‘[‘ ‘{print $2}’ |awk ‘{print $1}’|

sed ‘s#/# #g’|sed ‘s#:# #’` #通过行号获取取出开始时间戳

GET_END_TIME=`sed -n “${END_LINE}p” $APACHE_LOG|awk -F ‘[‘ ‘{print $2}’ |awk ‘{print $1}’|sed

‘s#/# #g’|sed ‘s#:# #’` #通过行号获取结束时间戳

15.蜘蛛分析

查看是哪些蜘蛛在抓取内容

/usr/sbin/tcpdump -i eth0 -l -s 0 -w – dst port 80 | strings | grep -i user-agent | grep -i -E ‘bot|crawler|slurp|spider’

网站日分析2(Squid篇)

2.按域统计流量

zcat squid_access.log.tar.gz| awk ‘{print $10,$7}’ |awk ‘BEGIN{FS=”[ /]”}{trfc[$4]+=$1}END{for

(domain in trfc){printf “%st%dn”,domain,trfc[domain]}}’

数据库篇

1.查看数据库执行的sql

/usr/sbin/tcpdump -i eth0 -s 0 -l -w – dst port 3306 | strings | egrep -i ‘SELECT|UPDATE|DELETE|INSERT|SET|COMMIT|ROLLBACK|CREATE|DROP|ALTER|CALL’

系统Debug分析篇

1.调试命令

strace -p pid

2.跟踪指定进程的PID

gdb -p pid

sar -n { DEV | EDEV | NFS | NFSD | SOCK | ALL }

 

sar 提供六种不同的语法选项来显示网络信息。-n选项使用6个不同的开关:DEV | EDEV | NFS | NFSD | SOCK | ALL 。DEV显示网络接口信息,EDEV显示关于网络错误的统计数据,NFS统计活动的NFS客户端的信息,NFSD统计NFS服务器的信息,SOCK显示套接字信息,ALL显示所有5个开关。它们可以单独或者一起使用。

 

 

sar(System Activity Reporter系统活动情况报告)是目前 Linux 上最为全面的系统性能分析工具之一,可以从多方面对系统的活动进行报告,包括:文件的读写情况、系统调用的使用情况、磁盘I/O、CPU效率、内存使用状况、进程活动及IPC有关的活动等。本文主要以CentOS 6.3 x64系统为例,介绍sar命令。

 

sar命令常用格式

sar [options] [-A] [-o file] t [n]

 

其中:

 

t为采样间隔,n为采样次数,默认值是1;

 

-o file表示将命令结果以二进制格式存放在文件中,file 是文件名。

 

options 为命令行选项,sar命令常用选项如下:

 

 

 

-A:所有报告的总和

 

-u:输出CPU使用情况的统计信息

 

-v:输出inode、文件和其他内核表的统计信息

 

-d:输出每一个块设备的活动信息

 

-r:输出内存和交换空间的统计信息

 

-b:显示I/O和传送速率的统计信息

 

-a:文件读写情况

 

-c:输出进程统计信息,每秒创建的进程数

 

-R:输出内存页面的统计信息

 

-y:终端设备活动情况

 

-w:输出系统交换活动信息

 

1. CPU资源监控

例如,每10秒采样一次,连续采样3次,观察CPU 的使用情况,并将采样结果以二进制形式存入当前目录下的文件test中,需键入如下命令:

 

sar -u -o test 10 3

 

屏幕显示如下:

 

17:06:16 CPU %user %nice %system %iowait %steal %idle

 

17:06:26 all 0.00 0.00 0.20 0.00 0.00 99.80

 

17:06:36 all 0.00 0.00 0.20 0.00 0.00 99.80

 

17:06:46 all 0.00 0.00 0.10 0.00 0.00 99.90

 

Average: all 0.00 0.00 0.17 0.00 0.00 99.83

 

输出项说明:

 

CPU:all 表示统计信息为所有 CPU 的平均值。

 

%user:显示在用户级别(application)运行使用 CPU 总时间的百分比。

 

%nice:显示在用户级别,用于nice操作,所占用 CPU 总时间的百分比。

 

%system:在核心级别(kernel)运行所使用 CPU 总时间的百分比。

 

%iowait:显示用于等待I/O操作占用 CPU 总时间的百分比。

 

%steal:管理程序(hypervisor)为另一个虚拟进程提供服务而等待虚拟 CPU 的百分比。

 

%idle:显示 CPU 空闲时间占用 CPU 总时间的百分比。

 

1. 若 %iowait 的值过高,表示硬盘存在I/O瓶颈

 

2. 若 %idle 的值高但系统响应慢时,有可能是 CPU 等待分配内存,此时应加大内存容量

 

3. 若 %idle 的值持续低于1,则系统的 CPU 处理能力相对较低,表明系统中最需要解决的资源是 CPU 。

 

如果要查看二进制文件test中的内容,需键入如下sar命令:

 

sar -u -f test

 

2. inode、文件和其他内核表监控

例如,每10秒采样一次,连续采样3次,观察核心表的状态,需键入如下命令:

 

sar -v 10 3

 

屏幕显示如下:

 

17:10:49 dentunusd file-nr inode-nr pty-nr

 

17:10:59 6301 5664 12037 4

 

17:11:09 6301 5664 12037 4

 

17:11:19 6301 5664 12037 4

 

Average: 6301 5664 12037 4

 

输出项说明:

 

dentunusd:目录高速缓存中未被使用的条目数量

 

file-nr:文件句柄(file handle)的使用数量

 

inode-nr:索引节点句柄(inode handle)的使用数量

 

pty-nr:使用的pty数量

 

3. 内存和交换空间监控

例如,每10秒采样一次,连续采样3次,监控内存分页:

 

sar -r 10 3

 

屏幕显示如下:

 

 

 

输出项说明:

 

kbmemfree:这个值和free命令中的free值基本一致,所以它不包括buffer和cache的空间.

 

kbmemused:这个值和free命令中的used值基本一致,所以它包括buffer和cache的空间.

 

%memused:这个值是kbmemused和内存总量(不包括swap)的一个百分比.

 

kbbuffers和kbcached:这两个值就是free命令中的buffer和cache.

 

kbcommit:保证当前系统所需要的内存,即为了确保不溢出而需要的内存(RAM+swap).

 

%commit:这个值是kbcommit与内存总量(包括swap)的一个百分比.

 

4. 内存分页监控

例如,每10秒采样一次,连续采样3次,监控内存分页:

 

sar -B 10 3

 

屏幕显示如下:

 

 

 

输出项说明:

 

pgpgin/s:表示每秒从磁盘或SWAP置换到内存的字节数(KB)

 

pgpgout/s:表示每秒从内存置换到磁盘或SWAP的字节数(KB)

 

fault/s:每秒钟系统产生的缺页数,即主缺页与次缺页之和(major + minor)

 

majflt/s:每秒钟产生的主缺页数.

 

pgfree/s:每秒被放入空闲队列中的页个数

 

pgscank/s:每秒被kswapd扫描的页个数

 

pgscand/s:每秒直接被扫描的页个数

 

pgsteal/s:每秒钟从cache中被清除来满足内存需要的页个数

 

%vmeff:每秒清除的页(pgsteal)占总扫描页(pgscank+pgscand)的百分比

 

5. I/O和传送速率监控

例如,每10秒采样一次,连续采样3次,报告缓冲区的使用情况,需键入如下命令:

 

sar -b 10 3

 

屏幕显示如下:

 

18:51:05 tps rtps wtps bread/s bwrtn/s

 

18:51:15 0.00 0.00 0.00 0.00 0.00

 

18:51:25 1.92 0.00 1.92 0.00 22.65

 

18:51:35 0.00 0.00 0.00 0.00 0.00

 

Average: 0.64 0.00 0.64 0.00 7.59

 

输出项说明:

 

tps:每秒钟物理设备的 I/O 传输总量

 

rtps:每秒钟从物理设备读入的数据总量

 

wtps:每秒钟向物理设备写入的数据总量

 

bread/s:每秒钟从物理设备读入的数据量,单位为 块/s

 

bwrtn/s:每秒钟向物理设备写入的数据量,单位为 块/s

 

6. 进程队列长度和平均负载状态监控

例如,每10秒采样一次,连续采样3次,监控进程队列长度和平均负载状态:

 

sar -q 10 3

 

屏幕显示如下:

 

19:25:50 runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15

 

19:26:00 0 259 0.00 0.00 0.00

 

19:26:10 0 259 0.00 0.00 0.00

 

19:26:20 0 259 0.00 0.00 0.00

 

Average: 0 259 0.00 0.00 0.00

 

输出项说明:

 

runq-sz:运行队列的长度(等待运行的进程数)

 

plist-sz:进程列表中进程(processes)和线程(threads)的数量

 

ldavg-1:最后1分钟的系统平均负载(System load average)

 

ldavg-5:过去5分钟的系统平均负载

 

ldavg-15:过去15分钟的系统平均负载

 

7. 系统交换活动信息监控

例如,每10秒采样一次,连续采样3次,监控系统交换活动信息:

 

sar -    W 10 3

 

屏幕显示如下:

 

19:39:50 pswpin/s pswpout/s

 

19:40:00 0.00 0.00

 

19:40:10 0.00 0.00

 

19:40:20 0.00 0.00

 

Average: 0.00 0.00

 

输出项说明:

 

pswpin/s:每秒系统换入的交换页面(swap page)数量

 

pswpout/s:每秒系统换出的交换页面(swap page)数量

 

8. 设备使用情况监控

例如,每10秒采样一次,连续采样3次,报告设备使用情况,需键入如下命令:

 

sar -d 10 3 –p

 

屏幕显示如下:

 

17:45:54    DEV    tps    rd_sec/s    wr_sec/s    avgrq-sz    avgqu-sz    await    svctm    %util

 

17:46:04    scd0    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00

 

17:46:04    sda    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00

 

17:46:04    vg_livedvd-lv_root    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00

 

17:46:04    vg_livedvd-lv_swap    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00

 

其中:

 

参数-p可以打印出sda,hdc等磁盘设备名称,如果不用参数-p,设备节点则有可能是dev8-0,dev22-0

 

tps:每秒从物理磁盘I/O的次数.多个逻辑请求会被合并为一个I/O磁盘请求,一次传输的大小是不确定的.

 

rd_sec/s:每秒读扇区的次数.

 

wr_sec/s:每秒写扇区的次数.

 

avgrq-sz:平均每次设备I/O操作的数据大小(扇区).

 

avgqu-sz:磁盘请求队列的平均长度.

 

await:从请求磁盘操作到系统完成处理,每次请求的平均消耗时间,包括请求队列等待时间,单位是毫秒(1秒=1000毫秒).

 

svctm:系统处理每次请求的平均时间,不包括在请求队列中消耗的时间.

 

%util:I/O请求占CPU的百分比,比率越大,说明越饱和.

 

1. avgqu-sz 的值较低时,设备的利用率较高。

 

2. 当%util的值接近 1% 时,表示设备带宽已经占满。

 

要判断系统瓶颈问题,有时需几个 sar 命令选项结合起来

怀疑CPU存在瓶颈,可用 sar -u 和 sar -q 等来查看

 

怀疑内存存在瓶颈,可用 sar -B、sar -r 和 sar -W 等来查看

 

怀疑I/O存在瓶颈,可用 sar -b、sar -u 和 sar -d 等来查看

Apache官方说:

与Apache 2.2.x相比,Apache 2.4.x提供了很多性能方面的提升,包括支持更大流量、更好地支持云计算、利用更少的内存处理更多的并发等。除此之外,还包括性能提升、内存利用、异步 I/O的支持、动态反向代理设置、与时间驱动的Web服务器相当或更好的性能、更强大的处理资源分配能力,更便捷的缓存支持以及可定制的高速服务器和代理 等。其它的功能还包括更简单的错误分析、更灵活的设置项、更强大的验证机制和更完整的文档。

Apache服务器项目管理委员会和Apache基金会主席Jim Jagielski表示,他们希望终端用户能真正地看到性能进步,Apache 2.4.x比许多以速度见长的Web服务器更快,例如 Nginx。

apache-2.2与新出的apache-2.4安装不同的地方在于,2.4版的已经不自带apr库,所以在安装apache-2.4之前,需要下载apr。

所需源码包

apr-1.4.6.tar.gz(可从http://apr.apache.org/download.cgi下载到最新版本)

apr-util-1.4.1.tar.gz(可从http://apr.apache.org/download.cgi下载到最新版本)

httpd-2.4.6.tar.gz(可从http://www.apache.org/dist/httpd/下载到最新版本)

pcre-8.33.tar.gz(可从ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/下载到最新版本)

安装Apache依赖库

#安装 apr

1
2
3
4
5
tar -xzvf ./apr-1.4.6.tar.gz
cd ./apr-1.4.6
mkdir /usr/local/apr
./configure --prefix=/usr/local/apr
make && make install

#安装 apr-util

1
2
3
4
5
tar -xzvf ./apr-util-1.4.1.tar.gz
cd ./apr-util-1.4.1
mkdir /usr/local/apr-util
./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr/bin/apr-1-config
make && make install

#安装 pcre

1
2
3
4
5
tar -xzvf ./pcre-8.33.tar.gz
cd ./pcre-8.33
mkdir /usr/local/pcre
./configure --prefix=/usr/local/pcre --with-apr=/usr/local/apr/bin/apr-1-config
make && make install

安装 Apache2.4.6

#切换到apache源码目录

1
2
3
4
tar -xzvf ./httpd-2.4.6.tar.gz
cd ./httpd-2.4.6
./configure --prefix=/usr/local/apache --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --with-pcre=/usr/local/pcre --enable-so --enable-deflate=shared --enable-expires=shared --enable-ssl=shared --enable-headers=shared --enable-rewrite=shared --enable-static-support --with-mpm=prefork
make && make install

编译参数解释:

  • –prefix=/usr/local/apache :指定安装目录
  • –with-apr=/usr/local/apr : apr库
  • –with-apr-util=/usr/local/apr-util :apr-util库
  • –with-pcre=/usr/local/pcre : pcre库
  • –enable-so : 允许运行时加载DSO模块(注意:so模块需静态编译)
  • –enable-deflate=shared : 将deflate模块编译为DSO
  • –enable-expires=shared : 将expires模块编译为DSO
  • –enable-ssl=shared : 将ssl模块编译为DSO
  • –enable-headers=shared : 将headers模块编译为DSO
  • –enable-rewrite=shared : 将rewrite模块编译为DSO
  • –enable-static-support : 使用静态连接(默认为动态连接)编译所有二进制支持程序
  • –with-mpm=prefork : 使用prefork形式的mpm

更详细的编译参数解释:http://lamp.linux.gov.cn/Apache/ApacheMenu/programs/configure.html

设置开启启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cp ./build/rpm/httpd.init  /etc/init.d/httpd  #使用init脚本管理httpd
chmod 755 /etc/init.d/httpd #增加执行权限
chkconfig --add httpd #添加httpd到服务项
chkconfig --level 2345 httpd on #设置开机启动
chkconfig --list httpd #查看是否设置成功

mv /etc/httpd /etc/httpd_old #移走旧的httpd文件夹
ln -s /usr/local/apache /etc/httpd #建立httpd的软链接,#到时候,Apache的配置文件路径为 /etc/httpd/conf/httpd.conf,其实真实路径为 /usr/local/apache/conf/httpd.conf

ln -sf /usr/local/apache/bin/httpd /usr/sbin/httpd #设置软链接以适应init脚本
ln -sf /usr/local/apache/bin/apachectl /usr/sbin/apachectl

rm -rf /var/log/httpd/
ln -s /usr/local/apache/logs /var/log/httpd

groupadd apache #添加apache用户组及用户
useradd -g apache -s /usr/sbin/nologin apache
chown -R apache:apache /usr/local/apache

修改init命令文件

  • 主要是修改文件中pidfile参数的值(进程文件指向)
1
vim /etc/init.d/httpd
  • 把其中的
1
pidfile=${PIDFILE-/var/run/${prog}.pid}
  • 修改为
1
pidfile=${PIDFILE-/usr/local/apache/logs/${prog}.pid}

配置防火墙,开启80端口

1
vim /etc/sysconfig/iptables

添加如下规则到22端口这条规则的下面即可

1
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

#重启防火墙

1
service iptables restart # 或 /etc/init.d/iptables restart

启动Apache

1
service httpd start # 或 /etc/init.d/httpd start

prefork 模式:

<IfModule mpm_prefork_module>
ServerLimit  20000
StartServers             20                      #指定服务器启动时建立的子进程数量
MinSpareServers          20                #指定空闲子进程的最小数量
MaxSpareServers         40                #指定空闲子进程的最大数量
MaxRequestWorkers      20000       #指定同一时间客户端最大接入请求的数量(单个进程并发线程数),任何超过该限制的请求都将进入等候队列,一旦一个连接被释放,队列中的请求将得到服务
MaxConnectionsPerChild   0         #指定每个子进程在其生存周期内允许伺服的最大请求数量,默认为10000,0表示子进程永远不结束 注意2.3.13版本以前的用 MaxClients
</IfModule>

http://httpd.apache.org/docs/current/mod/mpm_common.html

表单提交失败调试代码

1
echo array_values($model->getFirstErrors())[0];exit;

获取当前Controller name和action name(在控制器里面使用)

1
2
echo $this->id;
echo $this->action->id;

控制器获取当前模块

1
$this->module->id

use yii\log\Logger;

1
2
use yii\log\Logger;
\Yii::getLogger()->log('User has been created', Logger::LEVEL_INFO);

不生成label标签

1
2
3
4
// ActiveForm类
$form->field($model, '字段名')->passwordInput(['maxlength' => true])->label(false) ?>
// HTML类
Html::activeInput($type,$model,'字段名')

Yii2 获取接口传过来的 JSON 数据:

1
Yii::$app->request->rawBody;

防止 SQL 和 Script 注入:

1
2
3
4
5
use yii\helpers\Html;
use yii\helpers\HtmlPurifier;

echo Html::encode($view_hello_str) //可以原样显示<script></script>代码
echo HtmlPurifier::process($view_hello_str) //可以过滤掉<script></script>代码

大于、小于条件查询

1
2
3
4
5
// SELECT * FROM `order` WHERE `subtotal` > 200 ORDER BY `id`
$orders = $customer->getOrders()
->where(['>', 'subtotal', 200])
->orderBy('id')
->all();

搜索的时候添加条件筛选

1
2
3
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
// $dataProvider->query->andWhere(['pid' => 0]);
$dataProvider->query->andWhere(['>', 'pid', 0]);

有两种方式获取查询出来的 name 为数组的集合 [name1, name2, name3]:

方式一:

1
return \yii\helpers\ArrayHelper::getColumn(User::find()->all(), 'name');

方式二:

1
return User::find()->select('name')->asArray()->column();

打印数据

1
2
3
4
5
6
7
8
// 引用命名空间
use yii\helpers\VarDumper;

// 使用
VarDumper::dump($var);

// 使用2 第二个参数是数组的深度 第三个参数是是否显示代码高亮(默认不显示)
VarDumper::dump($var, 10 ,true);die;

表单验证,只要需要一个参数

1
2
3
4
5
6
7
8
9
10
public function rules()
{
return [
[['card_id', 'card_code'], function ($attribute, $param) {//至少要一个
if (empty($this->card_code) && empty($this->card_id)) {
$this->addError($attribute, 'card_id/card_code至少要填一个');
}
}, 'skipOnEmpty' => false],
];
}

where 多个查询条件示例

1
User::find()->where(['and', ['xxx' => 0, 'yyy' => 2], ['>', 'zzz', $time]]);

获取post数据

1
2
$post = Yii::$app->request->post();
$id = $post['id'];

查找 auth_times 表 type=1 并且 不存在 auth_item 表里面的数据

查找 auth_timestype=1 并且 不存在 auth_item 表里面的数据

1
2
3
4
5
6
7
8
9
// AuthItem.php 关键是 onCondition 方法
public function getAuthTimes()
{
return $this->hasOne(AuthTimes::className(), ['name' => 'name', ])->onCondition([AuthTimes::tableName() . '.type' => 1]);
}

// AuthTimes.php 文件
// ......
AuthItem::find()->joinWith('authTimes')->where([self::tableName() . '.name' => null])->all();

生成SQL:

1
SELECT `auth_item`.* FROM `auth_item` LEFT JOIN `auth_times` ON `auth_item`.`name` = `auth_times`.`name` AND `auth_times`.`type` = 1 WHERE `auth_times`.`name` IS NULL

SQL is not null条件查询

1
2
3
4
5
6
7
8
9
10
// ['not' => ['attribute' => null]]

$query = new Query;
$query->select('ID, City,State,StudentName')
->from('student')
->where(['IsActive' => 1])
->andWhere(['not', ['City' => null]])
->andWhere(['not', ['State' => null]])
->orderBy(['rand()' => SORT_DESC])
->limit(10);

校验 point_template_id 在 PointTemplate 是否存在

1
2
3
4
5
6
7
8
9
10
11
12
public function rules()
{
return [
...
[['point_template_id'], 'exist',
'targetClass' => PointTemplate::className(),
'targetAttribute' => 'id',
'message' => '此{attribute}不存在。'
],
...
];
}

Yii给必填项加星

1
2
3
4
div.required label:after {
content: " *";
color: red;
}

view里面获取当前action

1
var_dump(Yii::$app->controller->action->id);

关于事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Yii::$app->db->transaction(function() {
$order = new Order($customer);
$order->save();
$order->addItems($items);
});

// 这相当于下列冗长的代码:

$transaction = Yii::$app->db->beginTransaction();
try {
$order = new Order($customer);
$order->save();
$order->addItems($items);
$transaction->commit();
} catch (\Exception $e) {
$transaction->rollBack();
throw $e;
}

restful 获取 GET 和 POST 过来的数据(得到结果是数组)

1
2
3
4
5
// post
Yii::$app->request->bodyParams

// get
Yii::$app->request->queryParams;

获取GET数据

1
$server_id = Yii::$app->getRequest()->get('server_id');

查询的时候 where 的 OR 和 AND 一起用

1
2
3
4
5
Topic::updateAll(
['last_comment_time' => new Expression('created_at')],
// ['or', ['type' => Topic::TYPE, 'last_comment_username' => ''], ['type' => Topic::TYPE, 'last_comment_username' => null]]
['and', ['type' => Topic::TYPE], ['or', ['last_comment_username' => ''], ['last_comment_username' => null]]]
);

### 嵌套查询,groupBy 分组之后排序功能

1
2
3
4
5
$subQuery = new Query();
$subQuery->from(PostComment::tableName())->where(['status' => PostComment::STATUS_ACTIVE])->orderBy(['created_at' => SORT_DESC]);
$comment = PostComment::find()->from(['tmpA' => $subQuery])
->groupBy('post_id')
->all();

生成的语句是

1
SELECT * FROM (SELECT * FROM `post_comment` WHERE `status`=1 ORDER BY `created_at` DESC) `tmpA` GROUP BY `post_id`

Model 获取当前 module id

1
Yii::$app->controller->module->id

一个控制器调用其他控制器action的方法

1
2
3
Yii::$app->runAction('new_controller/new_action', $params);
// 或者
return (new SecondController('second', Yii::$app->module))->runAction('index', $data);

IP 白名单

1
2
3
4
$config['modules']['debug'] = [
'class' => 'yii\debug\Module',
'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.33.1'],
];

点击下载文件 action

1
2
3
4
5
6
7
8
9
10
public function actionDownload($id)
{
$model = $this->findModel($id);

if ($model) {
// do something
}
return \Yii::$app->response->setDownloadHeaders($model->downurl);

}

发送邮件 config/config.php中的components配置

1
2
3
4
5
6
7
8
9
10
11
12
'mailer' => [
'class' => 'yii\swiftmailer\Mailer',
'useFileTransport' => false,
'transport' => [
'class' => 'Swift_SmtpTransport',
'host' => 'smtp.gmail.com',
'username' => '[email protected]',
'password' => 'password12345678',
'port' => 587,//or 25/587
'encryption' => 'tls',//tls or ssl
]
],

使用

1
2
3
4
5
6
Yii::$app->mailer->compose()
->setFrom(['[email protected]' => Yii::$app->name])
->setTo('[email protected]')
->setSubject('test subject')
->setTextBody('test body')
->send();

修改登陆状态超时时间(到期后自动退出登陆) config/web.php中的components

1
2
3
4
5
6
7
'user' => [
'class'=>'yii\web\User',
'identityClass' => 'common\models\User',
'loginUrl'=>['/user/sign-in/login'],
'authTimeout' => 1800,//登陆有效时间
'as afterLogin' => 'common\behaviors\LoginTimestampBehavior'
],

修改返回的数据格式(详见Response::FORMAT_XXXX)

1
2
3
4
5
6
7
8
9
10
11
12
13
$result = array('code' => $code, 'msg' => $msg, 'data' => $data);
$callback = Yii::$app->request->get('callback',null);

$format = $callback ? Response::FORMAT_JSONP : Response::FORMAT_JSON;
Yii::$app->response->format = $format;

if($callback){
return array(
'callback' => $callback,
'data' => $result
);
}
return $result;

执行SQL查询并缓存结果

1
2
3
4
$styleId = Yii::$app->request->get('style');
$collection = Yii::$app->db->cache(function($db) use($styleId){
return Collection::findOne(['style_id'=>$styleId]);
}, self::SECONDS_IN_MINITUE * 10);

用户头像加域名

场景: 数据库有user 表有个avatar_path 字段用来保存用户头像路径

需求: 头像url需要通过域名http://b.com/作为基本url

目标: 提高代码复用

此处http://b.com/可以做成一个配置
示例:

User.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class User extends \yii\db\ActiveRecord
{
...
public function extraFields()
{
$fields = parent::extraFields();

$fields['avatar_url'] = function () {
return empty($this->avatar_path) ? '可以设置一个默认的头像地址' : 'http://b.com/' . $this->avatar_path;
};

return $fields;
}
...
}

ExampleController.php

1
2
3
4
5
6
7
8
9
10
class ExampleController extends \yii\web\Controller
{
public function actionIndex()
{
$userModel = User::find()->one();
$userData = $userModel->toArray([], ['avatar_url']);

echo $userData['avatar_url']; // 输出内容: http://b.com/头像路径
}
}

避免select里面的子查询被识别成字段

1
2
3
4
5
6
//避免select里面的子查询被识别成字段
$quert = User::find()
->select([
new Expression('count(*) as count , count(distinct mobile) as mnumber')
])->asArray()
->all();

like 查询

1
2
$query = User::find()
->where(['LIKE', 'name', $id.'%', false]);

来源:http://www.getyii.com/topic/47

上一篇:Yii2.0数据库查询实例(二)

常用查询

1
2
// WHERE admin_id >= 10 LIMIT 0,10
User::find()->select('*')->where(['>=', 'admin_id', 10])->offset(0)->limit(10)->all()
1
2
3
// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post`   
$subQuery = (new Query())->select('COUNT(*)')->from('user');
$query = (new Query())->select(['id', 'count' => $subQuery])->from('post');
1
2
// SELECT DISTINCT `user_id` ... 
User::find()->select('user_id')->distinct();

更新

1
2
3
4
5
6
7
8
9
10
11
//update();
//runValidation boolen 是否通过validate()校验字段 默认为true
//attributeNames array 需要更新的字段
$model->update($runValidation , $attributeNames);

//updateAll();
//update customer set status = 1 where status = 2
Customer::updateAll(['status' => 1], 'status = 2');

//update customer set status = 1 where status = 2 and uid = 1;
Customer::updateAll(['status' => 1], ['status'=> '2','uid'=>'1']);

删除

1
2
3
4
$model = Customer::findOne($id);
$model->delete();

$model->deleteAll(['id'=>1]);

批量插入

1
2
3
4
5
Yii::$app->db->createCommand()->batchInsert(UserModel::tableName(), ['user_id','username'], [
['1','test1'],
['2','test2'],
['3','test3'],
])->execute();

查看执行sql

1
2
3
//UserModel 
$query = UserModel::find()->where(['status'=>1]);
echo $query->createCommand()->getRawSql();

来源:http://www.yii-china.com/post/detail/24.html