Mmemcached的使用文档

编    写:袁    亮
时    间:2015-01-15
说    明:Mmemcached的使用文档

一、使用场景
1、在系统中,会有大量的比较耗时的重复计算,这会导致系统性能的急剧下降,为了解决该问题,我们希望将这些计算结果存下来,使得在一定时间内,直接获取结果就好。

2、例如:
一个首页,每天50W次访问,在首页上,有一个地方显示的是最近24小时内,回复数最多的10个用户,正常的取的话,我们需要取24小时内的所有回复,然后根据user_id进行groupby,并统计每个user_id对应的回复数,再根据回复数倒序,取前面10个,这会非常耗时间;
我们可以将计算出来的结果存储下来,在一定时间内(比如5分钟)内,直接返回这10个用户,那么这一块的系统性能提升接近:50W/24/(60/5)=1736倍(计算次数减少)

3、缓存技术在it的各个角落都能看到,是最基础的概念之一,不管是硬件、软件、互联网,都离不开它,是所有优化里第一个要接触的

二、常规使用方法
include_once("/opt/ci123/www/html/ci123libs/data/classes/hash/memcache/memcache.php"); //很多服务器上有

$key = "index_user_list";
$data = M::Get($key);
if($data !== false){ //没有设置值的时候,返回是false,flase本身设置到memcache里会变成空字符串
$data = '';//原有获取数据的逻辑
M::Set($key,$data,600);//将数据写入到Memcache中,并缓存600秒
}

三、缓存的两种基本形式
1、不精确,定时缓存
直接将结果,缓存到一定时间周期,过期则重新计算获取
优点:实现简单、效率高
缺点:源数据如果在缓存有效期内有更改,可能出现数据取出来是错误的
适用场景:对数据精确性要求不高
常见情况:浏览器静态缓存、nginx缓存、cdn缓存、dns缓存等等
Memcache缓存绝大多数都是这么使用

2、预先生成,精确缓存
当源数据有更改的时候,同时更改缓存中的数据
优点:数据精准
缺点:实现复杂,有些情况下甚至不可实现(与缓存本身违背,比如浏览器缓存)
适用场景:系统高度可控,并且对精度要求很高的情况
场景情况:mysql内部查询缓存、短链计算统计等
Memcache如果要使用这种的话,有一个比较简单的办法,在第一种做法的前提下,更新、删除等操作的时候,将相应的缓存删除,这样也能实现数据精确,而且实现简单。(不适用更新非常频繁的情况,否则缓存也白加了)

四、Memcached简单介绍
1、分布式的系统
2、纯内存缓存系统
3、key value结构的hashmap

五、避免出现的情况
1、存大key
场景:
比如我要缓存用户数据,有两种办法,一种是每一个用户一个key,里面对应的用户信息,uinfo_{$uid}
还有一种,我把所有用户信息写到一个key里,这样我只要一个key,节省了内存空间,uinfos
问题:
不要出现一个key里,存很大的数据,缓存粒度控制
但第二种其实是一个非常糟糕的设计,假设我有10W个用户,每个用户信息1Kb,那么这个key就将有100Mb
因此我每个页面取一次缓存,将会导致需要每次从memcache里取100M的数据,10个并发,内网千兆带宽就崩掉了

2、不要缓存执行次数很少的
场景:
有个统计,每天统计一下前一天的数据信息,大概要执行1分钟,太慢了,我要优化它,所以把他扔缓存里
问题:
这种就是一个非常典型的失败例子,缓存的数据后面压根不会用到,所有缓存能起效的前提,都必须是这个请求会是重复多次执行的

3、缓存可能不到时间就会失效(LRU替换算法,不要做精准的统计计算等操作)
场景:
记录页面的访问次数(精确的),因为mysql写太频繁导致锁严重,因此我把计数放在memcache里,很快,读取这些数据的时候我也通过memcache读取,一切都很美好。
问题:
某天你会突然发现,这统计数据不对啊,为什么昨天这个页面明明已经有5000多次了,今天怎么变300多了?而且缓存也没有过期啊,什么情况?
因为Memcahe会有内部的删除数据算法,在空间不够的情况下,会删除某些不常用的数据
解决办法:
持久层还是放在MYSQL里去实现,但可以把写合并,比如累加次数达到一定值的时候重新写回mysql,注意不要是定值重写,这会导致数据上涨的非常有规律,看起来很奇怪。
$num = M::Get("post_view_{$post_id}");
if($num > rand(10,30)){//平均20次更新写回mysql
//写回Mysql
//将memcache中的数字清空
}

4、Memcache不做持久化,服务重启,数据会全部丢失,启动的时候可能会导致数据库压力狂涨
如果项目很大的情况下,可能会出现挂掉之后数据库马上报警,又挂了
这个时候可以少量放开部分用户访问,然后慢慢加量,一般情况下不用管,直接硬抗几分钟就好了

5、命名 同一个系统里,不要出现两个同名的,会导致数据变的莫名其妙,统一管理
命名最好语意清晰,不会和别人写的导致重复
同一个Memcache的key管理,可以统一在一个文件里定义使用,防止因为key名重复导致逻辑出错

六、可视化 管理系统
MemAdmin
基于php和jquery开发,简单易用
http://192.168.0.249/memadmin/
admin fuyuan1906

七、Memcache本身的一些限制
1、最大过期时间30天(设置永久也无效)
2、最大键长128字节(不要取太长的,这个是会占内存空间的)
3、单个key的最大数据1M(一般不缓太大的数据,memcache的访问频率很高,如果单个key很大,带宽会非常高)
4、连接操作等无需验证,因此必须放在防火墙后面,只允许内网访问

调试排错能力

编    写:袁    亮
时    间:2015-01-20
说    明:php后端程序人员简单排错能力

一、说明
能力强不强是通过解决问题来体现的,不能解决问题的话,其他的都是白搭
ps:不要觉得错误莫名其妙,我这边明明是好的,用户那边有问题,就怀疑是电脑有问题啊,服务器有问题啊什么的,mysql有问题啊之类
99%的问题,都是程序上的问题,没能重新,只是你没重现出那个特殊的情况

二、常见错误类型
1、网站打不开,不能访问
2、能打开页面,但是页面显示不正常,比如白页,404等
3、操作失败,比如不能发帖,不能删帖等
4、逻辑错误,比如帖子的回复数不对,页面缓存一直错误
5、页面打开很慢,性能问题,超时,卡等
6、定时脚本执行结果不符合预期

三、网站不能访问
当有人反馈,网站打不开的时候,可以参考以下几个步骤来进行排查
ps:需要确定的是,直接访问不了,而不是访问的时候页面出错,访问出错请参考第四条
1、要到相应的链接及报错截图,自己打开电脑访问下,是否能正常
2、如果自己能正常访问,使用阿里测或者17测看下,是否正常
3、如果17测等不能正常访问,看下是否是自己加了host所以能正常访问
4、如果17测等有些不能正常访问,需要让对方ping下这个域名是到哪个ip
这个时候需要看该域名是否使用了CDN加速,如果使用了可能是CDN节点有问题
如果ping的ip和自己的电脑一样,那就需要看下对方访问出错的截图,是否是因为cookie头过长等原因导致不能访问
5、通过以上步骤,基本上都能搞定该类问题

四、页面能打开,但是显示不正常
该问题指的是,已经能正常由apache返回数据,但是返回的页面和正常页面不同
1、查看http返回头状态码(firebug或者chrome里,网络中都能很容易看到)
2、500
如果有显示报错,根据报错信息查看哪边出问题即可
如果直接白页,查看apache错误日志中的报错信息即可,或者复制一个临时文件出来,开启报错查看也可
3、404
是否是文件被删了
还是rewrite规则写的有问题,导致没有找到相应文件
也有可能是程序逻辑中判断,比如哪个资源没有了,所以显示的404,需要根据具体的代码逻辑来查找
4、502
一般来说,这是由nginx返回的,说明后端apache的响应时间过久
查看apache日志,找到超时的请求,看下执行了多久
在程序中的响应地方记录log,看是哪段代码超时了
一般来说,都是sql语句导致,查看对应数据库的慢查询sql即可快速找到,然后优化
5、200
页面如果显示错误信息等,根据错误提示调试代码即可
如果没有显示,可以查看apache错误日志
页面排版错误等,直接按F12查看调整或者查看源代码,一般来说html代码有问题,源代码里都会标红等,一眼就能看到
页面错误还有很大一种可能是静态文件缓存:CDN缓存,nginx缓存,浏览器缓存等等需要注意

重要:如果是正式项目中,页面有显示报错信息,务必将报错关闭,不允许在线上开启报错

五、操作失败
页面等能正常访问,但是进行相应操作的时候失败,比如发帖,删帖等
1、自己操作一遍,看下是否能成功
在操作测试的时候,请以真实的数据去测试,并及时删除,不要乱写什么test之类的进行测试
并且在测试完之后,马上将测试数据通过正常操作删除
2、如果自己能成功,其他人失败,看是否有失败的提示信息
比如权限不足,有违禁词等等,根据提示找到为什么失败
3、需要通过修改程序来debug的话,请务必保证在以下两种情况(线上项目)
3.1 项目有测试机
在测试机上进行调试,debug,改完之后,通过svn上线到线上版本
3.2 没有测试机,并且比较紧急的话
将相应的操作文件copy一份出来,然后输出调试,找到问题出在哪
改完之后,copy回去之前,先备份原文件,并diff比对下两个文件,保证没有改了多余的地方
copy覆盖回去之后,必须要进行全面测试,保证之前出问题的情况不会出现,以及正常操作的时候也没问题
不要出现原来的问题解决了,但是带来了新的问题
4、在3满足的情况下,参考以下的debug步骤
第一步就是开启报错,80%的问题,开启报错之后就能知道是什么问题了(或者看错误日志)
不要从头开始看代码,指望着看出来问题,这是不现实的,我很讨厌别人跟我在说,我在看
根据问题状况,定位到哪边出的问题,比如发帖之后,posts表没有插入数据,那就先找到插入posts表的代码在哪
找到这段代码的时候,可以通过var_dump,die看下这段代码是否执行到了,是否执行成功
如果执行不成功,sql的话,直接输出这条sql,放在phpmyadmin里执行看报错
如果没有执行到的话,向前找节点(if else判断等)),看是在哪一步判断导致没有运行到该出
在大段代码,设置debug节点的时候,不要很秀气的每次一行一行的设置,那会疯掉的
最简单的二分法,400行代码,出问题,先在200行左右var_dump,die一次,不行,就100行的地方,再不行,就50行的地方
5、注意事项
有些时候觉得有些bug莫名其妙,总是重现不了,需要考虑下,用户的情况跟你是不一样的
比如账号不同,比如cookie不同,比如当前电脑的时间不同,比如发了一些特殊符号等等都有可能导致用户出问题
之前还出现过一次因为bing爬虫爬取错误链接然后导致缓存出错的问题,这个问题在解决之前也是觉得莫名其妙

六、逻辑错误
比如帖子的回复数不对,用户的积分不对,用户付完钱,结果订单被取消等等
1、这种问题,一般来说都比较麻烦,首先需要知道的是有哪些地方涉及到该逻辑
2、猜测哪些操作可能导致该问题的出现,比如用户的积分多了,是不是该减积分的地方没减,加的时候加多了?
3、针对比较可能出问题的操作点,重新操作,看是否有问题

七、页面性能
该类问题比较大,只列几个粗略的方向
1、查看apache日志,找到最慢的那些请求和平均的请求时间,定位哪些访问有问题
2、找到对应请求,设置时间点,找到慢的程序块
3、具体分析,打印相应的块执行时间,找到问题点
4、查看mysql慢日志是一种比较快能定位到问题,一般来说web项目卡都是数据库层面出了问题

八、定时脚本执行未达到预期
1、确定定时脚本是否有执行
查看定时脚本执行log,是否有
如果没有,则查看下报错log,根据报错log调整
复制要执行的脚本,其他操作都不执行,只输出一个最简单的内容,修改定时脚本的执行时间,每分钟执行,看是否正常
2、脚本有执行,但是结果和预期不同
定时脚本编辑的时候,必须在各个关键节点输出日志
出问题之后,根据自己记录的log日志,查看哪一步跟预期不同
如果之前未记录,则根据问题表象,在相应节点输出相应的log,可以临时执行该定时脚本,查看是哪边出的问题

