Redis、Memecache、Mongodb

文章目录

    • 1 缓存系统Memcached
      • 1.1 Libmc安装配置
      • 1.2 缓存更新策略
      • 1.3 Memcached使用的经验
    • 2 键值对数据库Redis
      • 2.1 操作Redis
      • 2.2 Redis应用场景
        • 2.2.1 取最新N个数据的操作
        • 2.2.2 取TOP N操作(排行榜应用)
        • 2.2.3 计数器
        • 2.2.4 实时统计
      • 2.3 分片和集群管理
    • 3 NoSQL数据库和MongoDB
      • 3.1 MongoDB

1 缓存系统Memcached

绝大多数的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节点交互。

1.1 Libmc安装配置

sudo apt-get install memcached

pip install libmc

1.2 缓存更新策略

有两种常见的更新方案:

  • 懒惰式加载。客户端先查询Memcached,如果命中,则返回结果;如果没有命中(没有数据或已经过期),则从数据库中获得最新数据,并写回到Memcached中,最后返回结果。这种方法直接、简单。但是在高并发的场景下,突然失效会让后端数据库的压力骤增。
  • 主动更新。默认缓存永 不失效。当有数据需要更新时,同时也会把最新数据写回到Memcached中。这种更新如果耗时过长,应该使用异步的更新,如放在消息队列中。

Memcached的永不失效其实是设置超市时间为0,当内存不足时,会触发LRU机制,删除最近很少使用的内存空间。

1.3 Memcached使用的经验

  1. 批量获取时尽可能使用“mc.get_multi“,与同数量的”mc.get"相比,能减网络请求的次数。
  2. 对缓存全部更新,可以直接升级缓存键,在缓存键字符串最后加个版本号,比如:USER_KEY = 'web_develop:users:%s:v2
  3. 批量更新缓存的时候应该尽量少给后端数据库带来压力,需要对缓存预热
  4. Memcached不仅能缓存SQL查询的结果,它甚至还可以缓存HTML片段
  5. 按照Memcached协议,缓存的值必须小于1MB,内容可以是任意字符串。虽然Libmc通过拆分支持大于1MB、小于10MB的结果,但是不鼓励这么做。
  6. 不鼓励使用读/写/删之外的接口。不鼓励使用的接口包括但不限于:append、prepend、incr、decr、add、replace。Memcached并不适合做上面这些接口做的事,使用这些接口也会给运维带来困难。

2 键值对数据库Redis

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客户端中这些功能可以直接集成到项目中,能帮助运维收集服务状态监控数据,绘制性能图表等。

2.1 操作Redis

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) 对列表进行修剪,只保留索引从04点元素
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'}

2.2 Redis应用场景

当不需要数据库的高级功能(比如事务的回滚、关联查询、UPDATE操作等),且Redis能满足此应用场景时就可以选择Redis。除了缓存,其他常用的应用场景:

2.2.1 取最新N个数据的操作

利用redis列表的lpush和ltrim来保持最新N个数据

2.2.2 取TOP N操作(排行榜应用)

利用Redis的有序列表zadd()添加元素,zrevrange()从高到低对列表排序

2.2.3 计数器

利用redis的incr、decr、incrby

2.2.4 实时统计

位图

2.3 分片和集群管理

在大型网站应用中,热点数据量往往非常大,一个实例可能会放不下。无论是物理主机还是云主机,内存资源都是有限的,这个时候就要考虑横向扩展:由多台主机协同提供服务。现在多核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上,让运维和监控更方便。

3 NoSQL数据库和MongoDB

NoSQL最常见的解释是Not Only SQL,泛指非关系型的数据库。之前介绍的Redis就是一种NoSQL数据库。NoSQL不使用SQL作为查询语言。其数据存储不需要预先定义模式。只要保证程序的兼容即可:这在构建Web应用程序中尤为有用。它的出现还解决了关系型数据库本身无法克服的一些缺陷:

  • 对数据库可以进行高并发读写的需求
  • 对海量数据的高效率存储和访问的需求
  • 高可扩展性和可用性

而NoSQL相对于传统的关系型数据库的一些不足显得不再那么重要,或者可以通过其他方式来保证:

  • 数据库事务一致性需求
  • 数据库的实时读写需求
  • 对复杂的SQL查询,特别是多表关联查询的需求。这里需要强调一点,大数据量的Web应用都应该减少甚至忌讳使用多个大表的关联查询。

NoSQL是不能完全替代关系型数据库的,它们没有绝对的孰优孰劣,我们需要根据存储的数据以及操作数据的方式来选用不同的方案。

3.1 MongoDB

MongoDB是一个为当代Web应用而生的NoSQL数据库,它有如下优点:

1. 文档型存储。
2. 使用高效的二进制BSON作为数据存储。
3. 自带高可用及分区的解决方案,分别为副本集(Replica Set)和分片(sharding)。
4. 基于文档的富查询语言。
5. 内置聚合工具
6. MongoDB在3.0之后增加了高性能、可伸缩、支持压缩和文档级锁的数据存储引擎WiredTiger。

li yong

你可能感兴趣的:(其他)