redis

[TOC]

Introduction

Redis 是完全开源免费的,是一个高性能的key-value数据库。

性能极高,Redis数据库依赖于主存,在关系型数据库以外再配套Redis管理缓存数据将对性能会有很大的提升

redis是什么?

redis是一种基于内存的数据库,可以用作数据库、缓存、消息中间件,是一个高性能的key-value数据库,单进程单线程,支持10W QPS(每秒内的查询次数)

redis是单线程单进程模型,为什这么快?

1.Redis 完全基于内存,绝大部分请求是纯粹的内存操作,非常迅速,

2.数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度是 O(1)。

3.数据结构简单,对数据操作也简单。采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的 CPU 切换,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。

4.使用多路复用 IO 模型(同时监控多个IO,阻塞 IO)。

为什么选择 Redis 的缓存方案而不用 Memcached 呢?

1.存储方式上:Memcache 会把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。Redis 有部分数据存在硬盘上,这样能保证数据的持久性。

2.数据支持类型上:Memcache 对数据类型的支持简单,只支持简单的 key-value,,而 Redis 支持五种数据类型。

3.使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。Redis 直接自己构建了 VM 机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

4.Value 的大小:Redis 可以达到 1GB,而 Memcache 只有 1MB。

Redis 的持久化策略

RDB:快照形式是直接把内存中的数据保存到一个 dump 的文件中,定时保存,保存策略。AOF:把所有的对 Redis 的服务器进行修改的命令都存到一个文件里,命令的集合。Redis 默认是快照 RDB 的持久化方式。# install

centos安装

$ wget http://download.redis.io/releases/redis-5.0.5.tar.gz
$ tar xzf redis-5.0.5.tar.gz
$ cd redis-5.0.5
$ make
$ /home/jw/software/redis-5.0.7/src/redis-server
# 打开另一个窗口
$ /home/jw/software/redis-5.0.7/src/redis-cli

配置(centos7, redis源码安装为例)

# 设置模式, mac才能连接,临时修改
config get protected-mode #yes
config set protected-mode no


# 修改默认配置文件,并重新启动redis
#/home/jw/software/redis-5.0.7/redis.conf
#注释掉 
# bind 127.0.0.1

# 开启守护进程,即后台启动(vim搜索/daemonize)
daemonize yes
protected-mode no

注释掉这一行
# okip-grant-tables

# 重新启动redis
cd /home/jw/software/redis-5.0.7/src
./redis-server ../redis.conf



# 如果报错,ConnectionRefusedError: [Errno 61] Connection refused
# 关闭centos7防火墙
systemctl stop firewalld.service
systemctl disable firewalld.service

# 关闭redis-server
src/redis-cli shutdown


command

Help
Help <tab>  指定切换帮助????????
Help set
Help @string  查看命令组帮助

1.字符串

字符串是一种最基本简单的Redis值类型。说是字符串,其实可以是任意可以序列化的数据。 一个字符串类型的值最多能存储512M字节(521MB)的内容。

key value type

# 如果值,是数字(二进制,十进制,十六进制)例子
db = redis.Redis('172.16.177.160', port=6379)
print(db.keys('*'))
db.set('testbin', 0b01100010)  # 0x62 十六进制62,ascii表对应十进制98
print(db.get('testbin'))  # b'98'  占了2个字节

db.set('testbin1', 11)
print(db.get('testbin1'))  # b'11'

db.set('testbin2', 0x63)  # 十六进制63,ascii表对应十进制99
print(db.get('testbin2'))  # b'99'


# redis的键,默认都转换为字符串的例子:
# 键为数字,使用get方法时,会先转换成十进制的数字,然后再以字符串的方式显示
# 所以get时,有多种方法。
db.set(0b11, 99)  # 99等价0x63
print(db.get(0b11))  # b'99'
print(db.get(3))  # b'99'
print(db.get('3'))  # b'99'

string setting

set key value [EX seconds][PX millseconds][NX|XX]
EX 设置过期时间,秒
PX 设置过期时间,毫秒
NX 键不存在才能设置
XX 键存在才能设置


mset key value [key value ...] #设置多个键值对,key存在则覆盖,key不存在则增加 原子操作

msetnx key value [key value ...] #设置多个键值对,key不存在才能设置,key存在则设置失败,原子操作


# 例子:
set k1 a k2 b
setnx k4 d  # 等价set k4 d nx

set k5 e ex 5 # equal to setex k5 5

mset aa 1 bb 2
msetnx k1 a k3 c 


# 获取字符串
get key
mget key [key ...]
getset key value  #如果存在设置新值,不存在,则创建一个键值对

# 修改及查看
strlen key
append k7 999  #有键尾部追加,没有创建
getrange k7 0 3 #按索引切片
getrange k7 0 -1

