如何查看比redis更好的内存数据库吗redis中的数据

Redis不仅仅是一个简单的key-value内存数据库,Redis官网对自身的定义是“数据结构服务器”。通过用心设计各种数据结构类型的数据存储,可以实现部分的数据查询功能。因为在Redis的设计中,key是一切,对于Redis是可见的,而value对于Redis来说就是一个字节数组,Redis并不知道你的value中存储的是什么,所以要想实现比如
‘select * from users where user.location="shanghai"’
这样的查询,在Redis是没办法通过value进行比较得出结果的。但是可以通过不同的数据结构类型来做到这一点。比如如下的数据定义
users:1 {name:Jack,age:28,location:shanghai}
users:2 {name:Frank,age:30,location:beijing}
users:location:shanghai [1]
其中users:1 users:2 分别定义了两个用户信息,通过Redis中的hash数据结构,而users:location:shanghai 记录了所有上海的用户id,通过集合数据结构实现。这样通过两次简单的Redis命令调用就可以实现我们上面的查询。
Jedis jedis = jedisPool.getResource();
Set&String& shanghaiIDs = jedis.smembers("users:location:shanghai");
//遍历该set
//通过hgetall获取对应的user信息
jedis.hgetAll("users:" + shanghaiIDs[0]);
通过诸如以上的设计,可以实现简单的条件查询。但是这样的问题也很多,首先需要多维护一个ID索引的集合,其次对于一些复杂查询无能为力(当然也不能期望Redis实现像关系数据库那样的查询,Redis不是干这的)。
但是Redis2.6集成了Lua脚本,可以通过eval命令,直接在RedisServer环境中执行Lua脚本,并且可以在Lua脚本中调用Redis命令。其实,就是说可以让你用Lua这种脚本语言,对Redis中存储的key value进行操作,这个意义就大了,甚至可以将你们系统所需的各种业务写成一个个lua脚本,提前加载进入Redis,然后对于请求的响应,只需要调用一个个lua脚本就行。当然这样说有点夸张,但是意思就是这样的。
比如,现在我们要实现一个‘所有age大于28岁的user’这样一个查询,那么通过以下的Lua脚本就可以实现
public static final String SCRIPT =
"local resultKeys={};"
+ "for k,v in ipairs(KEYS) do "
+ " local tmp = redis.call('hget', v, 'age');"
+ " if tmp & ARGV[1] then "
table.insert(resultKeys,v);"
+ "return resultK";
执行脚本代码
Jedis jedis = jedisPool.getResource();
jedis.auth(auth);
List&String& keys = Arrays.asList(allUserKeys);
List&String& args = new ArrayList&&();
args.add("28");
List&String& resultKeys = (List&String&)jedis.evalsha(funcKey, keys, args);
return resultK
注意,以上的代码中使用的是evalsha命令,该命令参数的不是直接Lua脚本字符串,而是提前已经加载到Redis中的函数的一个SHA索引,通过以下的代码将系统中所有需要执行的函数提前加载到Redis中,我们的系统维护一个函数哈希表,后续需要实现什么功能,就从函数表中获取对应功能的SHA索引,通过evalsha调用就行。
String shaFuncKey = jedis.scriptLoad(SCRIPT);//加载脚本,获取sha索引
funcTable.put(funcName_age, shaFuncKey);//添加到函数表中
通过以上的方法,便可以使较为复杂的查询放到Redis中去执行,提高效率。
Redis学习笔记(八)redis之lua脚本学习
redis系列文章目录
使用spring-data-redis实现incr自增
Redis 利用Hash存储节约内存
Redis学习笔记(九)redis实现时时直播列表缓存,支持分页[热点数据存储...
利用redis + lua解决抢红包高并发的问题
抢红包的需求分析
抢红包的场景有点像秒杀,但是要比秒杀简单点。
因为秒杀通常要和库存相关。而抢红包则可以允许有些红包没有被抢到,因为发红包的人不会有损失,没抢完的钱再退回给发红包的人即可。
另外像小米...
jedis调用redis之源码
/*jadclipse*/// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.package redis.clients.je...
在Redis的学习中,对于脚本刚开始还是有很多的不理解,命令的参数含义也有很多不明白。于是找了很对博文记录下来,方便以后复习和理解。
由于我对于脚本的理解仅仅停留在基本的阶段,所以就先不探究脚本的应...
redis实现分布式锁
1.基本思想
http://doc.redisfans.com/string/set.html 有说明 如下
命令 SET resource-name anystring NX EX max-l...
Redis-Java客户端Jedis
一.Jedis的下载方式
Maven管理下,在pom文件中引入如下依赖即可:
dependency&
在Redis中执行Lua脚本有两种方法:eval和evalsha1.eval
eval 脚本内容 key个数 key列表 参数列表
如果Lua脚本较长,还可以使用redis-cli-eval直接执...
redis支持lua脚本,lua脚本的详细语法和简绍不在这篇文章的总结范围。这里只说一下redis和lua的结合。
redis执行lua脚本是原子性的,因此可以作为事务的解决方...
为了使用redis 做流控的功能,使用redis 的lua 脚本实现发送前的控制器,流程图如下:
lua脚本如下:
local function addToQueue(x,t...
java连接redis中的数据查、增、改、删操作的方法
package com.lml.
import java.util.HashM
import java.util.I
import java.util....
没有更多推荐了,[讨论]在windows下用哪些内存数据库,类似redis的
[问题点数:100分,结帖人6rl]
本版专家分:3255
结帖率 100%
CSDN今日推荐
本版专家分:10989
本版专家分:3255
本版专家分:1472
本版专家分:14765
本版专家分:3255
本版专家分:3255
本版专家分:14765
本版专家分:3255
本版专家分:46792
2013年12月 Delphi大版内专家分月排行榜第二
2014年4月 Delphi大版内专家分月排行榜第三2014年3月 Delphi大版内专家分月排行榜第三2013年5月 Delphi大版内专家分月排行榜第三2013年1月 Delphi大版内专家分月排行榜第三2010年11月 Delphi大版内专家分月排行榜第三2009年12月 Delphi大版内专家分月排行榜第三2009年11月 Delphi大版内专家分月排行榜第三
本版专家分:3255
匿名用户不能发表回复!
其他相关推荐博客分类:
一、什么叫Redis?
Redis的全称是:Remote Dictionary Server
二、Redis的基本介绍:
redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类keyvalue存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富。有字符串,链表,集合和有序集合。支持在服务器端计算集合的并,交和补集(difference)等,还支持多种排序功能。所以Redis也可以被看成是一个数据结构服务器。
redis中的“半持久化模式”和“全持久化模式”
Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为“半持久化模式”);也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。它提供了Java,Python,Ruby,Erlang,PHP客户端,使用很方便。
三、 Redis与其他key-value存储有什么不同?
主要有以下两个方面。
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,应为数据量不能大于硬件内存。在内存数据库方面的另一个优点是, 相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。 同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
四、Redis是单线程的,怎么提高多核CPU的利用率?
CPU不太可能是Redis的瓶颈,一般内存和网络才有可能是。 例如使用Redis的管道(pipelining)在liunx系统上运行可以达到500K的RPS(requests per second) ,因此,如果您的应用程序主要使用O(N) 或者O(log(N)) 的 命令,他们几乎不需要使用什么CPU。
然而,为了最大限度的使用CPU,可以在同一个服务器部署多个Redis的实例,并把他们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的,所以,如果你想使用多个CPU,你可以考虑一下分片(shard) 。。
在Redis的客户端类库里面,比如RB(Ruby的客户端)和Predis(最常用的PHP客户端之一),能够使用一致性哈希(consistent hashing)来处理多个Redis实例。
五、一个Redis实例最多能存放多少的keys,List、Set、Sorted Set他们最多能存放多少元素?
理论上Redis可以处理多达232的keys,并且在实际中进行了测试,每个实例至少存放了2亿5千万的keys。我们正在测试一些较大的值。任何list、set、和sorted set都可以放232个元素。换句话说,Redis的存储极限是系统中的可用内存值。
六、如果数据集需要使用非常大的内存,但不希望使用一致性哈希或其他方式将数据集分布在不同的节点,还能采用Redis吗?
一个可行的方案是同时使用传统数据库(Mysql或者其他的)和Redis,Redis里面存放状态信息(元数据,小但经常写的信息)和所有其他读写频繁的数据:用户身份验证token, 使用Redis List 存放与时间顺序有关的id列表、编码等等。然后使用MySQL(或其他)作为存储引擎来存放更大的数据, 创建一个自增长ID作为主键和一个较大的BLOB字段作为数据字段,访问MySQL的数据只能通过主键(ID) 。执行查询操作时,通过Redis读取数据, 但是当有读取大数据时需要通过主键(ID)访问MySQL数据库。
七、Redis的优点:
性能极高 – Redis能支持超过 100K+ 每秒的读写频率。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
下面是官方的bench-mark数据:测试完成了50个并发执行100000个请求。设置和获取的值是一个256字节字符串。Linux box是运行Linux 2.6,这是X3320 Xeon 2.5 ghz。文本执行使用loopback接口(127.0.0.1)。结果:写的速度是110000次/s,读的速度是81000次/s 。
八、redis常用命令:
就DB来说,Redis成绩已经很惊人了,且不说memcachedb和tokyocabinet之流,就说原版的memcached,速度似乎也只能达到这个级别。Redis根本是使用内存存储,持久化的关键是这三条指令:SAVE BGSAVE LASTSAVE .
当接收到SAVE指令的时候,Redis就会dump数据到一个文件里面。值得一说的是它的独家功能:存储列表和集合,这是它与mc之流相比更有竞争力的地方。不介绍mc里面已经有的东东,只列出特殊的:TYPE key — 用来获取某key的类型KEYS pattern — 匹配所有符合模式的key,比如KEYS * 就列出所有的key了,当然,复杂度O(n)RANDOMKEY - 返回随机的一个keyRENAME oldkeynewkey— key也可以改名列表操作,精华RPUSH key string — 将某个值加入到一个key列表头部LPUSH key string — 将某个值加入到一个key列表末尾LLEN key — 列表长度LRANGE key start end — 返回列表中某个范围的值,相当于mysql里面的分页查询那样LTRIM key start end — 只保留列表中某个范围的值LINDEX key index — 获取列表中特定索引号的值,要注意是O(n)复杂度LSET key index value — 设置列表中某个位置的值LPOP keyRPOP key — 和上面的LPOP一样,就是类似栈或队列的那种取头取尾指令,可以当成消息队列来使用了集合操作SADD key member — 增加元素SREM key member — 删除元素SCARD key — 返回集合大小SISMEMBER key member — 判断某个值是否在集合中SINTER key1 key2 ... keyN — 获取多个集合的交集元素SMEMBERS key — 列出集合的所有元素还有Multiple DB的命令,可以更换db,数据可以隔离开,默认是存放在DB 0
下一篇讲介绍:Redis的安装于配置。
文章的问题很多"问题是这个项目还很新,可能还不足够稳定,而且没有在实际的一些大型系统应用的实例。"在2012年的时候就有很多公司开始使用了,而现在更不用说了,我说知道的公司就有新浪微博,搜狐,腾讯,爱奇艺等,基本上redis已经作为互联网业界的标配了"此外,缺乏mc中批量get也是比较大的问题,始终批量获取跟多次获取的网络开销是不一样的。"楼主你不知道有mget的命令么?? 另外就算有些命令没有提供批量获取的,可以通过pipline来实现谢谢指正。我已经改正。
Josh_Persistence
浏览: 1056305 次
来自: 上海
暴露了你的东家,, 哈哈。我没找 ...
VCenter、ESXServer、Cluster这些实体类在 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'& 这些天项目上赶得有些急,每天都要学一些新东西,像WebLogic配置啊,Redis的用法啊,反向代理服务器啊(其实都是后台大佬家里有事,我就顶着了-_-||),老师布置的读论文的任务不好意思要拖一拖了((。?_?。)?I’m sorry~啊~),不过今天的东西也很有用,(如果毕设数据量大的话-_-||),相当有用啊。好了,现在开始Redis学习之旅吧。
&&Redis 是完全开源免费的一个高性能的key-value数据库
& Redis 有以下三个特点:
1)Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
2)Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储(这些数据类型主要指的的是value的,key是正常的字符串)
3)Redis支持数据的备份
&&Redis的安装:
根据自己的情况,新建一个目录,名字为redis(例如,D:\redis),注意路径中不要出现中文字符.
把下载好的Redis的压缩包解压到新建的指定文件夹中,可见一些可执行文件(版本不同可能此处会有区别) redis-server.exe
redis 服务器 redis-cli.exe
redis 命令行客户端 redis-benchmar.exe
redis 性能测试工具 redis-check-aof.exe
AOF 文件修复工具
& Redis简单的测试: 启动Redis服务器
开启一个cmd窗口
使用cd命令切换目录到 D:\redis
运行 redis-server.exe redis.windows.conf
启动成功后会看到启动的图案和启动信息(包括Redis版本、监听端口号等)
注意:这个服务器的运行窗口不能关闭,如果关闭Redis服务器就停了
注意:ctrl+c可以也关闭redis服务器
启动Redis客户端
另开启一个cmd窗口,原来的不要关闭,不然就无法访问服务端了
切换到redis目录下运行
redis-cli.exe -h 127.0.0.1 -p 6379
redis-cli.exe
不加任何参数默认就是127.0.0.1 和 6379
设置键值对操作 set name briup
取出键值对操作 get name
这时候客户端使用shutdown命令可以关闭服务端
其余的一些命令我也不做详细解释了,我使用Redis的重点在于使用Redis作为Mybatis的二级缓存!!!
mybatis缓存分为一级缓存和二级缓存
一级缓存,又叫本地缓存,是PerpetualCache类型的永久缓存,保存在执行器中(BaseExecutor),而执行器又在SqlSession(DefaultSqlSession)中,所以一级缓存的生命周期与SqlSession是相同的。
二级缓存,又叫自定义缓存,实现了Cache接口的类都可以作为二级缓存,所以可配置如encache等的第三方缓存。二级缓存以namespace名称空间为其唯一标识,被保存在Configuration核心配置对象中。
所以我们可以自定义缓存类,实现Mybatis提供的缓存接口Cache,其中的方法使用Jedis来实现,然后把这个缓存实现类配置为Mybatis的二级缓存提供者即可
Jedis相关配置文件和工具类
redis.properties
redis.pool.maxTotal=<span style="color: #0
redis.pool.maxIdle=<span style="color: #
redis.pool.maxWait=<span style="color: #00
redis.ip=<span style="color: #7.0.<span style="color: #.1
redis.port=<span style="color: #79
封装Jedis的工具类 JedisUtils.java
package com.briup.
import java.util.ResourceB
import redis.clients.jedis.J
import redis.clients.jedis.JedisP
import redis.clients.jedis.JedisPoolC
public class JedisUtils {
public static JedisPool jedisP
//ResourceBundle会查找classpath下的xxx.properties的文件,xxx是方法中指定的
ResourceBundle resourceBundle =
ResourceBundle.getBundle("redis");
int maxTotal = Integer.parseInt(resourceBundle.getString("redis.pool.maxTotal"));
int maxIdle = Integer.parseInt(resourceBundle.getString("redis.pool.maxIdle"));
int maxWait = Integer.parseInt(resourceBundle.getString("redis.pool.maxWait"));
String ip = resourceBundle.getString("redis.ip");
int port = Integer.parseInt(resourceBundle.getString("redis.port"));
JedisPoolConfig config = new JedisPoolConfig();
// 设置最大连接数
config.setMaxTotal(maxTotal);
// 设置最大空闲数
config.setMaxIdle(maxIdle);
// 设置超时时间
config.setMaxWaitMillis(maxWait);
// 初始化连接池
jedisPool = new JedisPool(config, ip, port);
public static void set(Object key, Object value) {
Jedis jedis = null;
jedis = jedisPool.getResource();
jedis.set(SerializingUtils.serialize(key), SerializingUtils.serialize(value));
} catch (Exception e) {
e.printStackTrace();
}finally {
jedis.close();
public static Object get(Object key) {
Jedis jedis = null;
jedis = jedisPool.getResource();
byte[] keyBytes = SerializingUtils.serialize(key);
if(jedis.exists(keyBytes)){
return SerializingUtils.deserialize(jedis.get(keyBytes));
} catch (Exception e) {
e.printStackTrace();
}finally {
jedis.close();
return null;
public static void del(Object key) {
Jedis jedis = null;
jedis = jedisPool.getResource();
jedis.del(SerializingUtils.serialize(key));
} catch (Exception e) {
e.printStackTrace();
}finally {
jedis.close();
public static void clear() {
Jedis jedis = null;
jedis = jedisPool.getResource();
jedis.flushDB();
} catch (Exception e) {
e.printStackTrace();
}finally {
jedis.close();
public static int getSize() {
Jedis jedis = null;
jedis = jedisPool.getResource();
} catch (Exception e) {
e.printStackTrace();
}finally {
jedis.close();
return jedis.dbSize().intValue();
public static Jedis getResource(){
return jedisPool.getResource();
SerializingUtils.java
package com.briup.
import java.io.ByteArrayInputS
import java.io.ByteArrayOutputS
import java.io.IOE
import java.io.ObjectInputS
import java.io.ObjectOutputS
public class SerializingUtils {
//java对象 ---& 字节数组
public static byte[] serialize(Object obj) {
ByteArrayOutputStream bos = null;
ObjectOutputStream out = null;
bos = new ByteArrayOutputStream();
out = new ObjectOutputStream(bos);
out.writeObject(obj);
out.flush();
catch (IOException e) {
e.printStackTrace();
}finally {
if(out!=null)out.close();
}catch (IOException e) {
e.printStackTrace();
return bos.toByteArray();
//字节数组 ---& java对象
public static Object deserialize(byte[] data) {
ObjectInputStream in = null;
Object obj = null;
ByteArrayInputStream bis = new ByteArrayInputStream(data);
in = new ObjectInputStream(bis);
obj = in.readObject();
}catch (Exception e) {
e.printStackTrace();
}finally {
if(in!=null)in.close();
}catch (IOException e) {
e.printStackTrace();
封装MyBatis的工具类 MyBatisSqlSessionFactory.java
package com.briup.
import java.io.IOE
import java.io.InputS
import org.apache.ibatis.io.R
import org.apache.ibatis.session.SqlS
import org.apache.ibatis.session.SqlSessionF
import org.apache.ibatis.session.SqlSessionFactoryB
public class MyBatisSqlSessionFactory {
private static SqlSessionFactory sqlSessionF
public static SqlSessionFactory getSqlSessionFactory(){
if(sqlSessionFactory == null){
InputStream inputStream = null;
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e.getCause());
return sqlSessionF
public static SqlSession openSession() {
return openSession(false);
public static SqlSession openSession(boolean autoCommit) {
return getSqlSessionFactory().openSession(autoCommit);
自定义cache接口实现类MybatisRedisCache.java
package com.briup.
import java.util.concurrent.locks.ReadWriteL
import org.apache.ibatis.cache.C
import org.apache.ibatis.cache.CacheE
import com.briup.utils.JedisU
* Cache为缓存接口,给缓存供应商的SPI(Service Provider Interface)
* Cache接口的实现类必须有一个具有String类型参数的构造方法,该参数作为实现类对象的id,对其进行唯一标识
public class MybatisRedisCache implements Cache{
public MybatisRedisCache(String id) {
* 清空缓存
public void clear() {
JedisUtils.clear();
* 获取缓存类对象的唯一标识
public String getId() {
return this.
* 从缓存中获取key对应的value
public Object getObject(Object key) {
return JedisUtils.get(key);
* 获取读写锁
* 可选的方法,从3.2.6起这个方法不再被框架核心调用
* 任何需要的锁,都必须由缓存供应商提供
public ReadWriteLock getReadWriteLock() {
return null;
* 获取缓存对象中存储的键/值对的数量
* 可选的方法,没有被框架核心调用
public int getSize() {
return JedisUtils.getSize();
* 保存key/value到缓存对象中
* key可以是任何对象
public void putObject(Object key, Object value) {
JedisUtils.set(key, value);
* 可选的方法,没有被核心框架调用,移除key对应的value
public Object removeObject(Object key) {
return null;
* 重新equals方法
public boolean equals(Object o) {
if (getId() == null)
throw new CacheException("Cache instances require an ID.");
if (this == o)
return true;
if (!(o instanceof Cache))
return false;
Cache otherCache = (Cache)
return getId().equals(otherCache.getId());
* 重新hashCode方法
public int hashCode() {
if (getId() == null)
throw new CacheException("Cache instances require an ID.");
return getId().hashCode();
在mybatis中开启二级缓存(虽然默认也是开启的)
&settings&
&setting name="cacheEnabled" value="true"/&
&/settings&
配置&cache/&标签 在需要缓冲的映射文件中加入&cache/&标签,并且指定提供缓存功能的cache实现类的全限定名
&cache type="XXX.MybatisRedisCache"&&/cache&
& 第一次运行,mybatis【发出】对应select进行查询数据库,然后在redis中查看key可知,已经把数据存进了redis中, 之后再运行,mybatis【不发出】sql语句,数据直接从redis中取出
阅读(...) 评论()Similar Posts
Most Commented (138) (102) (99) (91) (58) (49) (44) (44) (40) (40)
Recent Posts
Recent CommentsCategories
前几天微博发生了一起大的,很多技术的朋友都比较关心,其中的原因不会超出James Hamilton在On Designing and Deploying Internet-Scale Service(1)概括的那几个范围,James第一条经验“Design for failure”是所有互联网架构成功的一个关键。互联网系统的工程理论其实非常简单,James paper中内容几乎称不上理论,而是多条实践经验分享,每个公司对这些经验的理解及执行力决定了架构成败。
题外话说完,最近又研究了。去年曾做过一个,到目前为止,这个benchmark结果依然有效。这1年我们经历了很多眼花缭乱的key value存储产品的诱惑,从Cassandra的淡出(Twitter暂停在主业务使用)到HBase的兴起(Facebook新的邮箱业务选用HBase(2)),当再回头再去看Redis,发现这个只有1万多行源代码的程序充满了神奇及大量未经挖掘的特性。Redis性能惊人,国内前十大网站的子产品估计用1台Redis就可以满足存储及Cache的需求。除了性能印象之外,业界其实普遍对Redis的认识存在一定误区。本文提出一些观点供大家探讨。
1. Redis是什么
这个问题的结果影响了我们怎么用Redis。如果你认为Redis是一个key value store, 那可能会用它来代替MySQL;如果认为它是一个可以持久化的cache, 可能只是它保存一些频繁访问的临时数据。Redis是REmote DIctionary Server的缩写,在Redis在官方网站的的副标题是A persistent key-value database with built-in net interface written in ANSI-C for Posix systems,这个定义偏向key value store。还有一些看法则认为Redis是一个memory database,因为它的高性能都是基于内存操作的基础。另外一些人则认为Redis是一个data structure server,因为Redis支持复杂的数据特性,比如List, Set等。对Redis的作用的不同解读决定了你对Redis的使用方式。
互联网数据目前基本使用两种方式来存储,关系数据库或者key value。但是这些互联网业务本身并不属于这两种数据类型,比如用户在社会化平台中的关系,它是一个list,如果要用关系数据库存储就需要转换成一种多行记录的形式,这种形式存在很多冗余数据,每一行需要存储一些重复信息。如果用key value存储则修改和删除比较麻烦,需要将全部数据读出再写入。Redis在内存中设计了各种数据类型,让业务能够高速原子的访问这些数据结构,并且不需要关心持久存储的问题,从架构上解决了前面两种存储需要走一些弯路的问题。
2. Redis不可能比Memcache快
很多开发者都认为Redis不可能比Memcached快,Memcached完全基于内存,而Redis具有持久化保存特性,即使是异步的,Redis也不可能比Memcached快。但是测试结果基本是Redis占绝对优势。一直在思考这个原因,目前想到的原因有这几方面。
。和Memcached不同,Redis并没有选择libevent。Libevent为了迎合通用性造成代码庞大(目前Redis代码还不到libevent的1/3)及牺牲了在特定平台的不少性能。Redis用libevent中两个文件修改实现了自己的epoll event loop(4)。业界不少开发者也建议Redis使用另外一个libevent高性能替代libev,但是作者还是坚持Redis应该小巧并去依赖的思路。一个印象深刻的细节是编译Redis之前并不需要执行./configure。
CAS问题。CAS是Memcached中比较方便的一种防止竞争修改资源的方法。CAS实现需要为每个cache key设置一个隐藏的cas token,cas相当value版本号,每次set会token需要递增,因此带来CPU和内存的双重开销,虽然这些开销很小,但是到单机10G+ cache以及QPS上万之后这些开销就会给双方相对带来一些细微性能差别(5)。
3. 单台Redis的存放数据必须比物理内存小
Redis的数据全部放在内存带来了高速的性能,但是也带来一些不合理之处。比如一个中型网站有100万注册用户,如果这些资料要用Redis来存储,内存的容量必须能够容纳这100万用户。但是业务实际情况是100万用户只有5万活跃用户,1周来访问过1次的也只有15万用户,因此全部100万用户的数据都放在内存有不合理之处,RAM需要为冷数据买单。
这跟操作系统非常相似,操作系统所有应用访问的数据都在内存,但是如果物理内存容纳不下新的数据,操作系统会智能将部分长期没有访问的数据交换到磁盘,为新的应用留出空间。现代操作系统给应用提供的并不是物理内存,而是虚拟内存(Virtual Memory)的概念。
基于相同的考虑,Redis 2.0也增加了VM特性。让Redis数据容量突破了物理内存的限制。并实现了数据冷热分离。
4. Redis的VM实现是重复造轮子
Redis的VM依照之前的epoll实现思路依旧是自己实现。但是在前面操作系统的介绍提到OS也可以自动帮程序实现冷热数据分离,Redis只需要OS申请一块大内存,OS会自动将热数据放入物理内存,冷数据交换到硬盘,另外一个知名的“理解了现代操作系统(3)”的Varnish就是这样实现,也取得了非常成功的效果。
作者antirez在解释为什么要自己实现VM中提到几个原因(6)。主要OS的VM换入换出是基于Page概念,比如OS VM1个Page是4K, 4K中只要还有一个元素即使只有1个字节被访问,这个页也不会被SWAP, 换入也同样道理,读到一个字节可能会换入4K无用的内存。而Redis自己实现则可以达到控制换入的粒度。另外访问操作系统SWAP内存区域时block进程,也是导致Redis要自己实现VM原因之一。
5. 用get/set方式使用Redis
作为一个key value存在,很多开发者自然的使用set/get方式来使用Redis,实际上这并不是最优化的使用方法。尤其在未启用VM情况下,Redis全部数据需要放入内存,节约内存尤其重要。
假如一个key-value单元需要最小占用512字节,即使只存一个字节也占了512字节。这时候就有一个设计模式,可以把key复用,几个key-value放入一个key中,value再作为一个set存入,这样同样512字节就会存放10-100倍的容量。
这就是为了节约内存,建议使用hashset而不是set/get的方式来使用Redis,详细方法见参考文献(7)。
6. 使用aof代替snapshot
Redis有两种存储方式,默认是snapshot方式,实现方法是定时将内存的快照(snapshot)持久化到硬盘,这种方法缺点是持久化之后如果出现crash则会丢失一段数据。因此在完美主义者的推动下作者增加了aof方式。aof即append only mode,在写入内存数据的同时将操作命令保存到日志文件,在一个并发更改上万的系统中,命令日志是一个非常庞大的数据,管理维护成本非常高,恢复重建时间会非常长,这样导致失去aof高可用性本意。另外更重要的是Redis是一个内存数据结构模型,所有的优势都是建立在对内存复杂数据结构高效的原子操作上,这样就看出aof是一个非常不协调的部分。
其实aof目的主要是数据可靠性及高可用性,在Redis中有另外一种方法来达到目的:Replication。由于Redis的高性能,复制基本没有延迟。这样达到了防止单点故障及实现了高可用。
要想成功使用一种产品,我们需要深入了解它的特性。Redis性能突出,如果能够熟练的驾驭,对国内很多大型应用具有很大帮助。希望更多同行加入到Redis使用及代码研究行列。
(Google Groups)
(Google Groups)
(Google Groups)
(Salvatore antirez Sanfilippo)
上一篇博文在新浪微博上面有更多讨论及留言,有兴趣可以去围观。(需登录)
如想及时阅读Tim Yang的文章,可通过页面右上方扫码订阅最新更新。}

我要回帖

更多关于 内存数据库redis 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信