Leetao's Blog

Talk is cheap, show me the code

0%

什么是查询缓存

MySQL的查询缓存其实就是保存查询返回的完整结果.如果执相同 SQL 语句的时候,并且查询缓存是打开的,则 MySQL 会直接返回结果(在这一过程中 MySQL 还检查了一次用户权限),跳过解析,优化和执行过程.这里判断 SQL 语句是否相同是通过一个对大小写铭感的哈希查找实现的,这就意味着下面两条查询缓存是不同的:

1
2
SELECT id FROM t_user;
select id from t_user;

简单的了解了一下查询缓存,所以

如何查看查询缓存的相关参数

使用下述命令:

1
SHOW VARIABLES LIKE '%query_cache%';  

结果:

其中:

  1. have_query_cache 查询缓存是否可用
  2. query_cache_limit 可缓存具体结果的最大值
  3. query_cache_size 查询缓存的大小
  4. query_cache_type 阻止或者支持查询缓存

如何查看 SQL 查询在缓存中的命中的累积次数

使用下述命令:

1
SHOW STATUS LIKE 'Qcache_hits';

关于查询缓存

  1. 当查询语句中有一些不确定的数据时,则不会被缓存。
  2. 如果表发生变化,那么跟这个表相关的所有的缓存数据都将失效,这里的变化包括:INSERT, UPDATE , DELETE, TRUNCATE, ALTER TABLE 等,由此可见对于频繁更新的表,显然是不适合使用查询缓存的.
  3. 查询缓存可能可以降低查询时间,但是不能减少查询结果传输的时间消耗,所以是否使用它,需要结合实际情况参考.

对于第一条有如下的简单示例:

1
2
SELECT COUNT(id) FROM t_user WHERE post_date >= CURDATE(); // 不会被缓存
SELECT COUNT(id) FROM t_user WHERE post_date >= '2017-07-28'; // 会被缓存

什么是常量?

这点官方手册上写的很清楚.

可以用 define() 函数来定义常量,在 PHP 5.3.0 以后,可以使用 const 关键字在类定义之外定义常量。一个常量一旦被定义,就不能再改变或者取消定义。
常量只能包含标量数据( boolean , integer , float 和 string )。可以定义 resource 常量,但应尽量避免,因为会造成不可预料的结果。
可以简单的通过指定其名字来取得常量的值,与变量不同,不应该在常量前面加上 $ 符号。如果常量名是动态的,也可以用函数 constant() 来获取常量的值。用 get_defined_constants() 可以获得所有已定义的常量列表。

常量和变量的区别

这点官方手册也同样提到了.

  1. 常量前面没有美元符号($);
  2. 常量只能用 define() 函数定义,而不能通过赋值语句;
  3. 常量可以不用理会变量的作用域而在任何地方定义和访问;
  4. 常量一旦定义就不能被重新定义或者取消定义;
  5. 常量的值只能是标量。

定义常量的方式

1
2
3
4
<?php
const CONSTANT = "Hello world";
define ( "CONSTANT" , "Hello world." );
?>

define 和 const 的区别

两者的根本区别在于const在编译期间定义常量,而define则是在执行期间。感觉某种程度上const有点类似Java中的final关键字。由于const运行在编译期间,所以就意味着:
const 关键字定义常量必须处于最顶端的作用区域

不能在函数内,循环内以及 if 语句之内用 const 来定义常量, define 可以

1
2
3
4
5
6
7
if (...) {
const CONSTANT = 'Hello world'; // 错误
}

if (...) {
define ( "CONSTANT" , "Hello world." ); // 正确
}

const 大小写敏感,define可以忽略大小写敏感
const

1
2
3
const CONSTANT = "Hello world";
echo CONSTANT."\n";
echo Constant;

输出

1
2
3
4
Hello world

Notice: Use of undefined constant Constant - assumed 'Constant' in C:\Users\Leetao\Desktop\test.php on line 4
Constant

define

1
2
3
define ( "CONSTANT" ,  "Hello world.", true );
echo CONSTANT."\n";
echo Constant;

输出

1
2
Hello world.
Hello world.

const 只能接受静态的标量,而 define 可以采用任何表达式

1
2
const BIT_5 = 1 << 5;    // [1]
define('BIT_5', 1 << 5); // [2]

其中 [1] 在 PHP 5.6 之后的版本都可以正常使用,[2] 一直都是正确的用法

*const 采用普通的常量名称,*define可以采用表达式作为名称**

1
2
3
4
const  FOO = 'BAR';
for ($i = 0; $i < 32; ++$i) {
define('BIT_' . $i, 1 << $i);
}

*const 可以在当前命名空间定义常量,而*define则需要补全路径**

