Python全栈开发之Redis

No.1 NoSQL

  • 不支持SQL语句
  • 存储结构和关系数据库不同,而是采用key-value模式
  • NoSQL类型的数据没有一种通用的语言,每种数据库都有自己的api和适用场景
    • Mongodb
    • Redis
    • Hbase hadoop
    • Cassandra hadoop

      No.2 NoSQL和SQL数据的区别

  • 使用场景不同,SQL数据库适用于关系特别复杂的数据查询场景,NoSQL反之
  • SQL支持事务,NoSQL基本不支持事务

No.3 Redis

简介

  • Redis是一个采用C语言编写的开源的、支持网络、可持久化日志型、key-value数据库,并支持多种编程语言的api
  • Redis是NoSQL的一种,它支持多种key-value数据类型,借助一些高层级的接口使其可以胜任,如缓存、队列

特性

  • Redis支持数据的持久化,可以将内存的数据存储在磁盘中,重启时可以再次加载进行使用
  • Redis不仅仅支持简单的key-value类型的数据,还支持list、set、zset、hash等数据类型的数据
  • Redis支持数据的备份,即master-salve模式数据备份

优势

  • 性能极高,读110000次/s,写81000次/s
  • 丰富的数据类型,支持二进制的Strings、Lists、Hashes、Sets及Ordered Sets数据类型操作
  • 原子性操作,并且还支持几个操作合并后的原子性执行
  • 丰富的特性,支持publish/subscribe,通知,key过期等特性

应用场景

  • 用来做缓存
  • 可以在某些场景替代传统数据库(社交类应用)
  • 在大型系统中实现session共享、购物车等

参考资料

No.4 安装与配置

安装

# 下载
wget http://download.redis.io/releases/redis-3.2.8.tar.gz 
# 解压
tar -zxvf redis-3.2.8.tar.gz
# 将解压出来的文件放到/usr/local/redis目录
sudo mv ./redis-3.2.8 /usr/local/redis/ 
# 切换到/usr/local/redis目录
cd /usr/local/redis/ 
# 生成
sudo make 
# 生成测试
sudo make test 
会发现报错
You need tcl 8.5 or newer in order to run the Redis test 
Makefile:225: recipe for target ‘test’ failed 
make: * [test] Error 1
所以,我们要安装依赖
wget http://downloads.sourceforge.net/tcl/tcl8.6.1-src.tar.gz  
tar xzvf tcl8.6.1-src.tar.gz  -C /usr/local/ 
cd  /usr/local/tcl8.6.1/unix/
sudo ./configure  
sudo make  
sudo make install
再次执行测试
sudo make test
# 将redis的命令安装到/usr/local/bin/⽬录
sudo make install
# 进入/usr/local/bin目录查看
cd /usr/local/bin
ls -all
- redis-server redis服务器
- redis-cli redis命令行客户端
- redis-benchmark redis性能测试工具
- redis-check-aof AOF文件修复工具
- redis-check-rdb RDB文件检索工具
将配置文件拷贝到/etc/redis目录下
sudo cp /usr/local/redis/redis.conf /etc/redis/

配置

# 绑定ip:指定redis只接受此IP的请求,如果不设置,则默认接收所有请求
bind 127.0.0.1
# 端口,默认为6379
port 6379
# TCP连接中已完成队列(完成三次握手之后)的长度
tcp-backlog 511
# 是否以守护进程运行
daemonize yes
# 数据文件
dbfilename dump.rdb
# 数据⽂件存储路径
dir /var/lib/redis
# ⽇志⽂件
logfile /var/log/redis/redis-server.log
# 数据库,默认有16个
database 16
# 主从复制,类似于双机备份。
slaveof

命令

服务器

# 使用help查看把帮助文档
redis-server --help
# 启动
sudo service redis start
# 停止
sudo service redis stop
# 重启
sudo service redis restart
# 查看服务器进程
ps -ef|grep redis
# 杀掉redis服务器
sudo kill -9 pid
# 以指定配置文件运行redis服务器
sudo redis-server /etc/redis/redis.conf

客户端

