如何使用redis设计关系数据库

目录

  • redis设计关系数据库
  • 前言
  • 设计用户信息表结构
    • hash存储记录
    • set存储id
    • 图示
  • 索引/查询:
    • 1、select
      查询所有记录 : 类似sql的select
      from table_name
    • 2、根据主键查询记录
    • 3、其他列索引
  • c++ 实现
  • 小结

redis设计关系数据库


前言

最近需要一张用户信息表,因为数据量并不大,想先放在内存中,等需求变更了,再移到磁盘上,或者往mysql塞,那么问题来了,怎么用redis的数据类型设计一个关系数据库呢。

redis只有key-value这种存储结构,如果想利用它做成想其他数据库一样具备 增删改查等功能只能再设计了,这里分享一下我的设计方法,比较简单,我不知道算不算好,只是网上找了很久没找到一种通用的方法,如果有什么好的方法,还想请教一下各位,十分感谢。

设计用户信息表结构

hash存储记录

key值 : 域名:表名:主键
value值 :直接使用redis的Hash类型
如:

test:accounts_info:0 id 0 accounts ailumiyana_0 password 123456 nick_name sola_0
test:accounts_info:1 id 1 accounts ailumiyana_1 password 123456 nick_name sola_1
test:accounts_info:2 id 2 accounts ailumiyana_2 password 123456 nick_name sola_2
test:accounts_info:3 id 3 accounts ailumiyana_3 password 123456 nick_name sola_3

set存储id

另添加一个set集存放表主键, 也即id.
key值 : ids:域名:表名
value值: id
将已生成的用户id同时添加进set集合中.
我这里用了list演示,不设计类型的特殊方法的话,演示效果是一样的。

图示

索引/查询:

1、select
查询所有记录 : 类似sql的select
from table_name

有了set表后我们就可以使用redis中sort的get方法,获取所有记录.
sort ids:test:accounts_info get test:accounts_info:*->accounts get test:accounts_info:*->nick_name

2、根据主键查询记录

直接使用string类型建立主键到id的索引,其实id就是主键,但是我们一般不会用id去找记录,更多的使用account账号去找.
key值 : 域名:表名:列键名:列键值
这样我们直接用get 取得accounts的id 后再去hash中查找记录就行了.

3、其他列索引

最后可以根据表的需要建立一些其他索引,
方法同 2 ,使用类型不一定是set 哪个方便用哪个。
例如 我要统计最近登录的10个用户的信息, 那么我直接用list 的 lrange limit 0 10 就能取到.

c++ 实现

以上设计的c++实现,其中的redis的客户端使用了cpp_redis库。

例程中 :
1、我创建了一张 account_info的表 默认使用accounts 作为主键

2、插入4个用户信息.

3、查询用户ailu_1的记录值.

class table// : public redis::er_table
{
public:
    //! ctor
  table(void);
  //! dtor
  ~table(void) = default;

  //! copy ctor
  table(const table&) = delete;
  //! assignment operator
  table& operator=(const table&) = delete;

public:
  //! vector type to save table records.
  typedef std::vector<std::string> records_t;

  //! vector type to save table records entitys.
  typedef std::vector<std::string> entitys_t;

public:
  //! create table,
  //! default primary key is the records_t vec[0], if primary_key is empty!
  void create(const std::string& table_name, const records_t& vec, const std::string& primary_key = "");

public:
  //! incr primary key id.
  std::string incr_id();

  //! insert new entity to table, pelease orderly insert refer to records vector !
  //! return false while entity exits.
  bool insert(const entitys_t& vec);

  //! get entitys by primary key value.
  entitys_t get_entitys_by_primary_key_value(const std::string& primary_key_value);

private:
  //! get id by primary key value
  //! retrun "" while primary key inexitences.
  std::string get_id_by_primary_key_value(const std::string& primary_key_value);

private:
  //! redis client
  cpp_redis::client m_redis_client;

  //!
  records_t m_records;

  //! records count.
  std::size_t m_records_count;

  //! ids set key
  std::string m_ids;

  //! table name
  std::string m_table_name;

  //! incr key
  uint64_t m_incr_key;

  //! primary key
  std::string m_primary_key;
  std::size_t m_primary_key_index;
};

table::table()
  :m_records_count(0),
  m_incr_key(0)
{
  m_redis_client.connect();
  m_redis_client.select(3);
  m_redis_client.sync_commit();
}

void table::create(const std::string& table_name, const records_t& vec, const std::string& primary_key){
  assert(m_records_count == 0);
  m_ids = "ids:" + table_name;
  m_table_name = table_name;
  m_records = vec;
  m_records_count = vec.size();
  if(primary_key.empty()){
    m_primary_key = vec[0];
    m_primary_key_index = 0;
  } else {
    m_primary_key = primary_key;
    auto iter = std::find(vec.begin(), vec.end(), primary_key);
    if(iter == vec.end()){
      LOG_FATAL << "no such key.";
    }
    m_primary_key_index = iter - vec.begin();
  }

}

std::string table::incr_id(){
  return std::move(std::to_string(m_incr_key++));
}

std::string table::get_id_by_primary_key_value(const std::string& primary_key_value){

  std::future<cpp_redis::reply> fu = m_redis_client.get(primary_key_value);
  m_redis_client.sync_commit();

  cpp_redis::reply reply = fu.get();

  if(!reply.is_null()){
    return std::move(reply.as_string());
  }

  LOG_DEBUG << "primary_key " << primary_key_value << " inexitences. return \"\".";

  return "";
}

bool table::insert(const entitys_t& vec){
  assert(m_records_count != 0);
  assert(m_records_count == vec.size());

  std::string get_id = incr_id();

  // check whether the primary key already exists.
  std::string check_id = get_id_by_primary_key_value(vec[m_primary_key_index]);
  if(!check_id.empty()){
    return false;
  }

  // redis string type primary key to id index.
  //LOG_DEBUG << m_table_name + ":" + m_records[m_primary_key_index] + ":" + vec[m_primary_key_index];
  m_redis_client.set(m_table_name + ":" + m_records[m_primary_key_index] + ":" + vec[m_primary_key_index], get_id);

  // redis set type to save id.
  std::vector<std::string> id_vec = {get_id};
  m_redis_client.sadd(m_ids, id_vec);

  // redis hash type to save records key-value.
  std::vector<std::pair<std::string, std::string>> entitys_pair_vec;

  for(std::size_t i = 0; i < m_records_count; i++){
    entitys_pair_vec.emplace_back(make_pair(m_records[i], vec[i]));
  }

  m_redis_client.hmset(m_table_name + ":" + get_id, entitys_pair_vec);

  m_redis_client.sync_commit();

  return true;
}

table::entitys_t table::get_entitys_by_primary_key_value(const std::string& primary_key_value){
  std::string id = get_id_by_primary_key_value(m_table_name + ":" + m_records[m_primary_key_index] + ":" + primary_key_value);

  if(id == ""){
    static entitys_t static_empty_entitys_vec;
    return static_empty_entitys_vec;
    LOG_ERROR << "no this entitys";
  }

  entitys_t vec;

  std::future<cpp_redis::reply> reply = m_redis_client.hgetall(m_table_name + ":" + id);
  m_redis_client.sync_commit();

  std::vector<cpp_redis::reply> v = reply.get().as_array();
  auto iter = v.begin();
  for(iter++; iter < v.end(); iter += 2){
    //LOG_DEBUG << (*iter).as_string();
    vec.emplace_back((*iter).as_string());
  }

  return std::move(vec);
}

int main()
{
  table accounts_info;
  table::records_t records_vec = {"id", "accounts", "password", "nick_name"};
  accounts_info.create("sip:accounts_info", records_vec, "accounts");

  table::entitys_t entitys_vec0 = {"0", "ailu_0", "123", "sola_0"};
  accounts_info.insert(entitys_vec0);

  table::entitys_t entitys_vec1 = {"1", "ailu_1", "123", "sola_1"};
  accounts_info.insert(entitys_vec1);

  table::entitys_t entitys_vec2 = {"2", "ailu_2", "123", "sola_2"};
  accounts_info.insert(entitys_vec2);

  table::entitys_t entitys_vec3 = {"3", "ailu_3", "123", "sola_3"};
  accounts_info.insert(entitys_vec3);

  table::entitys_t ailu_1_accounts = accounts_info.get_entitys_by_primary_key_value("ailu_1");

  auto it = ailu_1_accounts.begin();
  while(it != ailu_1_accounts.end()){
    std::cout << *it << std::endl;
    it++;
  }

  getchar();
  return 0;
}

小结

目前给出了redis增查简单设计方法,更新和删除也是通过redis的基本方法对应设计即可,这里不再详述。
此外,可以看出redis的数据库设计还是比较灵活的,如何设计出最适合我们场景需求且高效的正是它难点所在。

原文地址:https://www.cnblogs.com/ailumiyana/p/10663833.html

时间: 2024-08-01 04:12:56

如何使用redis设计关系数据库的相关文章

《Redis设计与实现》读书笔记

花了几天时间把<Redis设计与实现>读完了,把一些心得记下来给大家分享. 第2章 简单动态字符串 redis里面的字符串对象都采用SDS结构实现.SDS有别于C风格的字符数组和java的String(定长).这种结构更像C++的String或者java的ArrayList<Character>.长度动态可变. redis的所有键值及字符串字面量都采用这种结构. 这一章节花了十几页去讲这个SDS结构,感觉全是废话,有时间建议去看ArrayList的源码. 第3章 链表 redis的

Redis设计与实现(一~五整合版)【搬运】

Redis设计与实现(一~五整合版) by @飘过的小牛 一 前言 项目中用到了redis,但用到的都是最最基本的功能,比如简单的slave机制,数据结构只使用了字符串.但是一直听说redis是一个很牛的开源项目,很多公司都在用.于是我就比较奇怪,这玩意不就和 memcache 差不多吗?仅仅是因为memcache是内存级别的,没有持久化功能.而redis支持持久化?难道这就是它的必杀技? 带着这个疑问,我在网上搜了一圈.发现有个叫做huangz的程序员针对redis写了一本书叫做<redis设

《Redis设计与实现》

<Redis设计与实现> 基本信息 作者: 黄健宏 丛书名: 数据库技术丛书 出版社:机械工业出版社 ISBN:9787111464747 上架时间:2014-6-3 出版日期:2014 年6月 开本:16开 页码:1 版次:1-1 所属分类:计算机 > 数据库 > 数据库理论 > 综合 更多关于>>> <Redis设计与实现>   内容简介 书籍 计算机书籍 <redis设计与实现>全面而完整地讲解了redis的内部机制与实现方式,

数据库范式?编辑 设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。

数据库范式 设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小. 目前关系数据库有六种范式:第一范式(1NF).第二范式(2NF).第三范式(3NF).巴斯-科德范式(BCNF).第四范式(4NF)和第五范式(5NF,还又称完美范式). 第一范式(1NF) 所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,

《Redis设计与实现剖析- 前言》

现如今Redis已经不折不扣的成为缓存技术中的主流中间件,基本上大型的系统都会选择Redis缓存来提升系统性能. 由于在目前开发项目中也有使用Redis,在使用以及了解Redis的过程中被Redis优秀的设计与实现所吸引,Redis本身是基于C语言实现的高级应用,Redis内部也大量使用了经典数据结构(数组,链表,Hash表,队列,堆,跳跃表),刚好最近在巩固加深数据结构与算法这方面的基本功,所以就萌出了通过剖析Redis内部实现的方式,来复习巩固C语言的知识及其高级应用和经典数据结构的原理及其

Redis设计与实现 pdf扫描版【65M】高清下载

<Redis设计与实现>全面而完整地讲解了Redis的内部机制与实现方式,对Redis的大多数单机功能以及所有多机功能的实现原理进行了介绍,展示了这些功能的核心数据结构以及关键的算法思想,图示丰富,描述清晰,并给出大量参考信息.通过阅读本书,读者可以快速.有效地了解Redis的内部构造以及运作机制,更好.更高效地使用Redis. <Redis设计与实现>主要分为四大部分.第一部分“数据结构与对象”介绍了Redis中的各种对象及其数据结构,并说明这些数据结构如何影响对象的功能和性能.

Redis设计思路学习与总结

版权声明:本文由宋增宽原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/222 来源:腾云阁 https://www.qcloud.com/community 宋增宽,腾讯工程师,16年毕业加入腾讯,从事海量服务后台设计与研发工作,现在负责QQ群后台等项目,喜欢研究技术,并思考技术演变,专注于高并发业务架构的设计与性能优化. 下半年利用空余时间研究和分析了部分Redis源码,本文从网络模型.数据结构和内存管理.持久化和多机

Redis设计与实现读书笔记(一) SDS

作为redis最基础的底层数据结构之一,SDS提供了许多C风格字符串所不具备的功能,为之后redis内存管理提供了许多方便.它们分别是: 二进制安全 减少字符串长度获取时间复杂度 杜绝字符串溢出 减少内存分配次数 兼容部分C语言函数 下面将简要阐述SDS基础结构,并介绍这些功能相应的实现细节. SDS字符类型定义非常简单,以redis3.0.7为例: typedef char *sds; struct sdshdr { unsigned int len; //定义当前字符串长度(不包含'\0')

学习笔记-Redis设计与实现-事件

Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件: 文件事件(file event):Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接,而文件事件就是服务器对套接字操作的抽象. 时间事件(time event):Redis服务器中的一些操作(比如serverCron函数)需要在给定的时间点执行,而时间事件就是服务器对这类定时操作的抽象. 12.1 文件事件 Redis基于Reactor模式开发了自己的网络事件处理器,被称为文件事件处理器(file event