🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
对比着关系型数据库,我们对redis key的设计一般有以下两种格式: - **表名:主键名:主键值:列名** - **表名:主键值:列名** 在所有主键名都是id的情况下(其实我个人不喜欢这种情况,比如user表,它的主键名就应该是user_id,而不是id,这样在表与表之间关联的时候一目了然) > 用冒号作为分割是设计key的一种不成文的原则,遵循这种格式设计出的key在某些redis客户端下可以有效的识别; ![key设计技巧.jpg-120.9kB][1] 但是,在关系型数据中,除主键外,还有可能根据其他列来查询。 如上表中, username 也是极频繁查询的,往往这种列也是加了索引的。 转换到k-v数据中,则也要相应的生成一条按照该列为主的key-value。 Set user:username:lisi:uid 9 #但是要保证username是唯一的; 这样,我们可以根据username:lisi:uid ,查出userid=9, 再查user:9:password/email ... ## mysql与redis的数据转换实例 **mysql数据准备** ```sql 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'); ``` 我们有以下查询需求: ```sql # 《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中,我们可以如下操作: 一个标签下面可以包含很多书籍,一个书籍也可以包含很多标签。这种从属关系如果没有排序需求的,我们可以使用集合: 1. 可以准确表达从属关系,一个标签PHP的集合下面有:哪些书籍(存ID就可以了) 2. 集合不仅可以方便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 ``` ### 总结如下: 1. 表达从属关系(一对多,多对多),最好用集合; 比如: 书名和标签,关注与被关注(微博粉丝关系)等等。 2. 求最近的,一般利用链表后入后出的特性。比如:最近N个登录的用户,可以维护一个登录的链表,控制他的长度,使得里面永远保存的是最近的N个登录用户。 3. 对于排序,积分榜这类需求,可以用有序集合,比如:我们把用户和登录次数统一存储在一个sorted set里,然后就可以求出登录次数最多用户。 4. 对于大数据量的非是即否关系,还可以通过位图(setbit)的方式,比如:1亿个用户, 每个用户 登陆/做任意操作,记为今天活跃,否则记为不活跃;(每天一个位图来记录,会员id就是位图的位置); ### mysql导出数据到redis http://ju.outofmemory.cn/entry/28047 http://www.cnblogs.com/kgdxpr/p/3643672.html [1]: http://static.zybuluo.com/a5635268/d09cw6x4mkt4k2nmpsxyzz0n/key%E8%AE%BE%E8%AE%A1%E6%8A%80%E5%B7%A7.jpg