redis数据结构

Redis是什么

1. 介绍

redis是业界主流的key-value nosql 数据库之一。它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中。redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

2. 优点

  • 异常快速
    Redis是非常快的,每秒可以执行大约110000设置操作,81000个/每秒的读取操作。
  • 支持丰富的数据类型
    Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。
    这使得在应用中很容易解决的各种问题,因为我们知道哪些问题处理使用哪种数据类型更好解决。
  • 操作都是原子的
    所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器得到的是更新后的值(最新值)。
  • MultiUtility工具
    Redis是一个多功能实用工具,可以在很多如:缓存,消息传递队列中使用(Redis原生支持发布/订阅),在应用程序中,如:Web应用程序会话,网站页面点击数等任何短暂的数据;

3. 安装redis环境

3.1 window环境

下载地址: https://github.com/MicrosoftArchive/redis/releases
安装:将压缩包解压即可
启动: 进入下载目录,创建一个启动文件文件startup.cmd ,在文件中输入

redis-server redis.windows.conf

双击startup.cmd就可以启动redis服务器.
然后点击同目录下的redis-cli.exe(redis自带的一个客户端工具)文件就可以连接到redis服务器.

3.2 Linux环境

  1. 官网下载源码.tar.gz安装包。
  2. 解压。
  3. 编译。进入redis源码目录。make
  4. 安装。make install PREFIX=/usr/local/redis
    PREFIX参数指定redis的安装目录。一般软件安装到/usr目录下

注意: 编译前要确保系统已经安装了gcc, 安装gcc: yum install gcc
启动服务与连接: 进入安装目录,运行指令

./redis-server # 启动redis服务器

./redis-cli -h 192.168.25.131 -p 6379 # 连接redis
-h: 连接服务器的地址, 默认localhost
-p: 服务的端口号, 默认6379

./redis-cli shutdown 关闭redis

3.3. redis服务后台启动,需修改配置redis.conf中daemonize 属性

# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes

daemonize默认为no, 改为yes

4. 远程访问redis

1. 绑定ip

# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
# JUST COMMENT THE FOLLOWING LINE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bind 127.0.0.1

redis.conf配置中默认为127.0.0.1只允许本地访问redis,可以再其后添加其他允许访问redis服务器的ip,用空格隔开。也可以直接将127.0.0.1改为0.0.0.0,让所有ip都可以连接redis服务器。
重启redis

redis-server path/redis.conf

2. redis 配置log文件

logfile "/var/log/redis/redis.log"

3. 设置验证码用来提供远程登入

# use a very strong password otherwise it will be very easy to break.
# requirepass foobared

找到上述位置,将requirepass 前的注释去掉,foobared改为你要设置的验证码;
修改后通过配置文件重启redis服务

一些相关指令

auth redis验证码      #  使用授权命令授权
config get requirepass   #  查看密码
config set requirepass yourPassword   # 设置临时密码

2. python操作redis

2.1 下载操作redis的模块

pip install redis

2.2 连接

  • 方式一, 直接连接
    redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。
  • 方式二, 创建连接池,然后从连接池中获取连接。
    redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。
import redis
#  直接连接
r1 = redis.Redis(host='192.168.25.131', port='6379', password='root')

#  创建连接池,在从中获取连接
pool = redis.ConnectionPool(host='192.168.25.131',
                            port='6379', password='root')
r2 = redis.Redis(connection_pool=pool)

3. redis基本操作

redis一共有16页db,分别为0到15,在控制台可以通过select db 切换db页,例如 select 2 切换到第三个db, 不同db之间的数据互不干扰。这样可以将name相同的数据分别存到不同的db页,而不产生冲突。

del key [key ...]
exists key [key ...]
keys pattern
expire key seconds
rename key newkey
move key db
randomkey
type key
scan cursor [MATCH pattern] [COUNT count]

delete(*names)       #    根据name删除redis中的任意数据类型
 
exists key [key ...]    #   检测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

4. redis数据类型

4.1 String

