0%

我喜欢钱, 很喜欢, 喜欢的不得了
![](/images/001.png) 鲍勃有两把钥匙,一把是公钥,另一把是私钥。 ![](/images/002.png) 鲍勃把公钥送给他的朋友们----帕蒂、道格、苏珊----每人一把。 ![](/images/003.png) 苏珊要给鲍勃写一封保密的信。她写完后用鲍勃的公钥加密,就可以达到保密的效果。 ![](/images/004.png) 鲍勃收信后,用私钥解密,就看到了信件内容。这里要强调的是,只要鲍勃的私钥不泄露,这封信就是安全的,即使落在别人手里,也无法解密。 ![](/images/005.png) 鲍勃给苏珊回信,决定采用"数字签名"。他写完后先用Hash函数,生成信件的摘要(digest)。 ![](/images/006.png) 然后,鲍勃使用私钥,对这个摘要加密,生成"数字签名"(signature)。 ![](/images/007.png) 鲍勃将这个签名,附在信件下面,一起发给苏珊。 ![](/images/008.png) 苏珊收信后,取下数字签名,用鲍勃的公钥解密,得到信件的摘要。由此证明,这封信确实是鲍勃发出的。 ![](/images/009.png) 苏珊再对信件本身使用Hash函数,将得到的结果,与上一步得到的摘要进行对比。如果两者一致,就证明这封信未被修改过。 ![](/images/010.png) 复杂的情况出现了。道格想欺骗苏珊,他偷偷使用了苏珊的电脑,用自己的公钥换走了鲍勃的公钥。此时,苏珊实际拥有的是道格的公钥,但是还以为这是鲍勃的公钥。因此,道格就可以冒充鲍勃,用自己的私钥做成"数字签名",写信给苏珊,让苏珊用假的鲍勃公钥进行解密。 ![](/images/011.png) 后来,苏珊感觉不对劲,发现自己无法确定公钥是否真的属于鲍勃。她想到了一个办法,要求鲍勃去找"证书中心"(certificate authority,简称CA),为公钥做认证。证书中心用自己的私钥,对鲍勃的公钥和一些相关信息一起加密,生成"数字证书"(Digital Certificate)。 ![](/images/012.png) 鲍勃拿到数字证书以后,就可以放心了。以后再给苏珊写信,只要在签名的同时,再附上数字证书就行了。 ![](/images/013.png) 苏珊收信后,用CA的公钥解开数字证书,就可以拿到鲍勃真实的公钥了,然后就能证明"数字签名"是否真的是鲍勃签的。

http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html

身不饥寒,天未曾负我;学无长进,我何以对天

获得当前日期+时间

1
2
mysql> select now();
mysql> select sysdate();

不同之处在于:now() 在执行开始时值就得到了, sysdate() 在函数执行时动态得到值。

获得当前日期

1
mysql> select curdate();

获得当前时间

1
mysql> select curtime();

获得当前 UTC 日期时间函数

1
select utc_timestamp(), utc_date(), utc_time(), now()

MySQL 为日期增加一个时间间隔

1
2
3
4
5
6
7
8
9
10
11
set @dt = now();
select date_add(@dt, interval 1 day); -- add 1 day
select date_add(@dt, interval 1 hour); -- add 1 hour
select date_add(@dt, interval 1 minute); -- ...
select date_add(@dt, interval 1 second);
select date_add(@dt, interval 1 microsecond);
select date_add(@dt, interval 1 week);
select date_add(@dt, interval 1 month);
select date_add(@dt, interval 1 quarter);
select date_add(@dt, interval 1 year);
select date_add(@dt, interval -1 day); -- sub 1 day

MySQL 为日期减去一个时间间隔

1
select date_sub('1998-01-01 00:00:00', interval '1 1:1:1' day_second);

DATE() 函数返回日期或日期/时间表达式的日期部分

1
DATE(date)

前一天

1
date_sub(curdate(),interval 1 day);

统计日期

1
SELECT  DATE(updated_at) AS dd, COUNT(*) FROM m_shells GROUP BY dd

我们这么努力 也不过是为了成为一个普通人

索引

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构
我们知道,数据库查询是数据库的最主要功能之一。我们都希望查询数据的速度能尽可能的快,因此数据库系统的设计者会从查询算法的角度进行优化。最基本的查询算法当然是顺序查找(linear search),这种复杂度为O(n)的算法在数据量很大时显然是糟糕的,好在计算机科学的发展提供了很多更优秀的查找算法,例如二分查找(binary search)、二叉树查找(binary tree search)等。如果稍微分析一下会发现,每种查找算法都只能应用于特定的数据结构之上,例如二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,但是数据本身的组织结构不可能完全满足各种数据结构(例如,理论上不可能同时将两列都按顺序进行组织),所以,在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

