super in Python

requests的源码中,有这么一段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class HTTPAdapter(BaseAdapter):
__attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize',
'_pool_block']

def __init__(self, pool_connections=DEFAULT_POOLSIZE,
pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES,
pool_block=DEFAULT_POOLBLOCK):
if max_retries == DEFAULT_RETRIES:
self.max_retries = Retry(0, read=False)
else:
self.max_retries = Retry.from_int(max_retries)
self.config = {}
self.proxy_manager = {}

super(HTTPAdapter, self).__init__()

self._pool_connections = pool_connections
self._pool_maxsize = pool_maxsize
self._pool_block = pool_block

self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)

看到这,有个困惑,里面的super是什么个意思?明明直接调用父类的不就行了吗?

1
2
3
4
5

class LoggingDict(dict):
def __setitem__(self, key, value):
logging.info('Setting %r to %r' % (key, value))
super().__setitem__(key, value)

就是在原来setitem的基础上,加上了一个输出log,至于设置dict,还是让父类来完成。super要比父类直接调用更优,是因为它是’a computed indirect reference’。这样的好处是,我们不需要去显式地去写出父类的名字,万一以后要改基类,也很方便。与此同时,由于是间接引用,我们来看个范例。

1
2
class LoggingOD(LoggingDict, collections.OrderedDict):
pass

这样的话,我们调用顺序就是LoggingDictOrderedDictDict,这样我们就是直接把key,value直接存进了OrderedDict

这时,如果我们来看一下MRO(类继承顺序)。

1
2
3
4
5
6
>>> pprint(LoggingOD.__mro__)
(<class '__main__.LoggingOD'>,
<class '__main__.LoggingDict'>,
<class 'collections.OrderedDict'>,
<class 'dict'>,
<class 'object'>)

其实,super就干了件这个。

1
2
3
def super():
mro = inst.__class__.mro()
return mro[mro.index(cls)+1]

在 MRO 中,基类永远出现在派生类后面,如果有多个基类,基类的相对顺序保持不变。

Ref:
1.Python’s super() considered super!

单机器性能监控

因为工作需要,整理了一些命令来查看机器的运行情况。

  • uptime

先uptime来看看平均负载,先对实例的负载有一个宏观的认识。

1
2
uptime
23:40 up 3 days, 15:35, 1 user, load averages: 2.12 2.37 2.32
  • dmesg

查看最近10条系统消息。

1
2
3
4
5
6
7
8
9
10
11
➜  ~ dmesg -T| tail -10
[Tue May 31 10:58:31 2016] docker0: port 1(vethcf80d8f) entered disabled state
[Tue May 31 10:58:31 2016] docker0: port 1(vethcf80d8f) entered disabled state
[Tue May 31 10:58:31 2016] device vethcf80d8f left promiscuous mode
[Tue May 31 10:58:31 2016] docker0: port 1(vethcf80d8f) entered disabled state
[Tue May 31 18:22:56 2016] device veth0daef81 entered promiscuous mode
[Tue May 31 18:22:56 2016] IPv6: ADDRCONF(NETDEV_UP): veth0daef81: link is not ready
[Tue May 31 18:22:56 2016] IPv6: ADDRCONF(NETDEV_CHANGE): veth0daef81: link becomes ready
[Tue May 31 18:22:56 2016] docker0: port 1(veth0daef81) entered forwarding state
[Tue May 31 18:22:56 2016] docker0: port 1(veth0daef81) entered forwarding state
[Tue May 31 18:23:11 2016] docker0: port 1(veth0daef81) entered forwarding state
  • vmstat
1
2
3
4
5
6
7
8
9
10
11
➜  ~ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 124392 147184 312408 0 0 0 4 4 8 1 1 99 0 0
0 0 0 124384 147184 312408 0 0 0 0 221 519 0 0 100 0 0
0 0 0 124384 147184 312408 0 0 0 12 206 494 0 1 99 0 0

# r 表示队列长度
# free 表示空闲内存
# si, so 表示换入的内存和换出的内存
# us, sy, id, wa, st:CPU时间的不同组成部分,是所有CPU的平均数

统计虚拟内存信息。参数1表示,统计粒度为1秒。

  • mpstat
1
2
3
4
5
6
7
8
9
10
11
12
[root@chuanwu:~] # mpstat -P ALL 1
Linux 3.13.0-32-generic (dtstack-dev1) 07/22/2016 _x86_64_ (2 CPU)

11:09:04 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
11:09:05 PM all 24.75 1.49 5.94 14.85 0.99 0.00 0.00 0.00 0.00 51.98
11:09:05 PM 0 26.53 1.02 7.14 8.16 0.00 0.00 0.00 0.00 0.00 57.14
11:09:05 PM 1 23.23 2.02 4.04 21.21 1.01 0.00 0.00 0.00 0.00 48.48

11:09:05 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
11:09:06 PM all 33.00 3.50 4.50 23.00 1.00 0.00 1.00 0.00 0.00 34.00
11:09:06 PM 0 31.68 4.95 3.96 22.77 1.98 0.00 0.99 0.00 0.00 33.66
11:09:06 PM 1 34.65 2.97 4.95 22.77 0.99 0.00 0.00 0.00 0.00 33.66

打印每个CPU的状况。

  • pidstat
1
2
3
4
5
[root@chuanwu:~] # pidstat -p 12339
Linux 3.13.0-32-generic (dtstack-dev1) 07/22/2016 _x86_64_ (2 CPU)

11:10:58 PM UID PID %usr %system %guest %CPU CPU Command
11:10:58 PM 0 12339 0.19 0.13 0.00 0.31 0 python

根据pid来查询性能。

  • iostat
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
[root@chuanwu:~] # iostat -xz 1
Linux 3.13.0-32-generic (dtstack-dev1) 07/22/2016 _x86_64_ (2 CPU)

avg-cpu: %user %nice %system %iowait %steal %idle
15.44 0.29 4.05 6.55 0.11 73.56

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvda 0.00 3.87 0.62 3.93 7.57 75.80 36.60 0.13 27.45 6.79 30.74 1.31 0.60
xvdb 0.12 25.28 1.86 93.07 37.17 1034.45 22.58 0.58 6.09 5.71 6.10 1.76 16.70

avg-cpu: %user %nice %system %iowait %steal %idle
32.83 3.03 6.06 20.20 0.00 37.88

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvda 0.00 0.00 1.00 0.00 8.00 0.00 16.00 0.00 4.00 4.00 0.00 4.00 0.40
xvdb 0.00 72.00 0.00 215.00 0.00 1164.00 10.83 0.50 2.33 0.00 2.33 2.33 50.00

avg-cpu: %user %nice %system %iowait %steal %idle
22.61 4.02 3.02 24.12 0.00 46.23

Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
xvda 0.00 20.00 0.00 4.00 0.00 100.00 50.00 0.02 6.00 0.00 6.00 4.00 1.60
xvdb 0.00 76.00 0.00 228.00 0.00 1232.00 10.81 0.53 2.32 0.00 2.32 2.30 52.40

# util是个重要参数,如果超过60%需要特殊关注。
  • free
1
2
3
4
5
6
[root@chuanwu:~] # free -m
total used free shared buffers cached
Mem: 7983 7501 482 2 179 3635
-/+ buffers/cache: 3687 4296
Swap: 0 0 0
# 如果buffers,cache接近0,需要关注下iostat,磁盘I/O可能过高。
  • sar -n DEV 1
1
2
3
4
5
6
7
8
9
[root@dtstack-dev1:~] # sar -n DEV 1
Linux 3.13.0-32-generic (dtstack-dev1) 07/23/2016 _x86_64_ (2 CPU)

03:36:20 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
03:36:21 PM eth0 28.00 24.00 4.37 1.68 0.00 0.00 0.00 0.00
03:36:21 PM eth1 66.00 75.00 11.97 9.44 0.00 0.00 0.00 0.00
03:36:21 PM lo 219.00 219.00 81.38 81.38 0.00 0.00 0.00 0.00
03:36:21 PM veth7ce7c34 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
03:36:21 PM docker0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

查看网络接口的吞吐量。

  • sar -n TCP, ETCP 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@chuanwu:~] # sar -n TCP,ETCP 1
Linux 3.13.0-32-generic (dtstack-dev1) 07/23/2016 _x86_64_ (2 CPU)

03:43:02 PM active/s passive/s iseg/s oseg/s
03:43:03 PM 3.00 2.00 265.00 272.00

03:43:02 PM atmptf/s estres/s retrans/s isegerr/s orsts/s
03:43:03 PM 0.00 0.00 1.00 0.00 0.00

03:43:03 PM active/s passive/s iseg/s oseg/s
03:43:04 PM 19.00 30.00 1104.00 1097.00

03:43:03 PM atmptf/s estres/s retrans/s isegerr/s orsts/s
03:43:04 PM 0.00 6.00 0.00 0.00 0.00

03:43:04 PM active/s passive/s iseg/s oseg/s
03:43:05 PM 74.00 48.00 2349.00 2360.00

查看tcp相关指标。

  • top

Ref:

  1. 性能之巅

  2. 10条命令,一分钟分析Linux性能问题

刷leetcode

编程,我觉得要经历三个阶段。

  • make it run
  • make it work
  • make it faster

目前,我只做到了第二步。
等刷完,再去discuss板块里看看大神们是怎么优化的。

代码在这Leetcode

刷了有十几题,最大的感受就是无论什么样的题,总有大神,能用最短最精简的代码来搞定,而且运行时间要比你的短…

我觉得今天太忙真是一个说服自己的好托辞。

最近太忙, 没空看书。
最近项目需求多,没时间去刷题。
改个bug,累死了,今天先歇会。

诸如此类。

给自己定目标定一年了,说要刷leetcode,然后呢,一年之后,还是一题没刷。好羞耻…

一共也就358道题。

想起胡适先生的日记。

7月4日
新开这本日记,也为了督促自己下个学期多下些苦功。先要读完手边的莎士比亚的《亨利八世》……

7月13日
打牌。

7月14日
打牌。

7月15日
打牌。

7月16日
胡适之啊胡适之!你怎么能如此堕落!先前订下的学习计划你都忘了吗?
子曰:“吾日三省吾身。”…不能再这样下去了!

7月17日
打牌。

7月18日
打牌。

不想扯了。直接开刷吧。

leetcode

关于Python的property,yield,slot

yield

要理解yield,先看两段代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def fib(n):
a, b = 0, 1
for _ in xrange(n):
yield a
a, b = b, a + b

def chunks(l, n):
for i in range(0, len(l), n):
yield l[i:i+n]

def split_by_age(children, n):
ages = set(i.age for i in children)
for age in ages:
yield age, chunks(list(set(i for i in children if i.age == age)), n)

yield和return很像。不过yield返回的是一个生成器。生成器和迭代器很像,不过生成器只能遍历一次,因为每次next()被调用时,生成器会返回它脱离的位置,就是说它只能记住语句最后一次执行的位置和所有的数据值)。

property

在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改:

1
2
s = Student()
s.score = 9999 or 'hello world'

这显然不合逻辑。为了限制score的范围,可以通过一个set_score()方法来设置成绩,再通过一个get_score()来获取成绩,通过这两个方法就能限制score了。

1
2
3
4
5
6
7
8
9
10
11
class Student(object):

def get_score(self):
return self._score

def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value

这样,我们就能对score进行参数检查了。

1
2
3
4
5
6
7
8
>>> s = Student()
>>> s.set_score(60) # ok!
>>> s.get_score()
60
>>> s.set_score(9999)
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!

但是看着代码,好烦,一点也不Pythonic。

Python里面有个built-in方法property可以胜任这个工作。Python内置的@property装饰器就是负责把一个方法变成属性调用的:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student(object):

@property
def score(self):
return self._score

@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value

如果不定义@score.setter,property默认只是给score增加了一个getter属性,它是作为一个只读属性存在。

requests里面有段源码,可以参考下。

1
2
3
4
5
6
7
8
9
10
11
@property
def unverifiable(self):
return self.is_unverifiable()

@property
def origin_req_host(self):
return self.get_origin_req_host()

@property
def host(self):
return self.get_host()

slots

看段代码。

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
class Class(object):
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier

m = Class(1,2)
m.age = 1

class Class(object):
__slots__ = ['name', 'identifier']
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier

m = Class(1,2)

m.age = 1
---------------------------------------------------------------------------
Traceback (most recent call last)
<ipython-input-12-b504c75d350a> in <module>()
----> 1 m.age = 1

AttributeError: 'MyClass' object has no attribute 'age'

class MyClass(Class):
pass

c = Class()
---------------------------------------------------------------------------
Traceback (most recent call last)
<ipython-input-14-2e11f7fbc2a5> in <module>()
----> 1 c = Class()

TypeError: __init__() takes exactly 3 arguments (1 given)

c = Class(1,2)

c.age =1

使用slots有以下好处。

  • slots会限定只能设定这两个属性
  • 继承的子类不受限制
  • 能省点内存。

with

with这个关键词,挺常用的。今天偶然翻到一篇PEP,才发现原来一个with干了这么多事。

大概是这么个逻辑,PEP-343里描述得更详细,推荐读一下。

1
2
3
4
5
6
7
8
9
10
@contextmanager
def opening(filename):
f = open(filename)
try:
yield f
finally:
f.close()

with f = opening(filename):
...read data from f...

super

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class A(object):
def foo(self):
print 'A'

class B(A):
def foo(self):
print 'B'
super(B, self).foo()

class C(A):
def foo(self):
print 'C'
super(C, self).foo()

class D(B,C):
def foo(self):
print 'D'
super(D, self).foo()

d = D()
d.foo()
# DBCA

requests源码阅读

对网络请求一直很感兴趣,加上requests的代码很Pythonic,所以我打算读一下源码。

我fork的requests版本为2.8.1

我们先从GET请求入手。先用curl看下返回的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
$ curl -X GET http://www.httpbin.org/get\?ptype\=test
{
"args": {
"ptype": "test"
},
"headers": {
"Accept": "*/*",
"Host": "www.httpbin.org",
"User-Agent": "curl/7.49.1"
},
"origin": "116.216.16.35",
"url": "http://www.httpbin.org/get?ptype=test"
}

然后我们来用requests看看,get请求中到底发生了什么。

先进入requests.api调用get方法,然后调用request方法。不过在这个过程中,get,options请求会把allow_redirects的默认值改为True,head改为False。
这个地方不太清楚,我没怎么搞懂。可能是因为这个问题。requests上有几个issue都是关于跳转的问题。代码里作者在prepared request里面特意处理了。

Preflighted requests

Unlike simple requests (discussed above), “preflighted” requests first send an HTTP OPTIONS request header to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if:

It uses methods other than GET or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)

在调用request函数的get方法之后,

1
2
3

with sessions.Session() as session:
return session.request(method=method, url=url, **kwargs)

初始化一个Session实例,然后调用request。先看看初始化这个session之后做了些什么吧?

Session类继承自SessionRedirectMixin

我们之前提到,调用request时,会给allow_redirects附上不同的值。

requests源代码看起来其实蛮费力的,需要对HTTP有很深的认识才行。还有几篇RFC需要读。

MySQL索引(一)

为什么要用索引以及索引是怎么工作的

多数情况下,不使用索引,试图通过其他途径来提高性能,纯粹是浪费时间(出自《MySQL技术内幕》)。
那索引是怎么提高性能的呢?

  • 通过索引能获取数据的结束位置,从而跳过其他部分
  • 定位算法,可以快速定位第一个匹配值

InnoDB总是使用“B树”来创建索引,对于这种索引,在使用<, <=,=,=>,!=,BETWEEN操作符时有会很有效率。

Tip: BETWEEN在Django ORM对应range操作符。

1
2
3
4
5

import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))

相当于

1
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';

怎么建立索引

1. 总结业务场景,分析出最常用的会在where中出现的字段

比如以我们的项目而言,instance_nameuser_id, check_date出现的频率最高,所以这三个字段肯定需要建立索引。通过这样的索引,可以避免全表查找。

2. 数据维度势

维度就是说表中容纳的非重复值的个数。我们尽量应该选择一些区分度高的,区分度=count(distinct col)/count(*),按照美团的博客来讲,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录

3. 不要滥用索引

  • 由于在写入数据时,不仅要求写到数据行,还会影响所有的索引。所以索引建立越多,就会导致写入速度越慢。
  • 索引会占据磁盘空间。

4. 为字符串的前缀编索引

索引可以减少索引空间,从而加快速度。

5. 复合索引

比如地址,Province, City,通过这两个值得组合来建立索引。

6. 最左前缀匹配

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的顺序可以任意调整。

7. 不要把索引列加入计算

使用Expalin来优化SQL

我为了测试,在本机MySQL中,建了一张表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> create table if not exists front_end_team (id bigint auto_increment, gender tinyint not null default 0, name varchar(12) not null, age smallint not null, height decimal(6,2) not null, gmt_created datetime not null, gmt_modified datetime not null, primary key(id)) engine=innodb default charset utf8;
mysql> desc front_end_team;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(12) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
| height | decimal(6,2) | YES | | NULL | |
| gmt_created | datetime | NO | | NULL | |
| gmt_modified | datetime | NO | | NULL | |
| gender | tinyint(4) | YES | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

并往里面写了1w条数据。

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
mysql> select * from front_end_team limit 3;
+----+--------+--------+-----+--------+---------------------+---------------------+
| id | gender | name | age | height | gmt_created | gmt_modified |
+----+--------+--------+-----+--------+---------------------+---------------------+
| 1 | 0 | Tom | 32 | 154.32 | 2016-06-11 14:42:09 | 2016-06-11 14:42:09 |
| 2 | 0 | Sandro | 31 | 154.43 | 2016-06-11 14:42:28 | 2016-06-11 14:42:28 |
| 3 | 0 | Cgdali | 26 | 158.30 | 2016-06-11 14:42:49 | 2016-06-11 14:42:49 |
+----+--------+--------+-----+--------+---------------------+---------------------+
3 rows in set (0.00 sec)

mysql> show index from front_end_team\G
*************************** 1. row ***************************
Table: front_end_team
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: id
Collation: A
Cardinality: 10142
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
1 row in set (0.00 sec)

mysql>

这张表里面,目前只有主键建了索引。我安装的MySQL引擎是InnoDB,所以索引类型使用B树来实现。

好,目前为止,环境已经OK。来试试Expalin吧。我们的目标就是不断优化Explain返回的rows字段。

先大概解释下Expalin返回的字段名吧。

Column 意义
select_type select类型
table 展示行的table
type join类型
possible_keys 索引的可能取值
key 实际使用的索引
key_len 使用索引的长度
ref 跟索引
rows 关键指标
filtered
Extra

具体可参考MySQL Explain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 全表查询
mysql> explain select * from front_end_team\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: front_end_team
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 10142
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)

此时,key为NULL,没有使用任何索引。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

mysql> explain select * from front_end_team where id between 1 and 10004\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: front_end_team
partitions: NULL
type: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: NULL
rows: 5071
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)

虽然这两句话干的都是同一件事,但是使用between已经让MySQL使用索引来查询了。rows锐减一倍。

我们来验证一下刚刚的数据维度势的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

mysql> explain select * from front_end_team where gender = 0\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: front_end_team
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 10142
filtered: 10.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)

给gender加上索引,再试试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mysql> create index gender on front_end_team (gender smallint);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> explain select * from front_end_team where gender = 0\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: front_end_team
partitions: NULL
type: ref
possible_keys: gender
key: gender
key_len: 1
ref: const
rows: 4995
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)

这个地方挺出乎我意料的,我原以为这里MySQL会直接进行全表扫描。事实上,虽然gender的区分度很低,但是MySQL还是使用了这个索引。

截止目前,我们进行的都是单表查询,接下来看看多表的。

先来复习下left join,right join, inner join, outer join:

SQL Joins

Ref:

  1. 美团点评团队
  2. MySQL技术内幕

TCP vs UDP

在Quora上闲逛,看到TCP VS UDP

有两个回答很赞,凌晨睡不着,要么就翻译一下吧?

运输层是第四层的端对端协议。拿header里面的checksum举个例子,跟数据链路层和网络层的不同。如果你仔细观察的话,你会注意到这个checksum校验了两台主机之间的数据一致性。也就是说在路由器和交换机的传输过程中,并没有发生任何错误。这是UDP相较于TCP的唯一优点。

但是TCP提供的要多得多。其中一个就是保持有状态的连接。TCP的连接过程建立在SYN,SYN-ACK,ACK的请求上,这也保证了在发送机器发送数据时,接收机器正处于listen的状态。接收机器在接受到数据之后,还需要发送ack来告诉发送机器。连接会随着对话的结束而断开。

UDP则没有连接建立机制,也就意味着发送机器压根就不知道接收机器有没有在listen自己发送出的数据。

那我们要怎样才能保证数据包被接受了呢?

很可惜,并不能。这就意味着跑UDP的应用程序必须得考虑到这些特性。程序员在开发时,不仅要考虑到应用程序本身,还需要考虑如何分解数据包,如何通过UDP来发送,如何拿到接收机器的返回。接收机器会对每个数据包响应吗?还是没十个数据包?还是应该从不做回应?程序员,无从得知这些。

那如果使用TCP的程序呢,还需要考虑如何分解数据包吗?

并不需要。这里要讲一下TCP的第二个特性。他是面向字节流的。应用程序发出的字节流,TCP完全可以计算出MTU,分解数据,并且给字节流分派好序列号,然后发送给接收主机。

而UDP程序员却得把数据分成块,每次丢一块给UDP,来生成一个数据报。

所以TCP分配序列号并在接受到数据包之后也会收到ACK。这就是有序的传输。

当然,目标主机也有可能会收到大量的乱序数据包。数据包在传输过程中,因为拥塞,可能会丢失,延迟。在发送给应用层之前,TCP有一个缓冲区来接暂时保存这些数据包。TCP会确保所有的数据包都按序接收到了。这个过程,一直持续到接收到接收主机所期望收到的ack。

既然接收主机有缓冲区,那发送方必须得保证发送的数据包不能让接收方的缓冲区溢出。接收方会通过流控制,来告诉对方自己的缓冲区是否已满。

TCP最赞的一个特性就是它的拥塞控制。TCP会运用加性增,乘性减的概念来决定它的发送速率。当它连续收到对应的ack时,它会自己尝试着增加速率,而当它遇到有包丢失时,就会判定系统中有堵塞,自行降低速度。

这就导致了一系列的很有趣的副作用,比如减少一个路由器的缓冲需求。

TCP和UDP应用通过socket来通信。这两者,一个比较有趣的区别就是,TCP必须要有要一个唯一的socket来让两个终端设备通信,然而同样的UDP socket却能同时发送给多个接收方。

网络小白,如有错误,请指正,不甚感激。

Pratical VIM笔记

1. .重复上一次操作

2. >G会增加从当前行到文档末尾的缩进

组合#1和#2来试试。

1
2
3
4
5
6
7
8
9
10
11
Txt:
1 Line One
2 Line Two
3 Line Three

Command:>Gj.j.j.


1 Line One.
2 Line Two.
3 Line Three.

3. 行尾添加分号

1
2
3
4
5
6
7
8
9
10
11
12
13
# vim中A=$a

Txt:
1 Line One
2 Line Two
3 Line Three

Command:A;<Esc>j.j.


1 Line One.;
2 Line Two.;
3 Line Three.;

4. 重复进行一些操作

1
2
3
4
5
6
Txt:
I have some delicious apples.

Command: c2wbought some<Esc>

I have bought some apples.

5. VIM一些操作符

Command Usage
g~ 反转大小写
gu 转成小写
gU 转成大写

可视模式下

Command Usage
~ 反转大小写
u 转成小写
U 转成大写

Python中的_, __, __xx__

看MySQL看得有点头大,写点好玩的吧。来看看_, __以及__xx__

_

_在Python中,有三种用途:

  • python shell中,来保存上次返回的结果。
1
2
3
4
5
6
7
In [1]: a, b = 3, 4

In [2]: a+b
Out[2]: 7

In [3]: _
Out[3]: 7
  • i18n模块中会用print _("Hello World")
    如果在翻译文件中定义了
1
2
msgid "Hello World"
msgstr "世界你好"

执行print _("Hello World"),则会显示世界你好。其实,这是因为Django i18n在翻译时会把gettext缩写成( from django.utils.translation import ugettext as )。

  • 遇到一次性的变量时,会给它起名叫_

在Python Cookbook中,有这样一段源码。

1
2
>>> data = [ 'ACME', 50, 91.1, (2012, 12, 21) ] 
>>> _, shares, price, _ = data >>> shares 50 >>> price 91.1

此外,在可以在类的方法或属性前加一个“_”单下划线,意味着该方法或属性不应该去调用,它并不属于API。
Django中有段源码:

1
2
3
4
5
6
7
8
9
10

class BaseForm(StrAndUnicode):

def _get_errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean()
return self._errors

errors = property(_get_errors)

_表示该方法是私有的,不应该访问。

__

__避免子类覆盖其内容。

__xx__

__xx__经常是操作符或本地函数调用的magic methods

几个有趣的Python模块

glances

1
2
3
pip install glances

glances

能实时看到自己主机的信息。效果很赞。

when.py

直接上demo。还是很简洁的。

1
2
3
4
5
6
7
8
9
>>> import when
>>> when.timezone()
'Asia/Shanghai'
>>> when.today()
datetime.date(2013, 5, 14)
>>> when.tomorrow()
datetime.date(2013, 5, 15)
>>> when.now()
datetime.datetime(2013, 5, 14, 21, 2, 23, 78838)

fn.py

fn.py,最近正在学习的一个函数式编程模块,也很酷。
结合fn.py之后的lambda语句更加简洁,比如:

1
2
3
from fn import _
assert list(map(_ * 2, range(5))) == [0,2,4,6,8]
assert list(filter(_ < 10, [9,10,11])) == [9]

sh

对这个模块简直大爱。sh可以让py很完美地兼容bash。比如

1
2
3
4
5
6
from sh import git

print git.branch("-v")

# result
* master commit-id commit-msg

So cool!

那我们可以用这个模块做一些更炫酷的事情。比如我每天都需要查看服务器日志。

我可以通过sh模块,先scp我需要的日志下来,然后程序分析,对于需要实时盯着的日志,我们还可以引入tail -f来配合。好吧,我在一本正经地胡说八道。随便一个log文件,都要过g,怎么scp…
举个栗子:


from sh import tail

for line in tail("-f", "/var/log/some_log_file.log", _iter=True):
    print line

这样就可以tail日志了。