【GitHub】:示例代码
【GitHub】:redis-plus-plus
1、Redis简介 |
1、Redis与其他数据库和软件对比
名称 | 类型 | 数据存储选项 | 查询类型 | 附加功能 |
---|---|---|---|---|
Redis | 内存存储的非关系数据库 | 字符串、列表、集合、散列表、有序集合 | 各数据类型均有专属命令,另外还有批量操作和不完全的事务支持 | 发布与订阅,主从复制,持久化,脚本 [存储过程] |
memcached | 内存存储的键值缓存 | 键值之间的映射 | 创建、读取、更新、删除以及其他几个命令 | 为提升性能而设的多线程服务器 |
MySQL | 关系数据库 | 1数据库可含n表,1表可含n行;可处理n表的视图;支持空间和第三方扩展 | SELECT、DELETE、INSERT、UPDATE、函数、存储过程 | 支持ACID性质 [需用lnnoDB],主从复制和主主复制 |
PostgreSQL | 关系数据库 | 1数据库可含n表,1表可含n行;可处理n表的视图;支持空间和第三方扩展;支持可定制类型 | SELECT、INSERT、UPDATE、DELETE、内置函数、自定义的存储过程 | 支持ACID性质,主从复制,由第三方支持的多主复制 |
MongoDB | 硬盘存储的非关系文档存储 | 1数据库可含n表,1表可含n无schema的BSONÄ档 | 创建、读取、更新、删除、条件查询等 | 支持map-reduce操作,主从复制,分片,空间索引 |
Redis与memcached异同:均可存键值映射,前者自动写硬盘有两种方式,支持更多类型,可主可辅
2、附加特性
内存数据库经典问题:服务器关闭时,存储的数据去向
Redis持久化:1、时间点转储 2、所有修改命令追加至文件
主存复制特性:从服务器获取主服务器初始副本,之后靠修改命令持续更新,客户端可读任意从服务器
3、使用Redis理由
1、数据存内存,请求无需经查询分析器或查询优化器处理【随机写快】
2、避免操作临时数据
2、Redis数据结构简介 |
数据结构类型:STRING [字符串]、LIST [列表]、SET [集合]、HASH [散列] 和 ZSET [有序集合]
结构类型 | 结构存储的值 | 结构的读写能力 |
---|---|---|
STRING | 字符串、整数、浮点数 | 操作字符串整体或者部分;自增或自减整数和浮点数 |
LIST | 链表,表上各节点均含一字符串 | 链表两端可推或弹元素;根据偏移量修剪链表;读取1…n个元素;根据值查找或者移除元素 |
SET | 含字符串的无序收集器,且被包含的各字符串均不相同 | 添加、获取丶移除单个元素;检查一元素是否在集合中;计算交集、并集丶差集;从集合里随机获取元素 |
HASH | 含键值对的无序散列表 | 添加、获取丶移除单个键值对;获取所有键值对 |
ZSET [有序集合] | 字符串成员与浮点数分值之间的有序映射,元素的排序由分值大小决定 | 添加、获取、删除单个元素;根据分值范围或成员来获取元素 |
1、Redis中的字符串
命令 | 行为 |
---|---|
get | 获取存储在给定键中的值 |
set | 设置存储在给定键中的值 |
del | 删除存储在给定键中的值 [可用于所有类型] |
// 字符串命令操作: redis-cli -h 192.168.86.3 -a 123456 set hello world // set执行成功返ok get hello // 获取hello键中的值 del hello // 删键值对,返成功删除数 get hello // 键已不存在, 返nil quit
2、Redis中的列表
命令 | 行为 |
---|---|
rpush | 将给定值推入列表的右端 |
lrange | 获取列表在给定范围上的所有值 |
lindex | 获取列表在给定位置上的单个元素 |
lpop | 从列表的左端弹出一个值,并返回被弹出的值 |
redis-cli -h 192.168.86.3 -a 123456 rpush list-key item rpush list-key item2 rpush list-key item3 // 向列表推入新元素之后, 该命令会返回列表当前的长度 lrange list-key 0 -1 // 使用0为范围的起始索引,-1为范围的结束索引,可以取出列表包含的所有元素 lindex list-key 1 lpop list-key // 从列表里面弹出一个元素, 被弹出的元素将不再存在于列表 lrange list-key 0 -1
3、Redis中的集合
命令 | 行为 |
---|---|
sadd | 将给定元素添加到集合 |
smembers | 返回集合包含的所有元素 |
sismember | 检查给定元素是否存在于集合中 |
srem | 如果给定的元素存在于集合中,那么移除这个元素 |
sadd set-key item sadd set-key item2 sadd set-key item3 sadd set-key item // 返1添加成功, 返0则失败 smembers set-key // 元素多时会很慢, 慎用 sismember set-key item4 // 检查某元素是否在集合中 sismember set-key item srem set-key item2 // 返回被移除元素数 srem set-key item2 smembers set-key
4、Redis中的散列
命令 | 行为 |
---|---|
hset | 在散列里面关联起给定的键值对 |
hget | 获取指定散列键的值 |
hgetall | 获取散列包含的所有键值对 |
hdel | 如果给定键存在于散列里面,那么移除这个键 |
hset hash-key sub-key1 value1 hset hash-key sub-key2 value2 hset hash-key sub-key1 value1 // 返0表示键已存在 hgetall hash-key hdel hash-key sub-key2 hdel hash-key sub-key2 // 返0表示键不在散列里 hget hash-key sub-key1 hgetall hash-key
5、Redis中的有序集合
命令 | 行为 |
---|---|
zadd | 将一个带有给定分值的成员添加到有序集合里 |
zrange | 根据元素在有序排列中所处的位置,从有序集合里面获取多个元素 |
zrangebyscore | 获取有序集合在给定分值范围内的所有元素 |
zrem | 如果给定成员存在于有序集合,那么移除这个成员 |
zadd zset-key 728 member1 zadd zset-key 982 member0 zadd zset-key 982 member0 zrange zset-key 0 -1 withscores // 元素按分值大小排序 zrangebyscore zset-key 0 800 withscores // 根据分值获取部分元素 zrem zset-key member1 zrem zset-key member1 // 返0表示键不在有序集合里 zrange zset-key 0 -1 withscores
3、Redis处理文章业务 |
#pragma once #include <cstdio> #include <iostream> #include <list> #include <string> #include <tr1/unordered_map> #include <set> #include <assert.h> #include <sw/redis++/redis++.h> using namespace sw::redis; class chapter_01 { private: static const int ONE_WEEK_IN_SECONDS = 7 * 86400; static const int VOTE_SCORE = 432; static const int ARTICLES_PER_PAGE = 25; public: void run() { // Create an Redis object, which is movable but NOT copyable. auto redis = Redis("tcp://123456@192.168.86.3:6379"); auto article_id = post_article( redis, "username", "A title", "http://www.google.com"); // 发布文章:作者,标题,链接 std::cout << "We posted a new article with id: " + article_id << std::endl; std::cout << "Its HASH looks like:" << std::endl; std::unordered_map<std::string, std::string> article_data; redis.hgetall("article:" + article_id, // Hash, return key-vals std::inserter(article_data, article_data.begin())); // 获取文章散列中所有信息 for (auto& item : article_data) { std::cout << " " + item.first + ": " + item.second << std::endl; } std::cout << std::endl; article_vote(redis, "other_user", "article:" + article_id); // 对文章投票,用户第一次投增票数和评分 auto votes = redis.hget("article:" + article_id, "votes").value(); // Hash, return val // 获取文章散列中票数 std::cout << "We voted for the article, it now has votes: " + votes << std::endl; assert(atoi(votes.c_str()) > 1); std::cout << "The currently highest-scoring articles are:" << std::endl; auto articles = get_articles(redis, 1); // 获取第1页按评分排序的文章 print_articles(articles); // 打印文章 assert(articles.size() >= 1); add_groups(redis, article_id, std::vector<std::string>{ "new-group" }); // 将所投文章加入某分组 std::cout << "We added the article to a new group, other articles include:" << std::endl; articles = get_group_articles(redis, "new-group", 1); // 获取该分组所有文章 print_articles(articles); assert(articles.size() >= 1); } // 发布并获取文章 std::string post_article(sw::redis::Redis& redis, std::string user, std::string title, std::string link) { std::string article_id = std::to_string(redis.incr("article:")); // 生成新文章ID std::string voted = "voted:" + article_id; redis.sadd(voted, user); // set, add key // 将用户加入文章投票名单 redis.expire(voted, ONE_WEEK_IN_SECONDS); // 设一周过期 auto now = time(NULL) / 1000; std::string article = "article:" + article_id; std::unordered_map<std::string, std::string> article_data; article_data.insert({ "title", title }); article_data.insert({ "link", link }); article_data.insert({ "user", user }); article_data.insert({ "now", std::to_string(now) }); article_data.insert({ "votes", "1" }); redis.hmset(article, article_data.begin(), article_data.end()); // Hash, create hash redis.zadd("score:", article, static_cast<double>(now + VOTE_SCORE)); // sorted set, add key-val // 加入评分排序的有序集合 redis.zadd("time:", article, static_cast<double>(now)); // 加入时间排序的有序集合 return article_id; } // 对文章投票 void article_vote(sw::redis::Redis& redis, std::string user, std::string article) { auto cutoff = (time(NULL) / 1000) - ONE_WEEK_IN_SECONDS; // 计算文章投票截止时间 auto ob = redis.zscore("time:", article).value(); // sorted set, return val if (ob < static_cast<double>(cutoff)) // 检查是否还可对文章投票 [有序集合相较散列返浮点数, 可不用转换] return; std::string article_id = article.substr(article.find_first_of(':') + 1); // 从article:id标识符里取出文章ID if (redis.sadd("voted:" + article_id, user) == 1) // set, add key // 第一次投票, 则增加投票数和评分 { redis.zincrby("score:", VOTE_SCORE, article); // sorted set, +increment redis.hincrby(article, "votes", 1); // Hash, +increment } } // 获取某页内所有文章 std::list<std::unordered_map<std::string, std::string>> get_articles(sw::redis::Redis& redis, int page) { return get_articles(redis, page, "score:"); } // 获取某页内所有文章 std::list<std::unordered_map<std::string, std::string>> get_articles(sw::redis::Redis& redis, int page, std::string order) { int start = (page - 1) * ARTICLES_PER_PAGE; // 设置文章起始索引和结束索引 int end = start + ARTICLES_PER_PAGE - 1; std::set<std::string> ids; redis.zrevrange(order, start, end, std::inserter(ids, ids.end())); // sorted set, val-desc, return range-keys // 获取该页众文章ID std::list<std::unordered_map<std::string, std::string>> articles; for (std::string id : ids) { std::unordered_map<std::string, std::string> article_data; redis.hgetall(id, std::inserter(article_data, article_data.begin())); // Hash, return key-vals // 根据文章ID获取文章详细信息 article_data.insert({ "id", id }); articles.push_back(article_data); } return articles; } // 对文章进行分组 void add_groups(sw::redis::Redis& redis, std::string article_id, std::vector<std::string> toAdd) { std::string article = "article:" + article_id; for (auto& item : toAdd) { redis.sadd("group:" + item, article); // set, add key // 将文章加入所属群组 } } // 获取分组中某页文章 std::list<std::unordered_map<std::string, std::string>> get_group_articles( sw::redis::Redis& redis, std::string group, int page) { return get_group_articles(redis, group, page, "score:"); } std::list<std::unordered_map<std::string, std::string>> get_group_articles( sw::redis::Redis& redis, std::string group, int page, std::string order) { std::string key = order + group; // 为每个群组的每种排列顺序创键 if (!redis.exists(key)) // 检查是否有缓存结果,无则排 { redis.zinterstore(key, { "group:" + group, order }, sw::redis::Aggregation::MAX); // sorted set // 根据评分或时间对文章排序 redis.expire(key, 60); // set key expiration time // 60秒过期,自动删 } return get_articles(redis, page, key); // 获取该页内所有文章 } void print_articles(std::list<std::unordered_map<std::string, std::string>> articles) { for (auto article : articles) { std::cout << " id: " + article["id"]; for (auto& item : article) { if (!item.first.compare("id")) { continue; } std::cout << " " + item.first + ": " + item.second << std::endl; } } } };