# 使用help查看帮助
redis-cli --help
# 连接redis
redis-cli -h host -p port -a password
# 切换数据库
select 2

No.5 数据交互

String

  • 字符串类型是Redis中最为基础的数据存储类型,它在是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等,在Redis中字符串类型的Value最多可以容纳的数据长度是512M

保存

# 设置键值
set key value
# 设置键值及过期时间,以秒为单位
setex key seconds value
# 一次设置多个值
mset key1 value1 key2 value2
# 追加值
append key value

获取

# 根据键获取值,键不存在返回nil
get key
# 根据键获取多个值
mget key1 key2

键命令(支持所有数据类型)

# 查找键,参数支持正则表达式
keys pattern
# 判断键是否存在,存在返回1,否则返回0
exists key
# 查看键对应的值的类型
type key
# 删除键及对应的值
del key

栗子

set name kernel 设置键为name,value为kernel
setex name 5 kernel 设置键为name,value为kernel,过期时间为5秒
mset name1 kernel name2 kernel 设置键为name1,值为kernel1,键为name2,值为kernel2
append name kernel 为键name追加kernel,结果为kernelkernel
get name 获取键name对应的值,结果为kernel
mget name1,name2 获取键name1,name2,返回结果为kernel1,kernel2
keys * 查看所有键
keys a* 查看a开头的所有键
exists name 查看键name是否存在,返回1
type name 查看键name对应的值的类型,返回string
del name1 name2 删除name1,name2及它们对应的值

hash

  • hash⽤于存储对象,对象的结构为属性、值
  • 值的类型为string

保存

# 设置单个属性
hset key field value
# 设置多个属性
hmset key field1 value1 field2 value2

获取

# 获取键所有的属性
hkeys key
# 获取一个属性的值
hget key field
# 获取多个属性的值
hmget key field field
# 获取所有的值
hvals key

删除

# 删除整个hash键和值,可以使用上文中的del命令
# 删除属性,属性对应的值也会响应删除
hdel key field1 field2

栗子

hset user name kernel 设置user的name属性为kernel
hmset user name kernel password 123 设置user的name属性为kernel,密码属性为123
hkeys user 获取user的所有键
hget user name 获取user的name属性值
hmget key name password 获取user的name,password属性值
hvals user 获取user的所有属性值
hdel user name 删除user的name属性

list

  • 列表的元素类型为string
  • 按照插⼊顺序排序

增加

# 在左侧添加数据
lpush key value1 value2
# 在右侧添加数据
rpush key value1 value2
# 在指定元素的前后添加数据
linsert key before或after 指定元素 欲添加数据
# 设置指定索引位置的值
lset key index value

获取

# 返回列表里指定范围内的元素
lrange key start stop 

删除

# 删除指定元素,将列表前count次出现的值为value的元素全部删除,如果count>0,就从头到尾移除,直到移除够count个,如果count<0,就从后向前移除,同样移除count的绝对值个,如果count=0,就移除所有
lrem key count value

栗子

lpush num 1 2 3 在左侧添加数据
rpush num 4 5 6 在右侧添加数据
linsert num before 2 8 在2的前面添加8
lset num 1 10 将索引为1的值修改为10
lrange num 1 5 返回num索引从1到5的值
lrem num 1 2 从左到右删除2个1

set

  • ⽆序集合
  • 元素为string类型
  • 元素具有唯⼀性,不重复
  • 集合没有修改操作

增加

# 添加元素
sadd key member1 member2

获取

# 返回所有的元素
smembers key

删除

# 删除指定元素
srem key member

栗子

sadd names zhangsan lisi wangwu 向键为names的集合中添加zhangsan、lisi、wangwu
smembers names 获取键为names的集合中的所有元素
srem names wangwu 删除键为name是集合中的元素wangwu

zset

  • 有序集合
  • 元素为string类型
  • 元素具有唯⼀性,不重复
  • 每个元素都会关联⼀个double类型的score,表示权重,通过权重将元素从⼩到⼤排序

增加

# 向键的集合中添加权重为score1,score2的元素
zadd key score1 member1 score2 member2

获取

# 返回指定范围的元素
zrange key start stop
# 返回权重值在min和max之间的元素
zrangebyscore key min max
# 返回成员member的score值

