MySQL 5.5升级到MySQL 5.6 

3,053 views

升级之前,最好把这台服务器在负载中卸下来,升级完成后重新加入负载。具体操作看是用什么负载均衡软件。

1. 阅读MySQL 5.6 release notes 和change list 以便查看MySQL 5.6 的新特性和兼容性
根据change list 更改相应的配置和SQL语法
1) Configuration Changes
2) SQL Changes
3) Server Changes

https://dev.mysql.com/doc/refman/5.6/en/upgrading-from-previous-series.html

2. 备份MySQL 5.5 所有数据用mysqldump

mysqldump -uroot –all-databases –routines –events > all.sql

3.暂停数据库,如果有主从关系,那么很重要的一点是要记录下从的同步节点的Binlog和pos

4.更改MySQL 5.5 二进制程序目录名字,以便升级失败快速回退

mv mysql5.5 mysql5.5_back

5. 解压MySQL 5.6 二进制程序到对应的目录

tar -zxvf mysql-advanced-5.6.14-linux2.6-x86_64.tar.gz

mv mysql-advanced-5.6.14-linux2.6-x86_64  ~/mysql5.6

6.如果MySQL 5.5 配置文件和MySQL 5.6配置文件不兼容,修改对应的配置项,例如:

[mysqld]
innodb_file_per_table=0

7.把刚刚mv掉的mysql5.5_back里的data复制到mysql5.6下的data里

cp -R ~/mysql5.5/data ~/mysql5.6/

8. 把5.5的my.cnf复制到5.6对应的目录下

9.启动5.6的mysql

~/mysql5.6/bin/mysqld_safe –default-files=~/mysql5.6/my.cnf

10. 运行mysql_upgrade 修复5.5到5.6之间的不兼容情况,记得运行的时候带上用户和密码,用户和密码是5.5的时候的用户密码就行。因为5.5的数据都拷到5.6了

~/mysql5.6/bin/mysql_upgrade -uwww -p

11. upgrade后,如果正常应该是输出各种OK, 最后进入命令行界面 mysql -uwww -p

检查版本的输出5.6, 看看slave的状态是不是正确的,不正确重新做一遍slave的配置就行。

PS:

其中会遇到很多情况,基本上会是配置问题,配置文件里新的配置项冲突,这个第一点讲过了需要更新到最新的,如果有疏漏,可以不使用后台的命令行启动mysql直观的看输出,或者看下err.log文件里面的错误日志,基本上都能说明清楚了,对应改过来基本没问题,这个步骤线上操作过好几台服务器,基本没问题。

 

技术QQ群:221073018

一个引用造成的血案, $a = &$b

6,301 views

这几天在群里面有人讨论怎样才算PHP基础好, 然后某人给出了一道引用题,是这样的:

$a = 1;

$b = &$a;

echo (++$a) + (++$a);

问,输出为什么是6?(我觉得,好恐怖的基础。如果是这样来判断基础好不好, 那么换成奇葩的JS,估计很多人要剖腹了……)

这是一道有意思的题目, 如果只是通过PHP表面去解释,一般也只能说到因为$a的值是指针地址这个点上。相信说完, 隐约知道是引用, 知道应该是同一个地址的值,但是还是有很多人云里雾里想不透彻吧。

如果通过内核来分析会更清晰一点,我给出我自己的理解。

首先,我们要明白php变量是怎么实现的。有很多文章已经说明了原理,我就不一一细说了,给出学习地址 “PHP变量”  , 接着, 还要明白PHP里引用计数的知识, 传送门 “PHP引用计数” 。 现在我认为你们都有基础的认识了, 然后我们来说下刚才的题目。

在这个PHP脚本被ZEND编译成opcode的时候, OPCODE的代码是这样的, 先看图:

Screenshot from 2014-04-01 23:55:21

 

这段OPCODE,先和鸟哥普及下知识吧, 地址是”深入原理之Opcodes” 。

我们来分析下步骤

首先,$a = 1; 的时候, 是!0, 1, 因为!0代表$a的cache地址, 这时候常量1赋值给$a, 对应的,$a 对应的zval结构中, refcount = 1,  is_ref = 0  。

到$b = &$a;的时候, 是引用赋值, 这时候,变量 $b和$a都指向同一个zval结构, 这个时候zval结构的refcount = 2, is_ref = 1。

到echo (++$a) + (++$a);了, 由图里面可以看出来, 先执行了两次PRE_INC, 就是两次前缀自增。由前面鸟哥说明的OPCODES里面操作数类型区别,显然,$2 !0和$3 !0分别可以看成$a自增后分别返回了2个新的变量IS_VAR(分别是$2, $3), 因为$a对应的zval结构中,refcount = 2, is_ref = 1, 所以新变量$2,$3没有分离, 他们都还同时指向同一个zval 。(关于为什么没有分离的知识,请查阅PHP写时复制和写时分离的相关知识吧,这里就不给出来了)

最后执行ADD指令的时候, 因为是同一个zval相加, 而当前zval因为自增了两次, 变成3, 所以就有了 3+3 = 6

整个过程变成PHP过程大概可以这么理解

$a = 1;

$b = &$a;

$c = &$a;

$d = &$a;

++$c;//或者是 $c = ++$a;

++$d;//或者是 $d = ++$a;

echo $c + $d;

其中$b = &$a 的意义只是为了把$a对应的zval结构中is_ref设置为1罢了。 如果没有了$b = &$a;这句,就是输出5了, 现在大家能想明白为什么是5了吧?

###########

最近开了个技术群, 有兴趣的朋友进来吹牛逼吧, QQ群号221073018

最近招PHP出的一道面试题

2,694 views

有简单的地方, 也有坑的地方。 但是基本上没有能答得让我比较满意。

分享下题目,如果有什么不合适的, 请指出来谢谢

你觉得下面的3个var_dump会输出什么,如果你是作者,在代码上,你会怎么修改到你觉得满意的程度?

Screenshot from 2014-02-19 22:10:37

 

Screenshot from 2014-02-19 22:11:30

 

 

 

我分享下我想要的答案:

1 在 protected $_a 这行,  把$_a变量修改成至少是拼音。。。中式英语也行。。。 best: $_interviewCount,相应的下面用到$_a的代码也一并改掉

2 构造函数public function Interview , 这样的同名构造函数, 虽然现在PHP5向下兼容, 但是会给程序带来维护的复杂度, 因为修改类名的时候, 还得再修改构造函数的名字。  改成 __constructor 不仅在语义上更好,而且降低了维护成本

3 getInterviewCount的静态方法里,

 

if的花括号至少应该和整个文件的风格统一, 应该要换行。

而且代码我觉得最好的方式应该改成

return $this->_a  ? $this->_a : $this->_a * 5;

另外, 如果PHP版本在5.4+的话,最好改成

return $this->_a  ? : $this->_a * 5;

只需要一行即可,既修掉了花括号的问题, 也考察了对PHP版本带来的更好性能的新变化的认识。

 

4 文件应该把最后的?>取消掉, 在?>后的换行和空格会给程序带来隐患, 具体隐患相信每个PHPer都知道

5 第一个var_dump输出并不是1或者5, 而是报错 Fatal error: Using $this when not in object context in xxx

因为getInterviewCount方法是静态方法, 静态方法可以直接调用, 不需要实例化, 因此没有实例化也就不存在$this了,也无法访问。 因此代码改成

return self::$_a ? : self::$_a * 5;此时,并没有完, 想要访问到$_a, 还必须把$_a改成静态变量

这时候输出0, 因为$_a并没有被构造函数初始化, 在执行乘法的时候PHP有一次强制转换, 变成 0 * 5

6 第二个var_dump输出2, 方法里注明$arg参数是传引用。当$arg给$return赋值的时候,这个赋值操作只是简单的赋值操作, 这里会有一次写时分离的操作在PHP内部, 把$arg指向的zval结构体分离出一份单独给$return, $return 的引用计数是 refcount=1, is_ref=0 , 而不是 refcount = 3, is_ref = 1, 即$return不会也是外部$test的引用. $arg还会保持原来的refcount = 2, is_ref = 1 , 当$arg + 1的时候, 外部的$test也+1,但是$return的值是2

7 第三个var_dump输出的是1,6

