共计 2988 个字符,预计需要花费 8 分钟才能阅读完成。
对比着关系型数据库,我们对 redis key 的设计一般有以下两种格式:
- 表名:主键名:主键值:列名
- 表名:主键值:列名 在所有主键名都是 id 的情况下(其实我个人不喜欢这种情况,比如 user 表,它的主键名就应该是 user_id,而不是 id, 这样在表与表之间关联的时候一目了然)
用冒号作为分割是设计 key 的一种不成文的原则,遵循这种格式设计出的 key 在某些 redis 客户端下可以有效的识别;
但是,在关系型数据中,除主键外,还有可能根据其他列来查询。
如上表中,username 也是极频繁查询的,往往这种列也是加了索引的。
转换到 k - v 数据中,则也要相应的生成一条按照该列为主的 key-value。
Set user:username:lisi:uid 9 #但是要保证 username 是唯一的; 这样,我们可以根据 username:lisi:uid,查出 userid=9,再查 user:9:password/email ...
mysql 与 redis 的数据转换实例
mysql 数据准备
CREATE TABLE `book` (`book_id` int(11) NOT NULL AUTO_INCREMENT, | |
`name` varchar(100) NOT NULL DEFAULT ''COMMENT' 书名 ', | |
`add_time` int(10) NOT NULL DEFAULT '0' COMMENT '添加时间', | |
PRIMARY KEY (`book_id`) | |
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='书本表'; | |
INSERT INTO book VALUES (5, 'PHP 圣经', UNIX_TIMESTAMP() ), (6, 'ruby 实战', UNIX_TIMESTAMP() ), (7, 'mysql 运维', UNIX_TIMESTAMP() ), (8, 'ruby 服务端编程', UNIX_TIMESTAMP() ); | |
CREATE TABLE `tag` (`tag_id` int(11) NOT NULL AUTO_INCREMENT, | |
`tag_name` char(40) NOT NULL DEFAULT ''COMMENT' 标签名 ', | |
PRIMARY KEY (`tag_id`) | |
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='标签表'; | |
INSERT INTO tag VALUES (1, 'PHP'), (2, 'ruby'), (3, 'mysql'), (4, 'database'); | |
CREATE TABLE `tag_book` (`tag_id` int(11) NOT NULL DEFAULT '0' COMMENT '标签 ID', | |
`book_id` int(11) NOT NULL DEFAULT '0' COMMENT '书 ID', | |
KEY `tag_id` (`tag_id`), | |
KEY `book_id` (`book_id`) | |
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='标签与书关系表'; | |
INSERT INTO `tag_book` (`tag_id`, `book_id`) VALUES ('4', '7'),('1', '5'),('2', '6'),('2', '8'); |
我们有以下查询需求:
#《mysql 运维》有几个标签:SELECT tag_name FROM `book` AS b INNER JOIN tag_book AS tb ON b.book_id = tb.book_id INNER JOIN tag AS t ON tb.tag_id = t.tag_id WHERE `name` = 'mysql 运维'; | |
# 标签 ruby 下有几本书:SELECT b.name FROM `book` AS b INNER JOIN tag_book AS tb ON b.book_id = tb.book_id INNER JOIN tag AS t ON tb.tag_id = t.tag_id WHERE t.`tag_name` = 'ruby'; |
换到 redis 中,我们可以如下操作:
一个标签下面可以包含很多书籍,一个书籍也可以包含很多标签。这种从属关系如果没有排序需求的,我们可以使用集合:
- 可以准确表达从属关系,一个标签 PHP 的集合下面有:哪些书籍(存 ID 就可以了)
- 集合不仅可以方便 CURD,还可以求并集交集等
set book:book_id:5:name 'PHP 圣经' | |
set book:book_id:6:name 'ruby 实战' | |
set book:book_id:7:name 'mysql 运维' | |
set book:book_id:8:name 'ruby 服务端编程' | |
sadd tag:tag_name:php:book_id 5 | |
sadd tag:tag_name:ruby:book_id 6 8 | |
sadd tag:tag_name:database:book_id 7 | |
sadd tag:tag_name:mysql:book_id 7 | |
# ruby 下面有哪些书 | |
127.0.0.1:6379> sort tag:tag_name:ruby:book_id get book:book_id:*:name | |
ruby 实战 | |
ruby 服务端编程 | |
# 标签同时包含 mysql,与 database 的书【取交集】127.0.0.1:6379> sinter tag:tag_name:database:book_id tag:tag_name:mysql:book_id | |
7 | |
# 在根据 book:book_id:7:name 获得书籍名称,但如果返回的数据量大,可以先添加一个 store 参数存到一个临时集合里,然后再用 sort 分页取回;# 查所有的 PHP 以及 mysql 的书;【取并集】127.0.0.1:6379> sunion tag:tag_name:php:book_id tag:tag_name:mysql:book_id | |
5 | |
7 | |
set book:book_id:9:name 'javascript 权威指南' | |
set book:book_id:10:name 'HTML+CSS' | |
sadd tag:tag_name:web:book_id 5 9 10 | |
#查 web 标签中的非 PHP 书籍 | |
127.0.0.1:6379> sdiff tag:tag_name:web:book_id tag:tag_name:php:book_id | |
9 | |
10 |
总结如下:
- 表达从属关系(一对多,多对多),最好用集合;比如:书名和标签,关注与被关注(微博粉丝关系)等等。
- 求最近的,一般利用链表后入后出的特性。比如:最近 N 个登录的用户,可以维护一个登录的链表,控制他的长度,使得里面永远保存的是最近的 N 个登录用户。
- 对于排序,积分榜这类需求,可以用有序集合,比如:我们把用户和登录次数统一存储在一个 sorted set 里,然后就可以求出登录次数最多用户。
- 对于大数据量的非是即否关系,还可以通过位图(setbit)的方式,比如:1 亿个用户, 每个用户 登陆 / 做任意操作, 记为今天活跃, 否则记为不活跃;(每天一个位图来记录,会员 id 就是位图的位置);
mysql 导出数据到 redis
http://ju.outofmemory.cn/entr…
http://www.cnblogs.com/kgdxpr…
正文完