RPC接口的使用


编	写:袁	亮
时	间:2015-01-15
说	明:xmlrpc的工作原理和使用

一、为什么需要这个?
	1、学名叫远程过程调用,即调用远程其他项目的函数方法
	2、例如A项目,里面有用户的图片,在B项目里,我需要获取到这个用户的图片,最老的方法,就是直接连A项目的数据库,重新写一遍获取数据的逻辑。这会带来非常多的问题:
		2.1 同样的一个功能,我需要重复写非常多遍
		2.2 如果项目要迁移,我都不知道要改多少地方(比如数据库连接等等)
		2.3 如果之前的功能稍微做点改动,我得把每个涉及到的项目都得改一遍,然后测试,保证没问题
	3、为了解决上述问题,我们希望通过接口的方式去调用其他项目的相应功能,xmlrpc就是其中一种解决办法
		soap,或者直接通过http获取数据等,也都可以解决
	4、数据采用xml的形式进行传递,

二、工作原理
	1、一次服务器与服务器之间的http调用
	2、客户端根据相应的参数,构建相应的http请求头信息
	3、使用fsockopen发送数据请求,请求数据转为xmlrpc协议格式
	4、服务端接收到请求,查看是否有定义相应的函数
	5、服务端调用已注册的rpc方法,并输出xml格式的返回值
	6、客户端接收到相应的响应数据后,将xml重新解析会php变量
	7、rpc过程结束

三、使用范例
	1、服务端编写接口
		include_once("../inc/class_xmlrpc.php");

		function messAdd($m,$p){
		    $p = $p[0];
		    $ms = new MessClass;
		    return $ms->messAdd($p);
		}

		$xmlrpc_funcs = array(// 注册函数
    		'messAdd',
		);
		$xmlrpc_name = 'api/mess';
		$xmlrpc_server = new xmlrpc_server();
		foreach ($xmlrpc_funcs as $v) {
		        $xmlrpc_server->register_method($xmlrpc_name.'.'.$v, $v);
		}
		$xmlrpc_server->call_method();

		1.1 引入xmlrpc类
		1.2 正常写一个函数,第一个参数预留,封装的类里用到的,第二个参数是客户端传过来的参数
			$p[0] $p[1] 分别代表第一、第二个参数,依次类推
		1.3 将本地的方法注册,使其可以成为可以被rpc调用的方法
			需要一个命名空间,类似api/mess
			register_method中,函数名可以不一样,但我习惯会命名成一样的
		1.4 查看是否有语法错误
			访问下接口地址,是否正常的输出xml格式的数据

	2、客户端调用
		include_once("../inc/class_xmlrpc.php");

		$c = new xmlrpc_client("http://commentdev.ci123.com/mess/rpc/server.php","api/mess");
		$p = array(
		    'to_user_id'    => 1535917,
		    'temp_id'       => 134,
			'target_id' => 3217,
		    'platforms'     => '1,3',
		    'user_id'   => 2199,
		);
		$data = $c->call("messAdd",$p);
		var_dump($data);

		2.1 引用封装的类
		2.2 初始化rpc client类
			api/mess需要跟服务端那边取的名字保持一致
		2.3 调用相应的方法
		2.4 这边data只能接受到服务端return的返回值,使用var_dump,echo等输出是不行的
			因为那边输出的是函数的返回值,var_dump等并不是函数返回值
		2.5 如果是测试机,需要在对应的服务器上加host,在本地加host是没用的
			因为这是服务器跟服务器之间的通信(如果在本机上测试,hosts是同一份)

四、常见问题
	1、在服务器上调用一个测试服务器的rpc,出错
		需要在服务器上加host,因为服务器找不到测试机的域名
	2、如果rpc所在服务器加了http basic认证
		可以在client初始化的时候,把账号、密码传过去
	3、超时时间在客户端初始化的时候设置
	4、测试用例保留
		客户端调用范例文件保存,在最开始die掉