按照key-value方式存储, 可以保存字符串,整数,浮点数。

1. 在Redis中设置值,默认,不存在则创建,存在则修改

set key value [EX seconds] [PX milliseconds] [NX|XX]
setex key seconds value
setnx key value

set(name, value, ex=None, px=None, nx=False, xx=False)
# 下面三个可以看做上面的拆分
setex(name, value, time)
psetex(name, time_ms, value)
setnx(name, value)

参数:
ex,过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有name不存在时,当前set操作才执行
xx,如果设置为True,则只有name存在时,岗前set操作才执行

2. 批量设置值

mset key value [key value ...]

mset(*args, **kwargs)

3. 获取值 / 获取并设置 / 批量获取值 / 获取子序列

get key
getset key value
mget key [key ...]
getrange key start end # 包含start和end, 按照字节取

get(key)
getset(name, value)   #   设置新值并获取原来的值
mget(keys, *args)
getrange(key, start, end)

4. 修改字符串内容

setrange key offset value

setrange(name, offset, value)
# 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
# 参数:
    # offset,字符串的索引,字节(一个汉字三个字节)
    # value,要设置的值

5. 二进制位操作

5.1 修改指定索引处的二进制值

setbit key offset value

setbit(name, offset, value)
# 对name对应值的二进制表示的位进行操作
# 参数:
    # name,redis的name
    # offset,位的索引(将值变换成二进制后再进行索引)
    # value,值只能是 1 或 0

例如:如果在Redis中有一个对应: name = "ofo",那么字符串ofo的二进制表示为:01100110 01101111 01101111
所以,如果执行 setbit('name', 15, 1),则就会将第15位设置为1, 那么最终二进制则变成01101111 01100111 01101111,即:"ogo"

注意: setbit 的offset可以为任意非负整数。

5.2 获取二进制表示中的1的个数

bitcount key [start end]

bitcount(key, start=None, end=None)
# 获取name对应的值的二进制表示中 1 的个数
# 参数:
    # key,Redis的name
    # start,位起始位置
    # end,位结束位置

bitcount 与 setbit结合可以用来统计数据,例如:
统计在线人数,定义一个key,每次上线一个人,就以那个人的id为索引,在key对应的值插入一个二进制的1,下线就改为0;通过bitcount统计1的个数,就可知在线人数.

5.3 取二进制中对应索引的二进制值

getbit key offset

getbit(name, offset)
# 获取name对应的值的二进制表示中的某位的值 (0或1)

6. 运算

incr key
incrby key increment
incrbyfloat key increment
decr key
decrby key decrement

incr(self, name, amount=1)
incrby(self, name, amount=1)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:
    # name,Redis的name
    # amount,自增数(必须是整数) 
# 注:incry 与 incrby 

incrbyfloat(self, name, amount=1.0)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:
    # name,Redis的name
    # amount,自增数(浮点型)

decr(self, name, amount=1)
# 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
# 参数:
    # name,Redis的name
    # amount,自减数(整数)
# 同decryby

7. 追加 / 删除 / 获取字节长度

append key value
del key [key ...]
strlen key

append(key, value)
# 在redis name对应的值后面追加内容
# 参数:
    key, redis的name
    value, 要追加的字符串

delete(self, *names) # 删除一个或多个键值对
strlen(name)    #   返回name对应值的字节长度(一个汉字3个字节)

4.2 hash操作

Redis中string结构是一个键值对,而hash结构如同java中的map, 也类似python中的dict, 一个key对应许多键值对。hash结构适合存储对象,如果redis内存足够大可以存储2**32 - 1键值对(40多亿)。

1. 与string结构类似的操作

参数含义与string类似的,不再特殊说明。

设置字段的值
hset key field value
hmset key field value [field value ...]
获取字段的值
hget key field
hmget key field [field ...]
获取key中键值对个数
hlen key
删除key中的字段
hdel key field [field ...]
对字段运算
hincrby key field increment
hincrbyfloat key field increment

hset(name, key, value)
hmset(name, mapping)
 #参数   mapping,字典,如:{'k1':'v1', 'k2': 'v2'}