你这辈子只能靠自己。

Like的参数以通配符开头时

尽量避免Like的参数以通配符开头,否则数据库引擎会放弃使用索引而进行全表扫描。
以通配符开头的sql语句,例如:

1
select * from t_credit_detail where Flistid like '%0'\G;

使用!= 或 <> 操作符时

尽量避免使用!= 或 <>操作符,否则数据库引擎会放弃使用索引而进行全表扫描。使用>或<会比较高效。

1
select * from t_credit_detail where Flistid != '2000000608201108010831508721'\G;

索引列参与计算

应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。

1
select * from t_credit_detail where Flistid +1 > '2000000608201108010831508722'\G;

对字段进行null值判断

应尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
低效:

1
select * from t_credit_detail where Flistid is null;

可以在Flistid上设置默认值0,确保表中Flistid列没有null值,然后这样查询:
高效:

1
select * from t_credit_detail where Flistid =0;

使用or来连接条件

应尽量避免在where子句中使用or来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
低效:

1
select * from t_credit_detail where Flistid = '2000000608201108010831508721' or Flistid = '10000200001';

避免select *

在解析的过程中,会将'*' 依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
所以,应该养成一个需要什么就取什么的好习惯。

order by 语句优化

任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。

  • 1.重写order by语句以使用索引;
  • 2.为所使用的列建立另外一个索引
  • 3.绝对避免在order by子句中使用表达式。

GROUP BY语句优化

提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉

低效:

1
2
3
4
5
SELECT JOB , AVG(SAL)
FROM EMP
GROUP by JOB
HAVING JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER';

高效:

1
2
3
4
5
SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT'
OR JOB = ‘MANAGER'
GROUP by JOB;

用 exists 代替 in

很多时候用 exists 代替 in 是一个好的选择:

1
select num from a where num in(select num from b);

用下面的语句替换:

1
select num from a where exists(select 1 from b where num=a.num);

使用 varchar/nvarchar 代替 char/nchar

尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

能用DISTINCT的就不用GROUP BY

1
SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID;

可改为:

1
SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10;

最左前缀匹配原则

非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

1
2
3
4
5
6
7
有一个复合索引:INDEX(`a`, `b`, `c`);

使用方式 能否用上索引
select * from users where a = 1 and b = 2 能用上a、b
select * from users where b = 2 and a = 1 能用上a、b(有MySQL查询优化器)
select * from users where a = 2 and c = 1 能用上a
select * from users where b = 2 and c = 1 不能

https://cloud.tencent.com/community/article/382852

多少次我们无醉不欢 咒骂人生太短 唏嘘相见恨晚
### 问题解决 在PHP开发过程中,使用`trim`想去掉空格,不成功; 打印出来字符串,后面确实带有空格,在控制台又显示有` `(此处所指的nbsp是实体,而不是nbsp四个字符) 后来改用`str_replace`来去掉空格,依然失败; 到底是什么原因,于是刚开始想到了是不是全角和半角的空格问题;试着换成全角的空格,依然失败; 后来又使用escape尝试输出,发现是xa0,于是就想`trim($converted, “xa0”)`,无果 在php手册的trim函数下评论中发现:
1
trim($otmp, chr(0xc2) . chr(0xa0))
使用后,成功去掉空格;另外,str_replace也可使用:
1
$otmp = str_replace(chr(0xc2) . chr(0xa0), '', $otmp);
效果一样,成功解决~~~ ps:此空格产生的原因是,我在获取到html代码之后strip_tags去掉所有标签,而产生了一些实体空格

html实体

在 HTML 中,某些字符是预留的。
在 HTML 中不能使用小于号(<)和大于号(>),这是因为浏览器会误认为它们是标签。
如果希望正确地显示预留字符,我们必须在 HTML 源代码中使用字符实体(character entities)。
字符实体类似这样:&entity_name;或者&#entity_number;
如需显示小于号,我们必须这样写: &lt;&#60;
提示:使用实体名而不是数字的好处是,名称易于记忆。不过坏处是,浏览器也许并不支持所有实体名称(对实体数字的支持却很好)。
不间断空格(non-breaking space)
HTML 中的常用字符实体是不间断空格( &nbsp; )。
浏览器总是会截短 HTML 页面中的空格。如果您在文本中写 10 个空格,在显示该页面之前,浏览器会删除它们中的 9 个。如需在页面中增加空格的数量,您需要使用 &nbsp; 字符实体。

父母在时,人生尚有来处。 父母去时,人生只剩归途。
### 安装swoole pecl 安装: `pecl install swoole`

看命令行提示,如果它提示说没有写php.ini,则自己手动在PHP.ini后面加上:
extension = "swoole.so"

