redis缓存数据库及Python操作redis

缓存数据库介绍

 NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库,随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,

特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。

NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。

 

NoSQL数据库的四大分类:

键值(Key-Value)存储数据库:

这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署。

但是如果DBA只对部分值进行查询或更新的时候,Key/value就显得效率低下了。[3]  举例如:Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB.

 

列存储数据库:

这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。如:Cassandra, HBase, Riak.

 

文档型数据库:

文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。

文档型数据库可 以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高。如:CouchDB, MongoDb. 国内也有文档型数据库SequoiaDB,已经开源。

 

图形(Graph)数据库:

图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API。[2]  如:Neo4J, InfoGrid, Infinite Graph.
因此,我们总结NoSQL数据库在以下的这几种情况下比较适用:1、数据模型比较简单;2、需要灵活性更强的IT系统;3、对数据库性能要求较高;4、不需要高度的数据一致性;5、对于给定key,比较容易映射复杂值的环境。

 

 

NoSQL数据库的四大分类表格分析:

分类Examples举例典型应用场景数据模型优点缺点
键值(key-value)[3]  Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。[3]  Key 指向 Value 的键值对,通常用hash table来实现[3]  查找速度快 数据无结构化,通常只被当作字符串或者二进制数据[3] 
列存储数据库[3]  Cassandra, HBase, Riak 分布式的文件系统 以列簇式存储,将同一列数据存在一起 查找速度快,可扩展性强,更容易进行分布式扩展 功能相对局限
文档型数据库[3]  CouchDB, MongoDb Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) Key-Value对应的键值对,Value为结构化数据 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 查询性能不高,而且缺乏统一的查询语法。
图形(Graph)数据库[3]  Neo4J, InfoGrid, Infinite Graph 社交网络,推荐系统等。专注于构建关系图谱 图结构 利用图结构相关算法。比如最短路径寻址,N度关系查找等 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。[3] 

redis

介绍:

 

redis是业界主流的key-value nosql 数据库之一。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、

zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。

在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,

数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

 

 

 

 

 

 

 

 

Redis优点:

 

  • 异常快速 : Redis是非常快的,每秒可以执行大约110000设置操作,81000个/每秒的读取操作。

  • 支持丰富的数据类型 : Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。

    这使得在应用中很容易解决的各种问题,因为我们知道哪些问题处理使用哪种数据类型更好解决。
  • 操作都是原子的 : 所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器得到的是更新后的值(最新值)。

  • MultiUtility工具:Redis是一个多功能实用工具,可以在很多如:缓存,消息传递队列中使用(Redis原生支持发布/订阅),在应用程序中,如:Web应用程序会话,网站页面点击数等任何短暂的数据;

 

 

安装Redis环境:

yum安装redis

1.yum安装

#前提得配置好阿里云yum源,epel源
#查看是否有redis包
yum list redis
#安装redis
yum install redis -y
#安装好,启动redis
systemctl start redis

复制代码

 

 
 
2.检测redis是否工作
 
redis-cli    #redis 客户端工具
#进入交互式环境后,执行ping,返回pong表示安装成功
127.0.0.1:6379> ping
PONG

 

 

源码安装redis,编译安装

编译安装的优势是:

  • 编译安装时可以指定扩展的module(模块),php、apache、nginx都是一样有很多第三方扩展模块,如mysql,编译安装时候,如果需要就定制存储引擎(innodb,还是MyIASM)
  • 编译安装可以统一安装路径,linux软件约定安装目录在/opt/下面
  • 软件仓库版本一般比较低,编译源码安装可以根据需求,安装最新的版本
 
1.下载redis源码
wget http://download.redis.io/releases/redis-4.0.10.tar.gz
2.解压缩
tar -zxf redis-4.0.10.tar.gz
3.切换redis源码目录
cd redis-4.0.10.tar.gz
4.编译源文件
make 
5.编译好后,src/目录下有编译好的redis指令
6.make install 安装到指定目录,默认在/usr/local/bin

 

redis可执行文件
./redis-benchmark //用于进行redis性能测试的工具
./redis-check-dump //用于修复出问题的dump.rdb文件
./redis-cli //redis的客户端
./redis-server //redis的服务端
./redis-check-aof //用于修复出问题的AOF文件
./redis-sentinel //用于集群管理

redis配置文件

redis配置文件名为
redis.conf 
这个文件可以自定义

 

 

redis.conf核心配置项

绑定ip,如需要远程访问,需要填写服务器ip
bind 127.0.0.1  

端口,redis启动端口
port 

守护进程方式运行
daemonize yes

rdb数据文件
dbfilename dump.rdb

数据文件存放路径
dir /var/lib/redis/

日志文件
logfile /var/log/redis/redis-server.log

主从复制
slaveof 

 

 

启动redis服务端

启动redis非常简单,直接./redis-server就可以启动服务端了,还可以用下面的方法指定要加载的配置文件:
./redis-server ../redis.conf
默认情况下,redis-server会以非daemon的方式来运行,且默认服务端口为6379。

 使用redis客户端

#执行客户端命令即可进入
./redis-cli  
#测试是否连接上redis
127.0.0.1:6379 > ping
返回pong代表连接上了

//用set来设置key、value
127.0.0.1:6379 > set name "luban"
OK
//get获取name的值
127.0.0.1:6379 > get name
"luban"

 Redis安全防护

为我们的redis数据库加层防护,防止被黑客攻击

 https://www.cnblogs.com/yunwangjun-python-520/p/11623692.html

redis数据结构

redis是一种高级的key:value存储系统,其中value支持五种数据类型
字符串(strings)
散列(hashes)
列表(lists)
集合(sets)
有序集合(sorted sets)

 

 基本命令

keys *         查看所有key
type key      查看key类型
expire key seconds    过期时间
ttl key     查看key过期剩余时间        -2表示key已经不存在了
persist     取消key的过期时间   -1表示key存在,没有过期时间

exists key     判断key存在    存在返回1    否则0
del keys     删除key    可以删除多个
dbsize         计算key的数量

 

 

 

 

 

Python操作Redis:

sudo pip install redis

 

 
 

Redis API使用

redis-py 的API的使用可以分类为:

  • 连接方式
  • 连接池
  • 操作
    • String 操作
    • Hash 操作
    • List 操作
    • Set 操作
    • Sort Set 操作
  • 管道
  • 发布订阅
 

连接方式

1、操作模式

redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,

Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。

 
import redis
r = redis.Redis(host='localhost', port=6379)
r.set('foo', 'Bar')
print r.get('foo')

 

2、连接池

redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。

可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。

 

连接池code:

 

#Author:Yun

import redis

pool = redis.ConnectionPool(host='localhost',port = 6379)

r = redis.Redis(connection_pool=pool)
r.set('foo2','Bar2')
print(r.get('foo2'))

 

 操作

1. String操作

 
redis中的String在在内存中按照一个name对应一个value来存储。如图:
 

 

set(name, value, ex=None, px=None, nx=False, xx=False)

 

 

在Redis中设置值,默认,不存在则创建,存在则修改
参数:
     ex,过期时间(秒)
     px,过期时间(毫秒)
     nx,如果设置为True,则只有name不存在时,当前set操作才执行
     xx,如果设置为True,则只有name存在时,岗前set操作才执行
127.0.0.1:6379> set  name luban
OK
127.0.0.1:6379> set  age 22
OK
127.0.0.1:6379> keys *  #获取所有的key
1) "age"
2) "name"
 
127.0.0.1:6379> set  name  hehe  ex 2#ex 2是指只存活两秒钟
OK
127.0.0.1:6379> get name
(nil)
 

 

 

setnx(name, value)

设置值,只有name不存在时,执行设置操作(添加)

 

 

get(name)

获取值

127.0.0.1:6379> get  name
"luban"
127.0.0.1:6379> get age
"22"
 

 

 

setex(name, value, time)

设置值
 参数:
 time,过期时间(数字秒 或 timedelta对象)

 

 

 mget(keys, *args)

批量获取

127.0.0.1:6379> mget n1  n2#批量获取Value
1) "1"
2) "2"

 

 

 getset(name, value)

1 设置新值并获取原来的值
127.0.0.1:6379> set  n3  3
OK
127.0.0.1:6379> get n3
"3"
127.0.0.1:6379> getset n3  hehe#设置新值并获取原来的值
"3"

 

 

getrange(key, start, end)

获取子序列(根据字节获取,非字符)
# 参数:
    # name,Redis 的 name
    # start,起始位置(字节)
    # end,结束位置(字节)
# 如: "武沛齐" ,0-3表示 "武"

127.0.0.1:6379> set  n1 luban
OK
127.0.0.1:6379> getrange  n1  0  2#切片
"lub"

 

 

 setrange(name, offset, value)

 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
# 参数:
    # offset,字符串的索引,字节(一个汉字三个字节)
    # value,要设置的值
127.0.0.1:6379> set n2 houyi
OK
127.0.0.1:6379> setrange  n2  1 ee#替换
(integer) 5
127.0.0.1:6379> get n2
"heeyi"
 
 
setbit(name, offset, value)
# 对name对应值的二进制表示的位进行操作
 
# 参数:
    # name,redis的name
    # offset,位的索引(将值变换成二进制后再进行索引)
    # value,值只能是 1 或 0
 
# 注:如果在Redis中有一个对应: n1 = "foo",
        那么字符串foo的二进制表示为:01100110 01101111 01101111
    所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
        那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
 
# 扩展,转换二进制表示:
 
    # source = "鲁班大师"
    source = "foo"
 
    for i in source:
        num = ord(i)
        print(bin(num).replace('b',''))
 
    特别的,如果source是汉字 "鲁班"怎么办?
  >>> ord('鲁')
  40065
  >>> bin(40065)
  '0b1001110010000001'
  >>> 答:对于utf
-8,每一个汉字占 3 个字节,那么 "鲁班" 则有 6个字节 对于汉字,for循环时候会按照 字节 迭代,那么在迭代时,将每一个字节转换 十进制数,然后再将十进制数转换成二进制
------------------------鲁班-------------------------------------------------------- 00000000 10011100 10000001 00000000 01110011 11101101

应用场景:
*用途举例,用最省空间的方式,存储在线用户数及分别是哪些用户在线
setbit n5 100 1#设置用户登录状态位,n5是用户名,100是用户id
127.0.0.1:6379> setbit  bb  1000 1#来一个用户标记一下
(integer) 0
127.0.0.1:6379> setbit  bb  100 1#来一个在标记一个
(integer) 0
127.0.0.1:6379> setbit  bb  230 1
(integer) 0
 
 
getbit(name, offset)
 获取name对应的值的二进制表示中的某位的值 (0或1)
应用场景:
getbit  n5 100 #检测用户登录状态位,是否在线,1-在线;0-不在线
127.0.0.1:6379> getbit bb 230#检测用户230 是否在线
(integer) 1        #在线
127.0.0.1:6379> getbit bb 233#检测用户233是否在线
(integer) 0        #不在线
127.0.0.1:6379>

应用:可以将10亿用户的的登陆状态和在线人数存储到10MB空间中去
 

 

 

bitcount(key, start=None, end=None)

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

127.0.0.1:6379> bitcount bb#统计上线用户数量
(integer) 3

 

 

strlen(name)

返回name对应值的字节长度(一个汉字3个字节)

127.0.0.1:6379> set hh '你好' OK 127.0.0.1:6379> strlen hh (integer) 6 127.0.0.1:6379>

 

 

incr(self, name, amount=1)

自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
 
 参数:
    name,Redis的name
    amount,自增数(必须是整数

127.0.0.1:6379> incr login_users#统计在线计数器 (integer) 1 127.0.0.1:6379> incr login_users (integer) 2 127.0.0.1:6379> incr login_users (integer) 3 127.0.0.1:6379> incr login_users (integer) 4 127.0.0.1:6379> incr login_users (integer) 5 127.0.0.1:6379> decr login_users (integer) 4 127.0.0.1:6379> decr login_users (integer) 3 127.0.0.1:6379>

 

 

incrbyfloat(self, name, amount=1.0)

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

127.0.0.1:6379> INCRBYFLOAT  2  1.2
"1.2"
127.0.0.1:6379> INCRBYFLOAT  2  1.2
"2.4"
127.0.0.1:6379> INCRBYFLOAT  2  1.2
"3.6"

 

 

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

 

 
append(key, value)
# 在redis name对应的值后面追加内容
 
# 参数:
    key, redis的name
    value, 要追加的字符串
127.0.0.1:6379> set  s1  sea
OK
127.0.0.1:6379> append  s1  blue
(integer) 7
127.0.0.1:6379> get  s1
"seablue"
127.0.0.1:6379>

 

2. Hash操作

hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强的数据 , redis中Hash在内存中的存储格式如下图:

 

 

                                                     

hset(name, key, value)

# name对应的hash中设置一个键值对(不存在,则创建;否则,修改)
 
# 参数:
    # name,redis的name
    # key,name对应的hash中的key
    # value,name对应的hash中的value
 
# 注:
    # hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
127.0.0.1:6379> hset  info  name luban
(integer) 1
127.0.0.1:6379> hset  info  age   22
(integer) 1
127.0.0.1:6379> hset  info  id    8848
(integer) 1
127.0.0.1:6379> hgetall info#获取info下的所有key-value
1) "name"
2) "luban"
3) "age"
4) "22"
5) "id"
6) "8848"

  

 

hmset(name, mapping) 
# 在name对应的hash中批量设置键值对
 
# 参数:
    # name,redis的name
    # mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
 
# 如:
    # r.hmset('xx', {'k1':'v1', 'k2': 'v2'})
127.0.0.1:6379> hmset  info2  k1  2  k2  3#批量设置
OK
127.0.0.1:6379> hkeys  info2
1) "k1"
2) "k2"

 

 
 hget(name,key)
# 在name对应的hash中获取根据key获取value

 

 

 hmget(name, keys, *args)
# 在name对应的hash中获取多个key的值
 
# 参数:
    # name,reids对应的name
    # keys,要获取key集合,如:['k1', 'k2', 'k3']
    # *args,要获取的key,如:k1,k2,k3
 
# 如:
    # r.mget('xx', ['k1', 'k2'])
    #
    # print r.hmget('xx', 'k1', 'k2')
127.0.0.1:6379> hmget  info3  n1 n2
1) "1"
2) "2"


 

 

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

127.0.0.1:6379> hgetall  info2
1) "k1"
2) "2"
3) "k2"
4) "3"

 

 

 hlen(name)
# 获取name对应的hash中键值对的个数

127.0.0.1:6379> hlen  info2#获取info2中有几个key
(integer) 2

 

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


127.0.0.1:6379> hkeys  info2
1) "k1"
2) "k2"

 

 

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

127.0.0.1:6379> hvals  info#获取info下的所有value
1) "luban"
2) "22"
3) "8848"

 

 

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

127.0.0.1:6379> hexists  info2   k1#检查info2下的k1这个key是否存在,存在返回1,否则0
(integer) 1
127.0.0.1:6379> hexists  info2   k3
(integer) 0
 
 
hdel(name,*keys)
# 将name对应的hash中指定key的键值对删除

127.0.0.1:6379> hdel  info2  k2#删除info2下的k2及其value
(integer) 1
127.0.0.1:6379> hget  info2  k2
(nil)

 

 

 hincrby(name, key, amount=1)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
# 参数:
    # name,redis中的name
    # key, hash对应的key
    # amount,自增数(整数)

127.0.0.1:6379> hincrby  info2  k2 1#自增name对应的hash中指定key的值,不存在则创建,value=amount
(integer) 1
127.0.0.1:6379> hincrby  info2  k2 1
(integer) 2
127.0.0.1:6379> hincrby  info2  k2 1
(integer) 3
127.0.0.1:6379> hincrby  info2  k2 1
(integer) 4
127.0.0.1:6379> hincrby  info2  k2 1
(integer) 5
 
 
 hincrbyfloat(name, key, amount=1.0)
# 自增name对应的hash中的指定key的值,不存在则创建key=amount
 
# 参数:
    # name,redis中的name
    # key, hash对应的key
    # amount,自增数(浮点数)
 
# 自增name对应的hash中的指定key的值,不存在则创建key=amount

127.0.0.1:6379> HINCRBYFLOAT   info5  n2  1.2
"1.2"
127.0.0.1:6379> HINCRBYFLOAT   info5  n2  1.2
"2.4"
127.0.0.1:6379> HINCRBYFLOAT   info5  n2  1.2
"3.6"
127.0.0.1:6379> hget info5 n2
"3.6"
127.0.0.1:6379>


 
 
 hscan(name, cursor=0, match=None, count=None)
使用以下命令启动完整哈希扫描
HSCAN myhash 0
使用与模式匹配的字段开始哈希扫描:
HSCAN myhash 0 MATCH order_*
使用与模式匹配的字段启动哈希扫描,并强制扫描命令执行更多操作
扫描:
HSCAN myhash 0 MATCH order_* COUNT 1000


# 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,导致内存被撑爆,而是分片的获取数据
 
# 参数:
    # name,redis的name
    # cursor,游标(基于游标分批去获取数据)
    # match,匹配指定key,默认None 表示所有的key
    # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
 
# 如:
    # 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
    # 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
    # ...
    # 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕


127.0.0.1:6379> HSCAN  info  0
1) "0"
2) 1) "name"
   2) "luban"
   3) "age"
   4) "22"
   5) "id"
   6) "8848"
127.0.0.1:6379> hscan info2 0 match k*#过滤以k开头的key 1) "0" 2) 1) "k1" 2) "2" 3) "k2" 4) "6" 127.0.0.1:6379>

 

 

 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)

3. list

List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:
 
 
          

 

lpush(name,values) 
# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
 
# 如:
    # r.lpush('oo', 11,22,33)
    # 保存顺序为: 33,22,11
 
# 扩展:
    # rpush(name, values) 表示从右向左操作
127.0.0.1:6379> lpush  names   luban  houyi  daji#先入后出创建列表
(integer) 3

 

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

127.0.0.1:6379> lrange  names 0  -1
1) "daji"
2) "houyi"
3) "luban"
 

 

 
 lpushx(name,value)
#在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边
 
# 更多:
    # rpushx(name, value) 表示从右向左操作
127.0.0.1:6379> lpushx   l5   hehe
(integer) 0
127.0.0.1:6379> lrange l5  0  -1
(empty list or set)

127.0.0.1:6379> lpush  L5  1 2 3 4
(integer) 4
127.0.0.1:6379> lrange L5 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> lpushx  L5  hehe
(integer) 5
127.0.0.1:6379> lrange L5 0 -1
1) "hehe"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379>
 
 
 llen(name)
# name对应的list元素的个数
127.0.0.1:6379> lrange L5 0 -1
1) "hehe"
2) "4"
3) "3"
4) "2"
5) "1"

127.0.0.1:6379> llen  L5
(integer) 5

 

 

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

127.0.0.1:6379> linsert  names  before  luban  houzi#在luban 前面插入数据
(integer) 6
127.0.0.1:6379> lrange  names 0  -1
1) "daji"
2) "houyi"
3) "houzi"
4) "luban"
5) "hanxin"
6) "laofuzi"
127.0.0.1:6379> linsert  names  after  luban  kai#在luban 后面插入数据
(integer) 7
127.0.0.1:6379> lrange  names 0  -1
1) "daji"
2) "houyi"
3) "houzi"
4) "luban"
5) "kai"
6) "hanxin"
7) "laofuzi"
 
 
lset(name, index, value)
# 对name对应的list中的某一个索引位置重新赋值
 
# 参数:
    # name,redis的name
    # index,list的索引位置
    # value,要设置的值
127.0.0.1:6379> lrange  names 0  -1
1) "daji"
2) "houyi"
3) "houzi"
4) "luban"
5) "kai"
6) "hanxin"
7) "laofuzi"
127.0.0.1:6379> lset  names  3 LuBan#重新给names列表中 索引为3的位置赋值
OK
127.0.0.1:6379> lrange  names 0  -1
1) "daji"
2) "houyi"
3) "houzi"
4) "LuBan"
5) "kai"
6) "hanxin"
7) "laofuzi"

 

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

127.0.0.1:6379> lrem  names 1  houzi  #删除指定的值
(integer) 1
127.0.0.1:6379> lrange  names 0  -1
1) "daji"
2) "houyi"
3) "LuBan"
4) "kai"
5) "hanxin"
6) "laofuzi"

 

 
 
 lpop(name)
# 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
 
# 更多:
    # rpop(name) 表示从右向左操作
127.0.0.1:6379> lpop  names#从列表最左边取出一个元素删除,类似于队列
"daji"
127.0.0.1:6379> lrange  names 0  -1
1) "houyi"
2) "LuBan"
3) "kai"
4) "hanxin"
5) "laofuzi"
127.0.0.1:6379> 

 

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

127.0.0.1:6379> lindex L5  1
"4"
127.0.0.1:6379> lindex L5  0
"hehe"
127.0.0.1:6379> lindex L5  2
"3"
127.0.0.1:6379>
 
 
 
 ltrim(name, start, end)
# 在name对应的列表中移除没有在start-end索引之间的值
# 参数:
    # name,redis的name
    # start,索引的起始位置
    # end,索引结束位置

127.0.0.1:6379> lrange  names 0  -1
1) "houyi"
2) "LuBan"
3) "kai"
4) "hanxin"
5) "laofuzi"
127.0.0.1:6379> ltrim  names  1 2#只保留列表names 索引为 1 -2 的元素
OK
127.0.0.1:6379> lrange  names 0  -1
1) "LuBan"
2) "kai"
 
 
 rpoplpush(src, dst)
# 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
# 参数:
    # src,要取数据的列表的name
    # dst,要添加数据的列表的name

127.0.0.1:6379> lrange  names 0  -1
1) "LuBan"
2) "kai"
127.0.0.1:6379> lrange  names2 0  -1
1) "hehe"
127.0.0.1:6379> rpoplpush  names  names2#移除names最右边的元素,同时添加到names2的最左边
"kai"
127.0.0.1:6379> lrange  names2 0  -1
1) "kai"
2) "hehe"
127.0.0.1:6379> lrange  names 0  -1
1) "LuBan"

 

 

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


 

 
 brpoplpush(src, dst, timeout=0)
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
 
# 参数:
    # src,取出并要移除元素的列表对应的name
    # dst,要插入元素的列表对应的name
    # timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
127.0.0.1:6379> brpoplpush  names names2  40
#从列表names的最右端移除一个元素并添加到另一个列表names2的左侧
#当列表中没有数据阻塞等待40s
"hehhe"
(12.69s)
127.0.0.1:6379> lrange  names2 0 -1
1) "hehhe"
2) "LuBan"
3) "kai"
4) "hehe"

应用场景:
两个进程,一个进程放数据,另一个进程取数据;
保证数据一致性,同步放数据,同步取数据并删除,双方不影响

 

 
 

4.set集合操作

 
Set操作,Set集合就是不允许重复的列表
 
 
 sadd(name,values)
# name对应的集合中添加元素

127.0.0.1:6379> sadd  names3  luban  luban  hehe  hehe  2323#集合天生去重
(integer) 3
 
 
smembers(name)
# 获取name对应的集合的所有成员
127.0.0.1:6379> smembers  names3
1) "2323"
2) "luban"
3) "hehe
 
 
scard(name)
获取name对应的集合中元素个数
127.0.0.1:6379> scard  names3#获取names3集合中的元素个数
(integer) 3
 
 
 sdiff(keys, *args)
在第一个name对应的集合中且不在其他name对应的集合的元素集合

127.0.0.1:6379> sadd  names4   hanxin  houzi  kai  2323 hehe

127.0.0.1:6379> sdiff  names3  names4#返回names3有的而names4中没有的元素
1) "luban"

 

 

 sdiffstore(dest, keys, *args)

# 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中
127.0.0.1:6379> sdiffstore n5  names3  names4
#将names3有的而names4中没有的元素重新加入到集合n5中
(integer) 1
127.0.0.1:6379> smembers n5
1) "luban"

 

 

 sinter(keys, *args)
# 获取多一个name对应集合的交集
127.0.0.1:6379> sinter  names3  names4#返回呢两个集合的交集
1) "2323"
2) "hehe"

 

 
 sinterstore(dest, keys, *args)
# 获取多一个name对应集合的交集,再讲其加入到dest对应的集合中
127.0.0.1:6379> SINTERSTORE  names34   names3  names4
(integer) 2
127.0.0.1:6379> SMEMBERS  names34
1) "hehe"
2) "2323"
 
 
 sismember(name, value)
# 检查value是否是name对应的集合的成员

127.0.0.1:6379> SISMEMBER  names34  'hehe'#是返回1
(integer) 1
127.0.0.1:6379> SISMEMBER  names34  123#不是返回0
(integer) 0

 

 
 smove(src, dst, value)
# 将某个成员从一个集合中移动到另外一个集合
127.0.0.1:6379> sadd  s2  1 2  3
(integer) 3
127.0.0.1:6379> sadd  s3  4 5  6
(integer) 3
127.0.0.1:6379> SMOVE  s2   s3  1
(integer) 1
127.0.0.1:6379> smembers  s2
1) "2"
2) "3"
127.0.0.1:6379> smembers  s3
 
 
 spop(name)
# 从集合的右侧(尾部)移除一个成员,并将其返回

127.0.0.1:6379> smembers  s3
1) "1"
2) "4"
3) "5"
4) "6"
127.0.0.1:6379> spop s3
"6"
 
 
 srandmember(name, numbers)
# 从name对应的集合中随机获取 numbers 个元素

127.0.0.1:6379> SRANDMEMBER  s3  2
1) "5"
2) "4"
127.0.0.1:6379> SRANDMEMBER  s3  2
1) "1"
2) "4"
127.0.0.1:6379> SRANDMEMBER  s3  2
1) "5"
2) "4"
 
 
 srem(name, values)
# 在name对应的集合中删除某些值


127.0.0.1:6379> smembers  s3
1) "1"
2) "4"
3) "5"
127.0.0.1:6379> srem  s3   1#删除s3集合中的成员1
(integer) 1
127.0.0.1:6379> smembers  s3
1) "4"
2) "5"

 

 
 sunion(keys, *args)
# 获取多个或一个name对应的集合的并集

127.0.0.1:6379> sunion   s2  
1) "2"
2) "3"
127.0.0.1:6379> sunion   s2  s3
1) "2"
2) "3"
3) "4"
4) "5"
 
 
 sunionstore(dest,keys, *args)
# 获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中
127.0.0.1:6379> sunionstore  s23_union s2  s3
(integer) 4
127.0.0.1:6379> smembers  s23_union
1) "2"
2) "3"
3) "4"
4) "5"

 
 
sscan(name, cursor=0, match=None, count=None)

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

 5、有序集合

 

有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,
所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。 
 
 
zadd(name, *args, **kwargs)
# 在name对应的有序集合中添加元素
# 如:
     # zadd('zz', 'n1', 1, 'n2', 2)
     #
     # zadd('zz', n1=11, n2=22)
127.0.0.1:6379> zadd  z1  10  he  3  hehe   6   hehe
(integer) 2
127.0.0.1:6379> zadd  z1  10  he  3  hehe   6   hehehe
(integer) 1
127.0.0.1:6379> ZRANGE  z1  0  -1 withscores
1) "hehe"
2) "3"
3) "hehehe"
4) "6"
5) "he"
6) "10"

 

 
zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
# 按照索引范围获取name对应的有序集合的元素
 
# 参数:
    # 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)

 

 

 zcard(name)
# 获取name对应的有序集合元素的数量
127.0.0.1:6379> ZRANGE  z1  0  -1  withscores
1) "daji"
2) "3"
3) "hehe"
4) "3"
5) "houyi"
6) "7"
7) "luban"
8) "10"
127.0.0.1:6379> zcard  z1
(integer) 4
 
 
 zcount(name, min, max)
# 获取name对应的有序集合中分数 在 [min,max] 之间的个数
127.0.0.1:6379> ZRANGE  z1  0  -1  withscores
1) "daji"
2) "3"
3) "hehe"
4) "3"
5) "houyi"
6) "7"
7) "luban"
8) "10"
127.0.0.1:6379> zcount  z1  3 4
(integer) 2
 
 
 zincrby(name, value, amount)
# 自增name对应的有序集合的 name 对应的分数
127.0.0.1:6379> zincrby  z3  3  num
"3"
127.0.0.1:6379> zincrby  z3  3  num
"6"
127.0.0.1:6379> zincrby  z3  3  num
"9"
127.0.0.1:6379> zincrby  z3  3  num
"12"
127.0.0.1:6379> zrange  z3  0  -1  withscores
1) "hehe"
2) "6"
3) "daji"
4) "8"
5) "num"
6) "12"
 
 
 zrank(name, value)
# 获取某个值在 name对应的有序集合中的排行(从 0 开始)
 
# 更多:
    # zrevrank(name, value),从大到小排序
127.0.0.1:6379> zrange  z3  0  -1  withscores
1) "hehe"
2) "6"
3) "daji"
4) "8"
5) "num"
6) "12"
127.0.0.1:6379> zrank  z3  daji
(integer) 1
 
 
zrem(name, values) 
# 删除name对应的有序集合中值是values的成员
 
# 如:zrem('zz', ['s1', 's2'])
127.0.0.1:6379> zrem  z3  'daji'
(integer) 1
127.0.0.1:6379> zrange  z3  0  -1  withscores
1) "hehe"
2) "6"
3) "num"
4) "12"
 
 
 zremrangebyrank(name, min, max)
# 根据排行范围删除
127.0.0.1:6379> zrange  z1  0  -1  withscores
1) "daji"
2) "3"
3) "hehe"
4) "3"
5) "houyi"
6) "7"
7) "luban"
8) "10"
127.0.0.1:6379> zremrangebyrank  z1  0  1
(integer) 2
127.0.0.1:6379> zrange  z1  0  -1  withscores
1) "houyi"
2) "7"
3) "luban"
4) "10"
 
 
 zremrangebyscore(name, min, max)
# 根据分数范围删除
127.0.0.1:6379> zrange  z4  0  -1  withscores
 1) "luban"
 2) "2"
 3) "wangzhaojun"
 4) "3"
 5) "daji"
 6) "6"
 7) "diaochan"
 8) "9"
 9) "zhenji"
10) "10"
127.0.0.1:6379> zremrangebyscore  z4  3  6#删除成绩在[3,6]
(integer) 2
127.0.0.1:6379> zrange  z4  0  -1  withscores
1) "luban"
2) "2"
3) "diaochan"
4) "9"
5) "zhenji"
6) "10"
 
 
zscore(name, value)
# 获取name对应有序集合中 value 对应的分数
127.0.0.1:6379> zrange  z4  0  -1  withscores
1) "luban"
2) "2"
3) "diaochan"
4) "9"
5) "zhenji"
6) "10"
127.0.0.1:6379> zscore  z4  'zhenji'
"10"
 
 
zinterstore(dest, keys, aggregate=None)
# 获取两个有序集合的交集,如果遇到相同值,则按照aggregate进行操作
# aggregate的值为:  SUM  MIN  MAX
127.0.0.1:6379> zadd  z5  3  luban
(integer) 1
127.0.0.1:6379> zrange  z5  0  -1  withscores
1) "luban"
2) "3"
127.0.0.1:6379> zrange  z4  0  -1  withscores
1) "luban"
2) "2"
3) "diaochan"
4) "9"
5) "zhenji"
6) "10"
127.0.0.1:6379> zinterstore  z45_sum  2  z4  z5
(integer) 1
127.0.0.1:6379> zrange  z45_sum  0  -1  withscores
1) "luban"
2) "5"
 
 
zunionstore(dest, keys, aggregate=None)
# 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作
# aggregate的值为:  SUM  MIN  MAX
127.0.0.1:6379> zrange  z6  0  -1 withscores
1) "luban"
2) "2"
127.0.0.1:6379> zrange  z4  0  -1 withscores
1) "luban"
2) "2"
3) "diaochan"
4) "9"
5) "zhenji"
6) "10"
127.0.0.1:6379> zunionstore  z46_union  2  z4  z6
(integer) 3
127.0.0.1:6379> zrange   z46_union  0 -1  withscores
1) "luban"
2) "4"
3) "diaochan"
4) "9"
5) "zhenji"
6) "10"
 
 
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,用来对分数进行操作

 

其他常用操作:

delete(*names)

 

# 根据删除redis中的任意数据类型

 

 

 select(num)
redis 默认有16个db 0-15,select 5切换db

 

 
 exists(name)
# 检测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

 

 
 

 管道

 
  redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,
则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。Redis的管道可以在大量数据需要一次性操作完成的时候,
使用Pipeline进行批处理,将一大堆的操作合并成一次操作,可以减少链路层的时间消耗,毕竟频繁操作是不好的。
 
  使用redis客户端 在本地测试Redis 10W条数据的读写

  不使用管道:

import redis
from time import time
pool = redis.ConnectionPool(db=2,)

r = redis.Redis(connection_pool=pool,)
start_time = time()
for i in range(100000):
    r.set('{}'.format(i), '{}'.format(i))
end_time = time()
print('读写10W条数据,共耗时(ms):', (end_time-start_time)*1000)

 


 

  使用管道:

import redis
from time import time
pool = redis.ConnectionPool(host='localhost', port=6379, db=4,)

r = redis.Redis( connection_pool=pool,)
pipe = r.pipeline(transaction=True)
start_time = time()
pipe.multi()
for i in range(100000):
    pipe.set(i, i)
pipe.execute()
end_time = time()
print('读写10W条数据,共耗时(ms):', (end_time-start_time)*1000)

 


 

   如果是频繁的操作Redis,使用管道技术去进行操作是可取的.不仅减少服务器压力,还能减少链路层中的时间消耗,批量处理频繁的操作,
将大量操作结合成少量的操作..这是十分可取的.

发布订阅

                                     

 

 

发布者:服务器

订阅者:Dashboad和数据处理

Demo如下:

 

#Author:Yun
import redis


class RedisHelper:

    def __init__(self):
        self.__conn = redis.Redis(host='localhost',port=6379,db=0)
        self.chan_sub = 'fm104.5'
        self.chan_pub = 'fm104.5'

    def public(self, msg):
        self.__conn.publish(self.chan_pub, msg)
        return True

    def subscribe(self):
        pub = self.__conn.pubsub()#打开收音机
        pub.subscribe(self.chan_sub)#调频
        pub.parse_response()#准备接收
        return pub
View Code

 

 

 
 发布者:
#发布方
from redishelper import RedisHelper

obj = RedisHelper()
obj.public('hello')

'''
命令行发布:
PUBLISH  fm104.5 hello

'''

 

 
 
 订阅者:
#订阅方
from redishelper import RedisHelper

obj = RedisHelper()
redis_sub = obj.subscribe()

while True:
    msg = redis_sub.parse_response()
    print(msg)

 

 
 
 
更多参见:
 
 
 
 
 
 
 

 什么时候用关系型数据库,什么时候 用NoSQL?

 

在以下情况下转到遗留关系数据库(RDBMS):


    1、数据结构良好,适用于关系数据库中的表格排列(行和列)。典型示例:银行帐户信息,客户订单信息,客户信息,员工信息,部门信息等。
    2、上述观点的另一方面是:面向模式的数据模型。当您为潜在使用RDBMS设计数据模型(表,关系等)时,您需要提出一个定义良好的模式:将有这么多表,
每个表都有一组已知的列,用于存储已知的数据键入格式(CHAR,NUMBER,BLOB等)。
    3、非常重要:考虑数据是否具有事务性质。换句话说,是否将在提供ACID语义的事务的上下文中存储,访问和更新数据,或者是否可以妥协某些/所有这些属性。
    4、正确性也很重要,任何妥协都是不可接受的。这源于这样一个事实:在大多数NoSQL数据库中,一致性被用于支持性能和可伸缩性(NoSQL数据库上的要点将在下面详述)。
    5、对于横向扩展架构没有强烈/迫切的需求;数据库,线性扩展(水平扩展)到群集中的多个节点。
    6、用例不适用于“高速数据摄取”。
   7、 如果客户端应用程序期望快速地将大量数据流入/流出数据库,那么关系数据库可能不是一个好的选择,因为它们并非真正用于扩展写入繁重的工作负载。
    8、为了实现ACID属性,特别是在编写器(INSERT,UPDATE,DELETE)代码路径中进行了大量额外的后台工作。这肯定会影响性能。
    9、用例不是“存储数PB范围内的大量数据”。
 
 
 

在以下情况下访问NoSQL数据库:


     1、数据不适用于固定(和预定)架构:
     2、可扩展性,性能(高吞吐量和低操作延迟),连续可用性是数据库底层架构必须满足的非常重要的要求。
     3、 “高速数据摄取”的不错选择。 这样的应用程序(例如IoT样式)在一秒钟内产生数百万个数据点,并且需要能够提供极端写入可伸缩性的数据库。
     4、横向扩展的固有能力允许在群集中的商用服务器上存储大量数据。 它们通常使用低成本资源,并且能够随着需求的增长线性增加计算和存储能力。
 
 
 value 在1K以上时,1000M网卡轻松的被跑慢,而且redis-server cpu连一个核心都没占用到,可见redis高效,redis的服务也不需要太高配置,瓶颈在网卡速度。

 

 

 Django配置Redis

 

安装

django项目中配置redis作为cache缓存,需要先安装django-redis模块

pip install django-redis

 

 

 配置

在settings.py中,按如下配置CACHE连接的redis信息

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100},
            # "PASSWORD": "密码",
            "DECODE_RESPONSES":True
        }
    },
}

 

使用

先获取redis连接:

import django_redis
CACHE = django_redis.get_redis_connection()

在视图中

...
CACHE.set(key, value)
...

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 

 

posted @ 2018-11-24 18:57  Mr_Yun  阅读(2213)  评论(0编辑  收藏  举报