hget(name,key)
hmget(name, keys, *args)
# 参数:
    # name,reids对应的name
    # keys,要获取key集合,如:['k1', 'k2', 'k3']
    # *args,要获取的key,如:k1,k2,k3
# 如:  r.mget('xx', ['k1', 'k2'])  等价于  r.hmget('xx', 'k1', 'k2')

hkeys(name)     #     获取name对应的hash中所有的key的值

hdel(name,*keys)   #   # 将name对应的hash中指定key的键值对删除

# 自增name对应的hash中的指定key的值,不存在则创建key=amount
hincrby(name, key, amount=1)  
hincrbyfloat(name, key, amount=1.0)

2. 其他操作

hgetall key
hkeys key
hvals key
hexists key filed
hsetnx key field value # 当字段不存在才为其设值

#  获取name对应hash的所有键值
hgetall(name)

# 获取name对应的hash中所有的key的值
hkeys(name)

# 获取name对应的hash中所有的value的值
hvals(name)

# 检查name对应的hash是否存在当前传入的key
hexists(name, key)

# hash结构中不存在对应的键才设置值
hsetnx(name, key, value)

4. 分片的获取数据

hscan key cursor [MATCH pattern] [COUNT count]

hscan(name, cursor=0, match=None, count=None)
# 参数:
    # name,redis的name
    # cursor,游标(基于游标分批取获取数据)
    # match,匹配指定key , 可以用正则匹配,默认None 表示所有的key
    # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
hscan_iter(name, match=None, count=None)
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据
# 参数:
    # match,匹配指定key,默认None 表示所有的key
    # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数  
# 如:
    # for item in r.hscan_iter('xx'):
    #     print item

4.3 list

list结构为链表(linked-list)结构,可以存储多个字符串,并且是有序的,能够存储2**32 - 1个节点(超过40亿个)。Redis的链表时双向的,因此能够也只能从左到右也可以从右到左遍历存储它的节点。链表结构益于删除和添加节点,但是查找性能不佳。

1. 添加 / 插入

lpush key value [value ...] # 将值添加到列表左边
lpushx key value # key存在时,才将值添加到列表左边
linsert key BEFORE|AFTER pivot value # 指定列表中的某个值,在其前或后插入一个新值
lset key index value # 对某个索引位置重新赋值

lpush(name,values)  
# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
# rpush(name, values) 表示从右向左操作

lpushx(name,value)
# 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
# rpushx(name, value) 表示从右向左操作

linsert(name, where, refvalue, value))
# 在name对应的列表的某一个值前或后插入一个新值
# 参数:
    # name,redis的name
    # where,BEFORE或AFTER
    # refvalue,标杆值,即:在它前后插入数据
    # value,要插入的数据

lset(name, index, value)
# 对name对应的list中的某一个索引位置重新赋值
# 参数:
    # name,redis的name
    # index,list的索引位置
    # value,要设置的值

2. 查看列表中元素个数

llen key

llen(name)    #     name对应的list元素的个数

3. 通过索引获取列表元素

lindex key index # redis索引从0开始

lindex(name, index)
# 在name对应的列表中根据索引获取列表元素

4. 分片获取数据

lrange key start stop # 包含 start 和 stop

lrange(name, start, end)
# 在name对应的列表分片获取数据
# 参数:
    # name,redis的name
    # start,索引的起始位置
    # end,索引结束位置

5. 弹出链表中元素

lpop key
rpop key

lpop(name)
# 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
# rpop(name) 表示从右向左操作

6. 删除指定值

lrem key count value
count 删除数量, value要删除的值

lrem(name, value, num)
# 在name对应的list中删除指定的值
# 参数:
    # name,redis的name
    # value,要删除的值
    # num,  num=0,删除列表中所有的指定值;
           # num=2,从前到后,删除2个;
           # num=-2,从后向前,删除2个

7. 移除不再区间的元素

ltrim key start stop

ltrim(name, start, end)
# 在name对应的列表中移除没有在start-end索引之间的值
# 参数:
    # name,redis的name
    # start,索引的起始位置
    # end,索引结束位置

8. 移除并添加

# 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
rpoplpush source destination
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
brpoplpush source destination timeout
# 将多个列表排列,按照从左到右去pop对应列表的元素
blpop key [key ...] timeout

rpoplpush(src, dst)
# 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
# 参数:
    # src,要取数据的列表的name
    # dst,要添加数据的列表的name

brpoplpush(src, dst, timeout=0)
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
# 参数:
    # src,取出并要移除元素的列表对应的name
    # dst,要插入元素的列表对应的name
    # timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞

blpop(keys, timeout)
# 将多个列表排列,按照从左到右去pop对应列表的元素
# 参数:
    # keys,redis的name的集合
    # timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞

# brpop(keys, timeout),从右向左获取数据

4.4 set集合

Redis的set集合不是线性表结构,而是一个哈希表结构,内部根据hash分子来存储和查找数据,所以是无序且不重复的。理论上可以存储232 - 1 (大约42亿)个元素。
因为采用的是哈希表结构,所以对于redis的集合插入、删除和查找的复杂度都是O(1)。**
注意点:

  • 对于set集合而言,每一个元素不能重复,插入相同记录时会失败
  • set集合为无序的
  • 集合的每一个元素都是String数据结构类型

Redis的集合可以对于不同的集合进行操作,比如求交集、差集和并集等。

1. 添加 / 移动 / 删除

sadd key member [member ...]

smove source destination member
spop key [count]

srem key member [member ...]

#  为name添加一个或多个元素 
sadd(name,values)   

#  将某个成员从一个集合中移动到另外一个集合
smove(src, dst, value)
# 从集合的右侧(尾部)移除一个成员,并将其返回
spop(name)

# 在name对应的集合中删除某些值
srem(name, values)

2. 差 / 交 / 并 集和操作

获取集合key与其他集合的差集
sdiff key [key ...]
将第一个key 与 其他集合的差集放入到 destination集合中
sdiffstore destination key [key ...]

获取多个集合的交集
sinter key [key ...]
交集放到destination
sinterstore destination key [key ...]

获取多个集合的并集
sunion key [key ...]
并集结果放到destination集合中
sunionstore destination key [key ...]

#   在第一个name对应的集合中且不在其他name对应的集合的元素集合
sdiff(keys, *args)
#  获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中
sdiffstore(dest, keys, *args)

# 获取多个name对应集合的交集
sinter(keys, *args)
# 获取多一个name对应集合的交集,再讲其加入到dest对应的集合中
sinterstore(dest, keys, *args)

# 获取多一个name对应的集合的并集
sunion(keys, *args) 
# 获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中
sunionstore(dest,keys, *args)

3. 获取集合中元素个数

scard key # 获取key集合中元素的个数

#   获取name对应的集合中元素个数
scard(name)   

4. 检查是否包含某元素

sismember key member

# 检查value是否是name对应的集合的成员
sismember(name, value)

5. 获取

获取key集合所有元素
smembers key
随机获取 count个元素
srandmember key [count]

smembers(name)
# 从name对应的集合中随机获取 numbers 个元素
srandmember(name, numbers)

6. 切片获取

sscan key cursor [MATCH pattern] [COUNT count]

sscan(name, cursor=0, match=None, count=None)
sscan_iter(name, match=None, count=None)
# 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大

4.5 zset有序集合

有序集合 和 集合类似,但是它是有序的;与无序集合的区别在于每一个元素除了值之外,它还会多一个分数。分数是一个浮点数,在java中使用双精度表示。根据分数,Redis支持对分数从小到大或从大到小排序。rset有序集合的每一个元素也是唯一的,但是不同的元素可以有相同的分数。
有序集合rset也是通过哈希表实现的,所以所以对于redis的集合插入、删除和查找的复杂度也都是O(1)。

1. 基本操作

