redis
What is redis?
Redis is an open source (BSD licensed), in-memory data structure store, used as database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs and geospatial indexes with radius queries. Redis has built-in replication, Lua scripting, LRU eviction, transactions and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster.
安装Redis
安装环境centos6.5 yum install redis redis-server看是否能正常进入服务 chkconfig --list |grep redis 检查redis是否开启自动启动 chkconfig redis on 开机自动启动 service redis start 启动服务 redis-cli 连接redis
python连接redis
1、操作模式
redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。
r = redis.Redis(host="localhost",port=6379) #测试 r.set('foo','bar') print(r.get('foo'))
2、连接池
redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。
pool = redis.ConnectionPool(host = "localhost",port = 6379) conn = redis.Redis(connection_pool=pool) #测试 conn.set('foo','bar') print(conn.get('foo'))
操作
1. String操作
redis中的String在在内存中按照一个name对应一个value来存储。如图:
set(name, value, ex=None, px=None, nx=False, xx=False)
在Redis中设置值,默认,不存在则创建,存在则修改 参数: ex,过期时间(秒) px,过期时间(毫秒) nx,如果设置为True,则只有name不存在时,当前set操作才执行 xx,如果设置为True,则只有name存在时,当前set操作才执行
官方解释:Since the SET command options can replace SETNX, SETEX, PSETEX, it is possible that in future versions of Redis these three commands will be deprecated and finally removed.
SETNX,SETEX,PSETEX这里我们就不说了,下面我们看下set的具体用法
pool = redis.ConnectionPool(host = "localhost",port = 6379) conn = redis.Redis(connection_pool=pool) conn.set('foo','bar') #设置值 print(conn.get('foo')) #获取值 conn.set('foo','bar_test') #如果key存在,修改值 print(conn.get('foo')) conn.set('hello','world',ex=5) #设置过期时间 print(conn.get('hello')) print(conn.get('foo')) conn.set('foo','bar',nx = True) #当key存在时,set不执行,相当于插入 print(conn.get('foo')) print(conn.get('foo')) conn.set('foo','bar',xx = True) #当key存在时,set不执行,相当于修改 print(conn.get('foo'))
mset(*args, **kwargs)
conn.mset(k1='v1',k2 = 'v2') #批量设置 print(conn.mget('k1','k2')) #批量获取 conn.mset({'k3':'v3','k4':'v4'}) #批量设置另外一种方法 print(conn.mget(['k3','k4'])) #批量获取另外一种方法
getset(name, value)
Atomically sets key
to value
and returns the old value stored at key
. Returns an error when key
exists but does not hold a string value.
print(conn.getset('k5','hello')) print(conn.get('k5'))
setrange(name, offset, value)
Overwrites part of the string stored at key, starting at the specified offset, for the entire length of value. If the offset is larger than the current length of the string at key, the string is padded with zero-bytes to make offset fit. Non-existing keys are considered as empty strings, so this command will make sure it holds a string large enough to be able to set value at offset.
Note that the maximum offset that you can set is 229 -1 (536870911), as Redis Strings are limited to 512 megabytes. If you need to grow beyond this size, you can use multiple keys.
print(conn.get('k2')) print(conn.setrange('k2',6,'world')) #根据offest修改值,如果offset的长度超过value本身的长度,则以0填充,不存在的key,就当成空字符串 print(conn.get('k2'))
getrange(key, start, end)
Returns the substring of the string value stored at key
, determined by the offsets start
and end
(both are inclusive). Negative offsets can be used in order to provide an offset starting from the end of the string. So -1 means the last character, -2 the penultimate and so forth.
The function handles out of range requests by limiting the resulting range to the actual length of the string.
print(conn.get('k2')) print(conn.getrange('k2',6,10)) print(conn.getrange('k2',-5,-1)) #-1最后,相当于列表,字符串的切片
setbit(name, offset, value)
print(ord('a')) #字符对应ascii 'a' - 97 二进制01100001 conn.set('k1','a') #值设置为a conn.setbit('k1',6,1) #这个是按1个byte来算,第0位就是0,第1位就是1 conn.setbit('k1',7,0) # print(conn.get('k1').decode()) #结果为b
说一种setbit的应用场景,假设我们要保存上亿用户的在线数据以及统计在线用户数,简单的办法是在数据库中新增一个字段来存储在线用户,这样会当数据量大时会导致查找慢
127.0.0.1:6379> setbit k20 1000 1 #可以把数据库的id与设置的位偏移量对应 (integer) 0 127.0.0.1:6379> bitcount k20 #统计在线用户数 (integer) 1 127.0.0.1:6379> setbit k20 10000 1 (integer) 0 127.0.0.1:6379> bitcount k20 (integer) 2 #我们来算下1MB能存储多少在线用户数,1M=1024KB = 1024*1024B=1024*1024*8 = 8388608 1MB就能存下800多万数据,可以说是非常省空间了
getbit(name, offset)
print(conn.getbit('k1',1)) print(conn.getbit('k1',3))
还有其它的一些函数,incr(整数自增1),decr(整数自减1),incrby(以指定的数自增),decrby(以指定的数自减),incrbyfloat(以指定的数自增float),strlen(得到value的长度),append(添加一个值到key)
还有一些关于位操作的函数详见https://redis.io/commands#string
2. Hash操作
hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强的数据 , redis中Hash在内存中的存储格式如下图:
下面对函数的解释均参照redis官网
HSET(key,field,value)
Sets field
in the hash stored at key
to value
. If key
does not exist, a new key holding a hash is created. If field
already exists in the hash, it is overwritten.
Return value
1
iffield
is a new field in the hash andvalue
was set.0
iffield
already exists in the hash and the value was updated.
HGET(key,field)
Returns the value associated with field
in the hash stored at key
.
Return value
Bulk string reply: the value associated with field
, or nil
when field
is not present in the hash or key
does not exist.
print(conn.hset('names','name1','lxj')) #当key不存在时,新建key,当field不存在,储存field和value。并且返回1 print(conn.hset('names','name2','sx')) print(conn.hget('names','name1')) #当field存在,返回对应的值 #结果: 1 1 b'lxj' print(conn.hset('names','name1','LXJ')) #当key存在时,field存在,修改field对应value。并且返回0 print(conn.hget('names','name1')) #当field存在,返回对应的值 print(conn.hget('names','name3')) #当field不存在,返回None print(conn.hget('k1','name3')) #当key不存在,返回None #结果: 0 b'LXJ' None None
HSETNX(key,field,value)
Sets field
in the hash stored at key
to value
, only if field
does not yet exist. If key
does not exist, a new key holding a hash is created. If field
already exists, this operation has no effect.
Return value
Integer reply, specifically:
1
iffield
is a new field in the hash andvalue
was set.0
iffield
already exists in the hash and no operation was performed.
print(conn.hsetnx('names','name1','test')) #当field存在时,不操作,返回0 print(conn.hset('names','name3','ss')) #当field不存在时,储存field和value,相当于添加 print(conn.hget('names','name3')) #当field存在,返回对应的值 print(conn.hget('names','name1')) #当field存在,返回对应的值 #结果 0 1 b'ss' b'LXJ'
批量设置值和获取值(hmset,hmget)
hmset(key, mapping) 批量设置值
hmget(name, keys, *args) 批量获取值
print(conn.hmset('names',{'name2':'SX','name5':'abc'})) #当field不存在,储存field和value。存在,覆盖field对应的值,返回True print(conn.hmget('names','name1',*['name1','name3'])) #当field存在,返回对应的值,返回值列表 print(conn.hmget('names',['name1','name3'])) #第二种写法 print(conn.hmget('names','name1','name3','name5')) #第三种写法 #结果 True [b'LXJ', b'LXJ', b'ss'] [b'LXJ', b'ss'] [b'LXJ', b'ss', b'abc']
HVALS(key)
Returns all values in the hash stored at key
.
HKEYS(key)
Returns all field names in the hash stored at key
.
print(conn.hkeys('names')) print(conn.hvals('names')) #结果 [b'name1', b'name2', b'name3', b'name5'] [b'LXJ', b'SX', b'ss', b'abc']
HGETALL(key)
Returns all fields and values of the hash stored at key
. In the returned value, every field name is followed by its value, so the length of the reply is twice the size of the hash.
print(conn.hgetall('names')) #结果 {b'name3': b'ss', b'name1': b'LXJ', b'name5': b'abc', b'name2': b'SX'}
HLEN(key)
Returns the number of fields contained in the hash stored at key
.
HSTRLEN(key,field)
Returns the string length of the value associated with field
in the hash stored at key
. If the key
or the field
do not exist, 0 is returned.
print(conn.hlen('names')) #返回key下面所有field的数目 print(conn.hstrlen('names','name3')) #返回key下field对应的value长度 结果: 4 2
HEXISTS(key,field)
Returns if field
is an existing field in the hash stored at key
.
HDEL key field [field ...]
Removes the specified fields from the hash stored at key
. Specified fields that do not exist within this hash are ignored. If key
does not exist, it is treated as an empty hash and this command returns 0
.
print(conn.hexists('names','name2')) #判断field是否在key中,返回True or False print(conn.hexists('names','name4')) 结果: True False
HINCRBY(key,field,increment)
Increments the number stored at field
in the hash stored at key
byincrement
. If key
does not exist, a new key holding a hash is created. If field
does not exist the value is set to 0
before the operation is performed.
The range of values supported by HINCRBY is limited to 64 bit signed integers.
HINCRBYFLOAT(key,field,increment)
Increment the specified field
of a hash stored at key
, and representing a floating point number, by the specified increment
. If the increment value is negative, the result is to have the hash field value decremented instead of incremented. If the field does not exist, it is set to 0
before performing the operation. An error is returned if one of the following conditions occur:
- The field contains a value of the wrong type (not a string).
- The current field content or the specified increment are not parsable as a double precision floating point number.
The exact behavior of this command is identical to the one of the INCRBYFLOAT command, please refer to the documentation of INCRBYFLOAT for further information.
conn.hset('age','lxj','20') print(conn.hincrby('age','ss',3)) #如果key或则field不存在,则创建。使用该函数value必须为整数,返回自增后的数 print(conn.hincrby('age','ss',-3)) print(conn.hincrby('age','lxj',3)) 结果: 3 0 23 conn.hset('age','lxj','20.1') print(conn.hincrbyfloat('age','sx1',3)) #如果key或则field不存在,则创建。使用该函数value可以为整数或则浮点数,返回自增后的数且均为浮点数 print(conn.hincrbyfloat('age','sx1',-3)) print(conn.hincrbyfloat('age','lxj',3.5)) 结果: 3.0 0.0 23.6
SCAN(cursor=0, match=None, count=None)
The SCAN command and the closely related commands SSCAN, HSCAN and ZSCAN are used in order to incrementally iterate over a collection of elements.
详见https://redis.io/commands/scan。
# 增量式迭代获取,对于数据大的数据非常有用,scan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆 # 参数: # cursor,游标(基于游标分批取获取数据) # match,匹配指定key,默认None 表示所有的key # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数,官网上好像是10个 # 如: # 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None) # 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None) # ... # 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕 currsor,data = conn.scan(0,'*') #cursor游标,类似读取文本的seek,match匹配的模式,count显示几条数据 #返回的是下一次迭代的新游标和读取出来的列表数据组成的元组 print(currsor,data) currsor1,data1 = conn.scan(currsor,'*') #读取剩下的数据,直到游标为0表示数据读完 print(data1) #结果 15 [b'k8', b'names', b'k1', b'k5', b'k3', b'age', b'k2', b'k7', b'k6', b'k9'] [b'k4']
SSCAN, HSCAN and ZSCAN与scan用法一致,调用参数有些许不同,都要指定key,以hscan为例
currsor,data = conn.hscan('names',0) #cursor游标,类似读取文本的seek,简单理解就是鼠标在的位置,match匹配的模式,count显示几条数据 #返回的是下一次迭代的新游标和读取出来的列表数据组成的元组 print(currsor,data) 结果: 0 {b'name5': b'abc', b'name1': b'LXJ', b'name2': b'SX', b'name3': b'ss'}
3. list
List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:
LPUSH(key,*value)
Insert all the specified values at the head of the list stored at key
. If key
does not exist, it is created as empty list before performing the push operations. When key
holds a value that is not a list, an error is returned.
It is possible to push multiple elements using a single command call just specifying multiple arguments at the end of the command. Elements are inserted one after the other to the head of the list, from the leftmost element to the rightmost element. So for instance the command LPUSH mylist a b c
will result into a list containing c
as first element, b
as second element and a
as third element.
RPUSH (key,*value)
Insert all the specified values at the tail of the list stored at key
. If key
does not exist, it is created as empty list before performing the push operation. When key
holds a value that is not a list, an error is returned.
It is possible to push multiple elements using a single command call just specifying multiple arguments at the end of the command. Elements are inserted one after the other to the tail of the list, from the leftmost element to the rightmost element. So for instance the command RPUSH mylist a b c
will result into a list containing a
as first element, b
as second element and c
as third element.
LRANGE(key ,start, stop)
Returns the specified elements of the list stored at key
. The offsets start
and stop
are zero-based indexes, with 0
being the first element of the list (the head of the list), 1
being the next element and so on.
These offsets can also be negative numbers indicating offsets starting at the end of the list. For example, -1
is the last element of the list, -2
the penultimate, and so on.
print(conn.lpush('courses',*['python','linux','mysql'])) #lpush相当于从列表左边插入。返回列表长度 print(conn.lrange('courses',0,-1)) #相当于切片,此时我们得到的队列顺序应该是mysql - linux - python print(conn.rpush('courses',*['redis','sqlalchemy','rabbitmq'])) #lpush相当于从列表的后面(右边)插入。返回列表长度 print(conn.lrange('courses',0,-1)) #此时我们得到的队列顺序应该是:之前的列表 - redis - sqlalchemy - rabbitmq #结果; 3 [b'mysql', b'linux', b'python'] 6 [b'mysql', b'linux', b'python', b'redis', b'sqlalchemy', b'rabbitmq']
LPUSHX(key ,value)
Inserts value
at the head of the list stored at key
, only if key
already exists and holds a list. In contrary to LPUSH, no operation will be performed when key
does not yet exist.
RPUSHX(key ,value)
Inserts value
at the tail of the list stored at key
, only if key
already exists and holds a list. In contrary to RPUSH, no operation will be performed when key
does not yet exist.
print(conn.lpushx('classes','11')) #仅当key存在时插入,插入方法同lpush print(conn.lrange('classes',0,-1)) print(conn.rpush('courses','python_test')) #仅当key存在时插入,插入方法同rpush。返回列表长度 print(conn.lrange('courses',0,-1)) #结果: 0 [] 8 [b'mysql', b'linux', b'python', b'redis', b'sqlalchemy', b'rabbitmq', b'python_test', b'python_test']
LPOP(key)
Removes and returns the first element of the list stored at key
.
Return value
Bulk string reply: the value of the first element, or nil
when key
does not exist.
RPOP(key)
Removes and returns the last element of the list stored at key
.
Return value
Bulk string reply: the value of the last element, or nil
when key
does not exist.
print(conn.lrange('courses',0,-1)) print(conn.lpop('courses')) #从头部删除value,返回删除的value print(conn.rpop('courses')) #从尾部删除value,返回删除的value print(conn.lrange('courses',0,-1)) #结果: [b'mysql', b'linux', b'python', b'redis', b'sqlalchemy', b'rabbitmq', b'python_test', b'python_test'] b'mysql' b'python_test' [b'linux', b'python', b'redis', b'sqlalchemy', b'rabbitmq', b'python_test']
blpop和brpop时阻塞版的lpop和rpop
print(conn.lrange('courses',0,-1)) print(conn.blpop('courses',0)) #阻塞版的lpop,如果list没有数据,则阻塞。可以设置timeout。0表示一直阻塞 print(conn.brpop('courses',0)) #阻塞版的rpop,如果list没有数据,则阻塞。可以设置timeout。0表示一直阻塞 #结果: [b'sqlalchemy', b'rabbitmq', b'python_test'] (b'courses', b'sqlalchemy') (b'courses', b'python_test')
具体详见https://redis.io/commands#list
linsert(self, key, where, refvalue, value)
LINSERT key BEFORE|AFTER pivot value
Inserts value
in the list stored at key
either before or after the reference value pivot
.
When key
does not exist, it is considered an empty list and no operation is performed.
An error is returned when key
exists but does not hold a list value.
Return value
the length of the list after the insert operation, or -1
when the value pivot
was not found.
print(conn.lrange('courses',0,-1)) print(conn.linsert('courses','before',0,'python')) #如果key存在,pivot(标杆)不存在,返回-1 print(conn.lrange('classes',0,-1)) print(conn.linsert('classes','before',0,'python')) #如果key不存在,不操作,返回0 print(conn.lrange('classes',0,-1)) #结果 [b'rabbitmq'] -1 [] 0 [] print(conn.lrange('courses',0,-1)) print(conn.linsert('courses','before','rabbitmq','python')) #如果key存在,pivot(标杆)存在,插入值,返回列表长度 print(conn.linsert('courses','after','rabbitmq','mysql')) #如果key存在,pivot(标杆)存在,插入值,返回列表长度 print(conn.lrange('courses',0,-1)) #结果: [b'rabbitmq'] 2 3 [b'python', b'rabbitmq', b'mysql']
RPOPLPUSH source destination
Atomically returns and removes the last element (tail) of the list stored atsource
, and pushes the element at the first element (head) of the list stored at destination
.
For example: consider source
holding the list a,b,c
, and destination
holding the list x,y,z
. Executing RPOPLPUSH results in source
holding a,b
and destination
holding c,x,y,z
.
If source
does not exist, the value nil
is returned and no operation is performed. If source
and destination
are the same, the operation is equivalent to removing the last element from the list and pushing it as first element of the list, so it can be considered as a list rotation command.
Return value
Bulk string reply: the element being popped and pushed.
BRPOPLPUSH source destination timeout
BRPOPLPUSH is the blocking variant of RPOPLPUSH. When source
contains elements, this command behaves exactly like RPOPLPUSH. When used inside a MULTI/EXEC block, this command behaves exactly like RPOPLPUSH. When source
is empty, Redis will block the connection until another client pushes to it or until timeout
is reached. A timeout
of zero can be used to block indefinitely.
See RPOPLPUSH for more information.
Return value
Bulk string reply: the element being popped from source
and pushed to destination
. If timeout
is reached, a Null reply is returned.
print(conn.lrange('courses',0,-1),conn.lrange('classes',0,-1)) print(conn.rpoplpush('courses','classes')) print(conn.lrange('courses',0,-1),conn.lrange('classes',0,-1)) #结果 [b'python', b'rabbitmq', b'mysql'] [] b'mysql' [b'python', b'rabbitmq'] [b'mysql'] 第二次结果 [b'python', b'rabbitmq'] [b'mysql'] b'rabbitmq' [b'python'] [b'rabbitmq', b'mysql'] 第三次 [b'python'] [b'rabbitmq', b'mysql'] b'python' [] [b'python', b'rabbitmq', b'mysql'] 第四次 [] [b'python', b'rabbitmq', b'mysql'] None [] [b'python', b'rabbitmq', b'mysql'] 当结果为空的时候,不再继续操作。如果我们用BRPOPLPUSH当pop列表为空时就进入阻塞模式 print(conn.lrange('courses',0,-1),conn.lrange('classes',0,-1)) print(conn.brpoplpush('courses','classes')) print(conn.lrange('courses',0,-1),conn.lrange('classes',0,-1)) 结果: [] [b'python', b'rabbitmq', b'mysql'] ..到这里程序就阻塞了 此时我们在redis-cli客户端执行添加value: 127.0.0.1:6379> lpush courses mysql (integer) 1 此时我们看程序已经运行完成了,完整的运行结果 [] [b'python', b'rabbitmq', b'mysql'] b'mysql' [] [b'mysql', b'python', b'rabbitmq', b'mysql']
llen获取列表长度,lindex获取下标对应的值
print(conn.lrange('courses',0,-1),conn.lrange('classes',0,-1)) print(conn.llen('classes')) print(conn.lindex('classes',1)) #结果 [] [b'mysql', b'python', b'rabbitmq', b'mysql'] 4 b'python'
LREM key count value
Removes the first count
occurrences of elements equal to value
from the list stored at key
. The count
argument influences the operation in the following ways:
count > 0
: Remove elements equal tovalue
moving from head to tail.count < 0
: Remove elements equal tovalue
moving from tail to head.count = 0
: Remove all elements equal tovalue
.
For example, LREM list -2 "hello"
will remove the last two occurrences of"hello"
in the list stored at list
.
Note that non-existing keys are treated like empty lists, so when key
does not exist, the command will always return 0
.
Return value
the number of removed elements.
print(conn.lpush('classes','mysql','python','mysql','python','rabbitmq','redis','rabbitmq')) print(conn.lrange('classes',0,-1)) print(conn.lrem('classes','mysql')) #count默认为0,全部删除 print(conn.lrem('classes','python',-2)) #匹配到的值从后往前删 print(conn.lrem('classes','rabbitmq',2)) #匹配到的值从前往后删 #结果: 11 [b'rabbitmq', b'redis', b'rabbitmq', b'python', b'mysql', b'python', b'mysql', b'mysql', b'python', b'rabbitmq', b'mysql'] 4 2 2 [b'redis', b'python', b'rabbitmq']
LTRIM key start stop
Trim an existing list so that it will contain only the specified range of elements specified. Both start
and stop
are zero-based indexes, where 0
is the first element of the list (the head), 1
the next element and so on.
For example: LTRIM foobar 0 2
will modify the list stored at foobar
so that only the first three elements of the list will remain.
start
and end
can also be negative numbers indicating offsets from the end of the list, where -1
is the last element of the list, -2
the penultimate element and so on.
Out of range indexes will not produce an error: if start
is larger than the end of the list, or start > end
, the result will be an empty list (which causes key
to be removed). If end
is larger than the end of the list, Redis will treat it like the last element of the list.
A common use of LTRIM is together with LPUSH / RPUSH. For example:
LPUSH mylist someelement
LTRIM mylist 0 99
This pair of commands will push a new element on the list, while making sure that the list will not grow larger than 100 elements. This is very useful when using Redis to store logs for example. It is important to note that when used in this way LTRIM is an O(1) operation because in the average case just one element is removed from the tail of the list.
print(conn.lpush('classes','mysql','python','mysql','python','rabbitmq','redis')) #push6条数据 print(conn.lrange('classes',0,-1)) print(conn.ltrim('classes',0,2)) #用切片的方法,保留切片的数据(保留前3条),丢弃其它数据 print(conn.lrange('classes',0,-1)) #通过切片获取元素,不修改列表,仅获取 #结果: 6 [b'redis', b'rabbitmq', b'python', b'mysql', b'python', b'mysql'] True [b'redis', b'rabbitmq', b'python']
4.set集合操作
Set操作,Set集合就是不允许重复的列表
SADD key member [member ...]
Add the specified members to the set stored at key
. Specified members that are already a member of this set are ignored. If key
does not exist, a new set is created before adding the specified members.
An error is returned when the value stored at key
is not a set.
Return value
Integer reply: the number of elements that were added to the set, not including all the elements already present into the set.
SMEMBERS key
Returns all the members of the set value stored at key
.
This has the same effect as running SINTER with one argument key
.
SCARD key
Returns the set cardinality (number of elements) of the set stored at key
.
print(conn.sadd('num',*[1,2,3,4,5])) #添加指定的values到key,返回添加成功的个数 print(conn.sadd('num',*[4,5,6,7,8])) print(conn.smembers('num')) #返回指定key的所有values print(conn.scard('num')) #统计指定key的value个数 #结果: 5 3 {b'3', b'6', b'4', b'2', b'7', b'8', b'5', b'1'} 8
SDIFF key [key ...]
Returns the members of the set resulting from the difference between the first set and all the successive sets.
SINTER key [key ...]
Returns the members of the set resulting from the intersection of all the given sets.
SUNION key [key ...]
Returns the members of the set resulting from the union of all the given sets.
print(conn.sadd('num',*[1,2,3,4,5])) #添加指定的values到key,返回添加成功的个数 print(conn.sadd('num',*[4,5,6,7,8])) print(conn.sadd('num1',*[3,4,5,6,7,8,9,10])) print(conn.sdiff('num','num1')) #返回在num而不在num的值,即集合中的差集 print(conn.sinter('num','num1')) #集合中的交集,返回num和num1的交集。这里key可以有好多个。 print(conn.sunion('num','num1')) #集合中的并集,返回num和num1的并集 结果: 5 3 8 {b'1', b'2'} {b'3', b'5', b'4', b'7', b'6', b'8'} {b'3', b'1', b'9', b'5', b'4', b'10', b'7', b'2', b'6', b'8'}
SDIFFSTORE destination key [key ...]
This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination
.
If destination
already exists, it is overwritten.
SINTERSTORE destination key [key ...]
This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination
.
If destination
already exists, it is overwritten.
SUNIONSTORE destination key [key ...]
This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination
.
If destination
already exists, it is overwritten.
print(conn.sdiffstore('num3','num','num1')) #返回在num而不在num1的个数,并添加到指定的key(num3)中 print(conn.sinterstore('num4','num','num1')) #集合中的交集,返回num和num1的交集个数。这里key可以有好多个。并添加到指定的key(num4)中 print(conn.sunionstore('num5','num','num1')) #集合中的并集,返回num和num1的并集个数 print(conn.smembers('num3'),conn.smembers('num4'),conn.smembers('num4')) #结果 2 6 10 {b'1', b'2'} {b'3', b'4', b'6', b'5', b'7', b'8'} {b'3', b'4', b'6', b'5', b'7', b'8'}
SISMEMBER key member
Returns if member
is a member of the set stored at key
.
SREM key member [member ...]
Remove the specified members from the set stored at key
. Specified members that are not a member of this set are ignored. If key
does not exist, it is treated as an empty set and this command returns 0
.
An error is returned when the value stored at key
is not a set.
SMOVE source destination member
Move member
from the set at source
to the set at destination
. This operation is atomic. In every given moment the element will appear to be a member of source
or destination
for other clients.
If the source set does not exist or does not contain the specified element, no operation is performed and 0
is returned. Otherwise, the element is removed from the source set and added to the destination set. When the specified element already exists in the destination set, it is only removed from the source set.
An error is returned if source
or destination
does not hold a set value.
print(conn.smembers('num')) print(conn.sismember('num',3)) #判断值是否存在,返回True 或False print(conn.srem('num',*[1,2,3])) #删除一个或多个值,返回删除的元素个数 print(conn.smove('num','num7',5)) #删除一个值并存储到另外一个集合中,如果值存在,返回True print(conn.smembers('num'),conn.smembers('num7')) #结果 {b'5', b'1', b'6', b'8', b'2', b'7', b'3'} True 3 True {b'7', b'8', b'6'} {b'5', b'4'}
SPOP key [count]
Removes and returns one or more random elements from the set value store at key
.
This operation is similar to SRANDMEMBER, that returns one or more random elements from a set but does not remove it.
SRANDMEMBER key [count]
When called with just the key
argument, return a random element from the set value stored at key
.
Starting from Redis version 2.6, when called with the additional count
argument, return an array of count
distinct elements if count
is positive. If called with a negative count
the behavior changes and the command is allowed to return the same element multiple times. In this case the number of returned elements is the absolute value of the specified count
.
When called with just the key argument, the operation is similar to SPOP, however while SPOP also removes the randomly selected element from the set, SRANDMEMBER will just return a random element without altering the original set in any way.
print(conn.smembers('num1')) print(conn.spop('num1')) #随机删除一个元素 print(conn.spop('num1')) print(conn.srandmember('num1',2)) #从key对应的集合中随机获取 numbers 个元素,返回列表形式 print(conn.smembers('num1')) #结果 {b'7', b'6', b'5', b'8', b'10', b'9', b'3', b'4'} b'8' b'3' [b'7', b'10'] {b'7', b'6', b'5', b'10', b'9', b'4'}
SSCAN key cursor [MATCH pattern] [COUNT count]
详见scan
有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
详见https://redis.io/commands#sorted_set
其它常用操作
delete(*names) # 根据删除redis中的任意数据类型 exists(name) # 检测redis的name是否存在 keys(pattern='*') # 根据模型获取redis的name # 更多: # KEYS * 匹配数据库中所有 key 。 # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 # KEYS h*llo 匹配 hllo 和 heeeeello 等。 # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo expire(name ,time) # 为某个redis的某个name设置超时时间 rename(src, dst) # 对redis的name重命名为 move(name, db)) # 将redis的某个值移动到指定的db下 randomkey() # 随机获取一个redis的name(不删除) type(name) # 获取name对应值的类型 scan(cursor=0, match=None, count=None) scan_iter(match=None, count=None) # 同字符串操作,用于增量迭代获取key
管道
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。
pipe = conn.pipeline(transaction=True) pipe.set('name','lxj') pipe.set('role','it') pipe.execute()
发布订阅
发布者:服务器
订阅者:Dashboad和数据处理
功能和rabbitmq相似,但是实现过程要简单很多
import redis class RedisHelper(object): def __init__(self): self.__pool = redis.ConnectionPool(host="localhost", port=6379) self.__conn = redis.Redis(connection_pool=self.__pool) self.chan_sub = 'fm104.5' #约定订阅者的频道 self.chan_pub = 'fm104.5' #约定发布者的频道 def public(self,msg): return self.__conn.publish(self.chan_pub,msg) #根据频道发送消息 def subscribe(self): pub = self.__conn.pubsub() #可以理解打开收音机 pub.subscribe(self.chan_sub) #调频 pub.parse_response() #准备接收 return pub
发布者
from redis_helper import RedisHelper obj = RedisHelper() while True: msg = input(">>") obj.public(msg) #发布消息
订阅者
from redis_helper import RedisHelper obj = RedisHelper() redis_sub = obj.subscribe() while True: msg = redis_sub.parse_response() #开始接收 print(msg)
更多res操作详见:https://redis.io/documentation
什么时候用关系型数据库,什么时候 用NoSQL?
Go for legacy relational databases (RDBMS) when:
- The data is well structured, and lends itself to a tabular arrangement (rows and columns) in a relational database. Typical examples: bank account info, customer order info, customer info, employee info, department info etc etc.
- Another aspect of the above point is : schema oriented data model. When you design a data model (tables, relationships etc) for a potential use of RDBMS, you need to come up with a well defined schema: there will be these many tables, each table having a known set of columns that store data in known typed format (CHAR, NUMBER, BLOB etc).
- Very Important: Consider whether the data is transactional in nature. In other words, whether the data will be stored, accessed and updated in the context of transactions providing the ACID semantics or is it okay to compromise some/all of these properties.
- Correctness is also important and any compromise is _unacceptable_. This stems from the fact that in most NoSQL databases, consistency is traded off in favor of performance and scalability (points on NoSQL databases are elaborated below).
- There is no strong/compelling need for a scale out architecture ; a database that linearly scales out (horizontal scaling) to multiple nodes in a cluster.
- The use case is not for “high speed data ingestion”.
- If the client applications are expecting to quickly stream large amounts of data in/out of the database then relational database may not be a good choice since they are not really designed for scaling write heavy workloads.
- In order to achieve ACID properties, lots of additional background work is done especially in writer (INSERT, UPDATE, DELETE) code paths. This definitely affects performance.
- The use case is not for “storing enormous amounts of data in the range of petabytes”.
Go for NoSQL databases when:
- There is no fixed (and predetermined) schema that data fits in:
- Scalability, Performance (high throughput and low operation latency), Continuous Availability are very important requirements to be met by the underlying architecture of database.
- Good choice for “High Speed Data Ingestion”. Such applications (for example IoT style) which generate millions of data points in a second and need a database capable of providing extreme write scalability.
- The inherent ability to horizontally scale allows to store large amounts of data across commodity servers in the cluster. They usually use low cost resources, and are able to linearly add compute and storage power as the demand grows.
source page https://www.quora.com/When-should-you-use-NoSQL-vs-regular-RDBMS