删除

# 删除指定元素
zrem key member member2
# 删除权重在指定范围的元素
zremrangebyscore key min max

栗子

zadd names 4 lisi 5 wangwu 6 zhangsan 向键为names添加元素lisi、wangwu、zhangsan,权重分别是4 5 6
zrange names 0 -1 获取键为names的集合中的所有元素
zrangebyscore names 4 5 获取键为names的集合中权重在4到5之间的成员
zscore names lisi 获取键为names的集合中元素lisi的权重
zrem names zhangsan 删除键为names的集合中元素张三
zremrangebyscore names 4 5 删除键为names的集合中权重在4到5之间的元素

No.6 与Python交互

方法

StrictRedis对象⽅法

# 通过init创建对象,指定参数host、port与指定的服务器和端⼝连接,host默认为localhost,port默认为6379,db默认为0
sr = StrictRedis(host='localhost', port=6379, db=0)

string

  • set
  • setex
  • mset
  • append
  • get
  • mget
  • key

keys

  • exists
  • type
  • delete
  • expire
  • getrange
  • ttl

hash

  • hset
  • hmset
  • hkeys
  • hget
  • hmget
  • hvals
  • hdel

list

  • lpush
  • rpush
  • linsert
  • lrange
  • lset
  • lrem

set

  • sadd
  • smembers
  • srem

zset

  • zadd
  • zrange
  • zrangebyscore
  • zscore
  • zrem
  • zremrangebyscore

django存储session

安装

pip install django-redis-sessions==0.5.6

修改setting.py文件

SESSION_ENGINE = 'redis_sessions.session'
SESSION_REDIS_HOST = 'localhost'
SESSION_REDIS_PORT = 6379
SESSION_REDIS_DB = 2
SESSION_REDIS_PASSWORD = ''
SESSION_REDIS_PREFIX = 'session'

测试

# 打开app01/views.py,创建视图
def session_set(request):
      request.session['name']='kernel'
      return HttpResponse('ok')

  def session_get(request):
      name=request.session['name']
      return HttpResponse(name)
# 配置url
# 在乌班图连接redis并切换到上文指定的数据库
redis-cli
select 2
keys *  # 1) "session:xddes84rchmy8ejsph43cqrrnaii31ji"
get session:xddes84rchmy8ejsph43cqrrnaii31ji
通过base64解密

No.7 主从服务

主从概念

  • 一个master可以拥有多个slave,一个slave又可以拥有多个slave,如此就形成了庞大的集群架构
  • master主要被用来写数据,slave被用来读数据
  • 通过主从配置实现读写分离

主从配置

配置主

# 查看当前主机的ip地址
ifconfig
# 修改/etc/redis/redis.conf
sudo vim /etc/redis/redis.conf
bind 192.168.1.189
# 重启redis服务
sudo service redis stop
redis-server /etc/redis/redis.conf

配置从

# 复制主服务的配置文件
sudo cp /etc/redis/redis.conf slave.conf
# 修改redis/slave.conf文件
sudo vi slave.conf 
bind 192.168.1.200
slaveof 192.168.1.189 6379
# 启动redis服务
sudo redis-server /etc/redis/slave.conf
# 查看主从关系
redis-cli -h 192.168.1.189 -p 6379 info Replication

No.8 Redis集群

为什么要有集群?

之前介绍了主从的概念,可以一主多从,但是如果当访问量过多,例如双11,主服务器肯定会挂掉,大公司一般会有很多的服务器,放在不同的地方,这样呢,一来保证数据的安全性,二来保证用户连接到的那台服务器离自己最近,通信质量最好

集群的概念?

集群是一组相互独立的通过网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理,一个客户与集群相互作用时,集群像是一个独立的服务器,集群配置是用于提高可用性和可缩放性

Redis集群分类

  • 软件

    • 只有一台主机,在这台主机上安装了许多个服务
  • 硬件

    • 存在于多台主机,每台电脑上都启动了redis服务或多个服务器

配置机器1

创建⽂件9420.conf

