redis缓存数据库及Python操做redis

缓存数据库介绍

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

特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了不少难以克服的问题,而非关系型的数据库则因为其自己的特色获得了很是迅速的发展。html

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

 

NoSQL数据库的四大分类:

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

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

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

 

列存储数据库:nginx

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

 

文档型数据库:github

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

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

 

图形(Graph)数据库:

图形结构的数据库同其余行列以及刚性结构的SQL数据库不一样,它是使用灵活的图形模型,而且可以扩展到多个服务器上。NoSQL数据库没有标准的查询语言(SQL),所以进行数据库查询须要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API。[2]   如:Neo4J, InfoGrid, Infinite Graph.
所以,咱们总结NoSQL数据库在如下的这几种状况下比较适用:一、数据模型比较简单;二、须要灵活性更强的IT系统;三、对数据库性能要求较高;四、不须要高度的数据一致性;五、对于给定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数据库加层防御,防止被黑客攻击

 http://www.javashuo.com/article/p-spojasgo-ba.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 操做
  • 管道
  • 发布订阅
 

链接方式

一、操做模式

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')

 

二、链接池

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)



127.0.0.1:6379> set hh '你好' OK 127.0.0.1:6379> strlen hh (integer) 6 127.0.0.1:6379>
返回name对应值的字节长度(一个汉字3个字节)

 

 

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)
# 同字符串的操做,用于增量迭代分批获取元素,避免内存消耗太大

 五、有序集合

 

有序集合,在集合的基础上,为每元素排序;元素的排序须要根据另一个值来进行比较,
因此,对于有序集合,每个元素有两个值,即:值和分数,分数专门用来作排序。 
 
 
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):


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

在如下状况下访问NoSQL数据库:


     一、数据不适用于固定(和预约)架构:
     二、可扩展性,性能(高吞吐量和低操做延迟),连续可用性是数据库底层架构必须知足的很是重要的要求。
     三、 “高速数据摄取”的不错选择。 这样的应用程序(例如IoT样式)在一秒钟内产生数百万个数据点,而且须要可以提供极端写入可伸缩性的数据库。
     四、横向扩展的固有能力容许在群集中的商用服务器上存储大量数据。 它们一般使用低成本资源,而且可以随着需求的增加线性增长计算和存储能力。
 
 
  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)
...
相关文章
相关标签/搜索