class RedisImageStorage(IOBase): def __init__(self, filename): self.filename = filename self._redis = StrictRedis(connection_pool=pool0) def write(self, data): self._redis.append('attendance:api:image:' + self.filename, data)
class LibRedis: # 默认所有key的前缀 key_prefix = 'RAWE_' # redis 连接对象 obj_redis = None # 默认的过期时间为3天 DEFAULT_EXPIRE = 259200 def __init__(self, host='127.0.0.1', port=6379, db=0, prefix=None, charset='utf-8'): """ 初始化 """ if not host or not port: return None if prefix: self.key_prefix = prefix.strip() # construct self.obj_redis = StrictRedis( host=host, port=port, db=db, charset='utf-8') def key_make(self, keyname=None): """ 处理所有key,增加前缀 如果实例化时没有设置,则使用默认前缀 """ if not keyname: return None return self.key_prefix + str(keyname).strip() def set_expire(self, keyname=None): """ 设置key的过期时间,装饰器调用 """ if not keyname: return None return self.obj_redis.expire(self.key_make(keyname), self.DEFAULT_EXPIRE) # -------------------------------------------------------- # String # -------------------------------------------------------- @wraps_set_expire def set(self, keyname=None, value=None): """ 设置指定 key 的值。 如果 key 已经存储其他值, SET 就覆写旧值,且无视类型。 return: 设置操作成功完成时,才返回 OK """ if not keyname or value is None: return None keyname = self.key_make(keyname.strip()) if isinstance(value, str): value = value.strip() return self.obj_redis.set(keyname, value) def get(self, keyname=None): """ 获取指定 key 的值。 return: key 的值 如果 key 不存在,返回 nil。 如果key 储存的值不是字符串类型,返回一个错误。 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.get(keyname) return None if not result else bytes.decode(result) def delete(self, keyname=None): """ 删除已存在的键。不存在的 key 会被忽略 return: 被删除 key 的数量 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.delete(keyname) @wraps_set_expire def append(self, keyname=None, value=None): """ 为指定的 keyname 追加值 如果 keyname 已经存在并且是一个字符串, APPEND 命令将 value 追加到 keyname 原来的值的末尾。 如果 keyname 不存在, APPEND 就简单地将给定 keyname 设为 value ,就像执行 SET keyname value 一样 return: 追加指定值之后, keyname 中字符串的长度 """ if not keyname or value is None: return None keyname = self.key_make(keyname.strip()) if isinstance(value, str): value = value.strip() else: value = str(value) return self.obj_redis.append(keyname, value) @wraps_set_expire def incr(self, keyname=None, expire=None): """ 将 keyname 中储存的数字值增一。 如果 keyname 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。 return: 执行 INCR 命令之后 key 的值 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.incr(keyname, 1) @wraps_set_expire def incrBy(self, keyname=None, amount=1): """ 将 keyname 中储存的数字加上指定的增量值。 如果 keyname 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。 return: 加上指定的增量值之后, key 的值 """ if not keyname or not amount: return None keyname = self.key_make(keyname.strip()) if isinstance(amount, int): amount = max(0, amount) else: amount = 1 return self.obj_redis.incrby(keyname, amount) @wraps_set_expire def decr(self, keyname=None): """ 将 key 中储存的数字值减一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。 return: 执行命令之后 key 的值 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.decr(keyname, 1) @wraps_set_expire def decrBy(self, keyname=None, amount=1): """ 将 keyname 所储存的值减去指定的减量值。 如果 keyname 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY 操作。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内 """ if not keyname or not amount: return None keyname = self.key_make(keyname.strip()) amount = int(amount) return self.obj_redis.decr(keyname, amount) # -------------------------------------------------------- # Hash 哈希 # 一个string类型的field和value的映射表,hash特别适合用于存储对象 # 每个 hash 可以存储 232 - 1 键值对(40多亿) # -------------------------------------------------------- @wraps_set_expire def hSet(self, keyname=None, key=None, value=None): """ 从哈希名为keyname中添加key1->value1 将哈希表key中的域field的值设为value。-ok -ok 如果key不存在,一个新的哈希表被创建并进行hset操作。 如果域field已经存在于哈希表中,旧值将被覆盖。 错误则 返回 FALSE 如果字段是哈希表中的一个新建字段,并且值设置成功,返回 1 。 如果哈希表中域字段已经存在且旧值已被新值覆盖,返回 0 。 """ if not keyname or not key or value is None: return None keyname = self.key_make(keyname.strip()) key = key.strip() return self.obj_redis.hset(keyname, key, value) @wraps_set_expire def hGet(self, keyname=None, key=None): """ 获取存储在哈希表中指定字段的值 返回给定字段的值。如果给定的字段或 key 不存在时,返回 None """ if not keyname or not key: return None keyname = self.key_make(keyname.strip()) key = key.strip() result = self.obj_redis.hget(keyname, key) if not result: return None # bytes to str return bytes.decode(result) @wraps_set_expire def hLen(self, keyname=None): """ 获取哈希表中字段的数量 哈希表中字段的数量。 当 keyname 不存在时,返回 0 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.hlen(keyname) @wraps_set_expire def hKeys(self, keyname=None): """ 获取哈希表中的所有域(field) 包含哈希表中所有域(field)列表。 当 key 不存在时,返回一个空列表 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.hkeys(keyname) if not result: return None # bytes to str ret_list = list() for v in result: ret_list.append(bytes.decode(v)) return ret_list @wraps_set_expire def hVals(self, keyname=None): """ 哈希表所有域(field)的值 包含哈希表中所有域(field)值的列表。 当 key 不存在时,返回一个空表 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.hvals(keyname) if not result: return None # bytes to str ret_list = list() for v in result: ret_list.append(bytes.decode(v)) return ret_list @wraps_set_expire def hGetAll(self, keyname=None): """ 获取在哈希表中指定 keyname 的所有字段和值 返回哈希表中,所有的字段和值 在返回值里,紧跟每个字段名(field name)之后是字段的值(value), 所以返回值的长度是哈希表大小的两倍。 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.hgetall(keyname) if not result: return None # bytes to str ret_dict = dict() for k, v in result.items(): ret_dict[bytes.decode(k)] = bytes.decode(v) return ret_dict def hExists(self, keyname=None, key=None): """ 查看哈希表 keyname 中,是否存在键名为key的字段 ashname含有给定字段key,返回 True。 keyname不存在 或 key 不存在,返回 False """ if not keyname or key is None: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.hexists(keyname, key) def hDel(self, keyname=None, *keys): """ 删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略 返回值 被成功删除字段的数量,不包括被忽略的字段 keyname 或 key 不存在则返回 0 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.hdel(keyname, *keys) # -------------------------------------------------------- # List 列表, 左(Left)为头部,右(Right)为尾部 # 一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素) # -------------------------------------------------------- @wraps_set_expire def lPush(self, keyname=None, *values): """ 将一个或多个值插入到列表头部, 返回操作后列表的长度。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。 注意:在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value 值 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.lpush(keyname, *values) @wraps_set_expire def lPop(self, keyname=None): """ 弹出队列头部元素,移除并返回列表的第一个元素。 返回列表的第一个元素。 当列表 key 不存在时,返回 None """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.lpop(keyname) @wraps_set_expire def rPush(self, keyname=None, *values): """ 将一个或多个值插入到列表的尾部(最右边), 返回操作后列表的长度。 如果列表不存在,一个空列表会被创建并执行 RPUSH 操作。 当列表存在但不是列表类型时,返回一个错误。 注意:在 Redis 2.4 版本以前的 RPUSH 命令,都只接受单个 value 值。 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.rpush(keyname, *values) @wraps_set_expire def rPop(self, keyname=None): """ 移除并获取列表最后一个元素 返回列表的最后一个元素。 当列表不存在时,返回 None """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.rpop(keyname) if not result: return None # bytes to str return bytes.decode(result) @wraps_set_expire def lLen(self, keyname=None): """ 获取列表长度 如果列表 key 不存在,则 key 被解释为一个空列表,返回 0 如果 key 不是列表类型,返回一个错误 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.llen(keyname) @wraps_set_expire def lTrim(self, keyname=None, start=0, end=-1): """ 让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除 下标 0 表示列表的第一个元素,1 表示列表的第二个元素 -1 表示列表的最后一个元素,-2 表示列表的倒数第二个元素 返回 True """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.ltrim(keyname, start, end) @wraps_set_expire def lGetRange(self, keyname=None, start=0, end=-1): """ 返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定 下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素 返回一个列表,包含指定区间内的元素 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.lrange(keyname, start, end) if not result: return None # bytes to str ret_list = list() for v in result: ret_list.append(bytes.decode(v)) return ret_list @wraps_set_expire def lRemove(self, keyname=None, value=None, count=1): """ 根据参数 COUNT 的值,移除列表中与参数 VALUE 相等的元素。 COUNT 的值可以是以下几种: count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。 count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。 count = 0 : 移除表中所有与 VALUE 相等的值。 返回被移除元素的数量。 列表或元素不存在时返回 0 """ if not keyname or value is None: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.lrem(keyname, count, value) # -------------------------------------------------------- # Set 无序集合 # Set 是 String 类型的无序集合。集合成员是唯一的。 # 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1) # 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员) # -------------------------------------------------------- @wraps_set_expire def sAdd(self, keyname=None, *values): """ 将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。 假如集合 key 不存在,则创建一个只包含添加的元素作成员的集合。 当集合 key 不是集合类型时,返回一个错误。 注意:在Redis2.4版本以前, SADD 只接受单个成员值。 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.sadd(keyname, *values) @wraps_set_expire def sCard(self, keyname=None): """ 获取集合key中元素的数量 集合的数量。 当集合 key 不存在时,返回 0 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.scard(keyname) def sDiff(self, keyname=None, *keys): """ 差集 返回所给key列表想减后的集合,相当于求差集 不存在的集合 key 将视为空集。 请注意顺序是前面的集合,减去后面的集合,求差集 返回包含差集成员的列表 """ if not keyname: return None other_keys = list() for k in keys: other_keys.append(self.key_make(k)) result = self.obj_redis.sdiff(keyname, *other_keys) if not result: return None # bytes to str ret_set = set() for v in result: ret_set.add(bytes.decode(v)) return ret_set @wraps_set_expire def sDiffStore(self, store_key=None, key=None, *keys): """ 差集并存储 给定所有集合的差集并存储在 store_key 中 将给定集合之间的差集存储在指定的集合中。 如果指定的集合 key 已存在,则会被覆盖 返回store_key结果集中的元素数量 """ if not store_key or not key: return None store_key = self.key_make(store_key.strip()) key = self.key_make(key.strip()) other_keys = list() for k in keys: other_keys.append(self.key_make(k)) return self.obj_redis.sdiffstore(store_key, key, *other_keys) def sInter(self, keyname=None, *keys): """ 交集 返回给定所有给定集合的交集。 不存在的集合 key 被视为空集。 当给定集合当中有一个空集或key不存在时,结果也为空集(根据集合运算定律)。 """ if not keyname: return None keyname = self.key_make(keyname.strip()) other_keys = list() for k in keys: other_keys.append(self.key_make(k)) result = self.obj_redis.sinter(keyname, *other_keys) if not result: return None # bytes to str ret_set = set() for v in result: ret_set.add(bytes.decode(v)) return ret_set @wraps_set_expire def sInterStore(self, store_key=None, key=None, *keys): """ 交集并存储 将给定集合之间的交集存储在指定的集合store_key中。 如果指定的集合已经存在,则将其覆盖 返回store_key存储交集的集合的元素数量 """ if not store_key or not key: return None store_key = self.key_make(store_key.strip()) key = self.key_make(key.strip()) other_keys = list() for k in keys: other_keys.append(self.key_make(k)) return self.obj_redis.sinterstore(store_key, key, *other_keys) def sUnion(self, keyname=None, *keys): """ 并集 所给key列表所有的值,相当于求并集 给定集合的并集。不存在的集合 key 被视为空集。 返回并集成员的列表 """ if not keyname: return None keyname = self.key_make(keyname.strip()) other_keys = list() for k in keys: other_keys.append(self.key_make(k)) result = self.obj_redis.sunion(keyname, *other_keys) if not result: return None # bytes to str ret_set = set() for v in result: ret_set.add(bytes.decode(v)) return ret_set @wraps_set_expire def sUnionStore(self, store_key=None, key=None, *keys): """ 并集存储 将给定集合的并集存储在指定的集合 store_key 中。 如果 store_key 已经存在,则将其覆盖 返回store_key存储并集的集合的元素数量 """ if not store_key or not key: return None store_key = self.key_make(store_key.strip()) key = self.key_make(key.strip()) other_keys = list() for k in keys: other_keys.append(self.key_make(k)) return self.obj_redis.sunionstore(store_key, key, *other_keys) @wraps_set_expire def sIsMember(self, keyname=None, value=None): """ 判断成员元素是否是集合的成员 如果成员元素是集合的成员,返回 True 如果成员元素不是集合的成员,或 key 不存在,返回 False """ if not keyname or value is None: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.sismember(keyname, value) @wraps_set_expire def sMembers(self, keyname=None): """ 返回集合中的所有的成员。 不存在的集合 key 被视为空集合 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.smembers(keyname) if not result: return None # bytes to str ret_set = set() for v in result: ret_set.add(bytes.decode(v)) return ret_set @wraps_set_expire def sRem(self, keyname=None, *values): """ 删除该数组中对应的值 移除集合中的一个或多个成员元素,不存在的成员元素会被忽略。 当 key 不是集合类型,返回一个错误。 在 Redis 2.4 版本以前, SREM 只接受单个成员值。 返回被成功移除的元素的数量,不包括被忽略的元素 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.srem(keyname, *values) @wraps_set_expire def sPop(self, keyname=None): """ 移除并返回集合中的一个随机元素 将随机元素从集合中移除并返回 移除的随机元素。 当集合不存在或是空集时,返回 None """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.spop(keyname) # bytes to str return None if not result else bytes.decode(result) @wraps_set_expire def sRandMember(self, keyname=None, count=1): """ 返回集合中的随机元素,而不对集合进行任何改动 从 Redis 2.6 版本开始, Srandmember 命令接受可选的 count 参数: 如果 count 为正数,且小于集合基数, 返回一个包含 count 个元素的数组,数组中的元素各不相同。 如果 count 大于等于集合基数,那么返回整个集合。 如果 count 为负数,返回一个数组, 数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。 返回:随机个数的元素列表 """ if not keyname: return None keyname = self.key_make(keyname.strip()) if isinstance(count, int): count = max(0, count) else: count = 1 result = self.obj_redis.srandmember(keyname, count) if not result: return None # bytes to str ret_list = list() for v in result: ret_list.append(bytes.decode(v)) return ret_list # -------------------------------------------------------- # Zset( sorted set ) 有序集合 # 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员 # 有序集合的成员是唯一的,但分数(score)却可以重复 # 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1) # 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员) # -------------------------------------------------------- @wraps_set_expire def zAdd(self, keyname=None, **kwargs): """ 将一个或多个成员元素及其分数值加入到有序集当中。 如果某个成员已经是有序集的成员,那么更新这个成员的分数值, 并通过重新插入这个成员元素,来保证该成员在正确的位置上。 如果有序集合 key 不存在,则创建一个空的有序集并执行 ZADD 操作。 当 key 存在但不是有序集类型时,返回一个错误。 返回: 被成功添加的新成员的数量,不包括那些被更新的、已经存在的成员。 注意: 在 Redis 2.4 版本以前, ZADD 每次只能添加一个元素 **kwargs: name1=score1, name2=score2 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.zadd(keyname, **kwargs) def zRangeByScore(self, keyname=None, min=None, max=None, withscores=False): """ 分数值正序 返回有序集中指定分数区间内的所有的成员。 有序集成员按分数值递减(从大到小)的次序排列。 具有相同分数值的成员按字典序的逆序(reverse lexicographical order )排列 返回; 指定区间内,带有分数值(可选)的有序集成员的列表 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.zrangebyscore( keyname, min, max, withscores=withscores) if not result: return None # bytes to str if not withscores: # return list zret = list() for field in result: zret.append(bytes.decode(field)) else: # return dict zret = list() for field, score in result: zret.append((bytes.decode(field), score)) zret = dict(zret) return zret def zRevRangeByScore(self, keyname=None, max=None, min=None, withscores=False): """ 分数值逆序 返回有序集中指定分数区间内的所有的成员。 有序集成员按分数值递减(从大到小)的次序排列。 具有相同分数值的成员按字典序的逆序(reverse lexicographical order )排列。 返回; 指定区间内,带有分数值(可选)的有序集成员的列表 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.zrevrangebyscore( keyname, max, min, withscores=withscores) if not result: return None # bytes to str if not withscores: # return list zret = list() for field in result: zret.append(bytes.decode(field)) else: # return dict zret = list() for field, score in result: zret.append((bytes.decode(field), score)) zret = dict(zret) return zret def zRank(self, keyname=None, member=None): """ 排名正序 返回有序集中指定成员的排名。 其中有序集成员按分数值递增(从小到大)顺序排列 如果成员是有序集 key 的成员,返回 member 的排名。 如果成员不是有序集 key 的成员,返回 None 。 """ if not keyname or member is None: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.zrank(keyname, member) def zRevRank(self, keyname=None, member=None): """ 排名逆序 返回有序集中指定成员的排名。 其中有序集成员按分数值递减(从大到小)排序 如果成员是有序集 key 的成员,返回 member 的排名。 如果成员不是有序集 key 的成员,返回 None 。 """ if not keyname or member is None: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.zrevrank(keyname, member) def zRange(self, keyname=None, start=None, end=None, withscores=False): """ 位置正序 返回有序集中,指定区间内的成员。 其中成员的位置按分数值递增(从小到大)来排序。 具有相同分数值的成员按字典序(lexicographical order )来排列 返回指定区间内,带有分数值(可选)的有序集成员的列表 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.zrange( keyname, start, end, withscores=withscores) if not result: return None # bytes to str if not withscores: # return list zret = list() for field in result: zret.append(bytes.decode(field)) else: # return dict zret = list() for field, score in result: zret.append((bytes.decode(field), score)) zret = dict(zret) return zret def zRevrange(self, keyname=None, start=None, end=None, withscores=False): """ 位置逆序 返回有序集中,指定区间内的成员。 其中成员的位置按分数值递减(从大到小)来排列。 具有相同分数值的成员按字典序的逆序(reverse lexicographical order)排列 返回指定区间内,带有分数值(可选)的有序集成员的列表 """ if not keyname: return None keyname = self.key_make(keyname.strip()) result = self.obj_redis.zrevrange( keyname, start, end, withscores=withscores) if not result: return None # bytes to str if not withscores: # return list zret = list() for field in result: zret.append(bytes.decode(field)) else: # return dict zret = list() for field, score in result: zret.append((bytes.decode(field), score)) zret = dict(zret) return zret def zRem(self, keyname, *member): """ 移除有序集中的一个或多个成员,不存在的成员将被忽略。 当 key 存在但不是有序集类型时,返回一个错误。 注意: 在 Redis 2.4 版本以前, ZREM 每次只能删除一个元素。 返回被成功移除的成员的数量,不包括被忽略的成员0 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.zrem(keyname, *member) def zRemRangeByRank(self, keyname=None, min=None, max=None): """ 删除正序 移除有序集中,指定排名(rank)区间内的所有成员 返回被移除成员的数量 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.zremrangebyrank(keyname, min, max) def zRemrangebyscore(self, keyname=None, min=None, max=None): """ 删除正序 移除有序集中,指定分数(score)区间内的所有成员 返回被移除成员的数量 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.zremrangebyscore(keyname, min, max) def zCard(self, keyname=None): """ 计算集合中元素的数量 当 key 存在且是有序集类型时,返回有序集的基数。 当 key 不存在时,返回 0 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.zcard(keyname) def zCount(self, keyname=None, min=None, max=None): """ 计算有序集合中指定分数区间的成员数量 返回分数值在 min 和 max 之间的成员的数量 """ if not keyname: return None keyname = self.key_make(keyname.strip()) return self.obj_redis.zcount(keyname, min, max)
# 批量赋值 data = {'name1':'Durant', 'name2':'James'} redis.mset(data) # key 若存在 value 做加法, 如果 key 不存在则直接创建 redis.incr('age', 1) redis.incr('age', 1) # 与 incr 相反, 给 value 做减法, key 不存在则直接创建 redis.decr('age',1) # 在 value 的后面追加内容 redis.append('name1', ' Hello') # 返回 value 指定的 start index / end index 之间的内容. 只支持 string 类型 print(redis.substr('name1',2, -2)) # getrange() 也效果对于字符串 等同 # ------- List 相关 -------- # 在指定的 key 的list 末尾增加元素. 如果 key 不存在则直接创建 redis.rpush('list', 1, 2, 3) # 与 rpush 相反, 在表的开头插入元素 redis.lpush('list', -1, 0)
print() print(redis.set('emotion', 'smile')) redis.set('name', 'Leo') redis.set('age', '19') print(redis.get('emotion')) print(redis.getset('emotion', 'humour')) print(redis.mget(['emotion', 'name', 'age'])) print(redis.setnx('newname', 'James')) # 不存在键才更新 print(redis.setex('country', 1, 'china')) redis.setrange('name', 3, '2019') print(redis.get('name')) print(redis.mset({'name1': 'Modric', 'name2': 'Van Dik'})) print(redis.msetnx({'name3': 'Salah', 'name4': 'Mane'})) #键均不存在才批量更新 print(redis.incr('age', 1)) print(redis.decr('age', 1)) print(redis.append('name', 'ooo')) print(redis.substr('name', 1, 4)) print(redis.getrange('name', 0, 3)) # 4.列表操作 print(redis.rpush('list', 1, 2, 3, 4, 5)) print(redis.lpush('list', 0)) print(redis.llen('list')) print(redis.lrange('list', 1, 3)) print(redis.ltrim('list', 1, 3)) print(redis.lindex('list', 1)) print(redis.lset('list', 1, 666)) print(redis.lrem('list', 1, 1)) print(redis.lpop('list')) print(redis.rpop('list')) #print(redis.blpop('list'))
unix://[:password]@/path/to/socket.sock?db=db # Redis Unix Socket 连接 \String操作 方法 作用 示例 示例结果 set(name, value) 给name赋值为value redis.set('name', 'Bob') True get(name) 返回数据库中key为name的string的value redis.get('name') b'Bob' getset(name, value) 给数据库中key为name的string赋予值value并返回上次的value redis.getset('name', 'Mike') b'Bob' mget(keys, *args) 返回多个key对应的value redis.mget(['name', 'nickname']) [b'Mike', b'Miker'] setnx(name, value) 如果key不存在才设置value redis.setnx('newname', 'James') 第一次运行True,第二次False setex(name, time, value) 设置可以对应的值为string类型的value,并指定此键值对应的有效期 redis.setex('name', 1, 'James') True setrange(name, offset, value) 设置指定key的value值的子字符串 redis.set('name', 'Hello') redis.setrange('name', 6, 'World') 11,修改后的字符串长度 mset(mapping) 批量赋值 redis.mset({'name1': 'Durant', 'name2': 'James'}) True msetnx(mapping) key均不存在时才批量赋值 redis.msetnx({'name3': 'Smith', 'name4': 'Curry'}) True incr(name, amount=1) key为name的value增值操作,默认1,key不存在则被创建并设为amount redis.incr('age', 1) 1,即修改后的值 decr(name, amount=1) key为name的value减值操作,默认1,key不存在则被创建并设置为-amount redis.decr('age', 1) -1,即修改后的值 append(key, value) key为name的string的值附加value redis.append('nickname', 'OK') 13,即修改后的字符串长度 substr(name, start, end=-1) 返回key为name的string的value的子串 redis.substr('name', 1, 4) b'ello' getrange(key, start, end) 获取key的value值从start到end的子字符串 redis.getrange('name', 1, 4) b'ello' # 源码 def set(self, name, value, ex=None, px=None, nx=False, xx=False): """ Set the value at key ``name`` to ``value`` ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds. ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds. ``nx`` if set to True, set the value at key ``name`` to ``value`` only if it does not exist. ``xx`` if set to True, set the value at key ``name`` to ``value`` only if it already exists. """
# print(redis.flushdb()) # 删除所有数据库所有键 # print(redis.flushall()) # 给赋值 并且返回原来的值 print(redis.getset('nickname', 'newname')) # 返回多个键值对应的value print(redis.mget(['nickname', 'name2'])) # 批量赋值 print(redis.mset({'n': 1, 'x': 2})) # 如果键不存在就设置值 否则不设置值 (只新增 不更新)第二条不起作用 print(redis.setnx('ni', 'wo')) print(redis.setnx('ni', '2')) # 如果键不存在就设置值 否则不设置值 (只新增 不更新)第二条不起作用 批量 print(redis.msetnx({'nix': 1, 'wox': 2})) print(redis.msetnx({'nix': 3, 'wox': 4})) # 设置键的值以及过期时间 可以新增以及更新 print(redis.setex('wo', 120, 'Lucy')) # 往value插入字符串 如果字符不存在会新建 如果位置不存在 会为空类型变成binary 如果是0就可以 print(redis.setrange('ni', 2, 'hao')) print(redis.setrange('nini', 2, 'hao')) # 增长 默认1 print(redis.incr('nix', 10)) # 减少 默认1 print(redis.decr('nix', 10)) # 值的最后位置追加 print(redis.append('nickname', 'nickname')) # 获取子串 开始 以及结束 不传就是 -1 print(redis.substr('nickname', 3)) # 获取子串 开始 以及结束 不传就是 报错 也可以是-1 print(redis.getrange('nickname', 4, 5))
redis.setnx('newname', 'heihei') # 设值饼设值有效期 redis.setex('name', 1, 'James') # 设值指定键的value 的子字符串 redis.set('name', 'Hello') redis.setrange('name', 6, 'World') # 批量赋值 redis.mset({'name1': 'Smith', 'name2': 'Curry'}) # 键都不存在时才批量赋值 redis.msetnx({'name3': 'Smith', 'name4': 'Curry'}) # 增操作 redis.incr('age', 1) # 减操作 不存在 则设置为-1 redis.decr('age', 1) # 追加 返回长度 redis.append('name', 'OK') # 截取子串 默认截取到末尾 redis.substr('name', 1, end=-1) redis.getrange('name', 1, 4) """列表操作""" # name-list 尾部添加 redis.rpush('list', 1, 2, 3) # name-list 头部添加 redis.lpush('list', 0) # 返回长度 以上三个都是返回列表大小 redis.llen('list') # 取范围 返回列表 redis.lrange('list', 1, 3) # 截取,保留范围内容 返回bool redis.ltrim('list', 1, 3) # 取索引元素 返回元素
class Analyzer(Thread): def __init__(self, parent_pid): """ Initialize the Analyzer """ super(Analyzer, self).__init__() self.redis_conn = StrictRedis( unix_socket_path=settings.REDIS_SOCKET_PATH) self.daemon = True self.parent_pid = parent_pid self.current_pid = getpid() self.anomalous_metrics = Manager().list() self.exceptions_q = Queue() self.anomaly_breakdown_q = Queue() def check_if_parent_is_alive(self): """ Self explanatory """ try: kill(self.current_pid, 0) kill(self.parent_pid, 0) except: exit(0) def send_graphite_metric(self, name, value): if settings.GRAPHITE_HOST != '': sock = socket.socket() sock.connect((settings.GRAPHITE_HOST, settings.CARBON_PORT)) sock.sendall('%s %s %i\n' % (name, value, time())) sock.close() return True return False def spin_process(self, i, unique_metrics): """ Assign a bunch of metrics for a process to analyze. """ # Discover assigned metrics keys_per_processor = int( ceil( float(len(unique_metrics)) / float(settings.ANALYZER_PROCESSES))) if i == settings.ANALYZER_PROCESSES: assigned_max = len(unique_metrics) else: assigned_max = i * keys_per_processor assigned_min = assigned_max - keys_per_processor assigned_keys = range(assigned_min, assigned_max) # Compile assigned metrics assigned_metrics = [unique_metrics[index] for index in assigned_keys] # Check if this process is unnecessary if len(assigned_metrics) == 0: return # Multi get series raw_assigned = self.redis_conn.mget(assigned_metrics) # Make process-specific dicts exceptions = defaultdict(int) anomaly_breakdown = defaultdict(int) # Distill timeseries strings into lists for i, metric_name in enumerate(assigned_metrics): self.check_if_parent_is_alive() try: raw_series = raw_assigned[i] unpacker = Unpacker(use_list=False) unpacker.feed(raw_series) timeseries = list(unpacker) anomalous, ensemble, datapoint = run_selected_algorithm( timeseries, metric_name) # If it's anomalous, add it to list if anomalous: base_name = metric_name.replace(settings.FULL_NAMESPACE, '', 1) metric = [datapoint, base_name] self.anomalous_metrics.append(metric) # Get the anomaly breakdown - who returned True? for index, value in enumerate(ensemble): if value: algorithm = settings.ALGORITHMS[index] anomaly_breakdown[algorithm] += 1 # Add anomaly datapoint to redis key = ''.join((settings.ANOMALY_NAMESPACE, base_name)) anomaly_full_uniques = settings.ANOMALY_NAMESPACE + 'unique_metrics' self.redis_conn.append(key, packb(datapoint)) self.redis_conn.sadd(anomaly_full_uniques, key) # It could have been deleted by the Roomba except TypeError: exceptions['DeletedByRoomba'] += 1 except TooShort: exceptions['TooShort'] += 1 except Stale: exceptions['Stale'] += 1 except Boring: exceptions['Boring'] += 1 except: exceptions['Other'] += 1 logger.info(traceback.format_exc()) # Add values to the queue so the parent process can collate for key, value in anomaly_breakdown.items(): self.anomaly_breakdown_q.put((key, value)) for key, value in exceptions.items(): self.exceptions_q.put((key, value)) def run(self): """ Called when the process intializes. """ while 1: now = time() # Make sure Redis is up try: self.redis_conn.ping() except: logger.error( 'skyline can\'t connect to redis at socket path %s' % settings.REDIS_SOCKET_PATH) sleep(10) self.redis_conn = StrictRedis( unix_socket_path=settings.REDIS_SOCKET_PATH) continue # Discover unique metrics unique_metrics = list( self.redis_conn.smembers(settings.FULL_NAMESPACE + 'unique_metrics')) if len(unique_metrics) == 0: logger.info( 'no metrics in redis. try adding some - see README') sleep(10) continue # Spawn processes pids = [] for i in range(1, settings.ANALYZER_PROCESSES + 1): if i > len(unique_metrics): logger.info( 'WARNING: skyline is set for more cores than needed.') break p = Process(target=self.spin_process, args=(i, unique_metrics)) pids.append(p) p.start() # Send wait signal to zombie processes for p in pids: p.join() # Grab data from the queue and populate dictionaries exceptions = dict() anomaly_breakdown = dict() while 1: try: key, value = self.anomaly_breakdown_q.get_nowait() if key not in anomaly_breakdown.keys(): anomaly_breakdown[key] = value else: anomaly_breakdown[key] += value except Empty: break while 1: try: key, value = self.exceptions_q.get_nowait() if key not in exceptions.keys(): exceptions[key] = value else: exceptions[key] += value except Empty: break # Send alerts if settings.ENABLE_ALERTS: for alert in settings.ALERTS: for metric in self.anomalous_metrics: if alert[0] in metric[1]: cache_key = 'last_alert.%s.%s' % (alert[1], metric[1]) try: last_alert = self.redis_conn.get(cache_key) if not last_alert: self.redis_conn.setex( cache_key, alert[2], packb(metric[0])) trigger_alert(alert, metric) except Exception as e: logger.error("couldn't send alert: %s" % e) # Write anomalous_metrics to static webapp directory filename = path.abspath( path.join(path.dirname(__file__), '..', settings.ANOMALY_DUMP)) with open(filename, 'w') as fh: # Make it JSONP with a handle_data() function anomalous_metrics = list(self.anomalous_metrics) anomalous_metrics.sort(key=operator.itemgetter(1)) fh.write('handle_data(%s)' % anomalous_metrics) # Log progress logger.info('seconds to run :: %.2f' % (time() - now)) logger.info('total metrics :: %d' % len(unique_metrics)) logger.info('total analyzed :: %d' % (len(unique_metrics) - sum(exceptions.values()))) logger.info('total anomalies :: %d' % len(self.anomalous_metrics)) logger.info('exception stats :: %s' % exceptions) logger.info('anomaly breakdown :: %s' % anomaly_breakdown) # Log to Graphite # self.send_graphite_metric('skyline.analyzer.run_time', '%.2f' % (time() - now)) # self.send_graphite_metric('skyline.analyzer.total_analyzed', '%.2f' % (len(unique_metrics) - sum(exceptions.values()))) # Check canary metric raw_series = self.redis_conn.get(settings.FULL_NAMESPACE + settings.CANARY_METRIC) if raw_series is not None: unpacker = Unpacker(use_list=False) unpacker.feed(raw_series) timeseries = list(unpacker) time_human = (timeseries[-1][0] - timeseries[0][0]) / 3600 projected = 24 * (time() - now) / time_human logger.info('canary duration :: %.2f' % time_human) self.send_graphite_metric('skyline.analyzer.duration', '%.2f' % time_human) self.send_graphite_metric('skyline.analyzer.projected', '%.2f' % projected) # Reset counters self.anomalous_metrics[:] = [] # Sleep if it went too fast if time() - now < 5: logger.info('sleeping due to low run time...') sleep(10)