1
2
3
namespace A\B\C;
const FOO = 'BAR';
define('A\B\C\FOO', 'BAR');

*const可以在类或者接口中使用,而*define不可以**

1
2
3
4
5
6
7
class Foo {
const BAR = 2; // 正确
}
// but
class Baz {
define('QUX', 2); // 错误
}

自PHP5.6之后 const 可以定义数组常量,而define在PHP7.0才可以

1
2
const FOO = [1, 2, 3]; 
define('FOO', [1, 2, 3]);

最近写Python的时候,写类方法的时候,编译器老是建议我把一些方法改成静态方法,所以静态方法和普通方法有什么区别呢?当然提到了静态方法,也顺带提一下类方法,其中关于静态方法和类方法的区别,下面一段英文我觉得已经很好的解释了。

@classmethod means: when this method is called, we pass the class as the first argument instead of the instance of that class (as we normally do with methods). This means you can use the class and its properties inside that method rather than a particular instance.
@staticmethod means: when this method is called, we don’t pass an instance of the class to it (as we normally do with methods). This means you can put a function inside a class but you can’t access the instance of that class (this is useful when your method does not use the instance).

看完上述的解释,估计会有一些认识了,接下来让我们看一个具体的示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

class Test(object):
test = 1
def __init__(self,test)
self.test = test

def get_test(self):
print(self.test)

@staticmethod
def sget_test(test):
print test

@classmethod
def cget_test(cls):
print cls.test

a = Test(2)
a.get_test() #[1]
a.sget_test(3) #[2]
A.sget_test(3) #[3]
Test.cget_test() #[4]

其结果如下:

1
2
3
4
2
3
3
1

上面的代码其实反映了这三种方法的使用场景,普通方法参数自带self,这说明,其侧重于对象实体调用,即它有很大程度上需要使用该对象实体特有的某些属性,这里代码指的就是实体a初始化传递的参数2;而静态方法则不管,对于它来说,无论是对象实体还是类调用,它的表现都一样(见代码[2],[3]处),由于其这种特性常被用来组织类之间有逻辑关系的函数;最后对于类方法来说,很显然它关于的是类本身而不是实体,如果你把一些方法定义成类方法,那么实际上你是希望用类来调用这个方法,而不是用这个类的实例来调用这个方法,示例代码中无论怎么被实例化,其类属性test永远都不会变~~

参考链接:
Meaning of @classmethod and @staticmethod for beginner?
装饰器@staticmethod和@classmethod有什么区别?

今天在Leetcode做题的时候,发现讨论区有人的代码出现使用了any()函数,查资料过程中,发现了还有一个all(),对这两个函数简单的对比总结一下.

any(..)

从python doc中得到如下图所示解释:

其意思是对于可迭代对象中有任意一个不为False的时候,返回True,如果可迭代对象为空的话,返回False

示例

all(..)

从python doc中得到下图所示解释:

意思是对于可迭代对象中所有值都为True的情况下返回True,如果当可迭代对象为空的时候,也返回True

示例

问题原因

未设置 Application Key 导致。

解决办法

在项目根目录下执行下述命令:

1
php artisan key:generate

注意:在执行该命令执行,需要先将 .env.example 复制并重新命名为 .env,否则会产生类似的错误信息

1
2
PHP Fatal error:  Uncaught UnexpectedValueException: The stream or file "/var/www/html/ASRM/storage/logs/laravel.log" could not be opened: failed to open stream: Permission denied in /v
ar/www/html/ASRM/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php:107

执行成功后会返回如下信息:

1
2
lt94@ubuntu:/var/www/html/ASRM$ php artisan key:generate
Application key [base64:yUNJlAGBxWrxRutexftuJzVmzvllHXANxerkojqLscg=] set successfully.

然后重新刷新页面,出现下图就说明没有问题了。

搭建 Laravel 完成之后,尝试访问默认页面,然后出现了
**Whoops, looks like something went wrong.**的错误提示信息。

问题原因

由于 Laravel 对异常进行了封装,所以只显示文字,并没有将详细的异常信息打印出来。

解决办法

修改你项目中的配置文件,位置 app/config/app.phpline 41,将

1
2
3
'debug' => env('APP_DEBUG', False)
//=> 修改为
'debug' => env('APP_DEBUG', True)

然后就可以根据错误信息调试自己的代码了。

在执行 php artisan migrate出现如下错误:

从网上找到了如下的原因和解决办法

问题原因

MySql支持的utf8编码最大字符长度为3字节,如果遇到4字节的宽字符就会出现插入异常。三个字节UTF-8最大能编码的Unicode字符是0xffff,即Unicode中的基本多文种平面(BMP)。因而包括Emoji表情(Emoji是一种特殊的Unicode编码)在内的非基本多文种平面的Unicode字符都无法使用MySql的utf8字符集存储。

