澳门美高梅手机网站Redis锁构造

单线程与隔离性

Redis是以单线程的办法来施行工作之,事务以串行的主意运行,也就是说Redis中单个命令的实践和作业的履都是线程安全的,不见面相互影响,具有隔离性。

在多线程编程中,对于共享资源的造访使怪的小心:

import threading

num = 1
lock = threading.Lock()


def change_num():
    global num
    for i in xrange(100000):
        #lock.acquire()
        num += 5
        num -= 5
        #lock.release()


if __name__ == '__main__':
    pool = [threading.Thread(target=change_num) for i in xrange(5)]
    for t in pool:
        t.start()
    for t in pool:
        t.join()
    print num

以未加锁之状况下,num是勿能够保持为1之。

倘于Redis中,并作执行单个命令具有老好之隔离性:

import redis

conn = redis.StrictRedis(host="localhost", port=6379, db=1)
conn.set('num', 1)


def change_num(conn):
    for i in xrange(100000):
    ┆   conn.incr('num', 5)
    ┆   conn.decr('num', 5)


if __name__ == '__main__':
    conn_pool = [redis.StrictRedis(host="localhost", port=6379, db=1)
                 for i in xrange(5)]
    t_pool = []
    for conn in conn_pool:
        t = threading.Thread(target=change_num, args=(conn,))
        t_pool.append(t)
    for t in t_pool:
        t.start()
    for t in t_pool:
        t.join()
    print conn.get('num')

模仿的5只客户端同时针对Redis中的num值进行操作,num最终结果会保持吗1:

1
real    0m46.463s
user    0m28.748s
sys 0m6.276s

用Redis中单个操作以及业务之原子性可以开多事情,最简便的虽是开全局计数器了。

论在短信验证码业务中,要界定一个用户以同一分钟内只能发送一不行,如果下关系项目数据库,需要为每个手机号记录上次殡葬短信的岁月,当用户要验证码时,取出与当前工夫开展自查自纠。

当即无异气象下,当用户短日点击多次常,不仅增加了数据库压力,而且还会见现出又询问均符合条件但数据库更新短信发送时间比较缓慢的题目,就会更发送短信了。

于Redis中化解当时等同题目便不行简短,只待为此手机号作为key创建一个在世期限为同样分钟之数值即可。key不有时时会发送短信,存在时时虽免克发送短信:

def can_send(phone):
    key = "message:" + str(phone)
    if conn.set(key, 0, nx=True, ex=60):
    ┆   return True
    else:
    ┆   return False

有关一些不可名的30分钟内限制访问还是下载5破的效应,将用户ip作为key,值设为次数上限,过期工夫使为限制时间,每次用户访问时自减即可:

def can_download(ip):
    key = "ip:" + str(ip)
    conn.set(key, 5, nx=True, ex=600)
    if conn.decr(key) >= 0:
    ┆   return True
    else:
    ┆   return False

前言:

吃完饭,一躺下,一个梦,醒了……

即时快要踏上上第二不善创业的道了,详情见:一个想法(续三):一卖IT技术联盟创业计划书,开启众筹创业征程

既今夜无眠寂静,就静静回忆下当年首先差创业之时光吧。

于自博客的十年里,那几年基本淡出博客园,离开了社区。

用多人不懂得自己那段时光,我为无记录过程,今天就算跟大伙分享同首回忆录吧。

Redis基本业务及乐观锁

则Redis单个命令具有原子性,但当多只令并行执行的时刻,会出再多之题目。

遵举一个中转的例子,将用户A的钱转给用户B,那么用户A的账户减少用跟B账户的长并且开展:

import threading
import time

import redis

conn = redis.StrictRedis(host="localhost", port=6379, db=1)
conn.mset(a_num=10, b_num=10)


def a_to_b():
    if int(conn.get('a_num')) >= 10:
        conn.decr('a_num', 10)
        time.sleep(.1)
        conn.incr('b_num', 10)
    print conn.mget('a_num', "b_num")


def b_to_a():
    if int(conn.get('b_num')) >= 10:
        conn.decr('b_num', 10)
        time.sleep(.1)
        conn.incr('a_num', 10)
    print conn.mget('a_num', "b_num")


if __name__ == '__main__':
    pool = [threading.Thread(target=a_to_b) for i in xrange(3)]
    for t in pool:
        t.start()

    pool = [threading.Thread(target=b_to_a) for i in xrange(3)]
    for t in pool:
        t.start()

运作结果:

['0', '10']
['0', '10']
['0', '0']
['0', '0']
['0', '10']
['10', '10']

