-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 108 KB
/
content.json
1
[{"title":"PHP性能优化-XHProf","date":"2017-04-11T16:12:16.000Z","path":"2017/04/12/PHP性能优化-XHProf/","text":"背景最近在做关于项目的接口性能优化的相关工作,于是想到了Xhprof,决定把它用在现有项目中,观察下接口的调用情况。 xhprof是一个函数级别的分层PHP性能分析工具,xhprof能统计每个函数的调用次数,cpu使用时间内存占用等多项指标,它将函数的开销,细分为调用者和被调用者的开销。展示页面基于浏览器非常人性化,分为两个性能报告页面,一个是报表格式的一个是直观化的图表格式。根据这些分析数据,可以轻松的看到程序的开销情况,找出低效率的程序进行优化。 在安装的过程中,踩了一些坑,总结一下经验。 注:对于应该不应该将XHProf部署到生产环境,还不知晓,但是官方说可以,但是最好是采样模式(1/1000请求)进行尝试,以降低服务器资源开销。 安装PHP的xhprof扩展从官网下载的安装包(wget http://pecl.php.net/get/xhprof-0.9.2.tgz),安装的过程中,make && make install会有[xhprof.lo] Error 1的报错。所以这里建议在github下载安装: 12345678910111213141516git clone https://github.com/phacility/xhprofcd xhprof/extension/usr/local/bin/phpize./configure --with-php-config=/usr/local/bin/php-configmake && make install[php.ini]在编译完成之后,需要在xhprof运行时配置(php.ini)在php.ini中加入extension=xhprof.so xhprof.output_dir=/usr/local/var/www/xhprof这个目录是用来存放分析的结果数据 在安装完成之后,可通过 php –ri xhprof 查看是否安装成功。 1234php --ri xhprofxhprof => 0.9.2CPU num => 4 此时说明扩展已经安装成功 安装Graphviz将XHProf的分析报告以程序的函数调用图来展现。 12345wget http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.34.0.tar.gztar zxvf graphviz-2.34.0.tar.gzcd graphviz-2.34.0./configuremake && make install 如加上–prefix编译参数,将安装完的主目录中的bin目录路径加入到环境变量PATH中,另外需要注意的如果通过XHProf提供的PHP UI界面查看函数调用图,需要将PHP的proc_open类的函数打开。 运行Xhprof在服务器或本地的web目录下,新建xhprof目录,然后将下载的xhprof项目的xhprof_html、xhprof_lib、examples放到新建的xhprof中。 1cp -R /Downloads/xhprof /usr/local/var/www/work/xhprof 在根目录下在创建目录,用来存放分析结果: 1mkdir -P /usr/local/var/www/xhprof 然后在Nginx及FPM增加以下配置: 1234567891011121314151617181920212223242526272829303132333435363738nginx.conf: server { listen 80; server_name xhprof.dev; root /usr/local/var/www/work/xhprof; index index.php index.html; access_log /usr/local/var/log/nginx/xhprof.dev.log; error_log /usr/local/var/log/nginx/xhprof.dev.log.err; location ~* \\.php$ { fastcgi_pass 127.0.0.1:9003; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php; include fastcgi_params; } } php-fpm:[xhprof.dev]user = wwwgroup = wwwlisten = 127.0.0.1:9003listen.allowed_clients = 127.0.0.1pm = dynamicpm.max_children = 512pm.start_servers = 32pm.min_spare_servers = 32pm.max_spare_servers = 64pm.max_requests = 1500pm.status_path = /h3_monitorslowlog = /usr/local/var/log/$pool-slow_logrequest_slowlog_timeout = 3request_terminate_timeout = 20catch_workers_output = nosecurity.limit_extensions = ""env[XHPROF_ROOT_PATH] = /usr/local/var/www/work/xhprof/;此配置是让xhprof加入到环境变量中使用 安装完成后,需要重启nginx及php-fpm: 12/usr/local/sbin/php-fpm restartnginx -s reload 在浏览器中输入:http://xhprof.dev/examples/sample.php,有如下的结果: 1Array ( [foo==>bar] => Array ( [ct] => 5 [wt] => 147 ) [foo==>strlen] => Array ( [ct] => 5 [wt] => 6 ) [bar==>bar@1] => Array ( [ct] => 4 [wt] => 19 ) [bar@1==>bar@2] => Array ( [ct] => 3 [wt] => 3 ) [bar@2==>bar@3] => Array ( [ct] => 2 [wt] => 1 ) [bar@3==>bar@4] => Array ( [ct] => 1 [wt] => 0 ) [main()==>foo] => Array ( [ct] => 1 [wt] => 276 ) [main()==>xhprof_disable] => Array ( [ct] => 1 [wt] => 0 ) [main()] => Array ( [ct] => 1 [wt] => 287 ) ) --------------- Assuming you have set up the http based UI for XHProf at some address, you can view run at http://xhprof.dev/index.php?run=58ec5faa0034c&source=xhprof_foo --------------- 在/usr/local/var/www/xhprof/output下生产下面的文件: 1234567891011121314➜ www cd xhprof/output➜ output lltotal 256-rw-r--r-- 1 Wicky admin 308 4 10 21:05 58eb8332ad434.xhprof_foo.xhprof-rw-r--r-- 1 Wicky admin 486 4 10 21:07 58eb8374b2143.xhprof_foo.xhprof-rw-r--r-- 1 Wicky admin 307 4 11 10:03 58ec3966e0a06.xhprof_foo.xhprof-rw-r--r-- 1 Wicky admin 483 4 11 10:03 58ec396f59585.xhprof_foo.xhprof-rw-r--r-- 1 Wicky admin 483 4 11 10:03 58ec39727a2da.xhprof_foo.xhprof-rw-r--r-- 1 Wicky admin 307 4 11 10:03 58ec397b5d9b6.xhprof_foo.xhprof-rw-r--r-- 1 Wicky admin 307 4 11 11:05 58ec47e69431d.xhprof_foo.xhprof-rw-r--r-- 1 Wicky admin 308 4 11 11:05 58ec47e7a6c41.xhprof_foo.xhprof-rw-r--r-- 1 Wicky admin 38529 4 11 11:15 58ec4a5f42fab.xhprof.xhprof-rw-r--r-- 1 Wicky admin 49627 4 11 11:24 58ec4c6a1876f.xhprof.xhprof-rw-r--r-- 1 Wicky admin 483 4 11 12:46 58ec5faa0034c.xhprof_foo.xhprof 选择生产的id,在浏览器中可观察运行分析结果:http://xhprof.dev/xhprof_html/?run=58ec4c6a1876f&symbol=Fw%5CApp%3A%3Arun 项目使用由于使用的公司项目,但是思路都差不多: 1234567891011121314xhprof_enable(XHPROF_FLAGS_NO_BUILTINS | XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY);#核心文件的执行 站点的配置...require 'index.php'...$xhprof_data = xhprof_disable();include_once $_SERVER['XHPROF_ROOT_PATH'] . "/xhprof_lib/utils/xhprof_lib.php";include_once $_SERVER['XHPROF_ROOT_PATH'] . "/xhprof_lib/utils/xhprof_runs.php";$xhprof_runs = new XHProfRuns_Default();$run_id = $xhprof_runs->save_run($xhprof_data, "xhprof");echo 'http://xhprof.dev/xhprof/xhprof_html/index.php?run=' . $run_id . '&source=xhprof_test'; 变量$run_id是本次请求生成分析结果的id,最后我们输出了一个链接地址,使用改地址就可以看到本次请求的分析结果。 结果报告:关系调用: 图中红色的部分为性能比较低,耗时比较长的部分,我们可以根据根据哪些函数被标记为红色对系统的代码进行优化。 Xhprof 报告字段含义Function Name:方法名称。 Calls:方法被调用的次数。 Calls%:方法调用次数在同级方法总数调用次数中所占的百分比。 Incl.Wall Time(microsec):方法执行花费的时间,包括子方法的执行时间。(单位:微秒) IWall%:方法执行花费的时间百分比。 Excl. Wall Time(microsec):方法本身执行花费的时间,不包括子方法的执行时间。(单位:微秒) EWall%:方法本身执行花费的时间百分比。 Incl. CPU(microsecs):方法执行花费的CPU时间,包括子方法的执行时间。(单位:微秒) ICpu%:方法执行花费的CPU时间百分比。 Excl. CPU(microsec):方法本身执行花费的CPU时间,不包括子方法的执行时间。(单位:微秒) ECPU%:方法本身执行花费的CPU时间百分比。 Incl.MemUse(bytes):方法执行占用的内存,包括子方法执行占用的内存。(单位:字节) IMemUse%:方法执行占用的内存百分比。 Excl.MemUse(bytes):方法本身执行占用的内存,不包括子方法执行占用的内存。(单位:字节) EMemUse%:方法本身执行占用的内存百分比。 Incl.PeakMemUse(bytes):Incl.MemUse峰值。(单位:字节) IPeakMemUse%:Incl.MemUse峰值百分比。 Excl.PeakMemUse(bytes):Excl.MemUse峰值。单位:(字节) EPeakMemUse%:Excl.MemUse峰值百分比。可以根据上述字段查找自己要看的数据,然后进行优化 其他可使用xhgui/xhprof.io等工具来分析xhprof结果。 参考资料xhprof地址 https://github.com/facebook/xhprof xhprof手册 http://php.net/xhprof xhprof.io https://github.com/gajus/xhprof.io xhgui https://github.com/perftools/xhgui oneapm http://www.oneapm.com/","tags":[{"name":"PHP","slug":"PHP","permalink":"http://yoursite.com/tags/PHP/"},{"name":"性能优化","slug":"性能优化","permalink":"http://yoursite.com/tags/性能优化/"},{"name":"XHProf","slug":"XHProf","permalink":"http://yoursite.com/tags/XHProf/"}]},{"title":"深入理解PHP(一)","date":"2017-02-19T16:20:23.000Z","path":"2017/02/20/深入理解PHP-一/","text":"从毕业开始使用PHP也有接近三年时间,对PHP的理解还只是停留在很表层的使用阶段,而没有深入去了解它的内部运行及处理机制。所以决定开始每周花一点时间去深入理解PHP的内核,当做是一些自己的学习笔记和总结。 运行模式及生命周期PHP运行模式 CGI(通用网关接口 / Common Gateway Interface) FastCGI(常驻型CGI / Long-Live CGI) CLI(命令行运行 / Command Line Interface) Web模块模式(Apache等Web服务器运行的模式) CGI: 本质上是以socket编程实现一个TCP/UDP协议的服务器,启动时创建TCP/UDP协议的服务器socket监听,并接受相关请求进行处理,并在此基础上添加模块的初始化、sapi初始化、模块关闭、sapi关闭等就构成了整个CGI的生命周期。 CGI全称是“通用网关接口”(Common Gateway Interface),它可以让一个客户端从网页浏览器向执行在Web服务器上的程序请求数据。 CGI描述了客户端和这个程序之间传输数据的一种标准。 CGI的一个目的是要独立于任何语言的,所以CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php,perl,tcl等。 存在的问题: 每当有用户请求,会创建CGI的子进程,然后处理完请求才会结束子进程。当 当用户请求数量非常大事,会大量占用系统资源,比如内存、CPU等,造成性能低下。 有多少客户端请求就会创建多少个CGI子进程,子进程反复加载是造成性能低下的主要原因。 webserver收到浏览器index.php的请求以后,会启动相应的CGI程序,也就是PHP解释器。接下来PHP解释器会解析php.ini文件,加载模块,初始化执行环境然后处理,按照CGI规定执行完成后返回结果,结束进程,webserver再返回浏览器。 browser->webserver-CGI/FastCGI->PHP->close CGI->webserver->browser FastCGI:是CGI的升级版本,像一个常驻(long live)的CGI,它可以一直在执行,激活以后不需要每次都花时间去fork进程。 工作原理: webserver启动时载入FastCGI管理器(PHP-FPM)。 FastCGI进程管理器自身初始化,启动多个CGI解释器进程等待webserver连接。 客户端请求到达webserver时,FastCGI选择一个CGI解释器进程并连接。 FastCGI子进程处理完后将标准输出和错误信息由同一连接返回Webserver,当关闭FastCGI子进程连接时,请求宣告完成。FastCGI等待下一个连接。 使用FastCGI,只需要解析一次php.ini文件,初始化数据结构也只需要一次,不需要像CGI那样每次都经历这个过程。一个好处持续数据库连接可以工作。 优点: 从稳定性看,FastCGI是以独立的进程池来运行CGI,单个进程死掉,系统可以丢弃并重新分配进程来处理。 从安全性看,FastCGI支持分布式运算,和宿主Server可以独立,FastCGI Down不会将Server弄垮。 从性能上看,FastCGI将动态逻辑处理从Server中分离开,I/O处理留给Server处理。 不足: 因为需要开启多个进程,FastCGI需要更多的服务器内存,PHP-CGI进程解释器需要消耗7-25M内存,若开启500个进程则是很大的内存数。 Tips: 如果php-fpm的单个子进程占用内存一直居高不下,如图需要看php-fpm的配置中max_request是否打开,不开启会导致fpm进程一直不释放内存,可设置为max_request=300-500,请求次数达到这个值后php-fpm进程进行重启,保存内存不增长。如果内存不释放,导致占用内存增加不够用,从而导致php-fpm不可用造成nginx开始报502。如果是间歇性502一般是php-fpm进程重启造成的。如果参数设置的太小,导致无进程可用也会报502。进程占用内存计算方法(内存/20M)。 CLI PHP-CLI是PHP Command Line Interface的简称,就是PHP在命令行运行的接口,区别于在Web服务器上运行的PHP环境(PHP-CGI,ISAPI等)。PHP的CLI Shell脚本适用于所有的PHP优势,使创建要么支持脚本或系统甚至与GUI应用程序的服务端,在Windows和Linux下都是支持PHP-CLI模式的,如php xx.php来执行。 开始和结束首先看一段代码: 12345<?php echo "hello world"; $a = 1+1; echo $a;?> 通常我们在Apache或Nginx这类web服务器来测试PHP脚本,或者在CGI的命令行模式下执行,脚本执行完后,Web服务器应答,浏览器显示应答信息,或者在命令行标准输出上显示内容。以CGI执行为例,会执行下面的几个步骤: Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens) Parsing, 将Tokens转换成简单而有意义的表达式 Compilation, 将表达式编译成Opocdes Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。 脚本执行的开始都是以SAPI接口实现开始的,只是不同的SAPI接口会实现他们特定的工作,如Apache的mod_php SAPI实现需要初始化从Apache获取的一些信息,将输出内容返回给Apache,其他的SAPI也是类似。 PHP开始执行要经过两个主要的阶段:处理请求之前的开始阶段和请求之后的阶段。开始阶段主要有两个过程:第一个是模块初始化阶段(MINIT),在整个生命周期之内只进行一次。第二个过程是模块激活阶段(RINIT),发生在请求阶段,每次请求之前都会进行模块激活。例如PHP注册一些扩展模块,则在MINIT阶段会回调所有模块的MINIT函数。 请求到达之后PHP初始化执行脚本的基本环境,包括保存运行过程中变量名称、值内容符号表,所有的函数及类等信息的符号表。然后PHP会调用所有RINIT的函数,各模块也会执行一些相关操作。 请求处理完成后,执行到脚本末尾或是调用die()/exit()函数,PHP都将进入结束阶段。也分为两个环节,一个请求结束后停用模块(RSHOUTDOWN,对应RINIT),一个在SAPI生命周期结束时关闭模块(MSHUTDOWN, 对应MINIT)。 单进程SAPI生命周期 启动在调用每个模块的模块初始化前,会有一个初始化的过程,包括以下: 初始化若干个变量 初始化若干常量 初始化Zend引擎和核心组件 解析php.ini 全局操作函数的初始化 初始化静态构造的模块和共享模块(MINIT) 禁用函数和类 ACTIVATION调用php_request_startup做请求初始化操作,还有其他的操作: 激活Zend引擎 gc_reset函数来重置垃圾收集机制 init_complier初始化编译器 init_executor初始化中间代码的执行过程 激活SAPI 环境初始化 $_POST、$_GET、$_COOKIE、$_SERVER、$_ENV、$_FILES等 模块请求初始化 运行php_execute_script函数包含了运行PHP脚本的全部过程 DEACTIVITION关闭请求的过程是若干个关闭操作的集合,存在于php_request_shutdown函数。 结束 flush 关闭zend引擎 多进程SAPI生命周期以Apache为例,Apache启动后会fork多个子进程,进程间内存独立,每个子进程都会经过开始和结束环节,所以在整个生命周期中可能会有多个请求。如下图所示: 多线程的SAPI生命周期整个进程的生命周期内会并行的重复着 请求开始-请求关闭环节,如下图: Zend引擎Zend引擎是PHP实现的核心,提供了语言实现上的基础设施。如:语法实现、脚本的编译运行环境、扩展机制、内存管理。很多PHP扩展也是使用的ZendAPI。 参考资料 http://www.laruence.com/2008/08/11/147.html 风雪之隅 https://www.kancloud.cn/digest/php-src/136264 PHPer进阶 http://www.php-internals.com/book/ 深入理解PHP内核","tags":[{"name":"PHP","slug":"PHP","permalink":"http://yoursite.com/tags/PHP/"},{"name":"CGI","slug":"CGI","permalink":"http://yoursite.com/tags/CGI/"},{"name":"PHP-FPM","slug":"PHP-FPM","permalink":"http://yoursite.com/tags/PHP-FPM/"}]},{"title":"MySql的那些事儿","date":"2017-02-18T13:46:55.000Z","path":"2017/02/18/MySql的那些事儿/","text":"工作到现在,做过一些系统的数据库设计,当在被问到如何使用范式来设计的时候,其实我是懵逼的。然而在自己查过资料以后,对范式的理解其实和自己潜意识中的设计经验是一致的。在对索引的优化的经验中,了解了下何为索引、索引的结构、索引的使用、及索引的优化方式也有了一点总结,也了解了下MySQL内部的执行及优化过程。 SCHEMA设计范式:一张数据表的表结构所符合的某种设计标准的级别1NF/2NF/3NF/BCNF/4NF/5NF,一般最多到BCNF就够。 1NF1NF:符合1NF的关系中的每个属性都不可再分,是关系型数据库的最基本要求。最小属性只能是一个值,不能被拆分成多个字段,否则的话,它就是可分割的,就不符合一范式。可能导致的问题 1.数据冗余过大,针对上表某一学生的学号、姓名、系名、对应的系主任等数据重复多次 2.插入异常,新建了某个系无法将系主任对应到相应的系中 3.删除异常,将学生信息删除,系名、系主任也被删除 4.修改异常,小明转系,多条数据都要被修改 2NF2NF: 在1NF的概念基础上,消除了非主属性对于码的部分函数依赖。通俗的说,根据主键将数据表的各依赖关系解耦。就是要有主键,要求其他字段都依赖于主键。依上表可以将表关系拆为:模式分解以后的新数据为下图:由上图可以看出,删除异常及增加系信息仍无改进。 3NF3NF:3NF在2NF的基础之上,消除了非主属性对于码的传递函数依赖。就是要消除传递依赖,方便理解,可以看做是“消除冗余”。在上图的基础上,可进一步拆解为以下关系:分解后的新数据为: 已经看出上述的数据冗余问题已经消除了。 BCNFBCNF:在满足第二第三范式的情况下,主属性内部也不能部分或传递依赖。判断方法:箭头左边的必须是候选码,不是候选码的就不是BC范式。 举个例子:如学号、学生名字、学生QQ,设置学号为主键,但是qq、名字都可作为候选键,不符合BC范式。解决的方案是保留一个候选键为主键,从而保证每个表中只有一个候选键。 一般的系统设计到第三范式就可以满足了,BC范式太苛刻,大多数情况用不到。 加快ALTER TABLE的操作速度Mysql的ALTER TABLE操作的性能对大表来说是个很大的问题,会导致服务中断,因为涉及到索引结构的变更。有以下两个技巧可以避免: 在一台不提供服务的机器上执行操作,然后和提供服务的主库进行切换。 先建立一张临时表,通过重命名和删表的操作来交换。 CHAR和VARCHARVARCHAR 用于存储可变长字符串,比定长类型更节省空间。 需要额外的1或2个额外字节记录字符串长度,如果列的最大长度小于或者等于255字节,则只使用1个字节表示,否则使用 2 个字节。 节省了存储空间,所以对性能也有帮助。但是,行变长时,如果页内没有更多的空间可以存储,MyISAM 会将行拆成不同的片段存储,InnoDB 则需要分裂页来使行可以放进页内。 每页最多能存多少数据? 2^8 = 256, 2^16 = 65536。数据能否超过65536?如果不能,超过了会怎么样?– MySQL 中 VARCHAR 类型的最大长度限制为 65535。 CHAR 定长,根据定义分配足够的空间。当存储 CHAR 值时,MySQL 会删除所有的末尾空格。CHAR 值会根据需要采用空格进行填充以方便比较。 CHAR适合存储很短的字符串,或者所有值都接近同一个长度,比如密码的MD5值。 对于经常变更的数据, CHAR 也比 VARCHAR 更好,定长不容易产生碎片。 非常短的列, CHAR 比 VARCHAR 在存储空间上更有效率。 索引概念:索引优化应该是对查询性能优化最有效的手段。可以轻松提高几个数量级。 存储金字塔计算机中最重要的存储介质分为几类:硬盘、内存、二级缓存、寄存器。它们之间的对比如下:从上面的图中,我们可以看出,从下往上,速度从慢到快,制造成本也越来越高。几种有代表性的存储设备的典型访问速度如下:从图中大概可以看出:高速缓存的访问速度是主存的10~100倍,而主存的速度则是硬盘的1~10W倍,完全不是一个数量级的。 索引结构B+Tree 只有最底层的节点(叶子节点)才保存信息 其他节点只是在搜索中用来指引到正确的节点 B+树种的B不是代表二叉(binary),而是代表平衡(balance),因为 B+树是从最早的平衡二叉树演化而来,但是 B+树不是一个二叉树。 哈希表 构建一个哈希表需要以下定义: 元素的关键字 关键字的哈希函数。关键字计算出来的哈希值给出了元素的位置(叫做哈希桶)。 关键字比较函数。通过找到的正确哈希桶,在比较函数内找到要的函数。好的哈希函数在哈希桶里面包含非常少的元素,时间复杂度是O(1)。 InnoDB逻辑存储结构所有数据都被逻辑的存放在一个空间中,称为表空间。表空间由段、区、页组成,页也有在一些文档中被称为块。 InnoDB存储引擎是面向列的,数据是按行进行存放的。每个页存放的记录是有硬性规定,最多允许存放16KB~2-200行的记录,即7992行。 索引类型B-Tree索引NDB集群存储引擎内部使用了T-Tree,InnoDB则使用了B+Tree。MyISAM使用前缀压缩技术使索引更小。B-Tree通常认为所有的只都是按顺序存储的,并且每个叶子节点到根的距离相同,之所以能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描,而是从索引的根节点开始搜索。 叶子节点比较特别,他们的指针指向的是被索引的数据,而不是其他的节点页。B-Tree 对索引列是顺序组织存储的,所以很适合查找范围数据。 B-Tree使用限制 如果不是按照索引的最左列开始查找,则无法使用索引。 不能跳过索引中的列。 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。 B+树索引并不能找到一个给定键值的具体行。B+树索引能找到的只是被查找数据行所在的页。然后数据库通过把页读入到内存,再在内存中进行查找,最后得到要查找的数据。 自增ID的使用是为了解决页分裂的问题,一页总要被存满,然后新开一页继续,这种行为称为页分裂,mysql规定一个分裂因子,达到存储空间的15/16就存到下一页,页分裂会极大的影响维护索引的性能,故通常设定一个无意义的整数自增索引,有利于索引存储。如果是非自增或非整数,作为索引值,新增数据时需要寻找合适的位置,会在之前的页中插入,导致该页达到分裂因子的阀值引起索引页的分裂,索引文件发生变化,数据量大会浪费大量时间,产生碎片。 索引的优点: 索引大大减少了服务器需要扫描的数据量。 索引可以帮助服务器避免排序和临时表。 索引可以将随机 I/O 变为顺序 I/O 。 聚簇索引数据行存放在索引的叶子页中,数据行和相邻的键值紧凑的存储在一起。 优点: 相关数据保存在一起。 数据访问更快。索引和数据保存在同一个b-tree中,查询数据比非聚簇索引快。 缺点: I/O密集型应用,对于内存存储没有优势。 插入速度严重依赖插入顺序。 更新索引代价高,主键更新导致需要移动行的时候,面临页分裂,会占用更多磁盘空间。 可能导致全表扫描变慢,行比较稀疏、页分裂导致数据存储不连续的时候。 MyISAM和InnoDB数据分布MyISAM1.主键叶子节点存放数据行的指针。2.主键和其他索引没有区别。 InnoDB聚簇索引每一个叶子节点都包含了主键值、事务ID/MVCC回滚指针及剩余列。 在InnoDB表中按主键顺序插入行,对于主键做关联的操作性能会更好。 查询性能优化为什么会查询慢?查询的生命周期大致可以按照顺序来看:从客户端,到服务器,然后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端。在完成这些任务的时候,查询需要在不同的地方花费时间,包括网络,CPU 计算,生成统计信息和执行计划、锁等待(互斥等待)等操作,尤其是向底层存储引擎检索数据的调用,这些调用需要在内存操作、CPU 操作和内存不足时导致的 I/O 操作上消耗时间。 衡量查询开销的三个指标: 响应时间 扫描的行数 返回的行数 这三个指标可在Mysql的慢日志中查到,扫描行数过多可于此找出。 响应时间响应时间由两部分之和:服务时间+排队时间。 服务时间:数据库处理这个查询真正花了多少时间。 排队时间:指服务器因为等待某些资源而没有真正执行查询的时间—​可能是等 I/O 操作完成,也可能是等待行锁等待。一般最常见和重要的等待是 I/O 和锁等待。 扫描的行数和返回的行数并不是所有的行的访问代价是相同的。较短的行的访问速度更快,内存中的行也比磁盘中的行的访问速度要快得多。理想情况下扫描的行数和返回的行数应该是相同的。扫描的行数对返回的行数比率通常很小,一般在 1:1 和 10:1 之间。 扫描的行数和访问类型在评估查询开销的时候,需要考虑一下从表中找到某一行数据的成本。在 EXPLAIN 语句中的 type 列反应了访问类型。访问类型有很多种,从全表扫描到索引扫描、范围扫描、唯一索引查询、常数引用等。 重构查询的方式 一个复杂查询还是多个简单查询 切分查询 分解关联查询 查询执行的基础从客户端输入查询请求,到服务器返回查询结果,mysql的执行过程如下:执行的主要操作如下: 1.客户端发送一条查询给服务器。 2.服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果。否则进入下一阶段。 3.服务器进行 SQL 解析、预处理,再由优化器生成对应的执行计划。 4.MySQL 根据优化器生成的执行计划,调用存储引擎的 API 来执行查询。 5.将结果返回给客户端。 查询状态对于一个MySQL连接,或者是一个线程,任何时刻都有一个状态,表示了MySQL当前正在做什么,可以用SHOW FULL PROCESSLIST查看。 Sleep 线程正在等待客户端发送新的请求。 Query 线程正在执行查询或者正在将结果发送给客户端。 Locked 在 MySQL 服务器层,该线程正在等待表锁。在存储引擎级别实现的锁,例如 InnoDB 的行锁,并不会体现在线程状态中。 Analyzing and statistics 线程正在收集存储引擎的统计信息,并生成查询的执行计划。 Copying to tmp table [on disk] 线程正在执行查询,并且将结果集都复制到一个临时表中,这种状态一般要么是在做 GROUP BY 操作,要么是文件排序操作,或者是 UNION 操作。如果这个状态后面还有 on disk 标记,那表示 MySQL 正在将一个内存临时表放到磁盘上。 Sorting result 线程正在对结果集进行排序。 Sending data 这表示多种情况:线程可能在多个状态之间传送数据,或者在生成结果集,或者在向客户端返回数据。 查询缓存解析查询语句之前,查询缓存是打开的话,会优先查询命中缓存中的数据。检查是通过对大小写敏感的哈希查找实现的。 查询优化处理将SQL转换成一个执行计划,MySQL再将执行计划和存储引擎交互,包括:解析SQL、预处理、优化SQL执行计划。 语法解析器和预处理MySQL通过关键字解析SQL语句,生成对应的“解析树”,MYSQL解析器验证语法规则和解析查询。预处理根据MySQL规则进一步检查解析树是否合法。下一步预处理器验证权限。 查询优化器一条查询有多种执行方式,最终返回一样的结果。 排序优化排序是一个成本很高测操作,从性能考虑,需要避免对大量数据进行排序。排序的数量小于“排序缓冲区”,MySQL使用内存进行“快速排序”。内存不够,则先将数据分块,对独立块进行“快速排序”,在对子结果进行合并。 MySQL EXPLAIN详解 select_type parititons type possible_keys key key_len ref rows filtered extra type 这是重要的列,显示使用了何种连接类型。从最好到最差的连接类型为system、const、eq_ref、ref、range、index和all。 possible_keys 显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从where语句中选择一个合适的语句。 key 实际使用的索引。如果为null,则没有使用索引。很少的情况下,mysql会选择优化不足的索引。这种情况下,可以在select语句中使用use index(indexname)来强制使用一个索引或者用ignore index(indexname)来强制mysql忽略索引 key_len 使用的索引的长度。在不损失精确性的情况下,长度越短越好 ref 显示索引的哪一列被使用了,如果可能的话,是一个常数 rows mysql认为必须检查的用来返回请求数据的行数,影响的行数越小或是1的才没问题 extra 关于mysql如何解析查询的额外信息。但这里可以看到的坏的例子是using temporary和using filesort,意思mysql根本不能使用索引,结果是检索会很慢。 参考资料 地瓜哥 《MySQL学习笔记》 http://notes.diguage.com/mysql/ 知乎 《解释一下数据库的第一第二第三范式》 https://www.zhihu.com/question/24696366 《高性能MySQL》 《MySQL技术内幕》","tags":[{"name":"MySql","slug":"MySql","permalink":"http://yoursite.com/tags/MySql/"},{"name":"索引优化","slug":"索引优化","permalink":"http://yoursite.com/tags/索引优化/"},{"name":"查询优化","slug":"查询优化","permalink":"http://yoursite.com/tags/查询优化/"}]},{"title":"Rsync文件实时同步","date":"2017-01-08T13:39:05.000Z","path":"2017/01/08/Rsync文件同步服务器/","text":"在我们开发项目,Coding的过程中,有下面几种开发模式: 直接在服务器上开发,这样的优点是可以实时的修改程序调试,但是在涉及到多人开发的情况下,容易互相覆盖,同时开发效率也没有在IDE中高。 在本地IDE开发完,全量同步到远程服务器,这样的问题是如果项目过大,同步时间过长。 本地IDE开发增量同步,只将增量变化的文件同步。上面的方案三是最省事的,现在很多IDE默认支持通过SFTP来实现实时同步,和我们现在的方案其实差不多,由于在Mac下fswatch可以实时监控文件变化,Rsync可以实现同步文件。当然,你也可以自己实现一个server,通过http网络请求来完成,这里就不重复造轮子了。 fswatch和rsync由于需要fswatch和rsync,在MacOS下只需通过一下安装:12brew install fswatchbrew install rsync 关于fswatch可以阅读这篇文章,rsync的使用方法也可以参考这篇文档。Rsync 的特色:快速:第一次同步时 rsync 会复制全部内容,但在下一次只传输修改过的文件。安全:rsync 允许通过 ssh 协议来加密传输数据。更少的带宽:rsync 在传输数据的过程中可以实行压缩及解压缩操作,因此可以使用更少的带宽。特权:安装和执行 rsync 无需特别的权限基本语法:rsync options source destination 排除不需要的文件在开发过程中,在IDE中有一些配置文件或是我们不需要上传的文件,可以通过exclude参数过滤掉,如果是少量单个文件,可以如下使用:1rsync -rzluq --delete --exclude=".git/" xxx xxx 如果是针对多个文件夹,可以通过配置过滤文件的方式来实现:12345678rsync -rzluq --delete --exclude-from="ignore.txt" xxx xxx## ignore.txt 1 # ignore folder 2 # don't need to sync to server 4 .git/ 5 .svn/ 6 .idea/ 使用安装及配置好上述之后,可以按以下方式运行:1auto-deploy LOCAL_FILE REMOTE_ADDR 下面是我的一个栗子:12345Wicky@Wickys-MacBook-Pro:~/PhpstormProjects/lianjia_weixin$ auto-deploy /Users/Wicky/PhpstormProjects/lianjia_weixin/ /data0/www/htdocs/weixin.lianjia.com/------------Start Sync File-------------Current directory:/Users/Wicky/PhpstormProjects/lianjia_weixin/Destination:/data0/www/htdocs/weixin.lianjia.com//Users/Wicky/PhpstormProjects/lianjia_weixin/application/controllers/Api/User/List.php was rsynced 以上为所有流程,源码可以参考autoSync。","tags":[{"name":"rsync","slug":"rsync","permalink":"http://yoursite.com/tags/rsync/"},{"name":"文件同步","slug":"文件同步","permalink":"http://yoursite.com/tags/文件同步/"}]},{"title":"2016年终总结","date":"2016-12-30T03:02:45.000Z","path":"2016/12/30/2016年终总结/","text":"时间在今年过的特别快,一眨眼就到了2016年底,不管是工作还是生活上都有了一些变化,每年在这个时候还是做一下总结。 工作今年总的来说,过的并不如意,在美丽说接近整两年,最终因为资本的运作导致公司被合并,作为技术岗位的一个默默无名的小码农也不得不选择离开,很感谢两年来波神、卢总以及旭旭对我的指导及关心,在美丽说收获良多,往事一幕幕难以忘记,江湖之大,有缘自会相见。 再离开前场之后,经朋友推荐来到了百度(外卖),此时外卖已脱离百度独自发展了,加上HR对职位的描述以及百度系的吸引力,入职后发现并没有想象中的那样,加班加点很多,繁琐事情多,工作环境也不好,感觉在这样的团队自己的发展有限,空间也有限,所以在忙完520大促之后便离开了。 来现在的公司也是个意外,本来是打算去投奔之前的老领导,意外接到链家网的邀请,有鸟哥坐镇的链家网现在也吸收了不少好手,公司环境、福利都挺不错,就觉得来了。来了之后在Web架构部维护主站内容相关业务,后面组织架构调整到了基础架构部,做一些底层服务,基本还是以PHP为主的微服务,主要参与了内容仓库、Session服务、微信公众号平台、专题系统等项目,有部分参与也有部分作为主导,还提出了基于Kafka的高性能mq的想法,但是遗憾是没有把他实现为一个平台级产品。 过去一年由于四处折腾,多多少少在干些杂的事,来这边也有机会自主独立去负责一些项目,这块的能力有一些提升,业余时间也学习了Python、Golang等其他编程语言,虽然只是个入门级,但是技不压身,后面还要把前端JS相关的学习一遍。 本该在很久以前要做的个人blog,也在年底才开始总结,陆陆续续发布一些自己遇到的问题及解决的问题的经验,对自己的期望就是坚持下去。 总结 对技术的认知要有更深层的理解,不只是达到可运行的状态,还有保障系统的稳定性及SLA 项目多思考和总结,考虑产出及实现方案的成本 解决问题的能力需要加强,站在运维的角度去看问题,例如从tcpdump到wireshark,日志监控观察系统状况 完善技术的生态圈 技术期望 实现高性能Kafka Proxy中间件(Golang重写) 微信公众号平台 ELK日志监控平台 生活三月中去了斯里兰卡旅行,难以忘记在路上遇到的那些微笑,和路上遇到的美丽风景、美丽的人。 十月去了马来西亚,体验了深蓝色鸦片,真的会爱上,同时在槟城也遇到了大眼小姐。珍惜这一切缘分:) 想去的地方菲律宾杜马盖地 (PADI潜水证) 澳大利亚 新西兰 看过的书技术相关《重构-代码设计》、《GO语言学习笔记》、《SRE Google运维解密》、《深入浅出NodeJs》、《软技能-代码外的生存指南》、《Python核心编程》 非技术《白夜行》、《好吗好的》、《从0到1》、《菊与刀》、《从你的全世界路过》","tags":[{"name":"总结","slug":"总结","permalink":"http://yoursite.com/tags/总结/"}]},{"title":"Python文本转语音","date":"2016-12-25T05:46:59.000Z","path":"2016/12/25/Python文本转语音/","text":"之前有自学过一段时间Python,前几天看到一个同事在朋友圈发了段小视频,他要给他儿子听写小学课本的词语,但是他又不想自己拿着课本一字一句的给他儿子听写,就自己写了个小程序,配上标准的台湾女声。想到这个,Python的语音库对中文支持好的,pyttsx 这个第三方库也挺强大的,还能提供获取系统的声音库,支持各种各类的语言。 下面介绍下对这个库的使用,更多栗子可以参照他的官方文档 examples 。 安装pyttsx 很简单,安装这个Python lib就行: 1pip install pyttsx 使用 MacOS 系统中支持三种中文语音,分别是大陆、香港、台湾,可以按以下获取 : 12345678voices = engine.getProperty('voices')具体的中文有以下几种: 'com.apple.speech.synthesis.voice.mei-jia', 'com.apple.speech.synthesis.voice.sin-ji.premium', 'com.apple.speech.synthesis.voice.ting-ting'需要的时候直接指定即可:engine.setProperty('voice', random.choice(voices)) 其他的语种也是支持的,可在voices中获取。 有一点在使用的时候需要注意,在操作的过程中如果需要有同步操作,需要通过它自身的事件驱动来做,如在读文本的时候,需要同时显示文本,需要给他注册一个事件: 123456789 ....def printText(name): time.sleep(2) print nameengine.connect('started-utterance', printText) //注册回调事件text = '你好'engine.say(text, name=text) //指定回调的内容 .... 上面描述的text是要朗读的文本,name是要回调的事件。具体的程序参见readBoy。","tags":[{"name":"Python","slug":"Python","permalink":"http://yoursite.com/tags/Python/"},{"name":"语音","slug":"语音","permalink":"http://yoursite.com/tags/语音/"}]},{"title":"Kafka MQ的一些想法","date":"2016-12-13T16:23:37.000Z","path":"2016/12/14/Kafka-MQ的一些想法/","text":"背景在公司系统架构发展中,有若干场景需要可靠的队列服务来出传递数据或消息,这些场景可能有: 系统的数据同步,如保存在redis中的数据,需要落地到Mysql中,为了保证性能可扩展性,通过消息队列来执行数据的落地操作。 跨系统间的数据同步,如内容仓库中新增、修改、删除操作导致各个系统间数据的变化 事件的异步处理,如一些统计相关的数据 综合上面的一些场景,消息队列可以抽象成下面这样: 高可用,消息队列服务应该保持尽可能高的可用性,尤其是写消息的操作 低延迟,单次写队列的操作在1ms内完成,以提高吞吐量 可扩展,当多个业务接入时,随着消息量的增长,应当保持相对容易的扩展并且对上下游的透明的。 消息的安全性,保证消息不丢失并且可以回归(回源时间可控,一周以内) 接入成本低,对语言无要求(通过HTTP协议) 系统设计RedisQueue supervise脚本,重启机器或脚本要重新reload redis保存内存,没有持久化到磁盘,断电可能导致丢数据,可靠性差 pub/sub ,多写,一个操作通知多个业务方,上下游耦合严重,重复数据推到多个redis queue中 ….. KafkaQueue说明: MqProxy 各上游子系统(如订单系统),通过统一的MqProxy来提交消息。 MqProxy提供HTTP接口,上游提交消息时,声明Topic和消息内容。 MqProxy和Zookeeper保持长连接,以加速(实测PHP访问Zk大概有10-20ms的耗时)。 MqProxy处理消息的分区,在提交消息给MqProxy时,同时提供Partition Key。 Producer MqProxy作为Producer将消息提交到Kafka。 Broker 参考Kafka的系统架构,Broker部分提供对某个Topic的Partation和Replica机制,即每一个Topic的数据,都可按某个主键进行分片,并支持多个副本,Replication保持数据备份。 Broker需要实现Leader选举机制,并通过和Zookeeper保存各Topic/Partation的Leader。 Producer和Broker交互之前,通过Zookeeper获得对应Leader,而后进行交互。 Broker支持可扩展。 Consumer Consumer可支持队列服务本身的Consumer机制(Pull Offset)。 然而,现在的系统,大部分是PHP这样的系统,并不是通过deamon方式运行的,因此我们需要实现一个Pusher机制,使得消息的消费从拉模型变为推模型,使得下游需要接受消息的子系统,更低成本的接入。 因此,我们实现一套Push Consumer机制。 Pusher Consumer Pusher Consumer是可配置的常驻服务,可指定Topic->url,支持多个。 配置文件举例: 123456789101112131415161718{ "consumer_groups": [ //support multi callback url { "worker_num": 16, "url": "http://localhost", //callback url "retry_times": 4, //will retry if the callback request response non 200 code "bypass_failed":true, //auto jump to next if single message were processed failed,set to false if service need to process message exactlly "failed_sleep":"5s", //when bypass_failed set to true,sleep this time before retry "timeout": "3s", //the callback time "topics": [ //topic(s) consumed "test" ], "zookeepers": [ //zookeeper hosts "127.0.0.1:2181" ], } ]} pusher consumer会将消息有序推送到下游系统,各个下游系统都能够拿到所有的消息,自行进行处理,且进度各自独立,互不影响。 pusher consumer通过zookeeper保存各个下游当前的命令点,因此也是无状态的。 http callback 下游接受消息的接口,提供统一的消息接受格式。 同时,由于Pusher Consumer采用at least once的方式(即消息至少传输一次,由于网络等原因导致的超时或出错,pusher会自动重发直到超过retry次数或者返回成功),建议下游的接口是幂等,即多次调用也不会使得状态错误。","tags":[{"name":"Kafka","slug":"Kafka","permalink":"http://yoursite.com/tags/Kafka/"},{"name":"MQ","slug":"MQ","permalink":"http://yoursite.com/tags/MQ/"}]},{"title":"马来有趣的一些事儿","date":"2016-12-11T17:20:19.000Z","path":"2016/12/12/马来有趣的一些事儿/","text":"距离从马来回来已经差不多两个月了,一直没时间去整理马来的游记(其实是因为懒👀 ),再三回想,还是有几件有趣的事儿值得记录的,所以就不花时间赘述,稍微留下点文字就好了。 仙本那马布岛的小孩马布岛位于仙本那的东南方向,岛上大概有2000人,但是布依族老人和小孩很多,海上的吉普赛人,我们在岛上住了三天,在最后一天要离开的时候才决定上岛上转转,岛上很破旧,基本和贫民窟差不多。在Uncle Chang水屋旁边的一家小商店旁边,我们看到了这个小孩,他穿着破烂的巴塞罗那球衣,身上挂着一把破旧并且断了的玩具枪,背上也挂了一把断了的玩具长剑,脸上则是戴着一个有纸壳戳穿两个洞儿做成的面具,这个岛上的小孩儿不多,看到我们过来就伸手要Candy,虽然很穷,但是看出他们也还很乐观,保持着童真,和我们当时的童年很像,比起现在大城市里孩子的娇生惯养,他们或许真的是快乐的吧。 亚庇的青旅老板在亚庇住的青旅叫好望角,老板96年生人,和他媳妇儿两人经营着这家青旅,我们在他家住的时候,老板娘回国玩去了,老板天天窝在家打游戏,晚上12点就出去外面的酒吧玩,到第二天早上回来了,再睡到中午起床,之所以说他有趣,在他这个年纪竟然就过上了这样的生活,也还不错。 槟城的出租车师傅槟城是一个文艺气息很重的城市,和国内的鼓浪屿差不多,最出名的大概就是在2012年左右由北欧的青年画家联合一批创作者,在这座城市的多个角落画上了很多关于城市烙印的壁画,这也导致很多文艺青年慕名而来寻找它们。我们抵达槟城的时间是在晚上12点多,到达青旅后就直接办理入住了。第二天一早6点就起床寻找壁画,在去往周姓桥的路上遇上了一个很重要的人。槟城又叫GeorgeTown,里面的华人非常多,漫步者槟城街头,慢慢的汉字和中文招牌仿佛在告诉你不是在国外,早年下南洋的中国人创办了本地的很多商业化的东西,其中送我们去机场的梅先生也是这样的一个人,他和他的弟弟出生在马来西亚,从小在槟城长大,到现在40多年都没有离开过这里,他跟我说他的梦想就是要自己开着车从马来西亚经过泰国、缅甸,最后从云南入境到中国,他梦想这去一次昆明,他说想在有生之年能自己开着车回到祖国看看,他不相信飞机,他觉得飞机不安全,他还是信任自己的驾驶技术,他说等他赚到足够的钱就新买一辆车去完成他的梦想,然后他一定要留下我在北京的号码,也给了我的他的名片,让我一定要联系他。希望这个大叔能早日实现他的梦想。 兰卡威的光头老板 - Jeremy兰卡威又名浮罗交怡,位于马来西亚的西边,距离泰国很近,据说有些泰国人通过游泳偷渡到这边,不知道是真是假。在兰卡威住的EI Studio是一家音乐工作室改编而成的青旅,老板Jeremy88年生人,和他的另外一位光头朋友Fernando一起经营着这家青旅。老板的吉他弹得非常好,有一天下午在午睡的时候听到了他独自一人在Solo,谈的非常动听,和他聊天的时候,知道他其实是一个有梦想的音乐人,他的爸爸是葡萄牙人,妈妈是华人,所以从小他在华文学校长大,英文、中文说的都很好。兰卡威是他的故乡,他的爷爷奶奶在这生活。和他谈论起大城市的生活,他说早几年在吉隆坡上学和工作的时候,他妈妈说他的脸上从来就没有过笑容,所以他放弃了大城市的生活,来到兰卡威过上青旅老板的生活,他在做音乐的时候,曾经也去过北京、上海做过流浪歌手,说不定在中关村新中关大厦门口驻唱的时候我们还见过面。Jeremy是个很友善的人,希望他的音乐梦想能成功。 旅行就是在路上,发现不同的人,遇到不同的事,看没有见过的风景,有时风景是美的,有事人是更美的,人生的价值观就是在一次一次的旅行中悄然改变,原来还可以这样的生活,希望自己的脚步永远不会停歇。","tags":[{"name":"马来西亚","slug":"马来西亚","permalink":"http://yoursite.com/tags/马来西亚/"},{"name":"旅行","slug":"旅行","permalink":"http://yoursite.com/tags/旅行/"}]},{"title":"解决gitlab、github多账户生成rsa key覆盖的问题","date":"2016-12-11T13:07:10.000Z","path":"2016/12/11/解决gitlab、github多账户生成rsa-key覆盖的问题/","text":"由于公司使用的是gitlab,会使用ssh-keygen 生成rsakey,同时使用github的时候也要生成rsa-key,针对这种情况,在生成key的时候,难免会产生覆盖:12Generating public/private rsa key pair.Enter file in which to save the key (/Users/Wicky/.ssh/id_rsa): 针对这种情况,我们在生成key的时候指定生成id_rsa_github和id_rsa_github.pub,和id_rsa、rd_rsa.pub区分开,将id_rsa_github.pub 的内容填入github的SSH Key中即可。最后在 ~/.ssh/ 中创建config文件,加上以下几行代码:1234567891011# gitlabHost gitlab.com HostName gitlab.com PreferredAuthentications publickey IdentityFile ~/.ssh/id_rsa# github Host github.com HostName github.com PreferredAuthentications publickey IdentityFile ~/.ssh/id_rsa_github 输入 ssh -T [email protected] 测试测试OK即可","tags":[{"name":"github","slug":"github","permalink":"http://yoursite.com/tags/github/"}]},{"title":"Hello World","date":"2016-12-11T12:02:07.000Z","path":"2016/12/11/Hello-World/","text":"老早就想写Blog,但是一直没有付诸行动。读完李笑来先生的《和时间做朋友》,发现自己对时间的规划很不够,在工作中遇到的问题、生活中发生的有趣事情都没有记录的习惯,这样在一段时间以后,再回过来看又全然记不得。 这真的不是一个好习惯,所以从今天开始,慢慢花时间记录一些吧,未来还长,现在出发不晚… Hexo是个不错的工具,希望自己有时间可以自己研习一下。 :)","tags":[{"name":"Hexo","slug":"Hexo","permalink":"http://yoursite.com/tags/Hexo/"}]}]