存档

作者存档

select *导致的mysql线程sending data

2017年6月17日 没有评论

今天因为公司618活动到公司值班,早上10点钟左右的时候,领导突然跑过来说”select * from xxx”导致数据库服务器负载飙升,让我看一下什么问题。当时由于紧张还看错了表名,看成另外一张表,后来联系公司dba,又让他发了一遍引起数据库负载飙升的sql,发现原来是活动开始以后,大量查询活动信息引起的问题。

我们的活动信息表里面有大量的城市id和活动的表述信息,而当有大量的”select * from xxx”存在时,虽然这个表的数据量不是很大,只有区区几千条记录记录,但是大量的查询引起mysql线程状态卡在”sending data”时,服务器的负载就上来了。

那么什么是”sending data”状态哪?其实这是一个很容易引起误导的状态说明,”sending data”是包含读取数据+发送数据的。这里以innodb存储引擎来说,我们在使用索引找到我们所需的记录时,期初得到是索引列信息和主键信息,如果我们查询的信息索引列中已经包含,那么万事大吉,mysql会把这些信息发送给客户端。但是如果像我的例子中的是使用”select *”这种情况,或者要索引列中未包含我们需要需要的更多信息,那么这时mysql就会拿着主键id去数据行获取信息,然后再把些信息发送给客户端。

现在回到上面的问题,我们数据表记录数不多,为什么会引起”sending data”哪?我们活动信息表有几个字段是MEDIUMTEXT或者VARCHAR(3000)类似这种要存储比较长字符串内容的字段,所以在使用”select *”时我们把本来不需要,但是却占用很大空间的字段也返回了,造成了大量无用的IO操作,这里包含读取数据和发送数据。由于接口我们使用的是被动缓存,所以活动刚开始时这些请求都打到了数据库,后面接口缓存生效以后数据库压力就降下来了。

开发框架使用的是Lumen,而且我们主要使用的是里面的ORM-Elopment,有些地方没有注意查询时设定字段就引起了这个问题,说句实话以前在使用ORM时,我一直没有养成限制返回字段的习惯。希望看到这篇文章的同学也能引以为戒,以后尽量不要使用”seleect *”这样粗暴的查询方式。

git reflog恢复git reset删除数据

2017年5月11日 没有评论

我经常用git reset来恢复自己的工作区,但是偶尔也会失误把错误的分支的commit记录给删除掉了。今天工作中也有遇到一次,本来要删除测试分支的commit记录,但是在操作时没有注意所在的分支,就把工作分支的内容误删除掉了。那么我们如何恢复这些commit记录哪?

当你 (在一个仓库下) 工作时,Git 会在你每次修改了 HEAD 时悄悄地将改动记录下来。当你提交或修改分支时,reflog 就会更新。因此我们就可以用reflog的记录来恢复我们的工作区。

查看reflog记录


git reflog

你会看到如下的内容:

229e8d31 HEAD@{1}: pull origin xx: Fast-forward
fd54ce35 HEAD@{2}: checkout: moving from xxx to xxxx
66010f88 HEAD@{3}: merge xxxxx8: Merge made by the 'recursive' strategy.
085b2dba HEAD@{4}: reset: moving to HEAD~1
0f0bfb7d HEAD@{5}: checkout: moving from xxxx to xxxxx
73e400ba HEAD@{6}: commit: xxxxxx
5e9d0305 HEAD@{7}: reset: moving to HEAD~1
2ef40139 HEAD@{8}: reset: moving to 2ef40139
5e9d0305 HEAD@{9}: reset: moving to HEAD~1
2ef40139 HEAD@{10}: checkout: moving from sit to xxx
0f0bfb7d HEAD@{11}: merge xxx: Merge made by the 'recursive' strategy.

恢复工作区
我们可以根据commit的SHA来用git reset恢复。

git reset --hard fd54ce35

这时我们被我们误操作的记录就已经被恢复了,可以用git log来查看git提交记录。

分类: git, Linux 标签:

mysql使用NULL值问题

2017年5月3日 没有评论

Mysql中NULL值的概念会让刚接触MySql的用户引起混乱,他们经常会认为NULL值和空字符串”是相等的。其实他们是不同的,我在接触MySql一段时间以后,还是把这2种情况混为一谈。我们来看一下下面2个插入语句:


mysql> INSERT INTO my_table (phone) VALUES (NULL);
mysql> INSERT INTO my_table (phone) VALUES ('');

2个SQL语句都是向my_table表中的phone字段插入数据,但是第1个SQL插入1个空值(NULL值),而第2个插入的是1个空字符串。第1个可以理解为“我们还不知道用户的电话号码”,第2个SQL我们可以认为“我们知道这个人没有电话,所以并没有电话号码”,我们这是把它们附加到现实中可能会出现的2种业务情景中去。
为了便于处理空值(NULL值),我们可以使用”IS NULL”和”IS NOT NULL”操作符,或者使用IFNULL()函数。
在SQL使用的时候,在空值与其他任何其他值比较时都不会为真,即使比较的值也是1个空值。包含NULL的表达式总会产生一个NULL,除非是文档中另有说明的运算符和函数设计的表达式。下面的这些例子都会返回NULL:

mysql> SELECT NULL, 1+NULL, CONCAT('Invisible',NULL);

如果要搜索列中为NULL的值,你不能使用 expr=NULL.下面的语句不会返回任何记录,因为expr=NULL对任何表示永远都不会返回真。

mysql> SELECT * FROM my_table WHERE phone = NULL;

要搜索列中为NULL的值,你必须要使用IS_NULL来匹配。下面的语句展示了如何寻找为NULL的手机号码和为空的手机号码。


mysql> SELECT * FROM my_table WHERE phone IS NULL;
mysql> SELECT * FROM my_table WHERE phone = '';

要了解更多NULL相关的信息和例子,请查看 Section 4.3.4.6, “Working with NULL Values”
如果你在使用MyISAM,InnoDB,或者MEMORY存储引擎,你可以在包含NULL的列上加索引。除此以为你必须把索引列声明为NOT NULL,而且你也不能插入NULL到这个字段。
当你使用LOAD DATA INFILE来读取数据时,空或者缺失的列会被当做”处理。要加载NULL值到1个列中,请在你的数据文件中使用\N。字面意思的NULL也可能在某些特殊情况下使用。具体请查看14.2.6节”LOAD DATA INFILE语法”
当使用DISTINCT,GROUP BY,或ORDER BY,所用NULL被认为是相同的。
当使用ORDER BY时,NULL值会被排在第一个,如果你指出desc倒序排序时会排在最后一个。
统计函数如COUNT(),MIN(),和SUM()会忽略NULL值。表达式COUNT(*)会统计行数而不是单个列的值。例如,下面的语句会产生2次计数统计。第一个语句统计这个表中所用的行数,第二格语句统计age列不是NULL值的数量。

mysql> SELECT COUNT(*), COUNT(age) FROM person;

对某些数据类型,MySQL会特殊处理其中的NULL值。如果你要插入NULL到1个TIMESTAMP列,当前的时间会被插入进去(手册版本为MySQL5.7)。如果你要插入NULL值到整形或者浮点型的带有AUTO_INCREMENT属性自增列,下1个自增的数字会被插入。

本文是对MySQL5.7中手册的翻译,原文地址https://dev.mysql.com/doc/refman/5.7/en/problems-with-null.html,如有错误请指正。

bootstrap modal中ckeditor冲突bug

2016年12月23日 没有评论

这段时间在用一个bootstrap的前端后台框架做一个系统,里面设计到一些在线编辑的内容,正好这个前端框架里面的编辑器有给出ckeditor的例子,就拿来使用,也是折腾了好久了。上一次使用ckeditor好像还是刚毕业那会吧。我的部分内容添加和编辑放在bootstrap的modal里面,然后再在modal中嵌入在线编辑器(ckeditor),基本都调试好,在最后调试ckeditor的图片上传时,发现上传的图片的高度和宽度无法编辑,后来发现ckeditor弹出来的上传图片对话框中的所用input框都无法输入了。

正常情况下应该是这样的:

看,其中的宽度是可以选择和修改的,但是现状确实类似下面的:

宽度高度这些input输入框无法选择。我看前端框架里面有ckeditor demo,但是他没有使用modal,我这里使用modal了,所以这个应该是和modal的某些js冲突了。自己折腾了好久也没有解决,试着在google搜索”bootstrap modal ckeditor”时,看到搜索提示里面有个”bootstrap ckeditor modal fix js”,然后就使用这个关键词搜索了一下,果然在github上面有人遇到类似的问题,还给出了修复的js代码。