服务端

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
<?php
class Server
{
private $serv;
public function __construct() {
$this->serv = new swoole_server("0.0.0.0", 9501);
$this->serv->set(array(
'worker_num' => 1, //一般设置为服务器CPU数的1-4倍
'daemonize' => 1, //以守护进程执行
'max_request' => 10000,
'dispatch_mode' => 2,
'task_worker_num' => 8, //task进程的数量
"task_ipc_mode " => 3 , //使用消息队列通信,并设置为争抢模式
//"log_file" => "log/taskqueueu.log" ,//日志
));
$this->serv->on('Receive', array($this, 'onReceive'));
// bind callback
$this->serv->on('Task', array($this, 'onTask'));
$this->serv->on('Finish', array($this, 'onFinish'));
$this->serv->start();
}

public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
//echo "Get Message From Client {$fd}:{$data}n";
// send a task to task worker.
$serv->task( $data );
}

public function onTask($serv,$task_id,$from_id, $data) {
$array = json_decode( $data , true );
if ($array['url']) {
return $this->httpGet( $array['url'] , $array['param'] );
}

}

public function onFinish($serv,$task_id, $data) {
//echo "Task {$task_id} finishn";
//echo "Result: {$data}n";
}


protected function httpGet($url,$data){
if ($data) {
$url .='?'.http_build_query($data) ;
}
$curlObj = curl_init(); //初始化curl,
curl_setopt($curlObj, CURLOPT_URL, $url); //设置网址
curl_setopt($curlObj, CURLOPT_RETURNTRANSFER, 1); //将curl_exec的结果返回
curl_setopt($curlObj, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curlObj, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($curlObj, CURLOPT_HEADER, 0); //是否输出返回头信息
$response = curl_exec($curlObj); //执行
curl_close($curlObj); //关闭会话
return $response;
}

}
$server = new Server();

客户端

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
<?php
class Client
{
private $client;

public function __construct() {
$this->client = new swoole_client(SWOOLE_SOCK_TCP);
}

public function connect() {
if( !$this->client->connect("127.0.0.1", 9501 , 1) ) {
echo "Connect Error";
}
$data = array(
"url" => "http://192.168.10.19/send_mail" ,
"param" => array(
"username"=>'test',
"password" => 'test'
)
);
$json_data = json_encode($data);
$this->client->send( $json_data );
}
}

$client = new Client();
$client->connect();

查看与关闭

swoole好像没有很便捷的关闭方式。所以只能直接通过关闭进程来关闭。
查看命令:
ps -ef | grep php

结束单个进程:
kill -9 {进程号}

结束所有进程的命令:
killall -9 php

钱是买不到幸福的, 因为他本身就是幸福

PHP cURL所有函数列表:

http://php.net/manual/zh/ref.curl.php]http://php.net/manual/zh/ref.curl.php

以下是PHP中cURL多线程相关函数:

1
2
3
4
5
6
7
8
9
10
curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄
curl_multi_close — 关闭一组cURL句柄
curl_multi_exec — 运行当前 cURL 句柄的子连接
curl_multi_getcontent — 如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流
curl_multi_info_read — 获取当前解析的cURL的相关传输信息
curl_multi_init — 返回一个新cURL批处理句柄
curl_multi_remove_handle — 移除curl批处理句柄资源中的某个句柄资源
curl_multi_select — 等待所有cURL批处理中的活动连接
curl_multi_setopt — 为 cURL 并行处理设置一个选项
curl_multi_strerror — Return string describing error code

一般来说,想到要用这些函数时,目的显然应该是要同时请求多个URL,而不是一个一个依次请求,否则不如自己循环去调curl_exec好了。

步骤总结如下:

1、调用curl_multi_init,初始化一个批处理handle
2、循环调用 curl_multi_add_handle,往1中的批处理handle 添加curl_init来的子handle
3、持续调用 curl_multi_exec,直到所有子handle执行完毕。
4、根据需要循环调用 curl_multi_getcontent 获取结果
5、调用 curl_multi_remove_handle,并为每个字handle调用curl_close
6、调用 curl_multi_close

以下是一个通过并发请求抓取网页的demo:

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
<?php
$urls = array(
'https://979137.com/',
'http://www.sina.com.cn/',
'http://www.sohu.com/',
'http://www.163.com/'
);
//1、初始化一个批处理handle
$mh = curl_multi_init();

