绝大多数的Web应用都把数据存在数据库里,应用服务器从中读取数据。随着数据量和访问量的增加,频繁通过直接访问数据库获得数据的方式对数据库造成很大的负担。Memcached是一个高性能的分布式内存对象缓存系统,用在动态Web应用中减轻数据库负载。它通过在内存中缓存数据来减少读取数据看的次数,从而提高Web应用的速度。
常见的Python客户端有如下几种:
1. Python-memcached:纯Python实现的客户端,但是只实现了Memcached的基本操作功能。
2. Pymemcahe:Pinterest开源的纯Python实现的客户端,比Python-memcached要快。
3. Python-Libmemcached:豆瓣开源的使用Cpython实现的LIbmemcached的客户端。Libmemcached是Memcached的C/C++客户端,它支持一致性哈希、分布式、同步/异步传输等功能。豆瓣用的Libmemcached是打过补丁的。
4. Pylibmc:它也是Libmemcached的一个使用C编写的Python封装的客户端。
5. Libmc:它是使用C++和Cpython编写的一个轻量高效的Memcached Python客户端,支持以一致性哈希环的方式一次性与多个Memcached节点交互。
sudo apt-get install memcached
pip install libmc
有两种常见的更新方案:
Memcached的永不失效其实是设置超市时间为0,当内存不足时,会触发LRU机制,删除最近很少使用的内存空间。
USER_KEY = 'web_develop:users:%s:v2
Redis是一个基于内存的键值对存储系统,常用作数据库、缓存和消息代理。它支持字符串、字典、列表、集合、有序集合、位图(Bitmaps)、地理位置、HyperLoglog等多种数据结构,所以常常被称为数据结构服务器。Redis支持事务、分片、主从复制,支持RDB(将内存中的数据保存在文件中)和AOF(类似于MySQL的binlog)两种持久化方式,还支持订阅分发、Lua脚本、集群(Redis3.0加入的功能)等特性。在用作缓存时Redis和Memcached功能类似,但它还能做到Memcached不能做到的几点:
1. Web应用中常需要将一些重要数据持久化到硬盘,避免宕机等原因导致数据丢失。Redis会周期性把更新的数据写入磁盘或者追加到命令日志中,并且在此基础上实现了主从同步。而Memcached在进程关闭之后数据就会丢失。
2. 一些业务为了简化工作,需要使用列表、集合这样只有Redis才支持的数据结构。相对于Memcached,Redis有更多的应用场景。
3. Redis提供了丰富的命令。比如,可以通过通配符查看线上已经存在的键、判断一个键是否存在(Memcached这点很不方便,没有设置缓存和设置的缓存为None不好区分),方便地获取服务器信息和统计数值(通过INFO)等。在Python客户端中这些功能可以直接集成到项目中,能帮助运维收集服务状态监控数据,绘制性能图表等。
In [4]: conn = redis.Redis(password='123kkk')
In [5]: conn.rpush('a', '1') #a就是要操作的键,从右边加入元素
Out[5]: 1L
In [6]: conn.lrange('a', 0, -1) #表示从索引为0的元素到最后一个元素
Out[6]: ['1']
In [7]: conn.rpush('a', '2')
Out[7]: 2L
In [8]: conn.lrange('a', 0, -1)
Out[8]: ['1', '2']
In [9]: conn.lpush('a','3') #将值推入到列表的左端
Out[9]: 3L
In [10]: conn.lrange('a', 0, -1)
Out[10]: ['3', '1', '2']
In [11]: conn.lindex('a', 0)
Out[11]: '3'
In [12]: conn.rpush('a', *[4, 5, 6]) #一次性推入三个元素
Out[12]: 6L
In [13]: conn.lrange('a', 0, -1)
Out[13]: ['3', '1', '2', '4', '5', '6']
In [14]: conn.ltrim('a', 0, 4) 对列表进行修剪,只保留索引从0到4点元素
Out[14]: True
In [15]: conn.lrange('a', 0, -1)
Out[15]: ['3', '1', '2', '4', '5']
In [16]: conn.lrange('a', 1, -1)
Out[16]: ['1', '2', '4', '5']
In [17]: conn.ltrim('a', 1, 4)
Out[17]: True
In [18]: conn.lrange('a', 0, -1)
Out[18]: ['1', '2', '4', '5']
In [19]: conn.lpop('a') #移除并返回列表最左端的元素
Out[19]: '1'
In [20]: conn.rpop('a') #移除并返回列表最右端的元素
Out[20]: '5'
In [21]: conn.lrange('a', 0, -1)
Out[21]: ['2', '4']
In [22]: conn.hset('d', 'a', 'a') #给名字为d的键添加一个名字为a,值为a的字段
Out[22]: 1L
In [24]: conn.hmset('d', {'b':2,'c':3}) #一次性添加多个字段
Out[24]: True
In [25]: conn.hget('d','a') #获取字段a的值
Out[25]: 'a'
In [26]: conn.hmget('d', ['a', 'b']) #获取多个字段的值
Out[26]: ['a', '2']
In [27]: conn.hgetall('d') #获取所有字段
Out[27]: {'a': 'a', 'b': '2', 'c': '3'}
当不需要数据库的高级功能(比如事务的回滚、关联查询、UPDATE操作等),且Redis能满足此应用场景时就可以选择Redis。除了缓存,其他常用的应用场景:
利用redis列表的lpush和ltrim来保持最新N个数据
利用Redis的有序列表zadd()添加元素,zrevrange()从高到低对列表排序
利用redis的incr、decr、incrby
位图
在大型网站应用中,热点数据量往往非常大,一个实例可能会放不下。无论是物理主机还是云主机,内存资源都是有限的,这个时候就要考虑横向扩展:由多台主机协同提供服务。现在多核CPU、几十GB内存、SSD都非常普遍,硬件资源成本却越来越低。当单机的Redis实例不能满足需要时,我们可以通过一致性哈希(Consistent Hashing)算法将Redis数据的键进行散列,通过哈希函数,让特定的键映射到特定的Redis节点上:这就是分片。在Redis3.0之前,需要借助客户端或者代理实现分片。遗憾的是,Redis的Python客户端redis-py目前还没有实现这个分片功能。
常见的分片和集群管理方式有如下三种。
1. Twemproxy: 它是在Redis3.0之前通用的方式,它是Twitter开发的一个支持Memcached ASCII和Redis协议的、单线程,使用C编写的代理。一般一个Redis应用会由多个Twemproxy来管理,少数Twemproxy负责写,多数负责读。通常使用Redis自带的Sentinel来实现故障的自动切换以达到高可用。Twemproxy可以定时向Redis Sentinel拉取信息,从而替换出现异常的节点。它最大的缺点是无法平滑地扩容/缩容,不便于韵味;其次是没有友好的控制面板。需要注意的是,看上去Twitter已经不再继续维护它了。
2. Redis Cluster:它是Redis3.0添加的集群方式,也是未来自动分片和高可用的首选方式。这种方式使用数据分片而非一致性哈希来实现,简单地说,就是一个Redis集群包含16384个哈希槽,数据库中每个键都属于这16384个哈希槽的其中一个(使用CRC16函数对键获得校验值,对16384取余来计算键值属于哪个槽)。集群中的每个节点负责处理一部分哈希槽,当添加新的节点时,只需要将对应的某些槽移动到新的添加的节点上;当移出节点时,就把这个被移出节点上面的槽分配到其他节点上,而且这个添加和下线的过程不会阻塞整个集群。这个思路很好,只是目前还没有看到大型网站成功的案例,需要继续关注。
3. Codis:豌豆荚在生产环境使用的Redis分布式集群解决方案。对于上层的应用来说,连接到Codis代理和连接原生的Redis Server没有明显的区别(有少量还不支持的命令),上层的应用可以像使用单机的Redis一样使用,Codis底层会处理请求的转发,不停机的数据迁移等工作,所有后边的一切事情,对于前面的客户端来说是透明的,可以简单地认为后边连接的是一个内存无限大的Redis服务。它还兼容Twemproxy,而且能很容易地把数据从Twemproxy迁到Codis上,让运维和监控更方便。
NoSQL最常见的解释是Not Only SQL,泛指非关系型的数据库。之前介绍的Redis就是一种NoSQL数据库。NoSQL不使用SQL作为查询语言。其数据存储不需要预先定义模式。只要保证程序的兼容即可:这在构建Web应用程序中尤为有用。它的出现还解决了关系型数据库本身无法克服的一些缺陷:
而NoSQL相对于传统的关系型数据库的一些不足显得不再那么重要,或者可以通过其他方式来保证:
NoSQL是不能完全替代关系型数据库的,它们没有绝对的孰优孰劣,我们需要根据存储的数据以及操作数据的方式来选用不同的方案。
MongoDB是一个为当代Web应用而生的NoSQL数据库,它有如下优点:
1. 文档型存储。
2. 使用高效的二进制BSON作为数据存储。
3. 自带高可用及分区的解决方案,分别为副本集(Replica Set)和分片(sharding)。
4. 基于文档的富查询语言。
5. 内置聚合工具
6. MongoDB在3.0之后增加了高性能、可伸缩、支持压缩和文档级锁的数据存储引擎WiredTiger。
li yong