精英联盟总队 缓存框架Redis、Memcached技术选型,「微服务」( 三 )


数组的每一个元素代表当前程序所分配的内存块的个数 , 且内存块的大小为该元素的下标 。 在源码中 , 这个数组为zmalloc_allocations 。 zmalloc_allocations[16]代表已经分配的长度为16bytes的内存块的个数 。 zmalloc.c中有一个静态变量used_memory用来记录当前分配的内存总大小 。
所以 , 总的来看 , Redis采用的是包装的mallc/free 。
在Redis中 , 并不是所有的数据都一直存储在内存中的 。 这是和Memcached相比一个最大的区别 。
当物理内存用完时 , Redis可以将一些很久没用到的value交换到磁盘 。 Redis只会缓存所有的key的信息 , 如果Redis发现内存的使用量超过了某一个阀值 , 将触发swap的操作 , Redis根据“swappability=age*log(size_in_memory)”计算出哪些key对应的value需要swap到磁盘 。
然后再将这些key对应的value持久化到磁盘中 , 同时在内存中清除 。
这种特性使得Redis可以保持超过其机器本身内存大小的数据 。
当然 , 机器本身的内存必须要能够保持所有的key , 毕竟这些数据是不会进行swap操作的 。
同时由于Redis将内存中的数据swap到磁盘中的时候 , 提供服务的主线程和进行swap操作的子线程会共享这部分内存 , 所以如果更新需要swap的数据 , Redis将阻塞这个操作 , 直到子线程完成swap操作后才可以进行修改 。
当从Redis中读取数据的时候 , 如果读取的key对应的value不在内存中 , 那么Redis就需要从swap文件中加载相应数据 , 然后再返回给请求方 。
这里就存在一个I/O线程池的问题 。 在默认的情况下 , Redis会出现阻塞 , 即完成所有的swap文件加载后才会响应 。 这种策略在客户端的数量较小 , 进行批量操作的时候比较合适 。 但是如果将Redis应用在一个大型的网站应用程序中 , 这显然是无法满足大并发的情况的 。
所以Redis运行我们设置I/O线程池的大小 , 对需要从swap文件中加载相应数据的读取请求进行并发操作 , 减少阻塞的时间 。
Redis与Memcached内存方式对比Memcached使用预分配的内存池的方式 , 使用slab和大小不同的chunk来管理内存 , Item根据大小选择合适的chunk存储 , 内存池的方式可以省去申请/释放内存的开销 , 并且能减小内存碎片产生 , 但这种方式也会带来一定程度上的空间浪费 , 并且在内存仍然有很大空间时 , 新的数据也可能会被剔除 。
Redis使用现场申请内存的方式来存储数据 , 并且很少使用free-list等方式来优化内存分配 , 会在一定程度上存在内存碎片 , Redis根据存储命令参数 , 会把带过期时间的数据单独存放在一起 , 并把它们称为临时数据 , 非临时数据是永远不会被剔除的 , 即便物理内存不够 , 导致swap也不会剔除任何非临时数据(但会尝试剔除部分临时数据) , 这点上Redis更适合作为存储而不是cache 。
数据存储及持久化Memcachedmemcached不支持内存数据的持久化操作 , 所有的数据都以in-memory的形式存储 。
Redisredis支持持久化操作 。
redis提供了两种不同的持久化方法来将数据存储到硬盘里面 , 一种是快照(snapshotting) , 它可以将存在于某一时刻的所有数据都写入硬盘里面 。 另一种方法叫只追加文件(append-onlyfile , AOF),它会在执行写命令时 , 将被执行的写命令复制到硬盘里面 。
集群管理不同MemcachedMemcached本身并不支持分布式 , 因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储 。


推荐阅读