setrange k7 1 abc #指定索引覆盖字符串

# 自增 自减, 给某个键增加或者减小值
# 字符串值会被解释成64位有符号的十进制整数来操作,
# 结果依然转成字符串
incr key
incrby key decrement
incrby key 4

decr key
decrby key decrement
decrby key 5


expire time

批量设置过期时间

/home/jw/software/redis-5.0.7/src/redis-cli keys "a*"|xargs -i /home/jw/software/redis-5.0.7/src/redis-cli expire {} 5
# two method set expire time
expire key seconds  # equal to: setex key seconds 555
# 多少秒后过期
expire key timestamp  # 到指定的时间点过期,在指定Unix时间戳过期

# 毫秒后过期 pexpire


# 持久化key,取消过期时间
persist key
#

# ttl (Time To Live)
# key没设置过期时间,返回-1
# key在过期时间内,返回剩余秒数
# key设置了过期时间,但是已经消亡了,返回-2(2.8版本之前返回-1)
ttl key

key operation

Redis key 需要一个二进制值,可以用任何二进制序列作为key值,可以是简单字符串,也可以是个JPEG 文件的二进制序列。空字符串也是有效key值 .

Key取值原则 :

键值不需要太长,消耗内存,而且查找这类键值的计算成本较高

键值不宜过短,可读性较差

习惯上key采用'user:123:password'形式,表示用户标号为123密码

keys pattern
pattern:
  * 任意长度字符,查看所有
  ?任意一个字符
  [] 集合中的 一个 字符

keys *  # 查看所有的key
keys k?  
keys k*
keys ??
keys k[13]
keys k[1-3] #表示范围

type key
exists key
rename key newkey
del key [key ...]

库操作

redis-cli --help
redis-cli -n 2 # 登陆第2个库

select 2 # 选择第2个库

flushdb # 清除当前库
flushall # 清除所有库

bitmap

**一个字符串类型的值最多能存储512M字节的内容,接近43亿个位 **

位图不是真正的数据类型,它是定义在字符串类型上,只不过把字符串按位操作
一个字符串类型的值最多能存储512M字节的内容,可以表示2**32位 位上限: 

 512=2**9
 1M=1024*1024=2**(10+10)
 1Byte=8bit=2**3bit
 521M = 2(9+10+10+3)bit = 2**32 b = 4294967296 b ,接近43亿个位 
bitcount str1 # 统计字符串中1的个数,equal to: bitcount str1 0 -1
bitcount str1 0 0 # 第一个字节中1的个数
bitcount str1 0 1 # 第一个和第二个字节中1的个数

set s4 7
# 字符串7
# 字符串1对应的ascii为十进制49,字符串7对应的ascii为十进制55
# ascii 55=32 + 16 + 4 +2 +1
# 对应的2进制 0011 0111
bitpos s4 1 [start][end] # 1在指定区间内第一次出现的位置 结果为2
bitpos s4 0 [start][end] # 0在指定区间内第一次出现的位置 结果为0

getbit s4 0  # 0
setbit s4 0 1
set str1 abc
setbit str1 6 1 
get str1 # cbc

setbit str1 7 0
get str1 # bbc
  • 位操作
# a 
# ascii码为97=64 + 32 + 1
# 二进制表示为 0110 0001

# b 
# ascii码为98=64 + 32 + 2
# 二进制表示为 0110 0010

set s1 ab
bitcount s1 #6
bitcount s1 0 0 #第0字节的位数
bitcount s1 1 1 #第1字节的位数
set s2 a
set s3 b
bitop and/or/xor/not s8 s2 s3
# 或操作为c
bitop or s8 s2 s3 # get s8 # 结果为c


set cn 中
get cn 
bitcount # 13个1,redis以utf-8的方式显示中文

练习:

1、按天统计网站活跃用户,日活、周活、月活等统计。

分析:

  • 以日期作为字符串,这个字符串后面可以存512字节内容

  • 512字节中的42亿位,表示42亿个用户

    setbit 20200413 1 1
    setbit 20200413 3 1
    setbit 20200413 120 1
    # 13号这一天共3个用户登陆
    bitcount 20200413 
    
    setbit 20200415 1 1
    setbit 20200415 3 1
    setbit 20200415 121 1
    # 15号这一天共3个用户登陆
    bitcount 20200415 
    # 求一周内的活跃用户,如果一周内一个用户登陆多次,只算1次?
    bitop or 2020413_19_week 202000413 202000415
    bitcount 2020413_19_week # 结果为4
    # 求一周内的活跃用户,如果一周内一个用户登陆多次,算多次?
    bitcount 20200415
    bitcount 20200413
    # 最后2个结果加起来
    
    

    2、网站用户的上线次数统计(活跃用户,比如一周之内的上线次数)

    分析:

    • 只能用户id作为字符串,字符串对应的40亿位,选出部分作为天数
    • 为什么不能天数作为字符串,42亿位作为用户id?因为比如用户1,永远出现在第一个位置,取or and 都只有一个值不能统计次数。
    setbit user_100 1 1
    setbit user_100 3 1
    setbit user_100 5 1
    setbit user_100 7 1
    bitcount user_100 #一周内登陆了4次
    