</pre>
<pre>$.fn.modal.Constructor.prototype.enforceFocus = function() {
    modal_this = this;
    $(document).on('focusin.modal', function (e) {
        if (modal_this.$element[0] !== e.target && !modal_this.$element.has(e.target).length
            && !$(e.target.parentNode).hasClass('cke_dialog_ui_input_select')
            && !$(e.target.parentNode).hasClass('cke_dialog_ui_input_text')) {
            modal_this.$element.focus()
        }
    })
};</pre>
<pre>

我将这段代码放入ckeditor的配置文件(config.js)里面,然后重试,果然问题解决!这里把自己问题解决的部分细节也说了出来,是有点感叹google对程序员战斗力提升的厉害啊,如此看来无怪乎国外的程序员厉害,人家不用翻墙也可以google啊!

phar函数介绍

2016年11月30日 没有评论

php官方关于phar手册的地址,不过由于这个模块比较相对比较冷僻,所以没有人翻译手册,这里针对一些常用的函数,对手册做一些简明的翻译。

public Phar::__construct ( string $fname [, int $flags [, string $alias ]] )

构造函数,用来构建Phar对象。

$fname是一个指向一个phar归档文件的路径或者要创建的归档文件存放的路径,注意这个文件的扩展名必须包含“.phar”。

$flags传入给父类RecursiveDirectoryIteratord的标志。

$alias在调用流功能时应引用此Phar归档的别名.

错误和异常:被调用2次时,抛BadMethodCallException异常,当$fname指定的归档文件打不开时抛UnexpectedValueException异常。

 

public array Phar::buildFromDirectory ( string $base_dir [, string $regex ] )

从指定目录中的文件构造phar归档,此方法需要 将 php.ini 中的 phar.readonly 设为 0 以适合 Phar 对象. 否则, 将抛出PharException.用目录中的内容填充phar归档文件,第2个可选参数是一个正则表达式,用来过滤内容,匹配正则表达式的包含到归档文件中,否则排除。如果想要控制的更精细,试试Phar::buildFromIterator()。

$base_dir指定要填充到归档文件内容的目录路径(相对路径和绝对路径均可)。

$regex是一个可选正则参数用来过滤目录内的文件,只有匹配正则表达式的文件路径会被填充到归档文件中。

返回将文件的内部路径映射到文件系统上文件的完整路径的关联数组。

 

public void Phar::addFile ( string $file [, string $localname ] )

将一个文件从文件系统添加到 phar 档案中,此方法需要 将 php.ini 中的 phar.readonly 设为 0 以适合 Phar 对象. 否则, 将抛出PharException。通过这个方法,任何文件或者 URL 可以被添加到 phar 档案中。如果第二个可选参数 localname 被设定, 那么文件则会以该参数为名称保存到档案中,此外,file 参数会被作为路径保存在档案中。 URLs 必须提交 localname 参数,否则会抛出异常。 这个方法与 ZipArchive::addFile() 类似.

$file需要添加到 phar 档案的文件在磁盘上的完全(绝对)或者相对路径。

$localname文件保存到档案时的路径。

没有返回值,失败时会抛出异常。

 

public bool Phar::setDefaultStub ([ string $index [, string $webindex ]] )

用来将归档文件的加载器或者引导脚本设置为默认的加载器(几个英文单词吃不准,后面是原文 Used to set the PHP loader or bootstrap stub of a Phar archive to the default loader)。其实这个函数是Phar::createDefaultStub() 和Phar::setStub()的一个封装。

$index是一个归档文件内部的相对路径,如果在cli模式(命令行)下运行phar文件时执行的脚本。

$webindex是一个归档文件内部的相对路径,如果通过web浏览器访问phar归档文件时执行的脚本。

成功返回true,失败返回false。

 

public void Phar::compressFiles ( int $compression )

压缩当前phar归档中的文件。

$compression压缩时必须为Phar::GZ或 Phar::BZ2 ,或者使用 Phar::NONE 移除已有的压缩.

 

public bool Phar::extractTo ( string $pathto [, string|array $files [, bool $overwrite = false ]] )