这也应该就是Laravel 5.4改用4字节长度的utf8mb4字符编码的原因之一。不过要注意的是,只有MySql 5.5.3版本以后才开始支持utf8mb4字符编码(查看版本:selection version();)。如果MySql版本过低,需要进行版本更新。

注:如果是从Laravel 5.3升级到Laravel 5.4,不需要对字符编码做切换。

解决问题

  1. 升级MySql版本到5.5.3以上。
  2. 手动配置迁移命令migrate生成的默认字符串长度,在AppServiceProvider中调用Schema::defaultStringLength方法来实现配置:
1
2
3
4
5
6
7
8
9
10
11
use Illuminate\Support\Facades\Schema;

/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Schema::defaultStringLength(191);
}

补充

我的 MySQL 版本是 5.5.53,同样产生了如上的错误.不过发现相应的表还是正常生成了。

再次出错

之后通过使用 Artisan 工具新建 Model 类及其附属的 Migration 和 Seeder(数据填充)类,在使用 artisan 生成 Migration出现如下的错误:

由手册可知,执行上述命令行其实是在执行 database/migrations中的所有文件的 up 函数,于是尝试修改所有的 up 函数,增加如下的判断:

1
2
3
if(!Schema::hasTable('table')) {
// 创建表
}

参考文章:
Laravel 5.4 migrate时报错: Specified key was too long error
Laravel 5.4: 特殊字段太长报错

What is yield

yield 关键字返回生成器( generator )的数据。

The heart of a generator function is the yield keyword. In its simplest form, a yield statement looks much like a return statement, except that instead of stopping execution of the function and returning, yield instead provides a value to the code looping over the generator and pauses execution of the generator function.

What is a generator function

生成器函数是一种更为紧凑和有效编写迭代器的方法。它允许你定义一个函数,在循环时计算和返回结果。

1
2
3
foreach (xrange(1, 10) as $key => $value) {
echo "$key => $value", PHP_EOL;
}

将会有如下的输出:

1
2
3
4
0 => 1
1 => 2

9 => 10

你同样可以控制在 foreach 中的 $key,通过如下的方式:

1
yield $someKey => $someValue;

在生成器函数中,someKey 是你想要出现的 key,someVaue 则是 val 中的值。在例子中,它则是 i

What’s the difference to normal functions?

你可能会觉得我们用 PHP 原生的 range 函数同样也可以实现一样的输出,这点没错,但是最大的区别是这两种方式的输出是如何得到的。

当我们使用 range 的时候,它在内存中将整个数组创建出来,并将整个数组交给 foreach 去遍历。换句话说,foreach 将自己来操作整个数组。range 函数和 foreach 函数只交互了一次。想想我们接收邮件包裹,派送人员把包裹给你了,然后就离开了,剩下的就需要你自己去打开包裹并发现包裹里装的是什么了。

当我们使用生成函数时,PHP将进入函数并执行它,直到它满足结束或遇到 yield 关键字。 当它遇到 yield 时,它将返回当时值的任何值到外部循环。 然后它回到生成器,并继续从它产生的地方开始。 由于你的 xrange 拥有一个 for 循环,它将执行并产生,直到达到$max。想象一下 foreach 和生成器打乒乓球的场景。

Why do I need that?

很显然,生成器可以被应用于内存有限的场景中。取决于你的环境而言,如果做一个 range(0,100000)循环可能导致你的脚本严重错误的话,使用生成器仍旧可以很好的运行。或者就如维基百科所言:

Because generators compute their yielded values only on demand, they are useful for representing sequences that would be expensive or impossible to compute at once. These include e.g. infinite sequences and live data streams.

生成器应该很快。 但请记住,当我们谈论很快时,我们通常是在非常少的场景中讨论。 所以在你现在跑掉并更改所有代码以使用生成器之前,请做一个基准测试,看看它在哪里有意义。

生成器的另一个用例是异步协同程序。 yield关键字不仅返回值,而且还接受它们

补充

PHP 的生成器不能满足所有迭代操作的需求,因为如果不查询,生成器永远不知道下一个要迭代的值是什么,在生成器中无法后退或者快进。生成器还是一次性的,无法多次迭代同一个生成器。不过,如果需要的,可以重建或者克隆生成器。

待测试

未完待续,通过生成器是否能够解决大文件加载问题,已经是否能够提高读取速度问题。

整理翻译自:
What does yield mean in PHP?

接着上一篇的demo,经过几天的折腾,基本上完成了类似链家网的效果.上一篇文章的地址Openlayers3仿链家网效果

实现思路

不同 Zoom 加载不同的图层

通过 view 的 chaneg:resolution 事件,在不同 zoom 下加载不同的图层