port 9420
bind 192.168.1.189
daemonize yes # 守护进程
pidfile 9420.pid # 将运行后的进程ID写到9420.pid文件中
cluster-enabled yes # 开启集群
cluster-config-file 9420_node.conf # 集群节点的配置文件
cluster-node-timeout 15000 # 连接集群节点的超时时间
appendonly yes

创建文件9421.conf

port 9421
bind 192.168.1.189
daemonize yes
pidfile 9421.pid
cluster-enabled yes
cluster-config-file 9421_node.conf
cluster-node-timeout 15000
appendonly yes

创建文件9422.conf

port 9422
bind 192.168.1.189
daemonize yes
pidfile 9422.pid
cluster-enabled yes
cluster-config-file 9422_node.conf
cluster-node-timeout 15000
appendonly yes

使⽤配置⽂件启动redis服务

redis-server 9420.conf
redis-server 9421.conf
redis-server 9422.conf

配置机器2

创建文件9423.conf

port 9423
bind 192.168.1.200
daemonize yes
pidfile 9423.pid
cluster-enabled yes
cluster-config-file 9423_node.conf
cluster-node-timeout 15000
appendonly yes

创建文件9424.conf

port 9424
bind 192.168.1.200
daemonize yes
pidfile 9424.pid
cluster-enabled yes
cluster-config-file 9424_node.conf
cluster-node-timeout 15000
appendonly yes

创建文件9425.conf

port 9425
bind 192.168.1.200
daemonize yes
pidfile 9425.pid
cluster-enabled yes
cluster-config-file 9425_node.conf
cluster-node-timeout 15000
appendonly yes

使⽤配置⽂件启动redis服务

redis-server 9423.conf
redis-server 9424.conf
redis-server 9425.conf

创建集群

  • redis的安装包中包含了redis-trib.rb,⽤于创建集群
  • 接下来的操作在192.168.1.189机器上进⾏
  • 将命令复制,这样可以在任何⽬录下调⽤此命令
sudo cp /usr/share/doc/redis-tools/examples/redis-trib.rb /usr/local/bin/
  • 安装ruby环境,因为redis-trib.rb是⽤ruby开发的
sudo apt-get install ruby
  • 运⾏如下命令创建集群
redis-trib.rb create --replicas 1 192.168.1.189:9420 192.168.1.189:9421 192.168.1.189:9422 192.168.1.200:9423 192.168.1.200:9424 192.168.1.200:9425
# 可能会报错,因为安装的ruby不是最新的
gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
sudo gem install redis 通过 gem 安装 redis 的相关依赖
# 然后重新执⾏指令

如何确定哪个服务器上写数据:CRC16

redis集群在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的,每个节点都保存各自的数据和整个集群的状态,每个节点都和其他所有节点相连接,而且这些连接处于活跃状态,这样保证我们获取到其他节点的数据,redis采用一种叫哈希槽的方式来分配数据,redis集群默认分为16384的槽,当我们设计一个key时,会用一种名叫CRC16的算法来取模得到所属的槽,然后将key分到这个槽的节点上,redis集群会把数据放到master节点上,然后这个master和其对应的slave之间进行数据同步,当读取数据时,也根据⼀致性哈希算法到对应的master节点获取数据,只有当⼀个master挂掉之后,才会启动⼀个对应的salve节点,充当 master

与Python交互

安装包

pip install redis-py-cluster

栗子

from rediscluster import *
if __name__ == '__main__':
  try:
      # 构建所有的节点,Redis会使⽤CRC16算法,将键和值写到某个节点上
      startup_nodes = [
          {'host': '192.168.1.189', 'port': '9420'},
          {'host': '192.168.1.189', 'port': '9421'},
          {'host': '192.168.1.189', 'port': '9422'},
      ]
      # 构建StrictRedisCluster对象
      src=StrictRedisCluster(startup_nodes=startup_nodes,decode_responses=True)
      # 设置键为name、值为itheima的数据
      result=src.set('name','kernel')
      print(result)
      # 获取键为name
      name = src.get('name')
      print(name)
  except Exception as e:
      print(e)

参考资料

猜你喜欢

转载自blog.51cto.com/13559120/2306796
今日推荐