将归档文件中的内容解压到指定目录。解压归档文件到硬盘时,会保留他们在归档文件中的权限。可选参数允许你控制那些文件提取到硬盘上或者当它们存在时是否覆盖它们。第2个可选参数可以是字符串或者数组,表示提取文件或目录的名字。默认的这个函数不会覆盖存在的文件,不过可以通过将第3个参数设置为true启用强制覆盖存在的文件。

$pathto解压提取到硬盘的路径。

$files可选参数指定提取归档文件或目录的名字,或者提取文件或目录的名字的数组。

$overwrite设置为true启用强制覆盖。

成功返回true,不过更好的办法是检查异常,如果没有异常抛出就是成功。

 

final public static bool Phar::loadPhar ( string $filename [, string $alias ] )

使用别名加载任意的phar归档文件。一般用来从外部来读取归档文件中的内容。最常用的就是给归档文件一个别名,随后就可以使用别名来引用归档文件,或者用于加载仅包含数据且不用于在PHP脚本中执行/包含的Phar存档。

$filename要打开的归档文件的相对或者绝对路径。

$alias设置一个用来引用归档文件的别名。许多phar档案在phar档案中指定一个显式别名,如果别名已经被制定过时,会抛出一个PharException异常。

成功时返回 TRUE, 或者在失败时返回 FALSE。

phar简介

2016年11月30日 没有评论

php5.3以后有一个非常不错的特性phar,有些类似java中的jar包,就是可以把php应用程序打包到一个单独的phar归档文件中。其实这个特性以前很早貌似就用,只不过最早是用php写的,所以性能上面不是特别好,后面官方用c扩展重新写一遍,文档可以查看Phar文档,不过目前90%的内容是英文的,下面我对介绍进行了一点简单翻译,当然英语水平一般,错误之处欢迎指正啊。

Phar简介

Phar扩展提供了一种将整个php应用放入一个单独文件被称为”phar”的归档,包更容易的发布和安装的解决方案。除了提供这种服务,phar扩展也提供了一种抽象化的文件格式的方法来创建和管理tar或者zip文件通过PharData类,很像Pdo提供了一种统一的接口来访问不同的数据库。和PDO不同之处,Pdo不能将数据在不同的数据库之间转换,Phar可以通过一行代码就可以在tar,zip和phar之间转换文件格式。查看Phar::convertToExecutable()示例。

phar是什么?phar归档文件最显著的特征是组合很多文件文件到一个单独的文件中。就像这样,一个phar文档提供了一种方式来发布一个完成的php应用程序在一个单独的文件中并且可以通过这个文件来运行它而无需解压到硬盘上。此外,phar归档文件也可以像其他php文件一样去被PHP执行,同时可以通过命令行和一台web服务器。Phar就像一种为PHP应用程序设计的拇指驱动器(U盘)。

Phar实现这些功能通过Stream Wrapper。一般来说,要使用一个在Phar归档中的php脚本文件,你可以用include。

函数介绍

可以参考另一篇关于phar函数手册的博客。

简单案例

这是一个比较简单的demo,希望通过这个能对phar有个比较初步的了解。

目录树如下:

|____build.php
|____config.json
|____extractPhar.php
|____lib
| |____class
| | |____empty.txt
| |____func.php
| |____stub.php
|____phar.phar
|____test_phar.php

build.php为生成phar归档文件的php脚本,config.json文件其实没什么用,extractPhar.php为解压phar到指定目录的脚本,lib目录下面就是测试用的库函数,phar.phar是用build.php生成的归档文件,test_phar.php是测试phar归档文件的脚本。下面我们一个一个的介绍这里面涉及到的脚本文件。

[build.php]

</pre>
<pre><?php</pre>
<pre>try{
    $phar = new Phar('phar.phar',0,'my.phar');
    $phar->buildFromDirectory(__DIR__.'/lib/');
    //$phar->buildFromDirectory(__DIR__.'/lib/',"/\.php/");
    $phar->addFile("config.json","config");
    $phar->setDefaultStub('stub.php','stub.php');
    $phar->compressFiles(Phar::GZ);
}catch (Exception $e){
    echo $e->getMessage();
}</pre>
<pre>