当走到global $var 和global $var2 的时候, 函数内部的$var, $var2虽然和外部的$var, $var2变量名相同, 但是其实它们是存在于不同的符号表里的, 理应是不同的作用域, 但是因为有了global, 这个声明会将函数内部的$var作为外部$var的引用, $var2同理。 当 $var2 = &$var发生的时候, $var2原来引用了外部的$var2内存地址, 现在变成引用$var的内存地址, 此时函数内部的$var,$var2和外部的$var内存地址相同, 当$var2 = 1;的时候,  函数内部的 $var, $var2和外部的$var 都变成1 ,外部的$var2仍然是6 。 各个变量的计数器变化如下

刚开始, 外部的$var, $var2 计数器都是 refcount=1, is_ref=0, 走到两个global完后, 外部和函数内部的$var, $var2计数器都变成 refcount=2, is_ref=1, $var2 = &$var的时候以及走到$var2 = 1,内部 $var2 和 $var和外部的$var 都是 refcount=3, is_ref=1, 外部的$var2 变回 refcount = 1, is_ref = 0 。

 

####

最近开了个技术群, 有兴趣的朋友进来吹牛逼吧, QQ群号221073018

tcp 内核参数修改减少TIME-WAIT后对NAT 用户的影响

2,644 views

减少TIME_WAIT连接状态。网络上已经有不少相关的介绍,大多是建议:

shell> sysctl net.ipv4.tcp_tw_reuse=1
shell> sysctl net.ipv4.tcp_tw_recycle=1

好了,这次悲剧了,参数设置后。 NAT用户访问,有的用户会无法访问服务器, 抓包是完全木有反映的。 为毛呢, 因为TCP的这2个内核参数设置后, 会直接影响NAT用户的访问, 参考引用:

为了提高TCP的性能,“RFC1323 – TCP Extensions for High Performance”提出了 一个机制(http://tools.ietf.org/html/rfc1323#page-29)来替代TIME- WAIT状 态的功能。linux 实现了它。这个机制通过记录来自每台主机的每个连接的分组时间戳来实现,要求 来自同一主机的同一连接的分组所携带的时间戳要比之前记录 的时间戳新,以便 “防止回绕的序号PAWS机制“ (http://tools.ietf.org/html/rfc1323#page-17) 丢弃接收属于旧连接的延时分组。这依赖于来自每个主机的每个 TCP连接分组所携 带的时间戳要单调递增才能实现。然而经过NAT的连接,其分组携带时间戳每个用户都不同的(甚至有人写了个论文,利用这个分组的时间戳来计算NAT 后端有多少台主机 http://phrack.org/issues.html?issue=63&id=3#article),也就是说同一个ip ,携带的时间戳不会单调递增。服务器端对同一个ip 过来的包的timestamp 做一个验证,导致这些连接分组被认为是属于旧 连接的延时分组而被丢弃。

所以呢, 把原来设置的2个参数设置回0就行了。

LINUX的DNS修改

1,852 views

以前修改LINUX的DNS很简单, 直接在/etc/resolv.conf里面添加修改就行了。 现在的LINUX的DNS管理软件不再这么做了, 大多数情况下,大家会看到resolv.conf有如下注释

# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND — YOUR CHANGES WILL BE OVERWRITTEN

然后不管怎么修改, 最后都会悲剧, 因为不会生效。

这时候要看当前LINUX的DNS管理软件是哪个了。 ubuntu13.10的是resolvconf, 有些可能是dnsmasq之类的。
resoveconf的话,解决办法如下:
修改/etc/resolvconf/resolv.conf.d/head, 在head里面添加
nameserver 8.8.8.8#你喜欢哪个用哪个
然后sudo resolvconf -u使之生效

其实我们可以通过man resolvconf看到如下3个文件的英文说明:
/etc/resolvconf/resolv.conf.d/base
/etc/resolvconf/resolv.conf.d/head
/etc/resolvconf/resolv.conf.d/tail

具体英文说明就不贴了。 3个文件分别用途是
base : 如果当前LINUX没有DNS,那么至少会使用这个base里面指定的配置
head : 会给默认/etc/resolv.conf的内容之前添加本文件的配置
tail : 会给默认/etc/resolv.conf的内容之后添加本文件的配置(没有自己创建去吧,记得设置对权限)

最后sudo resolvconf -u 生效基本上没啥问题了。