并发了账户总额变少的景。虽然是人工的吗从添自减命令中加加了100ms延迟,但以事实上出现很高的场面被凡是很可能出现的,两独令执行中执行了别的讲话。

这就是说现在设保的是简单个增减命令执行中未叫外命令的扰乱,Redis的工作可以达成这无异于目的。

Redis中,被MULTI命令和EXEC命令包围的富有命令会一个连片一个底履,直到有命令还推行了了。一个作业了后,Redis才见面失掉处理其他的命。也就是说,Redis事务是享有原子性的。

python中可用pipeline来创造工作:

def a_to_b():
    if int(conn.get('a_num')) >= 10:
    ┆   pipeline = conn.pipeline()
    ┆   pipeline.decr('a_num', 10)
    ┆   time.sleep(.1)
    ┆   pipeline.incr('b_num', 10)
    ┆   pipeline.execute()
    print conn.mget('a_num', "b_num")


def b_to_a():
    if int(conn.get('b_num')) >= 10:
    ┆   pipeline = conn.pipeline()
    ┆   pipeline.decr('b_num', 10)
    ┆   time.sleep(.1)
    ┆   pipeline.incr('a_num', 10)
    ┆   pipeline.execute()
    print conn.mget('a_num', "b_num")

结果:

['0', '20']
['10', '10']
 ['-10', '30']
['-10', '30']
['0', '20']
['10', '10']

可看看,两漫漫语句确实同实施了,账户总额不会见变换,但出现了负值的景况。这是坐作业在exec命令于调用之前是勿会见尽的,所以用读取的多少做判定及业务执行中便生出矣光阴不同,期间实际多少发生了变。

为保持数据的一致性,我们尚待用一个事情命令WATCH。WATCH可以本着一个键开展蹲点,监视后到EXEC命令执行前,如果叫监视的键值发生了变(替换,更新,删除等),EXEC命令会回一个破绽百出,而休见面真正的施行:

>>> pipeline.watch('a_num')
True
>>> pipeline.multi()
>>> pipeline.incr('a_num',10)
StrictPipeline<ConnectionPool<Connection<host=localhost,port=6379,db=1>>>
>>> pipeline.execute()
[20]
>>> pipeline.watch('a_num')
True
>>> pipeline.incr('a_num',10) #监视期间改变被监视键的值
30
>>> pipeline.multi()
>>> pipeline.incr('a_num',10)
StrictPipeline<ConnectionPool<Connection<host=localhost,port=6379,db=1>>>
>>> pipeline.execute()
    raise WatchError("Watched variable changed.")
redis.exceptions.WatchError: Watched variable changed.

现在呢代码加上watch:

def a_to_b():
      pipeline = conn.pipeline()
      try:
      ┆   pipeline.watch('a_num')
      ┆   if int(pipeline.get('a_num')) < 10:
      ┆   ┆   pipeline.unwatch()
      ┆   ┆   return
      ┆   pipeline.multi()
      ┆   pipeline.decr('a_num', 10)
      ┆   pipeline.incr('b_num', 10)
      ┆   pipeline.execute()
      except redis.exceptions.WatchError:
      ┆   pass
      print conn.mget('a_num', "b_num")


  def b_to_a():
      pipeline = conn.pipeline()
      try:
      ┆   pipeline.watch('b_num')
      ┆   if int(pipeline.get('b_num')) < 10:
      ┆   ┆   pipeline.unwatch()
      ┆   ┆   return
      ┆   pipeline.multi()
      ┆   pipeline.decr('b_num', 10)
      ┆   pipeline.incr('a_num', 10)
      ┆   pipeline.execute()
      except redis.exceptions.WatchError:
      ┆   pass
      print conn.mget('a_num', "b_num")

结果:

['0', '20']
['10', '10']
['20', '0']

成落实了账户转移,但是出三次尝试失败了,如果要是尽可能的比方每次交易且获得成功,可以加尝试次数要尝试时:

def a_to_b():
    pipeline = conn.pipeline()
    end = time.time() + 5
    while time.time() < end:
    ┆   try:
    ┆   ┆   pipeline.watch('a_num')
    ┆   ┆   if int(pipeline.get('a_num')) < 10:
    ┆   ┆   ┆   pipeline.unwatch()
    ┆   ┆   ┆   return
    ┆   ┆   pipeline.multi()
    ┆   ┆   pipeline.decr('a_num', 10)
    ┆   ┆   pipeline.incr('b_num', 10)
    ┆   ┆   pipeline.execute()
    ┆   ┆   return True
    ┆   except redis.exceptions.WatchError:
    ┆   ┆   pass
    return False