这里需要注意把php.ini 中的 phar.readonly 设为 0 以适合 Phar 对象. 否则, 将抛出PharException。

[func.php]

</pre>
<pre>function println($str){
    echo $str."\n";
}

function myFunc($input=__FILE__){
    println('Input File:'.$input);
    println('File Path:'.__DIR__);
}</pre>
<pre>

[stub.php]

</pre>
<pre><?php

require_once 'func.php';
myFunc(__FILE__);</pre>
<pre>

[test_phar.php]

</pre>
<pre><?php
include_once 'phar.phar';
include_once 'phar://my.phar/func.php';
myFunc(__FILE__);
$content = file_get_contents("phar://my.phar/config");
println($content);</pre>
<pre>

[extractPhar.php]

</pre>
<pre><?php
$phar = new Phar('phar.phar');
$phar->extractTo('./extract/');</pre>
<pre>

通过命令行脚本执行”php build.php”可以生成phar.phar归档文件。可以直接用”php phar.phar”执行phar归档文件,
它会执行”setDefaultStub”设置stub文件。extractPhar.php会将phar.phar归档文件中的内容解压到extract目录下面,
方面我们查看phar归档中的内容。
test_phar.php的执行结果如下:

Input File:phar:///Users/king/www/test/Phar/phar.phar/stub.php
File Path:phar:///Users/king/www/test/Phar/phar.phar
Input File:/Users/king/www/test/Phar/test_phar.php
File Path:phar:///Users/king/www/test/Phar/phar.phar
{
 "name" : "phar test project",
 "path" : "~/www/test/Phar"
}
extractPhar.php解压的extract目录如下:
|____class
| |____empty.txt
|____config
|____func.php
|____stub.php

ubuntu mysql5.7无法远程连接问题

2016年10月31日 没有评论

晚上在做联系xtrabackup时,实验环境是一台centos7和一台ubuntu 16.04的虚拟机,centos7上面安装的mysql在创建完远程连接账号以后可以正常登陆,但是ubuntu却无法登录,提示“ERROR 2003 (HY000): Can’t connect to MySQL server on ‘192.168.99.88’ (61)”。其实问题很简单,这里主要记录一下当时我解决问题的思路,当然当时思路不是很正确,以前也没有遇到过类似的问题。

第一时间我想到的是自己是不是把账号弄错了,ubuntu上面的mysql没有对应的账号(带上本机3台mysql难免会弄错),就用一个root账号在ubuntu上本地登录,然后查看当前的系统有哪些用户,发现自己登录用的远程账号和权限没有问题。


mysql -uroot -p

use mysql;

select * from user\G;

然后就是防火墙,是不是防火墙没有开启。那么就查看ubuntu的防火墙。


sudo iptables -L #查看防火墙规则列表

sudo iptables -F  #清空防火墙列表

做完这些还是无法登录,google了一下,发现是ubuntu系统下mysql的bind问题。ubuntu中mysql默认绑定的是127.0.0.1,这样就只允许本机登录,想要允许其他地方登录只需要把bind改成公网ip或者任何ip均可。

编辑mysql的配置文件”/etc/mysql/mysql.conf.d/mysqld.cnf”


#bind-address = 127.0.0.1
bind-address = 0.0.0.0

然后重启mysql,问题就解决了!

c语言函数指针

2016年10月18日 没有评论

最近在学习nginx相关的内容,看书上介绍nginx的源码,里面很多地方都用到了函数指针。对于我这种c语言菜鸟来说,看的是云里雾里的,搞不清楚要干嘛,就搜索了一些函数指针的内容,自己动手实验了一番。

在C系列语言中,任何一个变量,总是要先声明,之后才能使用的,这和php差别还是很大的。函数指针的声明可以按照如下”返回类型 (*变量名)(函数参数…)“进行,下面我来举几个例子。


void (*my_func1)(void);  //声明一个返回类型为空,参数为空,名字为my_func1的函数指针

int (*my_func2)(int x,int y);  //声明一个返回类型为整形,参数为整形x和整形y,名字为my_func2的函数指针

int (*my_func3)(int,int);   //声明一个返回类型为整形,参数为2个整形,名字为my_func3的函数指针,其实和上面的my_func2一样

void (*my_func4)(char *str);  //声明一个返回类型为空,参数为字符指针,名字为my_func4的函数指针

