MySQL 幻读分析

要理解MySQL幻读问题,就要先搞清楚InnoDB的锁的机制和隔离级别。

MySQL InnoDB事务的隔离级别有四级,默认是“可重复读”(REPEATABLE READ)。
– 未提交读(READ UNCOMMITTED)。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)。
– 提交读(READ COMMITTED)。本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不重复读)。
– 可重复读(REPEATABLE READ)。在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象(稍后解释)。
– 串行化(SERIALIZABLE)。读操作会隐式获取共享锁,可以保证不同事务间的互斥。

四个级别逐渐增强,每个级别解决一个问题。

  • 脏读,最容易理解。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据。
  • 不重复读。解决了脏读后,会遇到,同一个事务执行过程中,另外一个事务提交了新数据,因此本事务先后两次读到的数据结果会不一致。
  • 幻读。解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会“惊奇的”发现了这些新数据,貌似之前读到的数据是“鬼影”一样的幻觉。
    下面开始实验,首先看你数据库的隔离级别
    75F8E42D-4898-487F-B253-34BB58259294
    下面开始重复读实验
    1E1AD02F-E846-48C3-AEED-EC6687FEDAA5
    可以看到,左边的事物,查不到ID为5的行,却插不进去,因为这个事物开始的时候,ID为5是不存在的。这就产生了幻读。
    怎么解决呢?
    3E007E0A-8380-4901-ABBE-58AA99F10BCA
    用了for update,这样右边的插入就卡住了,需要等待左边的提交成功后才行。
    但是这种会锁表,不知道有没有更好的方法。
    参考资料
    http://tech.meituan.com/innodb-lock.html

SwitchyOmega插件的使用

好多小伙伴使用Shadow穿墙后就使用默认的设置,这样很麻烦,尤其是在公司使用内网的情况下,来回切换,我就简单说下如何设置。

首先Shadow 启动后会在本地建立一个1080的端口,用于代理。只要设置socket5代理用这个端口就行了,所以我们大可以关掉软件自带的代理模式,保持清爽,设置如下,取消勾选启用系统代理。

AB5001AC-0E4F-4DB3-9C75-3F92BE7DD055

然后安装Chrome浏览器,安装SwitchyOmega插件 去应用商店下载或在https://github.com/FelisCatus/SwitchyOmega/releases 下载

然后开始设置了。

首先设置一个模式,使用Shadow代理,选择新建情景模式,设置如下

641f6412121e5c4b4a8106d621e897b4

设置好后就可以设置自动代理了,选择自动切换,设置如下:

 

00E69715-7B5A-42A7-9608-F6705BA97E02

Autoproxy 链接为

https://raw.githubusercontent.com/calfzhou/autoproxy-gfwlist/master/gfwlist.txt

然后就可以愉快的上网了。

CAFD1AB5-0422-48CE-BCE8-8EFEC74436AD

遇到不能翻墙的网站,别着急,选择右上角的那个图标,设置如下

C699E952-A991-4F32-9B74-DDEC0EC6AECB

3A01272F-25FF-41E2-B53A-4A01654277BF

然后这个网站以后访问就走代理了。

很方便吧。

Python 装饰器和 functools 模块

什么是装饰器?

在 Python 语言里第一次看到装饰器不免让人想到设计模式中的装饰模式——动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
好吧,Python 中的装饰器显然和装饰模式毫无关系。那 Python 中的装饰器到底是什么呢?
简而言之,装饰器提供了一种方法,在函数和类定义语句的末尾插入自动运行代码。Python 中有两种装饰器:函数装饰器和类装饰器。

函数装饰器

简单的装饰器例子:

[code]
def decorator(F): # 装饰器函数定义
print "I’m decorator"
return F

@decorator
def foo():
print ‘Hello World!’
# 上面等价于 foo = decorator(foo)

foo()
"""
I’m decorator
Hello World!
"""

decorator(foo)() # 所以这里的输出与 foo() 相同
"""
I’m decorator
Hello World!
"""
[/code]



从上面运行后结果看出,装饰器就是一个能够返回可调用对象(函数)的可调用对象(函数)。
具有封闭作用域的装饰器

[code]
def decorator(func): # 装饰器函数
print ‘in decorator’
def wrapper(*args):
print ‘in decorator wrapper’
wrapper._calls += 1
print "calls = %d" % (wrapper._calls)
func(*args)
wrapper._calls = 0
return wrapper

@decorator
def foo(x, y):
print "x = %d, y = %d" % (x, y)

foo(1, 2) # 第一次调用
"""
in decorator
in decorator wrapper
calls = 1
x = 1, y = 2
"""

foo(2, 3) # 第二次调用
"""
in decorator wrapper
calls = 2
x = 2, y = 3
"""
[/code]

可以看出第一次调用 foo(1, 2) 时,相当于

[code]
foo = decorator(foo)
foo(1, 2)
[/code]

第二次调用 foo(2, 3) 时 foo 已经为 decorator(foo) 的返回值了
再看看一个装饰器类来实现的:

[code]
class decorator: # 一个装饰器类
def __init__(self, func):
print ‘in decorator __init__’
self.func = func
self.calls = 0
def __call__(self, *args):
print ‘in decorator __call__’
self.calls += 1
print "calls = %d" % (self.calls)
self.func(*args)

@decorator
def foo(x, y):
print "x = %d, y = %d" % (x, y)

foo(1, 2) # 第一次调用
"""
in decorator __init__
in decorator __call__
calls = 1
x = 1, y = 2
"""

