人生最大的幸福就是健康地生活着!
总结:
- redis最关注的问题: 是你要用python来解决数据存储(如何选择数据存储的类型)的问题;内存如何合理使用的问题;
- redis的数据安全指的是:将所有的二进制数据转成10进制后,在转成字符串;
- redis的本质是key-value对, 只不过value 可以为:str/Dict(hash 字典V 套字典)/set(集合),所以他的变化指的是value的类型变化; 他就是内存结构的典型的嵌套结构的一个复杂的K-V对;
- 任何 flashdb都是危险操作;
- 介绍 Redis 的对象系统中的字符串对象(String)、列表对象(List)、哈希对象(Hash)、集合对象(Set)和有序集合对象(ZSet)
- Redis 的list 可以当成缓冲队列 来使用的;业务场景:
参考:
- Redis数据结构详解,五种数据结构分分钟掌握
- 十二张图详解Redis的数据结构和对象系统
- 万字长文的Redis五种数据结构详解(理论+实战),建议收藏
Redis
官方网站:http://www.radis.io
中文网站:http://www.redis.cn
开源的(BSD协议),使用ANSI C 编写,基于内存的且支持持久化,高性能的Key-Value的NoSQL数据库。
支持数据结构类型丰富,有如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。
丰富的支持主流语言的客户端,C、C++、Python、Erlang、R、C#、Java、PHP、Objective-C、Perl、Ruby、Scala、Go、JavaScript。
用途:缓存(StackOverFlow)、数据库(微博)、消息中间件(微博)
Redis版本
目前主要版本为3.2
redis在2017年也发布了4.x版本,目前是4.0.x
部署环境是Linux,本次部署在CentOS 6.x上。
Windows版本由微软提供 https://github.com/MicrosoftArchive/redis
可视化工具RedisDesktopManager。windows目前使用redis-desktop-manager-0.8.x,0.9有点问题。
1. Redis安装
Linux单节点安装
# yum -y install gcc tcl
# tar xf redis-3.2.12.tar.gz
# mv redis-3.2.12/ redis
# cd redis
# make
------------------------------------编译安装成功;
LINK redis-server # 最常用的3个文件 ;
INSTALL redis-sentinel
CC redis-cli.o
Hint: It's a good idea to run 'make test' ;)
make[1]: Leaving directory `/root/redis-3.2.12/src'
如果出错,需要redis目录下
# cd deps
# make jemalloc
# make lua
# make linenoise
# make hiredis
# cd ..
缺省安装
# make install
默认安装到/usr/local/bin
自定义安装
# mkdir -p /magedu/redis
# make PREFIX=/magedu/redis install
可执行文件
cd src
redis-benchmark
redis-check-aof
redis-check-dump
redis-cli # 客户端
redis-server
[root@Centos7 src]# mkdir /magedu/redis -p # 创建目录
[root@Centos7 redis-3.2.12]# make PREFIX=/magedu/redis/ install # 拷贝文件;
cd src && make install
make[1]: Entering directory `/root/redis-3.2.12/src'
Hint: It's a good idea to run 'make test' ;)
INSTALL install
INSTALL install
INSTALL install
INSTALL install
INSTALL install
make[1]: Leaving directory `/root/redis-3.2.12/src'
[root@Centos7 src]# cd /magedu/redis/
[root@Centos7 redis]# ls
bin
[root@Centos7 redis]# cd bin/
[root@Centos7 bin]# ll
total 15080
-rwxr-xr-x. 1 root root 2433432 Aug 13 17:10 redis-benchmark
-rwxr-xr-x. 1 root root 24992 Aug 13 17:10 redis-check-aof
-rwxr-xr-x. 1 root root 5191752 Aug 13 17:10 redis-check-rdb
-rwxr-xr-x. 1 root root 2586480 Aug 13 17:10 redis-cli
lrwxrwxrwx. 1 root root 12 Aug 13 17:10 redis-sentinel -> redis-server
-rwxr-xr-x. 1 root root 5191752 Aug 13 17:10 redis-server
# 把redis变成一个服务;
[root@Centos7 utils]# ./install_server.sh
Welcome to the redis service installer
This script will help you easily set up a running redis server
Please select the redis port for this instance: [6379]
Selecting default: 6379
Please select the redis config file name [/etc/redis/6379.conf]
Selected default - /etc/redis/6379.conf
Please select the redis log file name [/var/log/redis_6379.log]
Selected default - /var/log/redis_6379.log
Please select the data directory for this instance [/var/lib/redis/6379]
Selected default - /var/lib/redis/6379
Please select the redis executable path []
环境变量
可以将下面的变量追加入到~/.bash_profile文件末尾
export REDIS_HOME=/magedu/redis
export PATH=REDIS_HOME/bin
配置PATH路径
[root@Centos7 bin]# nano ~/.bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
export PATH
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
export REDIS_HOME=/magedu/redis
export PATH=$PATH:$REDIS_HOME/bin
[root@Centos7 bin]# source ~/.bash_profile # 刷新
[root@Centos7 bin]# ls
redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
[root@Centos7 bin]# echo $REDIS_HOME # 配置路径成功;
/magedu/redis
# 服务
[root@Centos7 ~]# redis-3.2.12/utils/install_server.sh
Welcome to the redis service installer
This script will help you easily set up a running redis server
Please select the redis port for this instance: [6379]
Selecting default: 6379
Please select the redis config file name [/etc/redis/6379.conf]
Selected default - /etc/redis/6379.conf
Please select the redis log file name [/var/log/redis_6379.log]
Selected default - /var/log/redis_6379.log
Please select the data directory for this instance [/var/lib/redis/6379]
Selected default - /var/lib/redis/6379
Please select the redis executable path [] /magedu/redis/bin/redis-server # 路径要正确
Selected config:
Port : 6379
Config file : /etc/redis/6379.conf
Log file : /var/log/redis_6379.log
Data dir : /var/lib/redis/6379
Executable : /magedu/redis/bin/redis-server
Cli Executable : /magedu/redis/bin/redis-cli
Is this ok? Then press ENTER to go on or Ctrl-C to abort.
Copied /tmp/6379.conf => /etc/init.d/redis_6379
Installing service...
Successfully added to chkconfig!
Successfully added to runlevels 345!
Starting Redis server...
Installation successful!
[root@Centos7 bin]# cd /etc/init.d
[root@Centos7 init.d]# ll
total 48
-rw-r--r--. 1 root root 18281 Aug 19 2019 functions
-rwxr-xr-x. 1 root root 4569 Aug 19 2019 netconsole
-rwxr-xr-x. 1 root root 7928 Aug 19 2019 network
-rwxr-xr-x. 1 root root 4014 Nov 30 2017 rabbitmq-server
-rw-r--r--. 1 root root 1160 Jun 30 23:11 README
-rwxr-xr-x. 1 root root 1708 Aug 13 17:14 redis_6379
[root@Centos7 init.d]# mv redis_6379 redisd # 改一下服务名称;
redis服务
[root@Centos7 init.d]# chkconfig
Note: This output shows SysV services only and does not include native
systemd services. SysV configuration data might be overridden by native
systemd configuration.
If you want to list systemd services use 'systemctl list-unit-files'.
To see services enabled on particular target use
'systemctl list-dependencies [target]'.
netconsole 0:off 1:off 2:off 3:off 4:off 5:off 6:off
network 0:off 1:off 2:on 3:on 4:on 5:on 6:off
rabbitmq-server 0:off 1:off 2:on 3:on 4:on 5:on 6:off
[root@Centos7 init.d]# chkconfig redisd on # 开机启动 redis 2345
[root@Centos7 init.d]# chkconfig
Note: This output shows SysV services only and does not include native
systemd services. SysV configuration data might be overridden by native
systemd configuration.
If you want to list systemd services use 'systemctl list-unit-files'.
To see services enabled on particular target use
'systemctl list-dependencies [target]'.
netconsole 0:off 1:off 2:off 3:off 4:off 5:off 6:off
network 0:off 1:off 2:on 3:on 4:on 5:on 6:off
rabbitmq-server 0:off 1:off 2:on 3:on 4:on 5:on 6:off
redisd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
# redis 配置文件
[root@Centos7 src]# nano /etc/redis/6379.conf
bind 192.168.0.100 127.0.0.1
protected-mode yes
port 6379
tcp-keepalive 300 # TCP服务保存多长时间;
daemonize yes # 以后台服务跑起来
databases 16 # 总共有16 个库(0-15)
# 修改配置文件后 重新 启动 redisd
[root@Centos7 ~]# systemctl restart redisd.service
[root@Centos7 ~]# ss -tanl
LISTEN 0 128 127.0.0.1:6379 *:*
LISTEN 0 128 192.168.0.100:6379
Redis Windows安装
项目地址 https://github.com/MicrosoftArchive/redis
下载地址 https://github.com/MicrosoftArchive/redis/releases/download/win-3.2.100/Redis-x64-3.2.100.zip
解压缩,就可以用了。
2. Redis数据模型
redis支持数据模型非常丰富
2.1 键Key
Redis key 值是二进制安全的,这意味着可以用任何二进制序列作为key值,从形如”foo”的简单字符串到一个JPEG文件的内容都可以。空字符串也是有效key值
Key取值原则
键值不需要太长,消耗内存,而且查找这类键值的计算成本较高
键值不宜过短,可读性较差
习惯上key采用'user:123:password'形式,表示用户id为123的用户的密码
2.2字符串
字符串是一种最基本简单的Redis值类型。Redis字符串是二进制安全的,这意味着一个Redis字符串能包含任意类型的数据,例如: 一张JPEG格式的图片或者一个序列化的Ruby对象。
一个字符串类型的值最多能存储512M字节的内容。
1. python中redis编程
安装redis库
$ pip install redis
import redis
db = redis.Redis('192.168.0.100') # 默认本地6379的0号库
print(db.keys('*')) # 查看所有匹配keys
db.set(0b01100001, 'a') # 0x61(16进制61) bin二进制=>十进制97=>str :a
print(db.keys('*'))
#------------------------------
[]
[b'97']
print(db.get(0b01100001))
print(db.get('97')) # 用字符串 拿
#----------------------
b'a'
b'a'
# key的三种形式
db.set(0b11, 'abc')
print(db.get(0b11))
print(db.get(3))
print(db.get('3'))
#----------------------
b'abc'
b'abc'
b'abc'
注意:上例中0x62实际上发生了类型变化,因为返回的bytes类型98,实际上对应ASCII的98,已经是2字节了。
数值会先转换成10进制64位有符号数后,再转成字符串,存入redis中。
2. 查看帮助
> Help 查看帮助
> Help <tab> 使用tab键切换帮助
> Help set 查看set命令帮助
> Help @string 查看命令组帮助
3. 字符串设置
SET key value [EX seconds][PX milliseconds] [NX|XX]
设置字符串值
EX 设置过期时间,秒,等同于 SETEX key seconds value
PX 设置过期时间,毫秒,等同于 PSETEX key milliseconds value
NX 键不存在,才能设置,等同于 SETNX key value
XX 键存在时,才能设置
MSET key value [key value ...]
设置多个键的字符串值,key存在则覆盖,key不存在则增加
原子操作
MSETNX key value [key value ...]
key不存在则设置,key存在则失败。nx指不存在。
这个命令是原子操作(一个失败,操作全部失败)
4. 过期操作和生存时间
Redis中可以给每个Key设置一个生存时间(秒或毫秒),当达到这个时长后,这些键值将会被自动删除
EXPIRE key seconds
PEXPIRE key milliseconds
设置多少秒或者毫秒后过期
EXPIREAT key timestamp
PEXPIREAT key milliseconds-timestamp
设置在指定Unix时间戳过期
PERSIST key
持久key,即取消过期
Time To Live,Key的剩余生存时间
TTL key
PTTL key
key存在但没有设置TTL,返回-1
key存在,但还在生存期内,返回剩余的秒或者毫秒
key曾经存在,但已经消亡,返回-2(2.8版本之前返回-1)
图片.png
适用场景
一、多少秒过期,例如一个缓存数据失效
二、PEXPIREAT key milliseconds-timestamp,比如现在开始缓存数据,到0点失效
set s5 abc ex 20
ttl s5
setnx s6 6
expire s6 60
pttl s6
persist s6
ttl s6
EXPIREAT cache 1355292000
PEXPIREAT mykey 1555555555005
5. key操作
keys pattern
pattern可以取如下值:
1. * 任意长度字符
2. ? 任意一个字符
3. [] 字符集合,表示一个字符
keys *
keys s?
keys s[13]
keys s*
keys ??
keys s[1-3]
TYPE key | key类型 |
EXISTS key | key是否存在 |
RENAME key newkey 、 RENAMENX key newkey | 键重命名 |
DEL key [key ...] | 键删除 |
6. 字符串获取
GET key | 获取值 |
MGET key [key ...] | 获取多个给定的键的值 |
GETSET key value | 返回旧值并设置新值,如果键不存在,就创建并赋值 |
STRLEN key | 字符串长度 |
get s4
mget s1 s3 s5 s7
strlen s3
mgetset s5 100
7. 字符串操作
APPEND key value | 追加字符串。如果键存在就追加;如果不存在就等同于SET key value |
获取子字符串 | |
GETRANGE key start end | 索引值从0开始,支持负索引,-1表示最后一个字符。范围是[start, end],start必须在end的左边,否则返回空串(前后都包括) |
SETRANGE key offset value | 从指定索引处开始覆盖字符串,返回覆盖后字符串长度。key不存在会创建新的 |
127.0.0.1:6379[1]> get s4
(nil)
127.0.0.1:6379[1]> APPEND s4 0123
(integer) 4
127.0.0.1:6379[1]> get s4
"0123"
127.0.0.1:6379[1]> setrange s4 2 a
(integer) 4
127.0.0.1:6379[1]> get s4
"01a3"
127.0.0.1:6379[1]> setrange s4 2 abcde
(integer) 7
127.0.0.1:6379[1]> get s4
"01abcde"
8. 自增、自减
INCR key 和 DECR key | 步长1的增减 |
INCRBY key decrement 和 DECRBY key decrement | 步长增减 |
字符串值会被解释成64位有符号的十进制整数来操作,结果依然转成字符串
127.0.0.1:6379[1]> set s3 10
OK
127.0.0.1:6379[1]> incr s3
(integer) 11
127.0.0.1:6379[1]> incrby s3 100
(integer) 111
127.0.0.1:6379[1]> DECRBY s3 200
(integer) -89
127.0.0.1:6379[1]>
2.3 库操作
任何 flashdb都是危险操作,一定有损失;
登录不同的库
redis-cli --help
redis-cli -n 2
清除当前库数据
FLUSHDB
清除所有库中的数据
FLUSHALL
2.4 位图bitmap
位图不是真正的数据类型,它是定义在字符串类型上,只不过把字符串按位操作
一个字符串类型的值最多能存储512M字节的内容,可以表示2^32位位上限:
512=29
1M=1024*1024=210+10
1Byte=8bit=23bit
2(9+10+10+3) = 232 b = 4294967296 b ,接近43亿个位
SETBIT key offset value | 设置某一位上的值 offset偏移量,从0开始; value不写,默认是0 |
GETBIT key offset | 获取某一位上的值 |
BITPOS key bit [start][end] | 返回指定值0或者1在指定区间上第一次出现的位置 |
BITCOUNT key [start][end] | 统计指定位区间上值为1的个数,从左向右从0开始,从右向左从-1开始,注意官方start、end是位,测试后是字节 |
BITCOUNT testkey 0 0 | 表示从索引为0个字节到索引为0个字节,就是第一个字节的统计 |
BITCOUNT testkey 0 -1 | 等同于 BITCOUNT testkey;最常用的就是 BITCOUNT testkey |
# 记住 \x3 7
127.0.0.1:6379[1]> set s4 7
OK
127.0.0.1:6379[1]> getbit s4 0
(integer) 0
127.0.0.1:6379[1]> getbit s4 1
(integer) 0
127.0.0.1:6379[1]> getbit s4 2
(integer) 1
127.0.0.1:6379[1]> getbit s4 3
(integer) 1
127.0.0.1:6379[1]> getbit s4 4
(integer) 0
127.0.0.1:6379[1]> getbit s4 5
(integer) 1
127.0.0.1:6379[1]> getbit s4 6
(integer) 1
127.0.0.1:6379[1]> getbit s4 7
(integer) 1
#------------------------------------
127.0.0.1:6379[1]> set str1 abc
OK
127.0.0.1:6379[1]> setbit str1 6 1
(integer) 0
127.0.0.1:6379[1]> setbit str1 7 0
(integer) 1
127.0.0.1:6379[1]> get str1
"bbc"
#------------------------------------
# 按照位统计的;
127.0.0.1:6379[1]> set s4 77
OK
127.0.0.1:6379[1]> bitcount s4 0 0
(integer) 5
127.0.0.1:6379[1]> bitcount s4 1 1
(integer) 5
127.0.0.1:6379[1]> bitcount s4
(integer) 10
1. 位操作
BITOP命令用于对多个值(除NOT操作外)执行位运算操作,并将结果保存至指定的键值对中。BITOP命令将返回结果字符串的长度,其值等于输入中最长字符串的长度。
BITOP operation destkey key [key ...]
BITOP命令支持与(AND)、或(OR)、亦或(XOR)以及非(NOT)四个位运算操作,其使用方式为:
AND 与操作,使用方式为BITOP AND destkey srckey1 srckey2 ...
OR 或操作,使用方式为BITOP OR destkey srckey1 srckey2 ...
XOR 亦或操作,使用方式为BITOP XOR destkey srckey1 srckey2 ...
NOT 非操作,使用方式为BITOP NOT destkey srckey
当输入的字符串长度不同时,将使用0填充至与最长长度相同。若输入的键不存在则认定为一个空白字符串,并以0填充至与最长长度相同。
# 10101010
redis> SET key1 "\xaa"
OK
# 01010101
redis> SET key2 "\x55"
OK
# 11110000
redis> SET key3 "\xf0"
OK
# 01010101 01010101
redis> SET key4 "\x5555"
OK
与(AND)操作:
# 10101010 & 01010101 = 00000000
redis> BITOP AND result key1 key2
(integer) 1
redis> GET result
"\x00"
或(OR)操作:
# 10101010 | 01010101 = 11111111
redis> BITOP OR result key1 key2
(integer) 1
redis> GET result
"\xff"
亦或(XOR)操作:
# 10101010 ^ 11110000 = 01011010
redis> BITOP XOR result key1 key3
(integer) 1
# 字符Z二进制值为 01011010
redis> GET result
"Z"
或(OR)操作:
# !10101010 = 01010101
redis> BITOP NOT result key1
(integer) 1
# 字符U二进制值为 01010101
redis> GET result
"U"
不同长度的字符串进行位运算:
# key1的值将以0填充为 10101010 00000000
# 10101010 00000000 | 01010101 01010101 = 11111111 00000000
redis> BITOP OR result key1 key4
(integer) 2
redis> GET result
"\xffU"
思考:'a'位或'b'是什么?
set s1 ab
bitcount s1
bitcount s1 0 0
bitcount s1 1 1
set s2 a
set s3 b
bitop or s8 s2 s3 # 等于什么?
set cn 中
get cn
bitcount cn
2. 习题
1、网站用户的上线次数统计(活跃用户)
2、按天统计网站活跃用户
参考
1、网站用户的上线次数统计(活跃用户)
为每一个用户做上线记录,某天登录就标记一次。
用户ID为key,天作为offset,上线置为1
ID为500的用户,今年的第1天上线、第30天上线
SETBIT u:500 1 1
SETBIT u:500 30 1
BITCOUNT u:500
KYES u*
from redis import Redis
redis = Redis('192.168.0.100', db=2)
# user id 1
redis.setbit('u:1', 1, 1)
redis.setbit('u:1', 30, 1)
# user id 101
redis.setbit('u:101', 3, 1)
redis.setbit('u:1', 30, 1)
# user id 501
for i in range(3, 365, 3):
redis.setbit('u:501', i, 1)
for i in range(2, 365, 2):
redis.setbit('u:1000', i, 1)
active = []
inactive = []
users = redis.keys('u*')
print(users)
for user in users:
count = redis.bitcount(user)
if count > 100:
active.append(user)
else:
inactive.append(user)
print('活跃用户为{},{}'.format(active, len(active)))
print('不活跃用户为{},{}'.format(inactive, len(inactive)))
2、按天统计网站活跃用户
天作为key,用户ID为offset,上线置为1
求一段时间内活跃用户数
SETBIT 20160602 15 1
SETBIT 20160601 123 1
SETBIT 20160606 123 1求6月1日到6月10日的活跃用户
BITOP OR 20160601-10 20160601 20160602 20160603 20160610
BITCOUNT 20160601-10
结果为2
2.5 List列表
列表类型存储了一个有序的字符串列表。常用的操作是向两端插入新的元素。时间复杂度为O(1)。结构为一个链表。记录头和尾的地址。看到这里,Redis数据类型的列表类型一个重大的作用呼之欲出,那就是队列。新来的请求插入到尾部,新处理过的从头部删除。另外,比如微博的新鲜事。比如日志。列表类型就是一个下标从0开始的数组。由于是链表存储,那么越靠近头和尾的元素操作越快,越靠近中间则越慢。
- 其列表是基于双向链表实现,列表头尾增删快,中间增删慢
- 元素是字符串类型
- 元素可以重复出现
- 索引支持正索引和负索引,从左至右从0开始,从右至左从-1开始
命令说明
字母 | 说明 |
---|---|
B | Block阻塞 |
L | Left左起 |
R | Right 右起 |
X | exist 存在 |
LPUSH key value [value ...] | 从左边向队列中压入元素 | |
LPUSHX key value | 从左边向队列加入元素,要求key必须存在 | |
RPUSH key value [value ...] | 从右边向队列中压入数据 | |
RPUSHX key value | 要求key存在 | |
- | - | |
LPOP key | 从左边弹出列表中一个元素 | |
RPOP key | 从右边弹出列表中一个元素 | |
- | - | |
RPOPLPUSH source destination | 从源列表中右边pop一个元素,从左边加入到目标列表 | |
LRANGE key start stop | 返回列表中指定访问的元素,例如LRANGE user 0 -1 | |
- | - | |
LINDEX key index | 返回列表中指定索引的元素 | |
LSET key index value | 设置列表中指定索引位置的元素值,index不能超界 | |
LREM key count value | 从左边删除列表中与value相等的元素 | |
count > 0 | 从左至右搜索,移除与 value 相等的元素,数量至多为 count 次 | |
count < 0 | 从右至左搜索,移除与 value 相等的元素,数量至多为 -count次 | |
count = 0 | 移除列表中所有value值 | |
- | - | |
LTRIM key start stop | 去除指定范围外的元素 | |
RPUSH listkey c abc c ab 123 ab bj ab redis list | ||
LTRIM listkey 0 -1 | # 什么都没有去除 | |
LTRIM listkey 1 -1 | # 去掉左边头 | |
LTRIM listkey 1 10000 | ||
- | - | |
LINSERT key BEFORE | AFTER pivot value | 在列表中某个存在的值(pivot)前或后插入元素一次,key或pivot不存在,不进行任何操作 |
RPUSH lst 1 2 3 4 2 8
LINSERT lst AFTER 2 Python
LINSERT lst BEFORE 2 Ruby
阻塞(可以作为队列使用)
如果弹出的列表不存在或者为空,就会阻塞
超时时间设置为0,就是永久阻塞,直到有数据可以弹出
如果多个客户端阻塞在同一个列表上,使用First In First Service原则,先到先服务
BLPOP key [key ...] timeout | 列表左边阻塞弹出元素。timeout是超时秒数,为0为永久阻塞。 |
BRPOP key [key ...] timeout | 列表左边阻塞弹出元素 |
BRPOPLPUSH source destination timeout | 从一个列表尾部阻塞弹出元素压入到另一个列表的头部 |
# 阻塞式消息队列
BLPOP MyQueue 0
RPUSH MyQueue hello
习题
微博某贴最后评论的50条
LPUSH u1234:forumid:comments "这是第1条评论"
LPUSH u1234:forumid:comments "这是第2条评论"
LPUSH u1234:forumid:comments "这是第3条评论"
LTRIM u1234:forumid:comments 0 49
2.6 hash散列
值是由field和value组成的map键值对
field和value都是字符串类型
HSET key field value | 设置单个字段。field不存在创建,存在覆盖value |
HSETNX key field value | 设置单个字段,要求field不存在。如果key不存在,相当于field也不存在 |
HMSET key field value [field value ...] | 设置多个字段 |
HLEN key | 返回字段个数 |
HEXISTS key field | 判断字段是否存在。key或者field不存在,返回0 |
HGET key field | 返回字段值 |
HMGET key field [field ...] | 返回多个字段值 |
HGETALL key | 返回所有的键值对 |
HKEYS key | 返回所有字段名 |
HVALS key | 返回所有值 |
HINCRBY key field increment | 在字段对应的值上进行整数的增量计算 |
HINCRBYFLOAT key field increment | 在字段对应的值上进行浮点数的增量计算 |
HDEL key field [field ...] | 删除指定的字段 |
hash用途
节约内存空间
每创建一个键,它都会为这个键储存一些附加的管理信息(比如这个键的类型,这个键最后一次被访问的时间等等)
所以数据库里面的键越多,redis数据库服务器在储存附加管理信息方面耗费的内存就越多,花在管理数据库键上的CPU时间也会越多
不适合hash的情况
使用二进制位操作命令:因为Redis目前支持对字符串键进行SETBIT、GETBIT、BITOP等操作,如果你想使用这些操作,那么只能使用字符串键,虽然散列也能保存二进制数据
使用过期键功能:Redis的键过期功能目前只能对键进行过期操作,而不能对散列的字段进行过期操作,因此如果你要对键值对数据使用过期功能的话,那么只能把键值对储存在字符串里面
习题
用户维度统计
统计数包括:关注数、粉丝数、喜欢商品数、发帖数
用户为Key,不同维度为Field,Value为统计数
比如关注了5人
HSET user:100000 follow 5
HINCRBY user:100000 follow 1商品维度统计
统计值包括喜欢数,评论数,购买数,浏览数等
HSET item:58000 fav 500
HINCRBY item:58000 fav 1缓存用户信息
登录后,反复需要读取用户的常用信息,最好的方式就是缓存起来
set user:001 "bob,18,20010101"
mset user:001:name "bob" user:001:age 18 user:001:birthday "20010101"
hmset user:001 name "bob" age 18 birthday "20010101 # 推荐使用
2.7 Set集合
集合的元素是无序的、去重的,元素是字符串类型。
SADD key member [member ...] | 增加一个或多个元素,元素已存在将忽略 |
SREM key member [member ...] | 移除一个或多个元素,元素不存在自动忽略 |
SCARD key | 返回集合中元素的个数。不需要遍历。 |
SMEMBERS key | 返回集合中的所有元素(hash顺序)。注意,如果集合中元素过多,应当避免使用该方法 |
SISMEMBER key member | 元素是否是在集合中 |
# 拿数据的顺序 不一定一致 ;
127.0.0.1:6379> sadd f1 perter
(integer) 1
127.0.0.1:6379> sadd f1 john may tom
(integer) 3
127.0.0.1:6379> SMEMBERS f1
1) "tom"
2) "may"
3) "perter"
4) "john"
127.0.0.1:6379> sadd f1 ben
(integer) 1
127.0.0.1:6379> sadd f2 john may tom perter ben
(integer) 5
127.0.0.1:6379> SMEMBERS f2
1) "tom"
2) "may"
3) "perter"
4) "john"
5) "ben"
127.0.0.1:6379> SMEMBERS f1
1) "tom"
2) "may"
3) "perter"
4) "john"
5) "ben"
SRANDMEMBER key [count] 随机返回集合中指定个数的元素如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合
如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值
如果 count 为 0,返回空
如果 count 不指定,随机返回一个元素
集合运算
差集 | |
---|---|
SDIFF key [key ...] | 从第一个key的集合中去除其他集合和自己的交集部分 |
SDIFFSTORE destination key [key ...] | 将差集结果存储在目标key中 |
SADD number1 123 456 789
SADD number2 123 456 999
SDIFF number1 number2
交集 | |
---|---|
SINTER key [key ...] | 取所有集合交集部分 |
SINTERSTORE destination key [key ...] | 将交集结果存储在目标key中 |
SADD number1 123 456 789
SADD number2 123 456 999
SINTER number1 number2
并集 | |
---|---|
SUNION key [key ...] | 取所有集合并集 |
SUNIONSTORE destination key [key ...] | 将并集结果存储在目标key中 |
SADD number1 123 456 789
SADD number2 123 456 999
SUNION number1 number2
习题
微博的共同关注
需求:当用户访问另一个用户的时候,会显示出两个用户共同关注哪些相同的用户
设计:将每个用户关注的用户放在集合中,求交集即可
2.8 SortedSet有序集合
类似Set集合,有序的集合。
每一个元素都关联着一个浮点数分值(Score),并按照分值从小到大的顺序排列集合中的元素。分值可以相同
ZADD key score member [score member ...] | 增加一个或多个元素。如果元素已经存在,则使用新的score |
ZCARD key | 返回集合的元素个数 |
ZCOUNT key min max | 返回指定score范围元素的个数 |
ZSCORE key member | 显示分值 |
ZINCRBY key increment member | 增加或减少分值。increment为负数就是减少 |
ZRANGE key start stop [WITHSCORES] | 返回指定索引区间元素 如果score相同,则按照字典序lexicographical order 排列 默认按照score从小到大,如果需要score从大到小排列,使用ZREVRANGE |
ZREVRANGE key start stop [WITHSCORES] | 返回指定索引区间元素 如果score相同,则按照字典序lexicographical order 的 逆序 排列 默认按照score从大到小,如果需要score从小到大排列,使用ZRANGE |
ZRANK key member | 返回元素的排名(索引) |
ZREVRANK key member | 返回元素的逆序排名(索引) |
ZADD employees 3500 jack 4000 peter 4000 john 4500 tom 2500 david
ZCOUNT employees 3000 4000 # 空的;索引
ZCOUNT employees 0 1 # david jack
ZADD employees 3.2 david
ZSCORE employees david
ZINCRBY employees 1.5 jack
ZINCRBY employees -500 tom
ZRANGE employees 0 -1 WITHSCORES
ZRANK employees peter
ZREVRANGE employees 0 -1 WITHSCORES # 逆序后的索引0到-1,即返回所有
ZREVRANK employees peter
ZRANGEBYSCORE key min max [WITHSCORES][LIMIT offset count] 返回指定分数区间的元素,返回score默认属于[min,max]之间,元素按照score升序排列,score相同字典序
LIMIT中offset代表跳过多少个元素,count是返回几个。类似于Mysql
使用小括号,修改区间为开区间,例如 (5 或者 (10、5)
-inf和+inf表示负无穷和正无穷
ZREVRANGEBYSCORE key max min [WITHSCORES][LIMIT offset count] 降序返回指定分数区间的元素 返回score默认属于[min,max]之间,元素按照score降序排列,score相同字典降序
ZRANGEBYSCORE employees 3500 4000
ZRANGEBYSCORE employees (4000 5000
ZRANGEBYSCORE employees 4000 5000 LIMIT 1 5 # 跳过一个,返回至多5个
ZREVRANGEBYSCORE employees +inf -inf
ZREM key member [member ...] | 移除一个或多个元素。元素不存在,自动忽略 |
ZREMRANGEBYRANK key start stop | 移除指定排名范围的元素 |
ZREMRANGEBYSCORE key min max | 移除指定分值范围的元素 |
ZREMRANGEBYRANK employees 0 1
ZREMRANGEBYSCORE employees 4000 5000
集合运算
并集
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] | numkeys指定key的数量,必须 WEIGHTS选项,与前面设定的key对应,对应key中每一个score都要乘以这个权重 AGGREGATE选项,指定并集结果的聚合方式 |
SUM: | 将所有集合中某一个元素的score值之和作为结果集中该成员的score值,默认 |
MIN: | 将所有集合中某一个元素的score值中最小值作为结果集中该成员的score值 |
MAX: | 将所有集合中某一个元素的score值中最大值作为结果集中该成员的score值 |
ZADD scores1 70 tom 80 peter 60 john
ZADD scores2 90 peter 60 ben
ZUNIONSTORE scores-all 2 scores1 scores2
ZUNIONSTORE scores-all1 2 scores1 scores2 AGGREGATE SUM
ZUNIONSTORE scores-all2 2 scores1 scores2 WEIGHTS 1 0.5 AGGREGATE SUM
交集
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX] | numkeys指定key的数量,必须 WEIGHTS选项,与前面设定的key对应,对应key中每一个score都要乘以这个权重 AGGREGATE选项,指定并集结果的聚合方式 |
SUM: | 将所有集合中某一个元素的score值之和作为结果集中该成员的score值 |
MIN: | 将所有集合中某一个元素的score值中最小值作为结果集中该成员的score值 |
MAX: | 将所有集合中某一个元素的score值中最大值作为结果集中该成员的score值 |
习题
音乐排行榜怎样实现
每首歌的歌名作为元素(先不考虑重复)
每首歌的播放次数作为分值
ZREVRANGE来获取播放次数最多的歌曲(就是最多播放榜了,云音乐热歌榜,没有竞价,没有权重)
redis3 版本与 2X版本变化较大;
# redis库3.x版本
import redis
r = redis.Redis('127.0.0.1', 6379, db=3)
r.zadd('mboard', {'yellow':1, 'rolling in the deep':1, 'happy':1, 'just the way you are':1})
r.zadd('mboard', {'eye of the tiger':1, 'billie jean':1, 'say you say me':1, 'payphone':1})
r.zadd('mboard', {'my heart will go on':1, 'when you believe':1, 'hero':1})
r.zincrby('mboard', 50, 'yellow')
r.zincrby('mboard', 60, 'rolling in the deep')
r.zincrby('mboard', 68.8, 'my heart will go on')
r.zincrby('mboard', 70, 'when you believe')
allmusic = r.zrange('mboard', 0, -1, withscores=True)
print(type(allmusic))
for m in allmusic:
print(m)
print('-' * 30)
# 排行榜
musicboard = r.zrevrange('mboard', 0, 9, True)
print('欧美热歌榜')
for i, m in enumerate(musicboard):
print(i, *m)
# redis库 2.x版本
import redis
r = redis.Redis(host='192.168.142.135', port=6379, db=3)
r.zadd('mboard','yellow',1,'rolling in the deep',1,'happy',1,'just the way you are',1)
r.zadd('mboard','eye of the tiger',1,'billie jean',1,'say you say me',1,'payphone',1)
r.zadd('mboard','my heart will go on',1,'when you believe',1,'hero',1)
r.zincrby('mboard','yellow',50)
r.zincrby('mboard','rolling in the deep',60)
r.zincrby('mboard','my heart will go on',68.8)
r.zincrby('mboard','when you believe',70)
# 所有元素
allmusic = r.zrange('mboard', 0, -1, withscores=True)
print(type(allmusic))
for m in allmusic:
print(m)
print('-'*30)
# 排行榜
musicboard = r.zrevrange('mboard', 0, 9, True)
print('欧美热曲榜')
for i, m in enumerate(musicboard):
print(i, *m)
新浪微博翻页
新闻网站、博客、论坛、搜索引擎,页面列表条目多,都需要分页
blog这个key中使用时间戳作为score
ZADD blog 1407000000 '今天天气不错'
ZADD blog 1450000000 '今天我们学习Redis'
ZADD blog 1560000000 '几个Redis使用示例'
ZREVRANGE blog 10 20
显示所有博客中的最后的指定的条目
京东图书畅销榜
统计单日榜,计算出周榜单、月榜单、年榜单 , 怎么做?
每天统计一次排行榜
ZADD bk:it:01 1000 'java' 1500 'Redis' 2000 'haoop' 100 'scala' 80 'python'
ZADD bk:it:02 1020 'java' 1500 'Redis' 2100 'haoop' 120 'python' 110 'scala'
ZADD bk:it:03 1620 'java' 1510 'Redis' 3000 'haoop' 150 'storm' 120 'python'
求销售前10名
ZUNIONSTORE bk:it:01-03 3 bk:it:01 bk:it:02 bk:it:03
行吗?
因为上面的单日榜单是累计值,所以不能直接使用并集,要指定聚合运算为MAX
ZUNIONSTORE bk:it:01-03 3 bk:it:01 bk:it:02 bk:it:03 AGGREGATE MAX
ZREVRANGE bk:it:01-03 0 9 WITHSCORES
注意:如果参与并集元素的元素太多,会耗费大量内存和计算时间,可能会导致Redis服务阻塞,如果非要计算,选在空闲时间或备用服务器上计算。
另一种统计
# 常见:
ZADD bk:it:01 50 'java' 20 'Redis' 40 'haoop'
ZADD bk:it:02 70 'java' 30 'Redis' 20 'haoop'
ZADD bk:it:03 20 'java' 30 'Redis' 5 'haoop'
每天统计当天销售量,统计IT类图书一段时间的最新销售榜单
ZUNIONSTORE bk:it:01-03 3 bk:it:01 bk:it:02 bk:it:03 AGGREGATE SUM
ZREVRANGE bk:it:01-03 0 9 WITHSCORES
网友评论