不要将 redis 和 mysql 混为一谈
在接触 redis 之前,相信很多人都有 mysql 的使用经验。
mysql 的实体分层由上至下依次是:
实例(instance)
mysqld 进程。
- 库(database)
- 表(table)
- 记录(row)
- 字段(field)
redis 的实体分层由上至下依次是:
实例(instance)
redis 进程。
- 库(database)
- 键值(Key-Value)
我们很容易将 mysql 与 redis 的实例(instance)和库(database)等同起来,然而却大错特错。
其实
- redis 的实例(instance) 等同于 mysql 的库(database)
- redis 的库(database) 等同于 mysql 的表(table)
redis 的多库是鸡肋
redis 是无预定义结构的(schema-less)数据库,表(table )的存在意义不大,它更多地是做为命名空间(name space),由于使用的是很不友好的数字命名(默认 0-15), redis 中的 库(database)形同鸡肋。
以下为 redis 作者的观点,引用自 database names? - Google 网上论坛
I understand how this can be useful, but unfortunately I consider Redis multiple database errors my worst decision in Redis design at all… without any kind of real gain, it makes the internals a lot more complex. The reality is that databases don't scale well for a number of reason, like active expire of keys and VM. If the DB selection can be performed with a string I can see this feature being used as a scalable O(1) dictionary layer, that instead it is not.
With DB numbers, with a default of a few DBs, we are communication better what this feature is and how can be used I think. I hope that at some point we can drop the multiple DBs support at all, but I think it is probably too late as there is a number of people relying on this feature for their work.
使用 redis 多库是妥协的结果
有一种观点认为不同的应用(app)使用不同的库(database)可以从而避免键命名冲突。
redis 对于不同的库(database)没有提供任何隔离机制,完全依赖于应用(app)部署时约定使用不同的库(database)。
为什么不约定应用(app)使用的所有键都加上应用(app)前缀,或者每个应用(app)使用不同的 redis 实例(instance)呢?
从开发人员的角度来说
- 如果每个应用(app)使用不同的实例(instance),是最省事的,连接 redis 后,直接操作即可
- 如果每个应用(app)使用不同的库(database),略微麻烦一点,连接 redis 后,先执行一下
select
db
,redis 客户端库会提供支持
- 如果每个应用(app)的键(key)都加上应用(app)前缀,会很麻烦,每一处访问 redis 的代码都要涉及
从运维人员的角度来说
- 如果每个应用(app)使用不同的实例(instance),是最麻烦的,维护压力剧增,每个实例(instance)背后还要有配套的启动、停止脚本,监控,主备实例等
- 如果每个应用(app)使用不同的库(database),略微麻烦一点,分配并记录一下,告知开发人员使用指定的 redis 实例及库(database)
- 如果每个应用(app)的键(key)都加上应用(app)前缀,是最省事的,可以灵活地为应用(app)安排使用实例(instance)或库(database)
不同的应用(app)使用不同的库(database)这一方案被采用,很可能是开发人员与运维人员互相妥协的结果。
redis 的多库扩容难
redis 的数据量或者请求数过高,会导致 redis 不稳定,最终影响服务质量。
这时候就要考虑 redis 扩容了,需要将其中一个库(database)迁到新的实例(instance)上,过程如下:
- 停掉应用(app)
将应用(app)的 redis 库(database)同步到新的 redis 实例(instance)上
通过拷贝
dump.rdb
的方式同步(传输)数据,redis 实例(instance)上的所有库(database)数据都是混在一起的,其它应用(app)数据会增加数据同步(传输)时间及新 redis 实例(instance) 数据载入时间。如果通过工具在线将应用(app)的 redis 库(database)拷贝到运行中的新 redis 实例(instance)上,会很耗时,对本身负载就很高的 redis 添加更多压力,可能会影响其它应用(app)。
新 redis 实例(instance)设置为旧 redis 实例(instance) 的 slave,同步完成后取消 Master-Slave 关系,这种方法相对更好一些。
- 启动新的 redis 实例
- 修改应用(app)配置指向新的 redis 实例
- 启动应用(app)
清除两个 redis 实例中的脏数据
select
db
,然后执行flushdb
命令即可,这可能是使用多库最大的好处。
应用(app)的停机时间(Down Time)肯定短不了。
如果每个应用(app)使用不同的实例,需要将某个实例迁到新机器,则可以做到平滑扩容(迁移),过程如下:
- 应用(app)使用 redis sentinel 方式访问 redis
- 新机器部署 redis 新实例
- 新实例设置为旧 redis 实例的 slave
- 同步完成后进行 Master-Slave 换位
- 应用(app)会自动切到新的 redis 实例
- 旧的 redis 实例可以停掉