这么,Redis可以用工作实现类似于锁之建制,但这机制和涉及项目数据库的锁有所不同。关系项目数据库对被拜的数量实施开展加锁时,其它客户端尝试对吃加锁数据实施开展摹写副是会吃死的。

Redis执行WATCH时并无见面指向数码开展加锁,如果发现数目就给另外客户端抢先修改,只会通知执行WATCH命令的客户端,并无会见阻拦修改,这名乐观锁。

2010年,那年,我还死年轻

在成就SilverLight+WCF网络象棋的一系列公布后,开始聚集了一些人气。

尔后便起重复(之前开展了千篇一律坏,但黄了)进行了CYQ.Data开源系列的颁布,

乘机框架系列之不胫而走,与人气的汇,便不停的起网友建议我用框架赚钱(提出包括与培育机构成立协作,与公司的飞速支付建立协作等方式)

在网友的洗脑中,我竟晕的行动了。

那阵子本身寻找了一个大学之同乡师弟,试图说服他投钱(他那时候比较起钱)。

如果自己立刻只有略知一二要将框架形成普及,但怎么赚钱怎么开始之类的,都未曾感念了。

外叫上了其余一个行技术之爱侣跟自家一起吃了生米饭聊这行,但结果连无顶让看好。

或者因同乡及师兄关系,最后说可以先让自身2万块给自身失去打出市场调研先。

新兴尽管不曾新生了。。。

 

用SET()构建锁

之所以WACTH实现之乐观锁一般情况下是适用的,但在一个问题,程序会为就一个执破产的事情而不息地进行重试。当负载增加的时候,重试次数会上升至一个不足承受之境界。

假定要协调对的贯彻锁的说话,要避下几乎单情况:

  • 大抵只经过又获取了锁
  • 具备锁的过程在释放锁之前崩溃了,而其他进程也无掌握
  • 有着锁之展开运转时了长,锁被机关释放了,进程本身不知道,还会见尝试去放锁

Redis中假如落实锁,需要以一个命,SET()或者说是SETNX()。SETNX只见面在键不有的情状下为键设置值,现在SET命令于给定了NX选项的情下吧能兑现此效应,而且还会安装过时,简直就是是天生用来构建锁之。

只是使为得加锁之资源名也key设置一个价,要获取锁经常,检查是key存不有即可。若在,则资源就让另外进程取得,需要阻塞到其他进程释放,若无在,则树立key并拿走锁:

import time
import uuid


class RedisLock(object):

    def __init__(self, conn, lockname, retry_count=3, timeout=10,):
        self.conn = conn
        self.lockname = 'lock:' + lockname
        self.retry_count = int(retry_count)
        self.timeout = int(timeout)
        self.unique_id = str(uuid.uuid4())

    def acquire(self):
        retry = 0
        while retry < self.retry_count:
            if self.conn.set(lockname, self.unique_id, nx=True, ex=self.timeout):
                return self.unique_id
            retry += 1
            time.sleep(.001)
        return False

    def release(self):
        if self.conn.get(self.lockname) == self.unique_id:
            self.conn.delete(self.lockname)
            return True
        else:
            return False

取锁的默认尝试次数限制3不良,3不行获失败则赶回。锁之存期限默认设为了10s,若无主动释放锁,10s后沿会自行清除。

尚保存了取得锁时锁设置的价,当释放锁的时刻,会先判断保存之价值与目前沿的价是否一律,如果未均等,说明是沿过期被电动释放然后叫别进程取得了。所以锁之价值必须保持唯一,以免释放了另外程序得到之沿。

使用锁:

def a_to_b():
    lock = Redlock(conn, 'a_num')
    if not lock.acquire():
    ┆   return False

    pipeline = conn.pipeline()
    try:
    ┆   pipeline.get('a_num')
    ┆   (a_num,) = pipeline.execute()
    ┆   if int(a_num) < 10: 
    ┆   ┆   return False
    ┆   pipeline.decr('a_num', 10) 
    ┆   pipeline.incr('b_num', 10) 
    ┆   pipeline.execute()
    ┆   return True
    finally:
    ┆   lock.release()

放活锁经常为足以用Lua脚本来告诉Redis:删除这个key当且仅当这key存在以值是自个儿欲的死值:

    unlock_script = """
    if redis.call("get",KEYS[1]) == ARGV[1] then
    ┆   return redis.call("del",KEYS[1])
    else
    ┆   return 0
    end"""

