1.访问redis根目录 cd /usr/local/redis-2.8.19
redis查询所有的key redis查keys总数
2.登录redis:redis-cli -h 127.0.0.1 -p 6379
3.查看所有key值:keys
4.删除指定索引的值:del key
5.清空整个 Redis 服务器的数据:flushall
6.清空当前库中的所有 ke背景:近公司APP要上线积分功能,需要按用户、业务场景、不同的累计周期、相关的业务信息等校验积分获取策略。为方便测试同学测试过程中直接列出redis集群中相关的统计指标key,进而查询相关信息,方便测试。y:flushdb
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系uint64_t m_incr_key;统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
更多Redis相关技术文章,请访问Redis教程栏目进行学习!
redis设计关系数据库
前言
设计用户信息表结构
set存储id
图示
索引/查询:
1、select
查询所有记录 : 类似sql的selectfrom table_name
2、根据主键dictTypetype;查询记录
3、其他列索引
c++ 实现
小结
redis设计关系数据库
前言
近需要一张用户信息表,因为数据量并不大,想先放在内存中,等需求变更了,再移到磁盘上,或者往mysql塞,那么问题来了,怎么用redis的数据类型设计一个关系数据库呢。
设计用户信息表结构
key值 : 域名:表名:主键
如:
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的selectfrom table_name
有了set表后我们就可以使用redis中sort的get方法,获取所有记录.
sort ids:test:accounts_info get test:accounts_info:->accounts get test:accounts_info:->nick_name 2、根据主键查询记录
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);
~table(void) = default;
//! copy ctor
table(const table&) = delete;
//! assignment operator
table& operator=(const table&) = delete;
public:
//! vector type to se table records.
typedef std::vector
//! vector type to se table records entitys.
typedef std::vector
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
//!
records_t m_records;
//! records count.
std::size_t m_records_count;
//! ids set key
std::string m_ids;
//! table name
//! 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_entitys_pair_vec.emplace_back(make_pair(m_records[i], vec[i]));key.empty()){
m_primary_key = vec[0];
m_primary_key_index = 0;
} else {
m_primary_key = primary_key;
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
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);
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 se id.
std::vector
// redis hash type to se records key-value.
std::vector
for(std::size_t i = 0; i < m_records_count; i++){
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
m_redis_client.sync_commit();
std::vector
auto iter = v.begin();
for(iter++; iter < v.end(); iter += 2){
//LOG_DEBUG << (iter).as_string();
}return std::move(vec);
}int main()
{table accounts_info;
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的数据库设计还是比较灵活的,如何设计出适合我们场景需求且高效的正是它难点所在。
如何使用redis设计关系数据库
标签:returnmaindefaultcom增删改debincrprimary更新
“redis ”不能做条件查询。只适合做储存和读(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除取,无法处理后台逻辑。
Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不hash存储记录足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Ja,C/C++,C#,PHP,JaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树。存盘可以有意无意的对数据进行写作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取作的可扩展性和数据冗余很有帮助。
就DB来说,Redis成绩已经很惊人了,且不说memcachedb和Tokyo Cabinet之流,就说原版的memcached,速度似乎也只能达到这个级别。Redis根本是使用内存存储,持久化的关键是这三条指令:SAVE BGSAVE LASTSAVE …
Redis的外围由一个键、值映射的字典构成。与其他非关系型数据库主要不同在于:Redis中值的类型[1] 不仅限于字符串,还支持如下抽象数据类型:
字符串列表
无序不重复的字符串
有序不重复的字符串
键、值都为字符串的哈希表
值的类型决定了值本身支持的作。Redis支持不同无序、有序的列表,无序、有序的间的交集、并集等高级服务器端原子作。
redis提供五种数据类型:string,hash,list,set及zset(sorted set)。
redis的值有5种类型,不同的类型有不同的命令来获取: 字符直接 get key 队列 左端弹出一个元素 LPOP key 哈希 HGET key field SMEMBERS key 返回中的所有元素 有序ZRANGE key start stop 更详细的命「单元素作」 :对中的元素进行增删改查作和底层数据结构相关,如对字典进行增删改查时间复杂度为O(1),对跳表redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set()、zset(sorted set --有序)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和集及更丰富的作,而且这些作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改作写入追加的记录文件,并且在此基础上实现了master-sle(主从)同步。进行增删查时间复杂为O(logN)令可以查看redis常用命令。
首先测试了下keys和scan居然都只能查看到当客户端请求过来。如果首先到达node0,当时这个key(假设计算出槽节点为10086)所在的槽并不在node0 节点上(假设node0通过自己保存结构查询出来处理key的节点为node1,地址为127.0.0.1:7001),node0 会返回给客户端一个MOVED错误,结果类似如下当前节点的匹配到的key,what the !!
想到之redis只有key-value这种存储结构,如果想利用它做成想其他数据库一样具备 增删改查等功能只能再设计了,这里分享一下我的设计方法,比较简单,我不知道算不算好,只是网上找了很久没找到一种通用的方法,如果有什么好的方法,还想请教一下各位,十分感谢。前工作中也碰到过同样的问题,于是折腾了会儿写了个脚本:
redis提供了灵活的数据查询方式,牛的就是key的搜索支持正则表达式。
}}jedis.keys(“”);表示搜索所有key
jedis.keys(“abc”)表示搜索开头为abc的key数据
遍历了key就能遍历到value。
其实就是一个set
代码代码如下:
RedisDO rd = new RedisDO();
rd.open();
Set s = rd.jedis.keys("");
Iterator it = s.iterator();
while (it.hasNext()) {
String key = (String) it.next();
String value = rd.jedis.get(key);
System.out.println(key + value);
}rd.close();
rd的算法为集成redis 运算
代码代码如下:
package com.jaer.click.way;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;
public class RedisDO {
public Jedis jedis;
public void close(){
jedis = null;
}public Jedis open(){
config.setMaxActive(100);
config.setMaxIdle(20);
config.setMaxWait(1000l);
JedisPool pool;
pool = new JedisPool(config, "xxxxxxxx.xx.xx.xx", 6379);
boolean borrowOrOprSuccess = true;
try {
jedis = pool.getResource();
// do redis opt by instance
} catch (JedisConnectionExcpp_redis::client m_redis_client;ception e) {
borrowOrOprSuccess = false;
if (jedis != null)
pool.returnBrokenResource(jedis);
} finally {
if (borrowOrOprSuccess)
pool.returnResource(jedis);
}jedis = pool.getResource();
return jedis;
}/
@param args
/
// TODO Auto-generated method stub
redis相同的key是快。
value值 :直接使用redis的Hash类型redis相同的会覆盖,redis本身就是以key为主键的,key相同肯定覆盖。如果是要避免使用用一个KEY,可以在不同的系统生成GUID的方式做key,也可以让redis产生key给不同的系统使用。
3. redis常见性能问题和解决方案:redis相同使用Redis的脚本功能实现Redis中数据简单查询,有需要的朋友可以参考下。 在Redis的设计中,key是一切,对于Redis是可见的,而value对于Redis来说就是一个字节数组。
redis相同常用命令:
redis相同就DB来说,Redis成绩已经很惊人了,且不说memcachedb和Tokyo Cabinet之流,就说原版的memcached,速度似乎也只能达到这个级别。Redis根本是使用内存存储。
redis相同当接收到SAVE指令的时候,Redis就会dump数据到一个文件里面。
redis相同值得一说的是它的功能:存储列表和,这是它与mc之流相比更有竞争力的地方。
redis相同不介绍mc里面已经有的内容,只列出特殊的:TYPE key — 用来获取某key的类型KEYS pattern — 匹配所有符合模式的key,比如KEYS 就列出所有的key了,当然,复杂度O(n)。
从业务服务器到Redis服务器这条调用链路中变慢的原因可能有2个
//! dtor但是大多数情况下都是Redis服务的问题。但是应该如何衡量Redis变慢了呢?命令执行时间大于1s,大于2s?这其实并没有一个固定的标准。
我们可以使用如下命令来监测和统计测试期间的延迟(以微秒为单位)
比如执行如下命令
参数中的60是测试执行的秒数,可以看到延迟为3725微秒(3毫秒左右),如果命令的执行远超3毫秒,此时Redis就有可能很慢了!
那么Redis有哪些慢作呢?
Redis的各种命令是在一个线程中依次执行的,如果一个命令在Redis中执行的时间过长,就会影响整体的性能,因为后面的请求要等到前面的请求被处理完才能被处理,这些耗时的作有如下几个部分
Redis可以通过日志记录那些耗时长的命令,使用如下配置即可
执行如下命令,就可以查询到近记录的慢日志
之前的文章我们已经介绍了Redis的底层数据结构,它们的时间复杂度如下表所示
名称 时间复杂度 dict(字典) O(1) ziplist (压缩列表) O(n) zskiplist (跳表) O(logN) quicklist(快速列表) O(n) intset(整数) O(n)
「范围作」 :对进行遍历作,比如Hash类型的HGETALL,Set类型的SMEMBERS,List类型的LRANGE,ZSet类型的ZRANGE,时间复杂度为O(n),避免使用,用SCAN系列命令代替。(hash用hscan,set用sscan,zset用zscan)
「聚合作」 :这类作的时间复杂度通常大于O(n),比如SORT、SUNION、ZUNIONSTORE
「统计作」 :当想获取中的元素个数时,如LLEN或者SCARD,时间复杂度为O(1),因为它们的底层数据结构如quicklist,dict,intset保存了元素的个数
「边界作」 :list底层是用quicklist实现的,quicklist保存了链表的头尾节点,因此对链表的头尾节点进行作,时间复杂度为O(1),如LPOP、RPOP、LPUSH、RPUSH
「当想获取Redis中的key时,避免使用keys 」 ,Redis中保存的键值对是保存在一个字典中的(和Ja中的HashMap类似,也是通过数组+链表的方式实现的),key的类型都是string,value的类型可以是string,set,list等
例如当我们执行如下命令后,redis的字典结构如下
我们可以用keys命令来查询Redis定的key,如下所示
keys命令的复杂度是O(n),它会遍历这个dict中的所有key,如果Redis中存的key非常多,所有读写Redis的指令都会被延迟等待,所以千万不用在生产环境用这个命令(如果你已经准备离职的话,祝你玩的开心)。
「既然不让你用keys,肯定有替代品,那就是scan」
scan是通过游标逐步遍历的,因此不会长时间阻塞Redis
「用用zscan遍历zset,hscan遍历hash,sscan遍历set的原理和scan命令类似,因为hash,set,zset的底层实现的数据结构中都有dict。」
「如果一个key对应的value非常大,那么这个key就被称为bigkey。写入bigkey在分配内存时需要消耗更长的时间。同样,删除bigkey释放内存也需要消耗更长的时间」
如果在慢日志中发现了SET/DEL这种复杂度不高的命令,此时你就应该排查一下是否是由于写入bigkey导致的。
「如何定位bigkey?」
Redis提供了扫描bigkey的命令
可以看到命令的输入有如下3个部分
这个命令的原理就是redis在内部执行了scan命令,遍历实例中所有的key,然后正对key的类型,分别执行strlen,llen,hlen,scard,zcard命令,来获取string类型的长度,容器类型(list,hash,set,zset)的元素个数
使用这个命令需要注意如下两个问题
「如何解决bigkey带来的性能问题?」
我们可以给Redis中的key设置过期时间,那么当key过期了,它在什么时候会被删除呢?
「如果让我们写Redis过期策略,我们会想到如下三种方案」
定时删除策略对CPU不友好,当过期键比较多的时候,Redis线程用来删除过期键,会影响正常请求的响应
惰性删除读CPU是比较有好的,但是会浪费大量的内存。如果一个key设置过期时间放到内存中,但是没有被访问到,那么它会一直存在内存中
定期删除策略则对CPU和内存if(iter == vec.end()){都比较友好
「惰性删除」 客户端在访问key的时候,对key的过期时间进行校验,如果过期了就立即删除
「定期删除」 Redis会将设置了过期时间的key放在一个独立的字典中,定时遍历这个字典来删除过期的key,遍历策略如下
为了避免主线程一直在删除key,我们可以采用如下两种方案
Redis是一个内存数据库,当Redis使用的内存超过物理内存的限制后,内存数据会和磁盘产生频繁的交换,交换会导致Redis性能急剧下降。所以在生产环境中我们通过配置参数maxmemoey来限制使用的内存大小。
当实际使用的内存超过maxmemoey后,Redis提供了如下几种可选策略。
「Redis的淘汰策略也是在主线程中执行的。但内存超过Redis上限后,每次写入都需要淘汰一些key,导致请求时间变长」
可以通过如下几个方式进行改善
Redis的持久化机制有RDB快照和AOF日志,每次写命令之后后,Redis提供了如下三种刷盘机制
「当aof的刷盘机制为always,redis每处理一次写命令,都会把写命令刷到磁盘中才返回,整个过程是在Redis主线程中进行的,势必会拖慢redis的性能」
当aof的刷盘机制为everysec,redis写完内存后就返回,刷盘作是放到后台线程中去执行的,后台线程每隔1秒把内存中的数据刷到磁盘中
当aof的刷盘机制为no,宕机后可能会造成部分数据丢失,一般不采用。
「一般情况下,aof刷盘机制配置为everysec即可」
在持久化一节中,我们已经提到 「Redis生成rdb文件和aof日志重写,都是通过主线程fork子进程的方式,让子进程来执行的,主线程的内存越大,阻塞时间越长。」
可以通过如下方式优化
「当Redis性能急剧下降时就有可能是数据被换到Swap分区,我们该如何排查Redis数据是否被换到Swap分区呢?」
我们可以通过如下方式来解决
我们总结一下Redis的慢作
在Redis集群中,每个节点都会保存槽信息,比如Redis集群默认有16384个槽,假设node0节点保存了0-4000槽数据,node1保存了4001-8000槽数据,node2 保存了80001-16383槽数据,则在每个节点中,都保存有当前节点处理哪些槽数据,哪些数据由其他节点处理,如node0保存了0-4000由node0处理,4001-8000由node1处理,80001-16383由node2处理。
public static void main(String[] args) {这样客户端就知道它应该去127.0.0当机器的内存不够时,作系统会将部分内存的数据置换到磁盘上,这块磁盘区域就是Swap分区,当应用程序再次访问这些数据的时候,就需要从磁盘上读取,导致性能严重下降.1:7001再做请求