typedef void (*void_func_t)(void);  //typedef关键字是类型定义,这里就是定义了一个返回值为空,参数为空的函数指针类型,类型名称为void_func_t

相信经过上面的例子,对c语言的函数指针大家都有一个初步的概念,看了下面的代码相信大家基本都能初步掌握函数指针了.

文件名:main.c


#include <stdio.h>

//定义返回值为int,传入参数为2个int的函数指针
int (*point_func)(int,int);
int (*p_func)(int a,int b);
//定义返回值为int,传入参数为2个int的函数指针类型
typedef int (*sum_func)(int,int);

/**
 * 定义一个相加函数
 */
int sum(int x,int y){
 int r = x+y;
 printf("%d+%d=%d\n",x,y,r);
 return r;
}
//执行传入进去的函数
void exec_func(sum_func do_func){
 do_func(10,21);
}

int main() {
 int result;
 sum_func my_func;

my_func = sum;
 result = sum(1,2);
 point_func = &sum;
 p_func=point_func;
 //通过指针的方式调用函数
 (*point_func)(3,4);
 (*p_func)(5,6);
 p_func(7,8);
 exec_func(my_func);
 exec_func(point_func);
 return 0;
}


上面代码中我们既可以通过”(*p_func)(5,6)”也可以直接通过”p_func(7,8)”直接调用,前者是比较正规的写法,但是阅读起来是不是没有后者那么舒服顺畅。

编译上面代码”gcc main.c -o main”,执行代码就可以看到下面的输出结果:


1+2=3
3+4=7
5+6=11
7+8=15
10+21=31
10+21=31

 

centos7安装redis3.2

2016年9月27日 没有评论

最近在学习laravel框架,打算在虚拟机里面装个redis,使用yum安装时发现提供的版本是2.8。目前最新的版本是3.2,而3.0以后redis提供了cluster,对集群有很好的支持。在google了一番以后也没有找到redis3.2的源,于是就开始编译安装了。

redis官网下载最新版本的redis源码,然后开始编译安装,可以参考源码里面的README。


wget http://download.redis.io/releases/redis-3.2.4.tar.gz

tar -xvf redis-3.2.4.tar.gz

cd redis-3.2.4

cat README.md

make

make install

mkdir /etc/redis

cp utils/redis.conf /etc/redis/

redis-server /etc/redis/redis.conf

这时候用安装基本完成了,但是上面安装基本上按照默认的方式安装,但是有些地方可以自定义。比如redis安装位置,可以使用`make PREFIX=/some/other/directory install`,其中的“/some/other/directory”就是redis安装位置。

为了方面我们管理redis,我们可以写一个redis.service配置文件来方便我们管理redis。

这里我就给一个很简单的service配置文件例子:


[Unit]
Description=redis service file
Wants=network.target

[Service]
Type=forking
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
[Install]
WantedBy=multi-user.target

将上面的配置信息放入/lib/systemd/system/redis.service文件中,然后重新载入配置文件即可。还有一点就是需要修改/etc/redis/redis.conf中,将damonize改为yes,默认为no,即以守护进程的模式启动redis。


systemctl daemon-reload

#开机启动

systemctl enable redis.service

#启动redis

systemctl start redis.service

#关闭redis

systemctl stop redis.service

到此,redis的安装就完成了,可以使用redis-cli链接server了!

分类: Linux 标签:

php5.5新特性::class

2016年9月26日 没有评论

最近在了解laravel框架,发现它里面很多地方都使用了”类名::class”,这是php5.5以后新增的特性,功能有些类似get_class,不过get_class传入的是对象。

下面是官方手册中的描述和例子:

自 PHP 5.5 起,关键词 class 也可用于类名的解析。使用 ClassName::class 你可以获取一个字符串,包含了类 ClassName 的完全限定名称。这对使用了 命名空间 的类尤其有用。


<?php
namespace NS {
class ClassName {
}
echo ClassName::class;
}
?>

以上例程会输出:NS\ClassName.

我们可以拿它和get_class做一个比较:


<?php
NameSpace Test;

class Test{
}
var_dump(Test::class);
$t = new Test;
var_dump(get_class($t));

执行以后输出:

string(9) “Test\Test”
string(9) “Test\Test”

分类: PHP 标签: