Redis has been used for more than 2 years, often lamented the graceful and lean redis, perfectly formed.
Recently preface out the idea of using Python to implement a zset data structure in memory.
The idea is this:
Hash + SortedList
Where hash is used to make the acquisition of the complexity of the key value into O (1)
and using Bisect module dichotomy method to implement other operations in SortedList O (logn)
The code below.
#coding =utf-8from bisect Import bisect_left,bisect_right,insort# Define node class Snode:def __init__ (Self,key=none, score= Float ('-inf '), next=none): Self.key = key Self.score = Score def __lt__ (Self,other): return self. Score < GetAttr (other, ' score ', other) def __gt__ (self,other): #没定义__gt__的话会导致bisect_right出问题, even if __lt__ Retu is already defined RN Self.score > GetAttr (Other, ' score ', other) #定义数组, with bisect Maintenance Order class Slist (object): Def __init__ (self): self.ke Y2node = {} Self.card = 0 self.orderlist = [] def findpos (self, snode): CurPos = Bisect_left (self. Orderlist,snode) while 1:if self.orderlist[curpos].key==snode.key:break curp OS + = 1 return curpos def insert (Self,key,score): If not isinstance (score,int): Raise Exception (' score mus t be integer ') Snode = Self.key2node.get (key) if snode:if score = = Snode.score:r Eturn 0 del Self.Orderlist[self.findpos (snode)] Snode.score = score Else:self.card + = 1 Snode = SN Ode (key=key,score=score) Self.key2node[key] = Snode insort (self.orderlist, Snode) return 1 def Delete (Self,key): Snode = Self.key2node.get (key) if not Snode:return 0 self.card-= 1 Del Self.orderlist[self.findpos (Snode)] del Self.key2node[key] del snode return 1 def search ( Self,key): Return Self.key2node.get (Key) class Sortedset:def __init__ (self): Self.slist = Slist () def Zadd (self, Key, score): Return Self.slist.insert (Key, score) def zrem (self, key): Return Self.slist.delet E (key) def zrank (self, key): #score相同则按字典序 Snode = Self.slist.key2node.get (key) if not snode:r Eturn None return Self.slist.findpos (Snode) def zrevrank (self, key): Return Self.zcard-1-Self.zrank (k EY) def zscore (self,Key): Snode = Self.slist.key2node.get (key) return GetAttr (Snode, ' score ', None) def zcount (self, start, end ): ol = self.slist.orderlist return Bisect_left (ol,end+1)-Bisect_right (ol,start-1) @property def ZCA RD (Self): return Self.slist.card def zrange (self, start, end, Withscores=false): #score相同则按字典序 nodes = sel F.SLIST.ORDERLIST[START:END+1] If not nodes:return [] if Withscores:return [(X.key, X.score) fo R x in nodes] Else:return [x.key to X in nodes] def zrevrange (self, start, end, Withscores=false): Card = Self.zcard If End<0:end = end + Card if Start<0:start = start + Card nodes = Self.slist.orderlist[max (card-end-1, 0): Max (Card-start, 0)][::-1] if not nodes:return [] If Withscores:return [(X.key, X.score) for x in nodes] Else:return [X.key for x in nodes] def zrangebyscore (SELF, start, end, withscores=false): ol = self.slist.orderlist nodes = Ol[bisect_left (OL, start): Bisect_right ( OL, end)] if not nodes:return [] if Withscores:return [(X.key, X.score) for x in nodes] E Lse:return [X.key for X in nodes] def zrevrangebyscore (self, end, start, Withscores=false): return s Elf.zrangebyscore (Start, End, Withscores) [:: -1] def zincrby (self, key): Snode = Self.slist.key2node.get (key) If not Snode:return self.zadd (key, 1) score = Snode.score Self.zrem (key) return to self. Zadd (key, score+1) Import Contextlibimport timetimeobj = {}class timetrace: @contextlib. ContextManager def mark (self, Name): T = time.time () yield timeobj[name] = Time.time ()-T def stat (self): print '------- --benchmark (100000 requests)---------' for k,v in Timeobj.iteritems (): print ' {} {}s '. Format (k,v) TT = TI Metrace () if __name__ = =' __main__ ': s = SortedSet () s.zadd (' kzc ', +) S.zadd (' A ', 1) s.zadd (' B ', 2) s.zadd (' C ', 2) s.zadd (' d ', 6) S.zadd (' Hello ') s.zadd (' World ') S.zincrby (' Kzc ') print ' KZC score ', S.zscore (' Kzc ') print ' kzc rank ', S.ZR Ank (' kzc ') print ' Kzc Revrank ', S.zrevrank (' Kzc ') print ' Zcount (1,20) ', S.zcount (1,20) print ' Zrange (2,4,withscores =true) ', S.zrange (2,4,withscores=true) print ' Zrangebyscore (1,5,withscores=true) ', S.zrangebyscore (1,5,withscores= True) print ' Zrem ("C") ', S.zrem (' C ') print ' Zrangebyscore (1,5,withscores=true) ', S.zrangebyscore (1,5,withscores=true print ' Zcard ', s.zcard print ' S.zadd ("C", 7) ', S.zadd (' C ', 7) print ' Zcard ', s.zcard print ' Zrevrange all ', S.zrev Range (0,-1,withscores=true) #benchmark import random keys = [STR (x) for x in range (0,100000)] values = Range (0 , 100000) Random.shuffle (keys) with Tt.mark (' Zadd '): Map (lambda x,y:s.zadd (x, y), keys,values) with Tt.mark (' Zscore '): Map (S.zscore,keys) with Tt.mark (' Zrank '): Map (S.zrank,keys) Tt.stat ()
The results are as follows:
Implementation of a Redis zset data structure using Python