1
2
3
4
5
6
7
8
9
10
11
var changeViewToZoom = function () {
var view = map.getView();
view.on('change:resolution', function (event) {
var zoom = view.getZoom();
if (zoom >= xxxZoom) {
// 加载图层
} else {
// 加载其他图层
}
});
};

圆圈的绘制

根据圆圈的中心点坐标和半径绘制圆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 添加圆圈图层
*/
function genarateCircleLayer(CircleObjArrs, title) {
if (typeof arguments[2] != 'undefined') {
return createCircleLayer(createCircleSource(CircleObjArrs, arguments[2]), title);
}
return createCircleLayer(createCircleSource(CircleObjArrs), title)
}


function createCircleLayer(source, title) {
var circleLayer = new ol.layer.Vector({
title: title,
source: source,
style: circleStyleFunction
});
return circleLayer;
}


function createCircleSource(CircleObjArrs) {
var view = map.getView();
var zoom = view.getZoom();
var CircleSource = new ol.source.Vector();
var radius = 1 / Math.pow(2, zoom - 6);
// 判断是否定义 radius,否则才用默认值
if (typeof (arguments[1]) != 'undefined') {
radius = arguments[1];
}
for (var i = 0; i < CircleObjArrs.length; i++) {
var feature = new ol.Feature({
geometry: new ol.geom.Circle(CircleObjArrs[i].center, radius),
text: CircleObjArrs[i].text
});
CircleSource.addFeature(feature);
}

return CircleSource;
}

展示效果

展示效果包括:圆圈样式的切换,和圆圈下的图层显示.圆圈下的图层是在绘制圆圈的时候就一同加载了,只是样式设置透明,让它隐藏起来了,当鼠标移动到圆圈上,改变圆圈的样式的同时也改变了图层的样式,从而使得图层展示出来.主要调用forEachFeatureAtPixel方法,遍历当前坐标系下的所有feature,然后根据type切换不同样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
var displayFeature = function (pixel) {
var features = [];
map.forEachFeatureAtPixel(pixel, function (feature, layer) {
if (layer.getVisible()) { // 当图层被隐藏之后,光标下该图层忽略
features.push(feature);
}
});

var len = features.length;
if (len > 1) {
for (var i = 0; i < len; i++) {
var text = features[i].getProperties().text;
var type = features[i].getGeometry().getType();
features[i].setStyle(getSelectedPropertyStyle(type, text));
}
} else {
var featureArr = map.getLayers().getArray();
var featureArr_len = featureArr.length;
for (var j = 0; j < featureArr_len; j++) {
if (typeof featureArr[j].setStyle !== 'undefined') {
var source = featureArr[j].getSource();
features_by_source = source.getFeatures();

var feature_len = features_by_source.length;

for (var m = 0; m < feature_len; m++) {
if (typeof features_by_source[m].getProperties().text === 'undefined') {
text = 'none';
} else {
var text = features_by_source[m].getProperties().text
}
var type = features_by_source[m].getGeometry().getType();
features_by_source[m].setStyle(getUnselectedPropertyStyle(type, text));
}
}
}
}
};

var getUnselectedPropertyStyle = function (type, text) {
var style = null;
if (type == 'Circle') {
style = new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(57,172,106,0.9)',
opacity: '0.05'
}),
text: new ol.style.Text({
font: '14px sans-serif',
text: text,
textAlign: 'center',
rotateWithView: true,
fill: new ol.style.Fill({
color: '#FFFFFF'
}),
stroke: new ol.style.Stroke({
color: '#FFFFFF',
width: 1
})
})
// radius:100
})
}
if (type == 'MultiPolygon' || type == 'Polygon') {
style = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'rgba(255, 255, 0, 0)',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 0, 0)'
})
})
}
return style;
};

var getSelectedPropertyStyle = function (type, text) {
var style = null;
if (type == 'Circle') {
style = new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(228,57,60,0.9)',
opacity: '0.05'
}),
text: new ol.style.Text({
font: '14px sans-serif',
text: text,
textAlign: 'center',
// rotateWithView:true,
fill: new ol.style.Fill({
color: '#FFFFFF'
}),
stroke: new ol.style.Stroke({
color: '#FFFFFF',
width: 1
})
})
})
}
if (type == 'MultiPolygon' || type == 'Polygon') {
style = new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(57,172,106,0.1)'
})
})
}
return style;
};

由于使用鼠标滚轮控制地图缩放不可控,所以决定禁用鼠标滚轮事件.代码如下:

1
2
3
4
5
map.getInteractions().forEach(function (interaction) {
if (interaction instanceof ol.interaction.MouseWheelZoom) {
map.removeInteraction(interaction);
}
});