redis faq

为什么redis不同于其他key-value存储?

主要有两个原因。

  • 在key-value DB中,redis是一个不同的演化路径,它可以包含更复杂数据类型的值,在这些数据类型上定义原子的操作。Redis数据类型与基本的数据结构密切相关,并暴露给程序员,不需要额外的抽象层。
  • redis是一个内存数据库,但持久化在磁盘,那么它代表了一个不同的权衡,在数据集不能大于内存的限制下,达到高速的读写。内存数据库的另一个优势是,复杂数据结构的内存表示比在磁盘上同样数据结构更容易操作,因此小的内部复杂度下,redis可以做很多。同时两个在磁盘上的存储格式(RDB与AOF)不需要适合随机访问,因此他们可以是紧凑的并且总是以仅可追加的方式生成(甚至AOF日志交替是一个仅可追加操作,因为新版本是从内存中的数据副本生成)。然后相比传统的磁盘存储,这种设计也面临不同的挑战。作为内存上的主数据表示,redis操作必须小心处理,确保在磁盘上始终有已更新版本的数据集。

redis的内存占用是多少?

给你一些案例(所有案例都是使用64位实例获取的):

  • 一个空实例使用3MB内存。
  • 100万个小keys->字符串类型的value的键值对使用85MB内存。
  • 100万个keys->Hash类型的值,表示一个有5个字段的对象,使用160MB内存。

测试案例很简单。使用 redis-benchmark 工具 INFO memory 的命令生成随机数据集,然后校验已使用的空间。

64位系统相比32位系统存储相同的keys将使用多的多的内存,尤其是如果keys与values都很小。这是因为64位系统的指针花费8字节。但是当然优势是你可以有很多的内存在64位系统中,因此为了运行大型的redis server或多或少的的需要64位系统。另一种选择是分片。

我喜欢redis的高级操作与特性,但是我不喜欢它把所有的东西维持在内存,并且我不能有一个比内存更大的数据集。对于此有计划改变吗?

在过去,redis开发者尝试使用虚拟内存和其他系统为了允许redis比RAM更大的数据集,如果我们能做好一件事,我们会很快开心:内存提供数据,使用磁盘做存储。因此现在没有计划为redis构建一个磁盘后端。大部分redis功能,终究,都是它的当前设计的直接结果。
如果你真正的问题不是所需的总RAM内存,而事实上你需要切分你的数据集到多个redis实例,请读文档中的 Partitioning page 获取更多信息。
最近redis实验室,redis开发的赞助公司,开发了一个 “Redis on flash” 的解决方案,该方案能够使用RAM/flash的混合方式来访问更大的数据集,并有偏向的访问模式。你可以查看他们提供的更多信息,但是这个功能不是开源的Redis代码库的部分。

redis与磁盘数据库一起使用是一个好想法吗?

是的,一个常见的设计模式包括在redis中使用非常“重写”小数据(并且你需要使用redis数据结构的数据以有效的方式建模你的问题),并且大的blobs数据写入一个SQL或者传统的持久化磁盘数据库。类似的,有时使用Redis来将存储在磁盘数据库中的同一数据子集的另一个副本存储在内存中。这看起来类似缓存,但实际上是一个更具优势的模型,因为通常redis数据集与磁盘DB数据即一起更新,而不会在缓存未命中时刷新。

有什么我可以做的来降低redis内存使用?

如果你能,可以使用32位的redis实例。还要好好利用小hash,lists,sorted sets,与整形sets,因为redis可用于一些元素的特殊场景以更加紧凑的方式表示这些数据类型。更多的信息请阅读:Memory Optimization page。

如果redis运行超出内存会发生什么?

redis将被Linux内核OOM killer杀掉,或者出错崩溃,或者开始变慢。在现代操作系统中malloc()返回NULL并不常见,通常服务器将开始交换(如果配置了交换空间),并且redis性能将开始下降,所以你可能会注意到有一些地方不对。
redis有内建保护允许用户设置一个内存使用限制,在配置文件中使用 maxmemory 参数限制redis可以使用的内存。如果到达这个限制,redis将开始回复一个错误写命令(但是将继续接受读取命令),或者你可以配置它去驱逐keys当你使用redis为缓存达到最大限制时。
在你计划使用redis作为一个LRU缓存时,我们有详细的文档 Redis as an LRU cache。
INFO 命令报告redis正使用的内存数量,所以你可以写脚本,监控你的redis服务器,在redis到达限制前检查危险状态,临界条件