foo(2, 3) # 第二次调用
"""
in decorator __call__
calls = 2
x = 2, y = 3
"""
[/code]

装饰器参数

[code]
def decorator_wrapper(a, b):
print ‘in decorator_wrapper’
print "a = %d, b = %d" % (a, b)
def decorator(func):
print ‘in decorator’
def wrapper(*args):
print ‘in wrapper’
func(*args)
return wrapper
return decorator

@decorator_wrapper(1, 2) # 这里先回执行 decorator_wrapper(1, 2), 返回 decorator 相当于 @decorator
def foo(word):
print word

foo(‘Hello World!’)
"""
in decorator_wrapper
a = 1, b = 2
in decorator
in wrapper
Hello World!
[/code]

functools 模块

functools 模块中有三个主要的函数 partial(), update_wrapper() 和 wraps(), 下面我们分别来看一下吧。
partial(func[,args][, *keywords])
看源码时发现这个函数不是用 python 写的,而是用 C 写的,但是帮助文档中给出了用 python 实现的代码,如下:

[code]
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
[/code]

OK,可能一下子没看明白,那么继续往下看,看一下是怎么用的。我们知道 python 中有个 int([x[,base]]) 函数,作用是把字符串转换为一个普通的整型。如果要把所有输入的二进制数转为整型,那么就要这样写 int(’11’, base=2)。这样写起来貌似不太方便,那么我们就能用 partial 来实现值传递一个参数就能转换二进制数转为整型的方法。

[code]
from functools import partial
int2 = partial(int, base=2)
print int2(’11’) # 3
print int2(‘101′) # 5
from functools import partial
int2 = partial(int, base=2)
print int2(’11’) # 3
print int2(‘101’) # 5
[/code]

update_wrapper(wrapper, wrapped[, assigned][, updated])
看这个函数的源代码发现,它就是把被封装的函数的 module, name, doc 和 dict 复制到封装的函数中去,源码如下,很简单的几句:

[code]
WRAPPER_ASSIGNMENTS = (‘__module__’, ‘__name__’, ‘__doc__’)
WRAPPER_UPDATES = (‘__dict__’,)
def update_wrapper(wrapper,wrapped, assigned = WRAPPER_ASSIGNMENTS,updated = WRAPPER_UPDATES):
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
return wrapper
[/code]

具体如何用我们可以往下看一下。

[code]
def wraps(wrapped,assigned = WRAPPER_ASSIGNMENTS,updated = WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped,assigned=assigned, updated=updated)

[/code]

好,接下来看一下是如何使用的,这才恍然大悟,一直在很多开源项目的代码中看到如下使用。

[code]
from functools import wraps
def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwds):
print ‘Calling decorated function’
return f(*args, **kwds)
return wrapper

@my_decorator
def example():
"""这里是文档注释"""
print ‘Called example function’

example()

# 下面是输出
"""
Calling decorated function
Called example function
"""
print example.__name__ # ‘example’
print example.__doc__ # ‘这里是文档注释’
[/code]
其它参考链接

  • http://www.wklken.me/posts/2013/08/18/python-extra-functools.html
  • http://coolshell.cn/articles/11265.html
  • supervisor守护进程

    有时候写个脚本跑着跑着挂了,用supervisor守护进程挺方便的。
    Supervisor是一个Python开发的client/server系统,可以通过pip安装,需要注意的是Python2.7以下兼容性很差,很可能运行失败。

    pip install Supervisor 
    

    安装好后

    echo_supervisord_conf > /etc/supervisord.conf 
    

    生成配置文件
    然后我们在配置文件末尾追加想守护的进程即可,写法如下

    [program:program-name]
    command = python /root/program-dir/program-name.py 
    user = root  
    autostart = true  
    autorestart = true  
    

    配置好后可以执行supervisord启动,也可以killall -HUP supervisord
    再然后我们添加supervisord到系统服务中
    下面是Centos 示例代码

    #!/bin/sh
    #
    # /etc/rc.d/init.d/supervisord
    #
    # Supervisor is a client/server system that
    # allows its users to monitor and control a
    # number of processes on UNIX-like operating
    # systems.
    #
    # chkconfig: - 64 36
    # description: Supervisor Server
    # processname: supervisord
    
    # Source init functions
    . /etc/init.d/functions
    
    RETVAL=0
    prog="supervisord"
    pidfile="/tmp/supervisord.pid"
    lockfile="/var/lock/subsys/supervisord"
    
    start()
    {
            echo -n $"Starting $prog: "
            daemon --pidfile $pidfile supervisord -c /etc/supervisord.conf
            RETVAL=$?
            echo
            [ $RETVAL -eq 0 ] && touch ${lockfile}
    }
    
    stop()
    {
            echo -n $"Shutting down $prog: "
            killproc -p ${pidfile} /usr/bin/supervisord
            RETVAL=$?
            echo
            if [ $RETVAL -eq 0 ] ; then
                    rm -f ${lockfile} ${pidfile}
            fi
    }
    
    case "$1" in
    
      start)
        start
      ;;
    
      stop)
        stop
      ;;
    
      status)
            status $prog
      ;;
    
      restart)
        stop
        start
      ;;
    
      *)
        echo "Usage: $0 {start|stop|restart|status}"
    

    放在/etc/rc.d/init.d/supervisord 目录中,设置好执行权限后 我们chkconfig supervisord on 设置开机启动即可
    简单吧!