一、redis的概述和安装
1. redis概述
1.1 redis是什么
1)Redis:REmote DIctionary Server(远程字典服务器)
2)是完全开源免费的,用C语言编写的,遵守BSD开源协议,
是一个高性能的(key/value)分布式内存数据库,基于内存运行,
并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器。
3)Redis 与其他 key - value 缓存产品有以下三个特点:
3.1)Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
3.2)Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
3.3)Redis支持数据的备份,即master-slave模式的数据备份
1.2 能干嘛
1)内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务
2)取最新N个数据的操作,如:可以将最新的10条评论的ID放在List集合
3)模拟类似于HttpSession这种需要设定过期时间的功能
4)发布、订阅消息系统
5)定时器、计数器
2. 安装redis
2.1. 安装方式一:使用命令安装
由于企业里面做Redis开发,99%都是Linux版的运用和安装,
几乎不会涉及到Windows版。
安装包括服务端和客户端
在 Ubuntu 系统安装 Redi 可以使用以下命令:
sudo apt-get update
sudo apt-get install redis-server
启动 Redis 服务
redis-server
或者
sudo redis-server /etc/redis/redis.conf
使用 redis 客户端查看是否启动
redis-cli
检查是否安装成功
127.0.0.1 是本机 IP ,6379 是 redis 服务端口。现在我们输入 PING 命令。
以上说明我们已经成功安装了redis
2.2. 安装方式二:使用下载的压缩包安装
下载:打开redis官方网站(https://redis.io/),推荐下载稳定版本(stable)
http://www.redis.cn/
解压
tar zxvf redis-3.2.5.tar.gz
复制:推荐放到usr/local目录下
sudo mv -r redis-3.2.5/* /usr/local/redis/
进入redis目录
cd /usr/local/redis/
生成
sudo make
测试
sudo make test
这段运行时间会较长
安装:将redis的命令安装到/usr/bin/目录
sudo make install
3. 运行
3.1. 启动服务器
启动服务器:在桌面目录
redis-server 或者 sudo redis-server /etc/redis/redis.conf 按ctrl+c停止
3.2. 启动客户端
启动客户端:在新终端中运行如下代码
redis-cli
运行命令
ping set 'a' '123'
当添加键值后,发现在当前运行的目录下,创建了一个文件:dump.rdb,这个文件用于将数据持久化存储
4. 查看redis的版本
查看redis的版本有两种方式:
4 1. 查询服务端
redis-server --version
或者
redis-server -v
得到的结果是:
Redis server v=3.0.6 sha=00000000:0 malloc=jemalloc-3.6.0 bits=64 build=687a2a319020fa42
4.2 查询客户端
redis-cli --version
或者
redis-cli -v
得到的结果是:redis-cli 3.0.6
严格上说:通过 redis-cli 得到的结果应该是redis-cli 的版本,但是 redis-cli 和redis-server 一般都是从同一套源码编译出的。所以应该是一样的。
二、基本配置
1. 查看redis.conf配置文件
在原文件/usr/local/redis目录下,文件redis.conf为配置文件
已经移动到/etc/redis/目录下,文件redis.conf为配置文件
1.1 绑定地址
打开配置文件命令:sudo subl /etc/redis/redis.conf
绑定地址:如果需要远程访问,可将此行注释
bind 127.0.0.1
1.2 端口,默认为6379
建议不要修改
port 6379
1.3 守护进程
- 是否以守护进程运行
- 如果以守护进程运行,则不会在命令行阻塞,类似于服务
- 如果以非守护进程运行,则当前终端被阻塞,无法使用
推荐改为yes,以守护进程运行
daemonize no|yes
1.4 数据文件及设置存放位置
数据文件
dbfilename dump.rdb
设置数据文件存储路径
dir的默认值为./,表示当前目录
推荐改为:dir /var/lib/redis
注意,删除后,插入数据重启redis服务器才能看到数据库
2. redis基本命令
- 直接运行redis-server会直接运行,阻塞当前终端
- 一般配置文件都放在/etc/目录下
sudo cp /usr/local/redis/redis.conf /etc/redis/
2.1 查看redis当前状态
ps ajx|grep redis
2.2 启动redis服务
#推荐指定配置文件启动
sudo redis-server /etc/redis/redis.conf
或者
sudo service redis start
2.3 停止redis服务
sudo kill -9 redis的进程id
或者
sudo service redis stop
2.4 重启redis服务
sudo service redis restart
当配置文件重新配置后,一般会重启服务器这样配置才生效
三、数据操作
redis是key-value的数据,所以每个数据都是一个键值对
键的类型是字符串
值的类型分为五种:
- 字符串string
- 哈希hash
- 列表list
- 集合set
- 有序集合zset
数据操作的全部命令,
可以查看redis中文网站(http://redis.cn/commands.html)
接下来逐个介绍操作各类型的命令
1. string
- string是redis最基本的类型
- 最大能存储512MB数据
- string类型是二进制安全的,即可以为任何数据,比如数字、图片、序列化对象等
启动redis服务
sudo redis-server /etc/redis/redis.conf
或者
sudo service redis start
启动客户端:在新终端中运行如下代码
redis-cli
启动后效果如图
1.1 设置键值
1.1.1 设置键值
set key value
1.1.2 设置键值及过期时间,以秒为单位
SETEX key seconds value
1.1.3 设置多个键值--mset
注意,key和值不加引号也可以
MSET key value [key value ...] 案例: mset 'key1' 'hello' 'key2' 'world'
1.2 获取
1.2.1 根据键获取值,如果不存在此键则返回nil,get不区分大小写
根据键获取值,如果不存在此键则返回nil,get不区分大小写也就是说get和GET都可以得到数据
GET key
1.2.2 根据多个键获取多个值--mget
MGET key [key ...] 案例 mget 'key1' 'key2'
1.3 运算
- 要求:值是数字
1.3.1 将key对应的value加1
INCR key
1.3.2 将key对应的value加整数
INCRBY key increment
1.3.3 将key对应的value减1
DECR key
1.3.4 将key对应的value减整数
DECRBY key decrement
1.4 其它
1.4.1 追加值
APPEND key value
1.4.2 获取值长度
STRLEN key
2. 键的命令
2.1 查找键,参数支持正则
KEYS pattern
2.2 判断键是否存在,如果存在返回1,不存在返回0
EXISTS key [key ...]
2.3 查看键对应的value的类型
TYPE key
2.4 删除键及对应的值,删除成功返回1,失败返回0
DEL key [key ...]
2.5 设置和查看过期时间,以秒为单位
为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
可以对一个已经带有生存时间的 key 执行 EXPIRE 命令,新指定的生存时间会取代旧的生存时间。没有过期的是-1,过期的是-2
EXPIRE key seconds
查看有效时间,以秒为单位
TTL key
3. hash
hash用于存储对象,对象的格式为键值对
hash 特别适合用于存储对象。相对于将对象的每个字段存成单个string 类型。将一个对象
存储在hash 类型中会占用更少的内存,并且可以更方便的存取整个对象。省内存的原因是
新建一个hash 对象时开始是用zipmap(又称为small hash)来存储的。这个zipmap 其实并
不是hash table,但是zipmap 相比正常的hash 实现可以节省不少hash 本身需要的一些元
数据存储开销。尽管zipmap 的添加,删除,查找都是O(n),但是由于一般对象的field
数量都不太多。所以使用zipmap 也是很快的,也就是说添加删除平均还是O(1)。如果field
或者value 的大小超出一定限制后,redis 会在内部自动将zipmap 替换成正常的hash 实现.
这个限制可以在配置文件中指定。
hash-max-zipmap-entries 64 #配置字段最多64 个
hash-max-zipmap-value 512 #配置value 最大为512 字节
hset key field value 设置 hash field为指定值,如果 key不存在,则创建
返回值:
如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。
如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0 。
3.1 设置属性
3.1.1 设置单个属性--HSET
HSET key field value
例子:
hset py3 name '郭靖'
3.1.2 设置多个属性--HMSET
HMSET key field value [field value ...]
hmset py4 name 'gj' gender '0'
3.2 获取属性
3.2.1 获取一个属性的值--HGET
HGET key field
3.2.2 获取多个属性的值--HMGET
HMGET key field [field ...]
3.2.3 获取所有属性和值--HGETALL
HGETALL key
3.2.4 获取所有的属性--HKEYS
HKEYS key
3.2.4 返回包含属性的个数--HLEN
HLEN key
3.2.5 获取所有值--HVALS
HVALS key
3.3 其它
3.3.1 判断属性是否存在--
HEXISTS key field
3.3.2 删除属性及值
HDEL key field [field ...]
4. list(后面的爬虫用到)
1)列表的元素类型为string
2)按照插入顺序排序
3)在列表的头部或者尾部添加元素
4.1 设置
4.1.1 在头部插入数据--LPUSH--(爬虫用到这个语句)
语法和案例
LPUSH key value [value ...]
案例
lpush py5 'abc' 'efg'
查看数据
查看类型
4.1.2 在尾部插入数据--RPUSH
RPUSH key value [value ...]
具体案例
rpush py5 'hij' 'qlm'
查看插入的数据
4.1.3 在一个元素的前|后插入新元素--linsert
LINSERT key BEFORE|AFTER pivot value
具体案例:
linsert py5 after 'abc' '123'
- 设置指定索引的元素值
- 索引是基于0的下标
- 索引可以是负数,表示偏移量是从list尾部开始计数,如-1表
4.1.4 示列表的最后一个元素--LSET
LSET key index value
4.2 获取
4.2.1 返回存储在 key 的列表里指定范围内的元素
- start 和 end 偏移量都是基于0的下标
- 偏移量也可以是负数,表示偏移量是从list尾部开始计数,如-1表示列表的最后一个元素
LRANGE key start stop
例如:
lrange py5 2 -1
4.2.2 移除并且返回 key 对应的 list 的第一个元素
LPOP key
lpop py5
4.2.3 移除并返回存于 key 的 list 的最后一个元素
RPOP key
案例:
ppop py5
4.3 其它
4.3.1 裁剪列表,改为原集合的一个子集
- start 和 end 偏移量都是基于0的下标
- 偏移量也可以是负数,表示偏移量是从list尾部开始计数,如-1表示列表的最后一个元素
LTRIM key start stop
案例:
ltrim py5 1 2
4.3.2 返回存储在 key 里的list的长度
LLEN key
4.3.3 返回列表里索引对应的元素
LINDEX key index
5. set
1)无序集合
2)元素为string类型
3)元素具有唯一性,不重复
5.1 设置
5.1.1 添加元素--SADD
SADD key member [member ...]
例子:
sadd py6 abc 123 defg
5.2 获取
5.2.1 返回key集合所有的元素--SMEMBERS
SMEMBERS key
例子:得到py6集合所有元素
smembers py6
5.2.2 返回集合元素的个数--SCARD
SCARD key
例如:查看py6集合元素的个数
scard py6
5.3 其它
5.3.1 准备数据
sadd py6 defg 123 abc 456
sadd py7 abc 123 world 456
5.3.2 求多个集合的交集--sinter
SINTER key [key ...]
例如:求py6和py7两个集合的交集
sinter py6 py7
- 求某集合与其它集合的差集
SDIFF key [key ...]
例如:从py6中减掉py7中py6也存在的元素
sdiff py6 py7
例如:从py7中减掉py6中py7也存在的元素
sdiff py7 py6
- 求多个集合的合集
SUNION key [key ...]
例如:
sunion py6 py7
- 判断元素是否在集合中
SISMEMBER key member
6. zset
1)sorted set,有序集合
2)元素为string类型
3)元素具有唯一性,不重复
4)每个元素都会关联一个double类型的score,表示权重,通过权重将元素从小到大排序
5)元素的score可以相同
6.1 设置
6.1.1 添加--ZADD
ZADD key score member [score member ...]
6.2 获取
6.2.1 返回指定范围内的元素--ZRANGE
ZRANGE key start stop
6.2.2 返回元素个数--ZCARD
ZCARD key
6.2.3 返回有序集key中,score(权重)值在min和max之间的成员--ZCOUNT
ZCOUNT key min max
6.2.4 返回有序集key中,成员member的score值--ZSCORE
ZSCORE key member
四、高级
- 主要讨论发布订阅模块、主从配置两个知识点
1. 发布订阅
发布者不是计划发送消息给特定的接收者(订阅者),而是发布的消息分到不同的频道,不需要知道什么样的订阅者订阅。
订阅者对一个或多个频道感兴趣,只需接收感兴趣的消息,不需要知道什么样的发布者发布的。
发布者和订阅者的解耦合可以带来更大的扩展性和更加动态的网络拓扑。
客户端发到频道的消息,将会被推送到所有订阅此频道的客户端。
客户端不需要主动去获取消息,只需要订阅频道,这个频道的内容就会被推送过来。
1.1 消息的格式
- 推送消息的格式包含三部分
- part1:消息类型,包含三种类型
- subscribe,表示订阅成功
- unsubscribe,表示取消订阅成功
- message,表示其它终端发布消息
- 如果第一部分的值为subscribe,则第二部分是频道,第三部分是现在订阅的频道的数量
- 如果第一部分的值为unsubscribe,则第二部分是频道,第三部分是现在订阅的频道的数量,如果为0则表示当前没有订阅任何频道,当在Pub/Sub以外状态,客户端可以发出任何redis命令
- 如果第一部分的值为message,则第二部分是来源频道的名称,第三部分是消息的内容
1.2 命令
1.2.1 切换数据
select 0
或者
select 1
1.2.2 订阅
说明,
SUBSCRIBE 频道名称 [频道名称 ...]
subscribe频道名称 [频道名称 ...]
1.2.3 在另外一个终端上
1.2.4 取消订阅
直接使用Ctrl+c退出也可以取消订阅
- 如果不写参数,表示取消所有订阅
UNSUBSCRIBE 频道名称 [频道名称 ...]
或者
unsubscribe 频道名称 [频道名称 ...]
因为订阅平道后当前终端处于阻塞状态,无法输入取出订阅命令,那么在使用Python代码交互的时候,使用该该命令用于取消订阅某个或者一些频道。
1.2.5 发布
PUBLISH 频道 消息
或者
publish 平道 消息
1.2.6 接收的两个终端
2. 主从配置
一个master可以拥有多个slave,一个slave又可以拥有多个slave,如此下去,形成了强大的多级服务器集群架构。
比如,将ip为192.168.1.10的机器作为主服务器,将ip为192.168.1.11的机器作为从服务器。
2.1. 设置主服务器的配置
使用vim或者其他文本工具找到redis.conf配置文件
sudo vim /etc/redis/redis.conf
设置主服务器的配置
bind 192.168.1.29
保存退出,并且重启redis服务器
2.2. 设置从服务器的配置
使用vim或者其他文本工具找到redis.conf配置文件
sudo vim /etc/redis/redis.conf
注意:在slaveof后面写主机ip,再写端口,而且端口必须写
bind 192.168.1.91
slaveof 192.168.1.29 6379
保存退出,并且重启redis服务器
2.3. 在主服务器
在master和slave分别执行info命令,查看输出信息
2.3.1 进入客户端
redis-cli -h 192.168.1.29
2.3.2 在master上写数据
set hello world
2.3.3 在slave上读数据
Slave机器上自己的ip,一定要指定,否则会没有数据
get hello
五、Redis与Python交互
1. 安装包
到中文官网(http://redis.cn/clients.html)查找客户端代码
1.1 安装方式一:联网命令安装
sudo pip3 install redis
1.2 安装方式二:使用源码安装
unzip redis-py-master.zip cd redis-py-master sudo python setup.py install
2. 交互代码
2.1 引入模块
import redis
2.2 连接
try: red = redis.StrictRedis(host="192.168.31.114",port=6379) except Exception as e: print(e)
- 方式一:根据数据类型的不同,调用相应的方法,完成读写
- 更多方法同前面学的命令
else: #name是key,hello world是值 red.set("name","hello world") #根据key得到值 print(red.get("name"))#b'hello world'
运行效果:
- 方式二:pipline
- 缓冲多条命令,然后一次性执行,减少服务器-客户端之间TCP数据库包,从而提高效率
#缓冲多条命令,然后一次性执行,减少服务器-客户端之间TCP数据库包,从而提高效率 pipe = red.pipeline() pipe.set('name', 'world') print(pipe.get('name')) #执行命令 pipe.execute()
3. 封装
- 连接redis服务器部分是一致的
- 这里将string类型的读写进行封装
模块test.py代码如下:
import redis class RedisHelper(): def __init__(self,host='192.168.31.114',port=6379): self.__redis = redis.StrictRedis(host, port) def get(self,key): if self.__redis.exists(key): return self.__redis.get(key) else: return "" def set(self,key,value): self.__redis.set(key,value) if __name__ == "__main__": red = RedisHelper() #name是key,hello world是值 red.set("name","hello world") #根据key得到值 print(red.get("name"))#b'hello world'
六、综合案例:用户登录
1. 需求说明
业务过程如下:
输入用户名、密码
密码加密
判断redis中是否记录了用户名,如果有则成功
如果redis中没有用户名,则到mysql中查询
从mysql中查询成功后,将用户名记录到redis中
2. 代码实现
2.1 封装好的MySQL工具模块MySQLUtils.py
import pymysql import redis class RedisHelp(object): def __init__(self,host="192.168.31.114",port=6379): self.__redis = redis.StrictRedis(host=host,port=port) def get(self,key): if self.__redis.exists(key): return self.__redis.get(key) else: return "" def set(self,key,value): self.__redis.set(key,value) class MySQLHelp(object): def __init__(self,host): self.host=host self.port=3306 self.user='afu' self.password='123456' self.db='python2' self.charset='utf8' #建立链接 def open(self): self.conn = pymysql.connect( host=self.host, port=self.port, user=self.user, password=self.password, db=self.db, charset=self.charset ) #执行sql语句和查询数据 self.cursor = self.conn.cursor() #关闭链接,释放资源 def close(self): self.cursor.close() self.conn.close() #得到所以数据 def get_all(self,sql,params=[]): result = None try: #打开链接 self.open() self.cursor.execute(sql,params) #得到数据 result = self.cursor.fetchall() return result except Exception as e: print(e) # self.close() return result # 得到所以数据 def get_one(self, sql, params=[]): result = None try: # 打开链接 self.open() self.cursor.execute(sql, params) # 得到数据 result = self.cursor.fetchone() self.close() return result except Exception as e: print(e) return result #增加,删除,修改--提交事务 def cul(self, sql, params=[]): try: # 打开链接 self.open() count = self.cursor.execute(sql, params) # 提交事务 self.conn.commit() self.close() print("执行完毕!") return count except Exception as e: print(e) return None #增加数据 def insert(self,sql,params=[]): return self.cul(sql,params) #删除 def delete(self,sql,params=[]): return self.cul(sql,params) # 修改 def update(self, sql, params=[]): return self.cul(sql, params)
2.2 实现用户登录的代码
from hashlib import sha1 re = RedisHelp(host="localhost") name = input("请输入登录账号:") password = input("请登录密码:") #加密后的密码 password = sha1(password.encode()).hexdigest() uname = re.get("uname").decode("utf-8") upwd = re.get("upwd").decode("utf-8") print(type(uname)) print(uname == name) if str(uname) == name: if str(upwd) == password: print("登录成功,数据来自redis数据库") else: print("密码错误,数据来自redis数据库") else: help = MySQLHelp("localhost") #(id,uname,upwd) sql = "select * from userinfos where uname = %s" params = [name] result = help.get_one(sql,params) # print(result) if result == None: print("账号不存在") else: if result[1] == name and result[2] ==password: print("登录成功!来自mysql") #保存数据到redis下次可以使用 re.set("uname",name) re.set("upwd",password) else: print("密码错误!")
运行结果: