zoukankan      html  css  js  c++  java
  • NoSQL数据存储

    些数据库并不是关系型的,不支持 SQL。它们用来处理庞大的数据集、支持更加灵活的 数据定义以及定制的数据操作。这些被统称为 NoSQL(not only SQL) 。

    dbm family

    dbm格式是按照键值对的形式储存,封装在应用程序(例如网页浏览器)中,用来维护各种各样的配置。从以下角度看,dbm 数据库和 Python 字典是类似的:

    • 给一个键赋值,自动保存到磁盘中的数据库
    • 通过键得到对应的值

    下面看一个小栗子:
    open() 方法的第二个参数 'r' 代表读;'w' 代表写;'c' 表示读和写, 如果文件不存在则创建.

    In [1]: import dbm
    In [2]: db = dbm.open('definitions','c') 
    同字典一样创建键值对,给一个键赋值:
    In [3]: db['mustartd'] = 'yellow' 
    In [4]: db['ketchup'] = 'red'  
    In [5]: db['pesto'] = 'green'
    查看数据库长度,并通过键值获得数据
    In [6]: len(db) 
    Out[6]: 3
    
    In [7]: db['mustartd'] 
    Out[7]: b'yellow'
    关掉数据库,然后重新打开验证它是否被完整保存:
    In [8]: db.close() 
    
    In [9]: db = dbm.open('definitions','r') 
    
    In [10]: db['mustard'] 
    ---------------------------------------------------------------------------
    KeyError                                  Traceback (most recent call last)
    <ipython-input-10-8228d50ea548> in <module>()
    ----> 1 db['mustard']
    
    KeyError: 'mustard'
    
    In [11]: db['mustartd'] 
    Out[11]: b'yellow'

    键和值都以字节保存,因此不能对数据库对象db进行迭代,但是可以使用函数len()得到键的数目。注意哦:get()和setdefault()函数只能用于字典的方法。


    memcached

    memcached是一种快速的,内存键值对象的缓存服务器。它一般置于数据库之前,用于存储网页服务器会话数据。如果想要使用,需要一个memcached服务器和Python的驱动程序。
    这样的驱动程序很多,能够在Python3中使用的是python3-memcached,可以通过命令安装: pip install python-memcached

    连接到一个memcached服务器之后,可以做以下事项:

    • 赋值和取值
    • 其中一个值的自增或者自减
    • 删除其中一个键

    数据在memcached并不是持久化保存的,后面的可能会覆盖早些写入的数据,这是它的固有特性,因为它作为一个缓存服务器,通过舍弃旧数据避免程序运行时内存不足的问题。

    我们可以同事连接到多个memcached服务器,下面的栗子是连接一个:
    注意一点哦:在连接之前,一点要确保memcached服务开启。

    In [4]: import memcache
    
    In [5]: db = memcache.Client(['127.0.0.1:11211']) 
    
    In [6]: db.set('marco','polo') 
    Out[6]: True
    
    In [7]: db.set('ducks', 0)  
    Out[7]: True
    
    In [8]: db.get('ducks') 
    Out[8]: 0
    
    In [9]: db.incr('ducks',2)
    Out[9]: 2
    
    In [10]: db.get('ducks') 
    Out[10]: 2

    Redis

    Redis是一种数据结构服务器(data structure server)。和memcached类似,Redis服务器的所有数据都是基于内存的(现在也可以把数据放在磁盘)。不同于memcached,Redis可以实现:

    • 存储数据到磁盘,方便断电重启和提升可靠性
    • 保存旧数据
    • 提供多种数据结构,不限于简单字符串

    Redis的数据类型和Python很相似,Redis服务器会是一个或多个Python应用程序之间共享数据的非常有帮助的中间件。

    Python的Redis驱动程序redis-py可以使用命令安装: pip install redis

    Redis 服务器自身就有好用的文档。如果在本地计算机(网络名为 localhost)安装和启动 了 Redis 服务器,就可以开始尝试下面的程序。

    1.字符串

    具有单一值的一个键被称作Redis的字符串。简单的Python数据类型可以自动转换成Redis字符串。现在连接到一些主机(默认localhost)以及端口(默认6379)上的服务器:
    同样在连接之前,一点要确保memcached服务开启

    In [1]: import redis
    In [2]: conn = redis.Redis()
    redis.Redis(‘localhost’) 或者 redis.Redis('localhost', 6379) 会得到同样的结果。
    
    列出所有的键(目前为空):
    In [3]: conn.keys('*') 
    Out[3]: []
    
    给键 'secret' 赋值一个字符串;给键 'carats' 赋一个整数;给键 'fever' 赋一个浮点数:
    
    In [4]: conn.set('secret', 'ni!') 
    Out[4]: True
    
    In [5]: conn.set('carats', 24) 
    Out[5]: True
    
    In [6]: conn.set('fever', '101.5') 
    Out[6]: True
    
    通过键反过来得到对应的值:
    
    In [7]: conn.get('secret') 
    Out[7]: b'ni!'
    
    In [8]: 
    
    In [8]:  conn.get('carats') 
    Out[8]: b'24'
    
    In [9]: conn.get('fever') 
    Out[9]: b'101.5'
    
    这里的 setnx() 方法只有当键不存在时才设定值:
    
    In [10]: conn.setnx('secret', 'icky-icky-icky-ptang-zoop-boing!') 
    Out[10]: False
    
    方法运行失败,因为之前已经定义了 'secret':
    In [11]: conn.get('fever') 
    Out[11]: b'101.5'
    
    In [12]: conn.get('secret') 
    Out[12]: b'ni!'
    
    方法 getset() 会返回旧的值,同时赋新的值:
    In [13]: conn.getset('secret', 'icky-icky-icky-ptang-zoop-boing!') 
    Out[13]: b'ni!'
    
    In [14]: conn.get('secret') 
    Out[14]: b'icky-icky-icky-ptang-zoop-boing!'
    
    使用函数 getrange() 得到子串(偏移量 offset:0 代表开始,-1 代表结束):
    In [15]: conn.getrange('secret',-6,-1) 
    Out[15]: b'boing!'
    
    使用函数 setrange() 替换子串(从开始位置偏移):
    In [16]: conn.setrange('secret',0,'ICKY') 
    Out[16]: 32
    
    In [17]: conn.get('secret') 
    Out[17]: b'ICKY-icky-icky-ptang-zoop-boing!'
    
    接下来使用函数 mset() 一次设置多个键值:
    In [18]: conn.mset({'pie':'cherry','cordial':'sherry'}) 
    Out[18]: True
    
    使用函数 mget() 一次取到多个键的值:
    In [19]: conn.mget(['pie','fever']) 
    Out[19]: [b'cherry', b'101.5']
    
    使用函数 delete() 删掉一个键:
    In [20]: conn.delete('fever') 
    Out[20]: 1
    
    使用函数 incr() 或者 incrbyfloat() 增加值,函数 decr() 减少值:
    In [21]: conn.incr('carats')  
    Out[21]: 25
    
    In [22]: conn.incr('carats', 10)  
    Out[22]: 35
    
    In [23]: conn.decr('carats') 
    Out[23]: 34
    
    In [24]: conn.decr('carats', 15)  
    Out[24]: 19
    
    In [25]: conn.set('fever', '101.5') 
    Out[25]: True
    
    In [26]: conn.incrbyfloat('fever') 
    Out[26]: 102.5
    
    In [27]: conn.incrbyfloat('fever', 0.5) 
    Out[27]: 103.0
    
    不存在函数 decrbyfloat(),可以用增加负数代替:
    In [28]: conn.incrbyfloat('fever', -2.0)  
    Out[28]: 101.0

    2.列表

    Redis的列表仅能包含字符串。当第一次插入数据的时列表被创建。使用函数lpush()在开始处插入:

    In [1]: import redis
    In [2]: conn = redis.Redis()
    
    In [3]: conn.lpush('zoo','bear') 
    Out[3]: 1
    
    在开始处插入超过一项:
    In [4]: conn.lpush('zoo', 'alligator', 'duck') 
    Out[4]: 3
    
    使用 linsert() 函数在一个值的前或者后插入:
    In [5]: conn.linsert('zoo', 'before', 'bear', 'beaver') 
    Out[5]: 4
    
    In [6]: conn.linsert('zoo', 'after', 'bear', 'cassowary') 
    Out[6]: 5
    
    使用 lset() 函数在偏移量处插入(列表必须已经存在),但是会将原来偏移量的值覆盖:
    In [7]: conn.lset('zoo',2,'marmoset') 
    Out[7]: True
    
    使用 lrange() 函数取到给定偏移量范围(0~-1 代表全部)的所有值:
    In [8]: conn.lrange('zoo',0,-1) 
    Out[8]: [b'duck', b'alligator', b'marmoset', b'bear', b'cassowary']
    
    使用 rpush() 函数在结尾处插入:
    In [9]: conn.rpush('zoo', 'yak') 
    Out[9]: 6
    
    使用 lindex() 函数取到给定偏移量处的值:
    In [10]: conn.lindex('zoo', 3) 
    Out[10]: b'bear'
    
    In [11]: conn.lrange('zoo', 0, 2) 
    Out[11]: [b'duck', b'alligator', b'marmoset']
    
    使用 ltrim() 函数仅保留列表中给定范围的值(截取):
    In [12]: conn.ltrim('zoo', 1, 4) 
    Out[12]: True
    
    In [13]: conn.lrange('zoo',0,-1) 
    Out[13]: [b'alligator', b'marmoset', b'bear', b'cassowary']

    3.哈希表

    Redis的哈希表类似于Python中的字典,但它仅包含字符串,因此只能有一层结构,不能 进行嵌套。下面的例子创建了一个 Redis 的哈希表 song,并对它进行操作。

    使用函数hmset()在哈希表song设置字段do和字段re的值
    In [16]: conn.hmset('song',{'do': 'a deer', 're': 'about a deer'}) 
    Out[16]: True
    
    使用函数hset()设置一个单一字段值
    In [17]: conn.hset('song', 'mi', 'a note to follow re') 
    Out[17]: 1
    
    使用函数 hget() 取到一个字段的值:
    In [18]: conn.hget('song', 'mi') 
    Out[18]: b'a note to follow re'
    
    使用函数 hmget() 取到多个字段的值:
    In [19]: conn.hmget('song', 're', 'do') 
    Out[19]: [b'about a deer', b'a deer']
    
    使用函数 hkeys() 取到所有字段的键:
    In [20]: conn.hkeys('song') 
    Out[20]: [b're', b'do', b'mi']
    
    使用函数 hvals() 取到所有字段的值:
    In [21]:  conn.hvals('song')
    Out[21]: [b'about a deer', b'a deer', b'a note to follow re']
    
    使用函数 hlen() 返回字段的总数:
    In [22]: conn.hlen('song')
    Out[22]: 3
    
    使用函数 hgetall() 取到所有字段的键和值:
    In [23]: conn.hgetall('song') 
    Out[23]: {b'do': b'a deer', b'mi': b'a note to follow re', b're': b'about a deer'}
    
    使用函数 hsetnx() 对字段中不存在的键赋值:
    In [24]: conn.hsetnx('song', 'fa', 'a note that rhymes with la') 
    Out[24]: 1

    4.集合

    Redis的集合和Python的集合是完全类似的。

    在集合中添加一个或多个值:
    In [35]: conn.sadd('zoo1','duck', 'goat', 'turkey') 
    Out[35]: 3
    
    取得集合中所有值的数目:
    In [36]: conn.scard('zoo1') 
    Out[36]: 3
    
    返回集合中所有值:
    In [37]: conn.smembers('zoo1') 
    Out[37]: {b'duck', b'goat', b'turkey'}
    
    从集合中删掉一个值:
    In [38]: conn.srem('zoo1','turkey') 
    Out[38]: 1
    
    新建一个集合以展示一些集合间的操作:
    In [39]: conn.sadd('zoo2','tiger', 'wolf', 'duck') 
    Out[39]: 3
    
    返回集合zoo1和集合zoo2的交集
    In [40]: conn.sinter('zoo1','zoo2') 
    Out[40]: {b'duck'}
    
    获得集合zoo1和集合zoo2的交集,并存储到新集合zoo3
    In [41]: conn.sinterstore('zoo3','zoo1','zoo2') 
    Out[41]: 1
    
    In [42]: conn.smembers('zoo3') 
    Out[42]: {b'duck'}
    
    返回集合zoo1和集合zoo2的交集
    In [43]: conn.sunion('zoo1','zoo2') 
    Out[43]: {b'duck', b'goat', b'tiger', b'wolf'}
    
    存储并集结果到集合zoo4
    In [44]: conn.sunionstore('zoo4','zoo1','zoo2') 
    Out[44]: 4
    
    使用函数sdiff()得到它们的差集,得到zoo1包含而zoo2不包含的项:
    In [45]: conn.sdiff('zoo1','zoo2') 
    Out[45]: {b'goat'}
    
    将差集存储到集合zoo5:
    In [46]: conn.sdiffstore('zoo5','zoo1','zoo2')
    Out[46]: 1
    
    In [47]: conn.smembers('zoo5') 
    Out[47]: {b'goat'}

    有序集合

    Redis中功能最强大的数据类型之一是有序表(sorted set或者zset)。里面的值都是独一无二的,但是每一个值都关联对应浮点值分数(score)。可以通过值或者分数取得每一项。
    有序集合有很多用途:

    • 排行榜
    • 二级索引
    • 时间序列(把时间戳作为分数)

    下面以时间序列作为一个栗子,通过时间戳跟踪用户的登陆。时间表达式使用Unix的epoch值,它有python的time()函数返回:

    In [1]: import redis 
    In [2]: import time
    In [3]: conn = redis.Redis() 
    In [4]: now = time.time() 
    
    In [5]: now 
    Out[5]: 1484735598.2283144
    增加第一个访客:
    In [6]: conn.zadd('logins', 'smeagol', now) 
    Out[6]: 1
    
    五分钟后又一个访客:
    In [7]: conn.zadd('logins','sauro',now+(5*60)) 
    Out[7]: 1
    
    两小时后:
    In [8]:  conn.zadd('logins', 'bilbo', now+(2*60*60))  
    Out[8]: 1
    
    一天后:
    In [9]: conn.zadd('logins', 'treebeard', now+(24*60*60))  
    Out[9]: 1
    
    查看bilbo的登陆次序:
    In [10]: conn.zrank('logins','bilbo') 
    Out[10]: 2
    
    查看登陆时间:
    In [11]: conn.zscore('logins','bilbo') 
    Out[11]: 1484742798.2283144
    
    按照登陆的顺序查看每一个访客:
    In [12]: conn.zrange('logins',0,-1) 
    Out[12]: [b'smeagol', b'sauro', b'bilbo', b'treebeard']
    
    附带上登陆的时间:
    In [13]: conn.zrange('logins',0,-1,withscores=True) 
    Out[13]: 
    [(b'smeagol', 1484735598.2283144),
     (b'sauro', 1484735898.2283144),
     (b'bilbo', 1484742798.2283144),
     (b'treebeard', 1484821998.2283144)]

    6.位图

    位图(bit)是一种非常省空间且快速的处理超大集合数字的方式。假设你有一个很多用户 注册的网站,想要跟踪用户的登录频率、在某一天用户的访问量以及同一用户在固定时间 内的访问频率,等等。当然,你可以使用 Redis 集合,但如果使用递增的用户 ID,位图的 方法更加简洁和快速。
    首先为每一天创建一个集合(bitset)。为了测试,我们仅使用3天和部分用户ID:

    In [14]: days = ['2017-01-18','2017-01-19','2019-01-20']
    
    In [15]: big_spender = 1089 
    
    In [16]: tire_kicker = 40459 
    
    In [17]: late_joiner = 550212 
    
    每一天是一个单独的键,对应的用户ID设置位,例如第一天(2017-01-18)有来自 big_ spender(ID 1089) 和 tire_kicker(ID 40459) 的访问记录:
    
    In [18]: conn.setbit(days[0],big_spender, 1) 
    Out[18]: 0
    
    In [19]: conn.setbit(days[0], tire_kicker, 1) 
    Out[19]: 0
    
    第二天用户 big_spender 又有访问:
    In [20]: conn.setbit(days[1], big_spender, 1)  
    Out[20]: 0
    
    接下来的一天,big_spender 再次访问,并又有新人 late_joiner 访问:
    In [21]: conn.setbit(days[2], big_spender, 1)  
    Out[21]: 0
    
    In [22]:  conn.setbit(days[2], late_joiner, 1)  
    Out[22]: 0
    
    现在统计得到这三天的日访客数:
    In [23]: for  day in days: 
        ...:     conn.bitcount(day) 
        ...:      
    
    判断tire_kicker用户在第二天是否登陆:
    In [24]: conn.getbit(day[1],tire_kicker) 
    Out[24]: 0
    结果为未登录
    
    查看有多少访客每天都访问?
    In [25]: conn.bitop('and','everyday',*days) 
    Out[25]: 68777
    
    In [26]: conn.bitcount('everyday') 
    Out[26]: 1
    
    判断big_spender是不是每一天都登陆:
    In [27]: conn.getbit('everyday',big_spender) 
    Out[27]: 1
    
    查看三天总共有多少人登陆(不算重复项)
    In [28]: conn.bitop('or','allday',*days) 
    Out[28]: 68777
    
    In [29]: conn.bitcount('allday') 
    Out[29]: 3
    7.缓存和过期
    所有的Redis键都有一个生存期或者过期时间(expiration date),默认情况下,生存期是永久的。也可以使用expire()函数构造Redis键的生存期。下面的设置是以秒为单位数的:
    
    In [60]: ks = 'now you see ' 
    
    In [61]: conn.set(ks, 'but not for long') 
    Out[61]: True
    
    In [62]: conn.get(ks) 
    Out[62]: b'but not for long'
    
    In [63]: conn.expire(ks, 15)  
    Out[63]: True
    
    In [64]: conn.ttl(ks) 
    Out[64]: 8
    
    In [65]: conn.get(ks) 
    Out[65]: b'but not for long'
    
    In [66]: conn.get(ks) 

    expireat()命令给一个键设定过期时间,对于更新缓存是有帮助的,并且可以限制登陆会话。


    其他的NoSQL

    NoSQL 服务器都要处理远超过内存的数据,并且很多服务器要使用多台计算机。下面 列 出了值得注意的服务器和它们的 Python 库。

    NoSQL数据库

    Site                                        Python API 
     Cassandra(http://cassandra.apache.org/)   pycassa(https://github.com/pycassa/pycassa) 
     CouchDB(http://couchdb.apache.org/)       couchdb-python(https://github.com/djc/couchdb-python)
     HBase(http://hbase.apache.org/)           happybase(https://github.com/wbolster/happybase) 
     Kyoto Cabinet(http://fallabs.com/kyotocabinet/) kyotocabinet(http://fallabs.com/kyotocabinet/pythondoc/) 
     MongoDB(http://www.mongodb.org/)          mongodb(http://api.mongodb.org/python/current/) 
     Riak(http://basho.com/riak/)              riak-python-client(https://github.com/basho/riak-pythonclient) 

    转自:https://www.jianshu.com/p/8890dab85e09

  • 相关阅读:
    图片中添加文字
    几种经典的滤波算法(转)
    OPENCV初试
    图像处理和图像识别中常用的OpenCV函数
    SIP开发
    【转】opencv老是卡在某一层,
    大电子文件读取成二进制流方案
    C# 调试方法之即时窗口输出
    关于如何解锁你的WP7,以便安装自己开发的程序。
    Windows phone 7 之初体验(一.安装Windows phone 7 sdk)
  • 原文地址:https://www.cnblogs.com/fnlingnzb-learner/p/10477019.html
Copyright © 2011-2022 走看看