2.list列表

  • 列表中的元素是字符串类型
  • 底层基于双向链表实现,两端操作O(1) ,中间操作O(n)
  • 元素可重复出现
#取出元素
lrange comments 0 -1
lindex comments 0 #返回索引0对应的val
lset comments 1 comment33  #设置列表中指定索引位置的元素值,index不能超界
#增加数据
#插入
linsert comments before comment1 999
linsert comments after comment1 888
#两端增加
lpush comments comment100 comment77 comment66
lpushx comments comment 88 #key必须存在
rpush comments comment99
rpushx comments comment99
# 删除
lpop #弹出左边第1个
rpop #弹出右边第1个
rpoplpush comments1 comments2  # 右边pop一个元素,从左边加入到comments2
ltrim comments 1 5  #只留下1到5索引间的数据
ltrim comments 1 -1 #删除最新一条数据
ltrim comments 0 -1 #没删除
lrem comments 1 comment2 #从左向有搜索删除评论2,最多一次
lrem comments -2 comment3 #从右向左搜索,删除评论3,最多2次
lrem comments 0 comment4 #删除列表中所有评论4
#长度
llen comments
# 阻塞消息队列
# 如果多个客户端阻塞在同一个列表上,使用First In First Service原则,先到先服务
blpop comments  #左侧弹出没有阻塞
brpop comments
brpoplpush comments1 comments2 0 #从1右侧弹出塞到2左侧,无限等待
# 一直阻塞?
# BLPOP MyQueue 0
# RPUSH MyQueue hello

例子:

1某微博评论的最后50条

lpush comments "comment 1"
lpush comments "comment 2"
lpush comments "comment 3"
ltrim comments 0 2 # 以外的全部删除

3. hash 散列

值是由field和value组成的map键值对 ,field和value都是字符串类型。

hash作用:节约内存,提升cpu效率;因为每增加一个键,就会附加键的管理信息,占用的内存会增多,管理内存cpu所用的时间也会增多,而hash可以一个键存储多对数据。

常用操作

#设置
hmset user_100 name jack id 1
hset user_100 name jack
hsetnx user_100 age 20 # 设置单个字段,要求field不存在。如果key不存在,相当于field也不存在
#字段个数
hlen user_100
#in
hexists user_100 name
#获取
hgetall user_100
hkeys user_100
hvals user_100
hmget user_100 id name
hget user_100 name
#value增量计算
hincrby user_100 id 10
hincrby user_100 id -5
hincrbyfloat user_100 id 3.14
#删除
hdel user_100 age

例子:

1缓存用户信息

hmset user_100 name jack id 10

2商品维度计算,包括喜欢数、评论数

hmset item_100 fav 50 comments 80

3用户维度统计,关注数,被关注数

hmset user_100 follow 8 followed 900
# 其他2种方法
mset user_100_follow 8
mset user_100_followed 900

set user_100 "follow,8,followed,900"

4.集合

常用操作

#添加
sadd set1 a b c
#返回元素个数
scard set1

#返回全元素和指定个数元素
smembers set1
srangemember set1 2#如果集合元素大于2返回2个,如果集合元素个数小于等于2,返回全部
srangemember set1 -2#返回一个数组可重复2个
srangemember set1 0#返回空
srangemember set1 #随机返回1个

#in
sismember set1 a
#删除
srem set1 a
spop set1 #随机删除
#移动
smove set1 set2 a #把元素a, 从集合1移动到集合2

#集合
#交集
sinter set1 set2
sinterstore new_set set1 set2
#并集
sunion set1 set2
sunionstore new_set set1 set2
#差集
sdiff set1 set2  # 从集合1中减去集合2的元素
sdiffstore new_set set1 set2

1求微博共同关注好友

sadd set1 1 2 3
sadd set2 3 a b
sinter set1 set2

5.有序集合

类似Set集合,有序的集合。 每一个元素都关联着一个浮点数分值(Score),并按照分值从小到大的顺序排列集合中的元素。分值可以相同 .

常用操作

zadd mboard 20 yellow 5 happy
zcard mboard 
zcount mboard 5 20 # 返回指定score内的元素个数
zscore mboard yellow
zrank mboard yellow # 返回某个具体字段排名,默认从0开始
zrevrank mboard yellow
zincrby mboard 20 happy
zrange mboard 0 1 withscores #返回前两条数据, 索引值,如果score相同,则按照字典序lexicographical order 排列
zrevrange mboard 0 1 withscores
zrangebyscore mboard 10 19 withscores
zrangebyscore mboard (10 (19 withscores # 开区间
zrangebyscore mboard -inf +inf withscores # 无穷区间                          
zrevrangebyscore mboard 10 19 withscores # 反向
# 移除
zrem mboard happy                          
ZREMRANGEBYRANK mboard 0 1
ZREMRANGEBYSCORE mboard 4000 5000
#集合运算
聚合函数可以是max min sum 
#并集                          
zunionstore week_rank 2 rank_day1 rank_day2 [weights 1 0.5]  aggregate sum
#交集                          
zinterstore week_rank 2 rank_day1 rank_day2 aggregate max

例子:

1统计京东图书日排行榜,周排行榜,月排行榜

# 每一天的各类图书销售量
zadd book_it_day1 50 python 20 java 30 linux
zadd book_it_day2 51 python 15 java 25 linux
zadd book_it_day3 40 python 24 java 27 linux

#按日统计的排行榜
zrevrange book_it_day1 0 -1 withscores
# 周排行榜(每日记录当天销售量)
zunionstore book_it_weekrank 3 book_it_day1 book_it_day2 book_it_day3 aggregate sum
zrevrange book_it_weekrank 0 -1 withscores
# 周排行榜(每日记录总销售量)
zunionstore book_it_weekrank 3 book_it_day1 book_it_day2 book_it_day3 aggregate max
zrevrange book_it_weekrank 0 1 withscores  # 只选出前2条数据

注意:如果参与并集元素的元素太多,会耗费大量内存和计算时间,可能会导致Redis服务阻塞,如果非要计算,选在空闲时间或备用服务器上计算。

2.新浪微博翻页

zadd blog 20200413 瑶妹新戏开播 20200414 岳绮罗出来了
zadd blog 20200413 丽颖有匪定当
zrevrange blog 0 2 withscores  # 显示最新日期的3条数据

3.实现音乐榜单

zadd mboard yellow 10 happy 5
zincrby mboard 10 happy
zrevrange mboard 0 -1 withscores

redis编程

ConnectionPool

from redis import ConnectionPool, Redis

# 连接池,就是为了复用连接,而不是频繁创建TCP连接
pool = ConnectionPool.from_url("redis://172.16.177.160:6379/0")
print(pool)

redis_store1 = Redis(connection_pool=pool)
print(redis_store1.keys("*"))
print(redis_store1.connection_pool is pool)
print(pool._created_connections)  # 1
print(pool._in_use_connections)  # set()
print(pool._available_connections)  # [Connection<host=172.16.177.160,port=6379,db=0>]
print(pool._available_connections[0]._sock)
print('*' * 40)
redis_store1 = Redis(connection_pool=pool)
print(redis_store1.keys("a77*"))  # 每一命令用完后数据就又回到了_available_connections可用连接中
print(redis_store1.connection_pool is pool)
print(pool._created_connections)  # 与之前一样
print(pool._in_use_connections)  # 与之前一样
print(pool._available_connections)  # 与之前一样
print(pool._available_connections[0]._sock)
pool.disconnect()

pipeline

from redis import ConnectionPool, Redis
pool = ConnectionPool.from_url("redis://172.16.177.160:6379/0")

redis_store1 = Redis(connection_pool=pool)
pipe = redis_store1.pipeline()
print(pipe.keys("*"))
print(pipe.keys("a77*"))
print(pipe.execute())
# pipe.set('t1', 11)
# pipe.sadd('s1', 1,2,3,4)
# results = pipe.append('t1', 'abc').get('t1').smembers('s1').execute()
# print(results) # execute()的结果是所有操作的结果列表
# # [True, 4, 5, b'11abc', {b'3', b'1', b'4', b'2'}]
pool.disconnect()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • Redis key 值是二进制安全的,这意味着可以用任何二进制序列作为key值,从形如”foo”的简单字符串到一个...
    壹点零阅读 1,404评论 0 2
  • Redis is not a plain key-value store, it is actually a da...
    颍水书生阅读 482评论 0 0
  • https://cloud.tencent.com/developer/article/1159061 awk 3...
    duenboa阅读 432评论 0 0
  • BitMap是什么通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。Bitmaps 本...
    IT5阅读 2,708评论 1 3
  • 字符串类型是redis中最基本的数据类型,他能存储任何形式的字符串,包括二进制数据。可以用来存储用户邮箱,JSON...
    gzss阅读 983评论 0 2