在Linux下即使我还有很多空闲内存,后台也会因为fork()错误而保存失败!

简短回答: echo 1 > /proc/sys/vm/overcommit_memory
现在详细回答:
在现代操作系统中,redis后台保存模式依赖于fork的copy-on-write(写即拷贝)语义:redis forks(创建一个子进程)是父进程的完全副本。子进程dump DB至磁盘上并最终退出。理论上子进程需要使用与父进程同样多的内存作为副本,但是实际上要感谢由于大多数现代操作系统实现了copy-on-write语义,父子进程将共享公共内存页。一个内存页仅在子进程或父进程中发生变更时才会被复制。因为理论上,当子进程正在保存时,所有内存页都可能会发生改变,所以Linux无法预先知道子进程将占用多少内存,所以如果 overcommit_memory 的设置是0,fork将会失败,除非有必须的同样多的空闲RAM去真正复制所有父进程的内存页,如果你有一个3GB的redis数据集并且只有2GB的空闲内存结果将会是失败。
设置 overcommit_memory 为1告诉Linux放松并以一种更乐观的分配方式来执行fork,并且这确实是redis所希望的。
要了解Linux虚拟内存如何工作,以及 overcommit_memoryovercommit_memory 的其他替代方法,一个好的来源是这篇来自Red Hat Magazine的经典文章,“Understanding Virtual Memory”。你也可以参考proc(5)的手册页来了解可用值的解释说明

redis磁盘快照是原子的吗?

是的,redis后台保存进程总是在服务没有执行命令时forked,所以从磁盘快照角度来看,每个命令的报告是原子的在RAM中也是原子的

redis是单线程的。如何利用多个CPU/核心?

使用redis cpu成为你得瓶颈的这种情况并不常见,通常redis要么是内存要么是网络限制。例如,使用pipelining redis运行在一个普通的Linux系统上可以每秒发送100W个请求,所以如果你的应用主要使用O(N) 或 O(log(N))的命令,它几乎不会使用太多CPU。
然而,为了最大化CPU使用率你可以启动多个redis实例在同一个box(非翻译部分:可以理解为容器,因为一个实体机可能被划分为多个容器,也可以认为就是单个服务器后面直接以机器方式翻译)中,并把他们视为不同的服务器。有些情况下单个机器是不够的,所以你想要使用多个CPUs,你可以开始想想更早前面提到的一些分片方法。
在分区页你可以找到更多关于使用多个redis实例的信息Partitioning page。
然而在redis 4.0中我们开始让redis更加线程化。目前这仅限于后台删除对象,以及阻塞由redis模块实现的命令。在未来版本中,我们计划让redis越来越多的线程化。

一个单独redis实例可以持有keys的最大数量是多少?以及在Hash,List,Set,Sorted Set中最大元素数量是多少?

redis可以最多持有2^32个keys,并且每个实例实践测试过最小可以持有2.5亿个keys。
每个hash,list,set,与sorted set,可以持有2^32元素。
换句话说你的限制可能是系统中的可用内存。

我的slave自称与它的master有不同数量的keys,为什么?

如果你使用的keys有限制tiem to live(redis 过期)的话,这是正常行为。下面讲讲发生了什么:

  • master在第一次与slave同步时生成一个RDB文件。
  • RDB文件将不包含master中已经过期的keys,但是他们仍在内存中。
  • 然后这些keys仍然在redis master的内存中,即使逻辑已过期。他们不会被认为是存在的,但是随后内存将会被回收,在访问时增量和显示的回收。然而虽然这些keys不是数据集的逻辑部分,但是它们会在 INFO 输出中与 DBSIZE 命令中被告知。
  • 当slave读取由master生成的RDB文件时,这些keys的集合将不会被加载。

因此,对于设置了过期时间的很多keys集合的用户来说,在slave中看到的keys会减少,因此这是个假象,但是在实例内容中没有实际的逻辑差异

你可能感兴趣的:(数据库)