//2、往批处理handle 添加curl_init来的子handle
foreach ($urls as $i => $url) {
$conn[$i] = curl_init($url);
curl_setopt($conn[$i], CURLOPT_HEADER, 0);
curl_setopt($conn[$i], CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($mh, $conn[$i]);
}

//3、并发执行,直到全部结束。
do {
curl_multi_exec($mh, $active);
} while ($active);

//4、获取结果
foreach ($urls as $i => $url) {
$data = curl_multi_getcontent($conn[$i]);
echo ($data);
echo '<hr />';
}

//5、移除子handle,并close子handle
foreach ($urls as $i => $url) {
curl_multi_remove_handle($mh,$conn[$i]);
curl_close($conn[$i]);
}

//6、关闭批处理handle
curl_multi_close($mh);

https://979137.com/archives/133.html

开始总是分分钟都妙不可言 谁都以为热情它永不会减。

协程,又称微线程,纤程。英文名Coroutine。
子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。

所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。

子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。

协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

简单说明

注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断。比如子程序A、B:

1
2
3
4
5
6
7
8
9
def A():
print('1')
print('2')
print('3')

def B():
print('x')
print('y')
print('z')

假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:

1
2
3
4
5
6
1
2
x
y
3
z

但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。

优势

最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

例子

Python对协程的支持是通过generator实现的。

在generator中,我们不但可以通过for循环来迭代,还可以不断调用next()函数获取由yield语句返回的下一个值。

但是Python的yield不但可以返回一个值,它还可以接收调用者发出的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[消费者] Consuming %s...' % n)
r = '200 OK'

def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[生产者] Producing %s...' % n)
r = c.send(n)
print('[生产者] Consumer return: %s' % r)
c.close()

c = consumer()
produce(c)

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432090171191d05dae6e129940518d1d6cf6eeaaa969000

岁月你别催,该来的我不推。 岁月你别催,走远的我不追。

问题产品原因: windows 和 linux 换行符不一致; 所以必须存入数据库之前对一些不可见的符号过滤;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$file = $request->file('femails');
$emails = file_get_contents($file->getRealPath());
$emails = explode(PHP_EOL, $emails);
$ids = [];
foreach ($emails as $email) {
$result = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $email);
$recipient = Recipient::firstOrCreate(
[
'email' => trim($result)
],
[
'is_use' => 1
]
);
array_push($ids, $recipient->id);
}

有一天你会知道,人生没有我并不会不同
存储在IndexedDB里的数据是永久保存,不像cookies那样只是临时的。IndexedDB里提供了查询数据的功能,在online和offline模式下都能使用。你可以用IndexedDB存储大型数据。 ### IndexedDB 具有以下特点。 (1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。在对象仓库中,数据以“键值对”的形式保存,每一个数据都有对应的键名,键名是独一无二的,不能有重复,否则会抛出一个错误。 (2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。 (3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回到事务发生之前的状态,不存在只改写一部分数据的情况。 (4)同域限制 IndexedDB 也受到同域限制,每一个数据库对应创建该数据库的域名。来自不同域名的网页,只能访问自身域名下的数据库,而不能访问其他域名下的数据库。 (5)储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于250MB。IE的储存上限是250MB,Chrome和Opera是剩余空间的某个百分比,Firefox 则没有上限。 (6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据。

API实例

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
<script>
if('indexedDB' in window) {

let openRequest = indexedDB.open("yangzie",5);
let db;
openRequest.onupgradeneeded = function(e) {
console.log("数据库更新...");
//
db = e.target.result;

if (!db.objectStoreNames.contains('firstOS')){
db.createObjectStore("firstOS");
} else {
console.log('表已经存在');
}
};

openRequest.onsuccess = function(e) {
console.log("数据路打开成功!");

db = e.target.result;

// 事务
let transaction = db.transaction(["firstOS"], "readwrite");

let store = transaction.objectStore("firstOS");

let o = {p: "she is a girl"};

// 添加数据
let request = store.add(o,2);

request.onerror = function(e) {
console.log("数据添加失败!");
};

request.onsuccess = function(e) {
console.log("数据添加成功!");
};

// 修改数据
let tr = db.transaction(["firstOS"], "readwrite");

let storess = tr.objectStore("firstOS");
let p = { p:456 };
let urequest = storess.put(p, 1);

urequest.onerror = function () {
console.log("修改成功");
};

urequest.onsucces = function () {
console.log("修改失败");
};

// 获取数据
let t = db.transaction(["firstOS"], "readonly");
let stores = t.objectStore("firstOS");

let ob = stores.get(1);

ob.onsuccess = function(e) {
console.log("获取数据成功");
console.log(ob);
};

transaction.oncomplete = function(event) {
console.log('事务结束');
};

// 删除记录
let tt = db.transaction(["firstOS"], "readwrite");
let mrequest = tt.objectStore("firstOS").delete(1);

mrequest.onsuccess = function () {
console.log('删除成功');
};

mrequest.onerror = function () {
console.log('删除失败');
};
};

openRequest.onerror = function(e) {
console.log("数据库打开错误");
};

} else {
console.log('您的浏览器不支持')
}

</script>

http://javascript.ruanyifeng.com/bom/indexeddb.html