zadd key [NX|XX] [CH] [INCR] score member [score memb
zcard key
zincrby key increment membe # 自增元素对应的分数值
zrem key member [member .. # 删除元素
zremrangebyrank key start stop # 按排名范围删除
zremrangebyscore key min max # 按分数范围删除

# 在name对应的有序集合中添加元素
zadd(name, *args, **kwargs)
# 如:  zadd('zz', 'n1', 1, 'n2', 2)   或     zadd('zz', n1=11, n2=22)

# 获取name对应的有序集合元素的数量
zcard(name)  

# 自增name对应的有序集合的 name 对应的分数
zincrby(name, value, amount)

# 删除name对应的有序集合中值是values的成员
zrem(name, values)
# 根据排行范围删除
zremrangebyrank(name, min, max)
# 根据分数范围删除
zremrangebyscore(name, min, max)

2. 获取操作

获取分数在min和max中元素的个数
zcount key min max
获取值在min和max中元素的个数
zlexcount key min max

zrange key start stop [WITHSCORES] # 按照分数从小到大排, WITHSCORES 返回带上分数
zrevrange key start stop [WITHSCORES] # 从大到小

按分数范围获取元素,offset偏移量, count 为返回的个数
zrangebyscore key min max [WITHSCORES] [LIMIT offset count] # 从小到大
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count] # 从大到小

根据值的大小(从小到大)返回成员
zrangebylex key min max [LIMIT offset count]

返回集合中某个数在集合中的排名
zrank key member # 从小到大
zrevrank key member # 从大到小

返回集合中某个数的分数
zscore key member

# 获取name对应的有序集合中分数 在 [min,max] 之间的个数
zcount(name, min, max)

# 按照索引范围获取name对应的有序集合的元素
zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
# 参数:
    # name,redis的name
    # start,有序集合索引起始位置(非分数)
    # end,有序集合索引结束位置(非分数)
    # desc,排序规则,默认按照分数从小到大排序
    # withscores,是否获取元素的分数,默认只获取元素的值
    # score_cast_func,对分数进行数据转换的函数
# 从大到小排序
zrevrange(name, start, end, withscores=False, score_cast_func=float)

# 按照分数范围获取name对应的有序集合的元素
zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)
# 从大到小排序
zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)

# 根据值的大小返回成员,从小到大
zrangebylex( name, min, max, start=None, num=None)

# 获取某个值在 name对应的有序集合中的排行(从 0 开始)
zrank(name, value)
# zrevrank(name, value),从大到小排序

# 获取name对应有序集合中 value 对应的分数
zscore(name, value)

3. 交集 / 并集

zinterstore destination numkeys key [key ...] [WEIGHTS weight] [A
zunionstore destination numkeys key [key ...] [WEIGHTS weight] [A

# 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作
zinterstore(dest, keys, aggregate=None)
# aggregate的值为:  SUM  MIN  MAX

# 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作
zunionstore(dest, keys, aggregate=None)
# aggregate的值为:  SUM  MIN  MAX

4. 切片获取

zscan key cursor [MATCH pattern] [COUNT count]

zscan(name, cursor=0, match=None, count=None, score_cast_func=float)
zscan_iter(name, match=None, count=None,score_cast_func=float)
# 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作

4.6 基数 -- HyperLogLog

基数是一种算法。redis从2.8.9开始支持基数,用的比较少。例如一本书有几百万个单词,但是扣去重复的单词,这本书也就几千到一万多个单词而已,name内存足够存储它们了。
基数并不是存储元素,而是给某一个有重复元素的数据集合评估需要的空间单元数。

添加指定的元素到HypeLogLong中,如果已经存储元素,则返回0,添加失败
pfadd key element [element ...]
返回HypeLogLog的基数值
pfcount key [key ...]
合并多个HypeLogLog,并将其保存在deskey中
pfmerge destkey sourcekey [sourcekey ...]

pfadd(name, *values)
pfcount(*sources)
pfmerge(dest, *sources)

猜你喜欢

转载自www.cnblogs.com/jxkun/p/9375281.html