可以用conn.eval来运行Lua脚本:

    def release(self):
    ┆   self.conn.eval(unlock_script, 1, self.lockname, self.unique_id)

然,一个Redis单机锁就落实了。我们得用之锁来替WATCH,或者跟WACTH同时使用。

实际上利用受到还要依据工作来支配锁的粒度的题目,是沿住满结构或锁住结构被的等同多少有。

粒度越充分,性能更是差,粒度越小,发生死锁的几引领进一步充分。

 

2011开春,那年,我也还充分年轻

乘CYQ.Data的开源系列的版升级以及增温,秋色园QBlog系列也大张旗鼓的进展了。

蓦然的有平上,有各类网友于自己帮忙他形容了一个概括的要字站。

他吧开始教我怎么开要紧字站赚钱,给了自己同一批传奇的最主要字。

自家因此一个域名试点,一个大抵星期后,大概一上发生几十交一百差不多只IP。

外牵线人收了自我那么点流量,给了几十块,说而开老大,至少要投入几千个域名,N台服务器,搞N多单IP。

然后自己哪怕从来不和进了,毕竟传奇关键字就重重总人口于召开了,他吗当做。

后来底有一样上,有人倾心QBlog中的CMS性质,说可做站群,开始洗脑子我出创业。

在外的总动员下,我创建了一个博客园IT创业交流群,里面在了20大多私,聚于共谈谈。

并且形成了一如既往客简单的非到底计划之计划,这里还是时有发生存在:http://www.cyqdata.com/cy.html

但只有讨论,大伙并无履,每个人且于我表明了他们难以行动之说辞。

有些还特别奔本人勾勒了平客文档,提出同样客建议,表示只是会以时恰到好处的时刻才见面参加。

 

2011年被,那年,我荡着青春年少,辞职出来创业了

当下,公司有限年的合同期到了,我及领导者说就是不再上了,我要出来创业了。

立底场面,除了受网友洗脑子,DZ论坛的激励,还有一腔热血。

出于QBlog要走商业化,所以,做也组件有的CYQ.Data也不能不闭源了,故本来发展形势大好的开源组件,也为此活动及闭源商业化的世界里。

年轻是荡漾着,但对路该怎么动,其实我大茫然,没有工作,一个丁。

每日沉浸在改代码,加效果,发布新本子被,以持续的无暇状态来麻木自己的思量。

下一场深夜描绘技术博文,教程,或录视频,等到清晨公布于博客里。

每天依靠在网友的支撑与赞在坚持着不亮该怎么坚持的坚持不懈。

就如此以寂寞中连连了某些独月。。。

 

2011年底,那年,QBlog无望,微博初临时

于那段艰苦孤独的时日里,天天坚持分享的之艺文章。

连不曾自所预想被之迎来使用的目标用户,而是迎来了扳平广大而源码的伸手党。

下就逐步走向了开源的里程……

开源后,离商业更是摇摇无期了,中间偶尔有各自感兴趣之问话价钱,我自己都不清楚该怎么回答了……

……

有一致上,我还是是眼睁睁在屋子里,不知底凡是移着力量,或是录着视频,还是写在博文。

情人为自家打了只电话,说淘宝上有众多发售微博粉丝的,1000粉4片钱,问我力所能及免可知写单软件刷粉丝。

还发了片任何刷粉软件之示范截图,说可以吧,软件写出来后,他帮自己卖软件。

清醒的左右现状为即这么了,就花了几乎上把软件写好给他了(一初步功能就是是导入一批账号,然后点击批量关注一个哀号)

发过去之后,基本为从来不啥进展,因为他如先行打同样批判账号,而这批账号,好多且是好账号,所以赚不顶钱。

……

后来,偶尔没事吗协助另一个情人写了一个自行投票机(主要是电动断开重拔+简单的图样验证码自动识别)

……

每当恍着的我,莫名的就算顺手将本的刷粉软件改进了瞬间,把她由单机变成了大半用户+平台式的互粉模式。

今后,走及了微博创业之动向……

2012-2013年,随微博荡起陨落的2年

从创业之大势切入到微博,我为主淡出了博客园了,淡出技术社区了。

于公布了初浪微博之互粉平台软件,用户增加很快,市场需求很十分。

自身关上了一个朋友,中间以网上征了十几只兼职的学习者党联合抓这行。

以下分享中间历经的重重折腾事:

1:官网微博于封

在官网微博账号注册后,不交均等礼拜,真实用户的粉就过1w+了,每天的数字还于增强。

无独有偶兴奋于才刚始的好征兆,官网账号就叫新浪微博给封了,申诉为从不因此。

于是后来尽管没官网微博了,但建筑了官网网站。

2:登陆入口为封闭

由互粉属于灰色区,不能够光明正非常的走接口,技术及且是分析http协议,模拟进行。

官网微博刚被查封不久,软件之微博登陆也受封杀了(当时是分析的新浪网首页登陆,再超反微博之)。

于是乎找了初浪网的其他登陆入口(毕竟新浪的频道多,登陆中一个,都得以超过到微博)。

从没过几礼拜,然后以吃封杀了。

迫于,引入了WebBrowser组件,登陆时弹出浏览器窗口为用户手工登陆。

新浪虽封好不了,但用户体验差了多浩大。

3:360对等杀毒软件报病毒

由软件要加密的用,360且见面报病毒,每次和360谈判,都是提交exe文件过去,然后等。

只是由于软件更新的高频很快,很多时刻还是子夜翻新提升,360底人为,也只是只有你催了成百上千糟还理你同转头。

如此前后为坑了一半年多,直到我颁布了别样一个微博互评软件,忍无可忍用用豁达账号去360小将周鸿祎的微博下评论刺激时。

这才发生新的工作人员跟进,并吃了我一个自助提交的VIP账号,才算是干净底化解360报病的题目了。

可是还有QQ的杀毒卫士之类的,反正够折腾,但为没办法。

光杀毒软件的下载病毒提示,就损失了累累用户。

4:过于单纯天真的考虑

虽然有各种限制,但为阻止不了大量底要求以及用户。

敏捷,微博粉丝精灵已经成市场上该类软件的首先。

中推出的微博的大号互评、互转软件,也成为那个为欢迎之效力。

乘势微博营销概念的酷热,微博有奖转发活动吗到处荡开。

起那么些之用户找上自己道合作,利用阳台小号进行有奖活动转发。

啊时有发生不少任何的想法:制作很多之腔漫漫工作等。

……

确,大量之小号,当水军涌向哪呀就是得够呛一样全副。

在相应快速捞一把的花色:我倒坚称在免费,想着靠免费统一江湖。

于灰色的行里:我倒想念在公不扰民,盼在软件能走及美好。

徒天真的无可救药。

 

4:实名制限制

乘机国家针对实名制的渴求,软件及提供的敏捷小号注册功能已休克因此了。

于是乎小号的长足减少,带来的结果就是平均每会刷的粉大量之滑坡了。

前面平均等效上能够刷几千之,到最后只好刷到几百了。

 

5:没落的初浪微博

乘胜实名制和僵尸粉的打击力度加强,让水更清时,浑水摸鱼的微博营销概念也开始退潮了。

赋微信和走互联网的起,新浪微博已日渐衰退。

 

6:转向移动互联网的风投

在涉各种坑后,克服各种困难后,

带来在市场上同类软件第一底名头,带在几十万底用户数据,在初浪微博及挂钩了少于个投资人。

以及第一只投资人在深圳大致见了给,还为带动去采风加一个基于新浪微博之创业团,他们是做多少解析的。

对咱们的花色,他们仅发30万之投资意向,所以呢不怕不了了之。

仲个投资人,用微博私信联系后,直接打电话的,直接就让告知已经不照新浪微博之类型了。

 

7:无力回天的自力更生

当投资的风向标已转移,微博也开始式微,该何去何从?

唯其如此思考各种如何致富生存的章程:

1:对外提供刷粉、转发、评论工作。

(在软件一样开始,没有采集小号,是同一生串,导致去大量之账号,在实名制后,小号的发出就非常窘迫)

2:提供企业账号代运营。

3:软件增加VIP(增加魔法值(积分)购买)

4:软件增加一个非常的广告轮播(广告位出租)

收入从几百及几千更回归到几百,纵然有几十万底用户,大环境微博曾没落,依附于微博之也罢均无力回天了。

……

……

……

总结:

理讲起一二三,也抱不了大伙的心迹。

稍加事,需要自己经历,才刻骨于胸。

然而更一样次等创业便可教会人数多丛。

创业失败率高的原故相似之在总在:思维极度年轻,智商不够用,经验不足。

遂每经历一样糟破产,多同赖想总结,就攒了大半同份成功之票房价值。

一样不行完整的创业,最好有:创新的觉察,谨慎之计划,坚决的行动力,看透成败的心态。

发表评论

电子邮件地址不会被公开。 必填项已用*标注