??xml version="1.0" encoding="utf-8" standalone="yes"?> 在pom.xml中添?/p> 效果如图Q?/p>
]]>1 dredis支持
2 redis配置
3 redis服务器配|?/h1>
4 应用
5 ?/h1>
]]>
NoSQL Benchmarking
http://www.cubrid.org/blog/dev-platform/nosql-benchmarking/
http://www.badrit.com/blog/2013/11/18/redis-vs-mongodb-performance#.VMpfW2SUfsg
How many requests per second can I get out of Redis?
http://skipperkongen.dk/2013/08/27/how-many-requests-per-second-can-i-get-out-of-redis/
redis性能试Q测试ƈ发性能
http://my.oschina.net/pblack/blog/102394
]]>
Architecture for Redis cache & Mongo for persistence
http://stackoverflow.com/questions/11218941/architecture-for-redis-cache-mongo-for-persistence
MongoDB with redis
http://stackoverflow.com/questions/10696463/mongodb-with-redis/10721249#10721249
Caching Data in Spring Using Redis
http://caseyscarborough.com/blog/2014/12/18/caching-data-in-spring-using-redis/
Springside Redis
https://github.com/springside/springside4/wiki/Redis
Spring Cache注解+Redis
http://hanqunfeng.iteye.com/blog/2176172
]]>
]]>
Codis 是一个分布式 Redis 解决Ҏ, 对于上层的应用来? q接?Codis Proxy 和连接原生的 Redis Server 没有明显的区?(不支持的命o列表), 上层应用可以像用单机的 Redis 一样? Codis 底层会处理请求的转发, 不停机的数据q移{工? 所有后边的一切事? 对于前面的客L来说是透明? 可以单的认ؓ后边q接的是一个内存无限大?Redis 服务.
Codis 由四部分l成:
Codis Proxy (codis-proxy)
Codis Manager (codis-config)
Codis Redis (codis-server)
ZooKeeper
codis-proxy 是客Lq接?Redis 代理服务, codis-proxy 本n实现?Redis 协议, 表现得和一个原生的 Redis 没什么区?(像 Twemproxy), 对于一个业务来? 可以部v多个 codis-proxy, codis-proxy 本n是无状态的.
codis-config ?Codis 的管理工? 支持包括, d/删除 Redis 节点, d/删除 Proxy 节点, 发v数据q移{操? codis-config 本nq自带了一?http server, 会启动一?dashboard, 用户可以直接在浏览器上观?Codis 集群的运行状?
codis-server ?Codis 目l护的一?Redis 分支, Z 2.8.13 开? 加入?slot 的支持和原子的数据迁UL? Codis 上层?codis-proxy ?codis-config 只能和这个版本的 Redis 交互才能正常q行.
Codis 依赖 ZooKeeper 来存放数据\p?codis-proxy 节点的元信息, codis-config 发v的命令都会通过 ZooKeeper 同步到各个存zȝ codis-proxy.
Codis 支持按照 Namespace 区分不同的? 拥有不同?product name 的? 各项配置都不会冲H?
目前 Codis 已经是稳定阶D,目前豌豆?/a>已经在用该pȝ?/p> 架构Q?/p> Ҏ: 自动q 使用非常?/p> 囑Ş化的面板和管理工?/p> 支持l大多数 Redis 命oQ完全兼?nbsp;twemproxy 支持 Redis 原生客户?/p> 安全而且透明的数据移植,可根据需要轻松添加和删除节点 提供命o行接?/p> RESTful APIs 安装Q?/p> Install go go get github.com/wandoulabs/codis cd codis ./bootstrap.sh make gotest cd sample follow instructions in usage.md 界面截图Q?/p> Dashboard Migrate Slots
]]>
最单和基础的调用方式,
@Test public void test1Normal() { Jedis jedis = new Jedis("localhost"); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = jedis.set("n" + i, "n" + i); } long end = System.currentTimeMillis(); System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds"); jedis.disconnect(); }
很简单吧Q每?code style="padding: 1px 3px; margin: 0px 2px; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, 'monospace !important'; border: 1px solid #eeeeee; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: break-all; color: #40aa53; background-color: #fcfcfc;">set之后都可以返回结果,标记是否成功?/p>
redis的事务很单,他主要目的是保障Q一个client发v的事务中的命令可以连l的执行Q而中间不会插入其他client的命令?/p>
看下面例子:
@Test public void test2Trans() { Jedis jedis = new Jedis("localhost"); long start = System.currentTimeMillis(); Transaction tx = jedis.multi(); for (int i = 0; i < 100000; i++) { tx.set("t" + i, "t" + i); } List<Object> results = tx.exec(); long end = System.currentTimeMillis(); System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds"); jedis.disconnect(); }
我们调用jedis.watch(…)
Ҏ来监控keyQ如果调用后key值发生变化,则整个事务会执行p|。另外,事务中某个操作失败,q不会回滚其他操作。这一炚w要注意。还有,我们可以使用discard()
Ҏ来取消事务?/p>
有时Q我们需要采用异步方式,一ơ发送多个指令,不同步等待其q回l果。这样可以取得非常好的执行效率。这是道Q调用方法如下:
@Test public void test3Pipelined() { Jedis jedis = new Jedis("localhost"); Pipeline pipeline = jedis.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("p" + i, "p" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds"); jedis.disconnect(); }
Jedis提供的方法而言Q是可以做到在管道中使用事务Q其代码如下Q?/p>
@Test public void test4combPipelineTrans() { jedis = new Jedis("localhost"); long start = System.currentTimeMillis(); Pipeline pipeline = jedis.pipelined(); pipeline.multi(); for (int i = 0; i < 100000; i++) { pipeline.set("" + i, "" + i); } pipeline.exec(); List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds"); jedis.disconnect(); }
但是l测试(见本文后l部分)Q发现其效率和单独用事务差不多Q甚臌略微差点?/p>
@Test public void test5shardNormal() { List<JedisShardInfo> shards = Arrays.asList( new JedisShardInfo("localhost",6379), new JedisShardInfo("localhost",6380)); ShardedJedis sharding = new ShardedJedis(shards); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = sharding.set("sn" + i, "n" + i); } long end = System.currentTimeMillis(); System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds"); sharding.disconnect(); }
q个是分布式直接q接Qƈ且是同步调用Q每步执行都q回执行l果。类似地Q还有异步管道调用?/p>
@Test public void test6shardpipelined() { List<JedisShardInfo> shards = Arrays.asList( new JedisShardInfo("localhost",6379), new JedisShardInfo("localhost",6380)); ShardedJedis sharding = new ShardedJedis(shards); ShardedJedisPipeline pipeline = sharding.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("sp" + i, "p" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds"); sharding.disconnect(); }
如果Q你的分布式调用代码是运行在U程中,那么上面两个直连调用方式׃合适了Q因为直q方式是非线E安全的Q这个时候,你就必须选择q接池调用?/p>
@Test public void test7shardSimplePool() { List<JedisShardInfo> shards = Arrays.asList( new JedisShardInfo("localhost",6379), new JedisShardInfo("localhost",6380)); ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards); ShardedJedis one = pool.getResource(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = one.set("spn" + i, "n" + i); } long end = System.currentTimeMillis(); pool.returnResource(one); System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds"); pool.destroy(); }
上面是同步方式,当然q有异步方式?/p>
@Test public void test8shardPipelinedPool() { List<JedisShardInfo> shards = Arrays.asList( new JedisShardInfo("localhost",6379), new JedisShardInfo("localhost",6380)); ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards); ShardedJedis one = pool.getResource(); ShardedJedisPipeline pipeline = one.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("sppn" + i, "n" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); pool.returnResource(one); System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds"); pool.destroy(); }
事务和管道都是异步模式。在事务和管道中不能同步查询l果。比如下面两个调用,都是不允许的Q?/p>
Transaction tx = jedis.multi(); for (int i = 0; i < 100000; i++) { tx.set("t" + i, "t" + i); } System.out.println(tx.get("t1000").get()); //不允? List<Object> results = tx.exec(); … … Pipeline pipeline = jedis.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("p" + i, "p" + i); } System.out.println(pipeline.get("p1000").get()); //不允? List<Object> results = pipeline.syncAndReturnAll();
事务和管道都是异步的Q个人感觉,在管道中再进行事务调用,没有必要Q不如直接进行事务模式?/p>
分布式中Q连接池的性能比直q的性能略好(见后l测试部??/p>
分布式调用中不支持事务?/p>
因ؓ事务是在服务器端实现Q而在分布式中Q每Ҏ的调用对象都可能讉K不同的机器,所以,没法q行事务?/p>
q行上面的代码,q行试Q其l果如下Q?/p>
Simple SET: 5.227 seconds Transaction SET: 0.5 seconds Pipelined SET: 0.353 seconds Pipelined transaction: 0.509 seconds Simple@Sharing SET: 5.289 seconds Pipelined@Sharing SET: 0.348 seconds Simple@Pool SET: 5.039 seconds Pipelined@Pool SET: 0.401 seconds
另外Q经试分布式中用到的机器越多,调用会越慢。上面是2片,下面?片:
Simple@Sharing SET: 5.494 seconds Pipelined@Sharing SET: 0.51 seconds Simple@Pool SET: 5.223 seconds Pipelined@Pool SET: 0.518 seconds
下面?0片:
Simple@Sharing SET: 5.9 seconds Pipelined@Sharing SET: 0.794 seconds Simple@Pool SET: 5.624 seconds Pipelined@Pool SET: 0.762 seconds
下面?00片:
Simple@Sharing SET: 14.055 seconds Pipelined@Sharing SET: 8.185 seconds Simple@Pool SET: 13.29 seconds Pipelined@Pool SET: 7.767 seconds
分布式中Q连接池方式调用不但U程安全外,Ҏ上面的测试数据,也可以看接池比直q的效率更好?/p>
package com.example.nosqlclient; import java.util.Arrays; import java.util.List; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisShardInfo; import redis.clients.jedis.Pipeline; import redis.clients.jedis.ShardedJedis; import redis.clients.jedis.ShardedJedisPipeline; import redis.clients.jedis.ShardedJedisPool; import redis.clients.jedis.Transaction; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestJedis { private static Jedis jedis; private static ShardedJedis sharding; private static ShardedJedisPool pool; @BeforeClass public static void setUpBeforeClass() throws Exception { List<JedisShardInfo> shards = Arrays.asList( new JedisShardInfo("localhost",6379), new JedisShardInfo("localhost",6379)); //使用相同的ip:port,仅作试 jedis = new Jedis("localhost"); sharding = new ShardedJedis(shards); pool = new ShardedJedisPool(new JedisPoolConfig(), shards); } @AfterClass public static void tearDownAfterClass() throws Exception { jedis.disconnect(); sharding.disconnect(); pool.destroy(); } @Test public void test1Normal() { long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = jedis.set("n" + i, "n" + i); } long end = System.currentTimeMillis(); System.out.println("Simple SET: " + ((end - start)/1000.0) + " seconds"); } @Test public void test2Trans() { long start = System.currentTimeMillis(); Transaction tx = jedis.multi(); for (int i = 0; i < 100000; i++) { tx.set("t" + i, "t" + i); } //System.out.println(tx.get("t1000").get()); List<Object> results = tx.exec(); long end = System.currentTimeMillis(); System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds"); } @Test public void test3Pipelined() { Pipeline pipeline = jedis.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("p" + i, "p" + i); } //System.out.println(pipeline.get("p1000").get()); List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds"); } @Test public void test4combPipelineTrans() { long start = System.currentTimeMillis(); Pipeline pipeline = jedis.pipelined(); pipeline.multi(); for (int i = 0; i < 100000; i++) { pipeline.set("" + i, "" + i); } pipeline.exec(); List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined transaction: " + ((end - start)/1000.0) + " seconds"); } @Test public void test5shardNormal() { long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = sharding.set("sn" + i, "n" + i); } long end = System.currentTimeMillis(); System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds"); } @Test public void test6shardpipelined() { ShardedJedisPipeline pipeline = sharding.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("sp" + i, "p" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds"); } @Test public void test7shardSimplePool() { ShardedJedis one = pool.getResource(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String result = one.set("spn" + i, "n" + i); } long end = System.currentTimeMillis(); pool.returnResource(one); System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds"); } @Test public void test8shardPipelinedPool() { ShardedJedis one = pool.getResource(); ShardedJedisPipeline pipeline = one.pipelined(); long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { pipeline.set("sppn" + i, "n" + i); } List<Object> results = pipeline.syncAndReturnAll(); long end = System.currentTimeMillis(); pool.returnResource(one); System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds"); } }
实际MySQL是适合q行量数据存储的,通过Memcached热Ҏ据加载到cacheQ加速访问,很多公司都曾l用过q样的架构,但随着业务数据量的不断增加Q和讉K量的持箋增长Q我们遇C很多问题Q?/p>
MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容Q扩容和l护工作占据大量开发时间?/p>
Memcached与MySQL数据库数据一致性问题?/p>
Memcached数据命中率低或down机,大量讉K直接IK到DBQMySQL无法支撑?/p>
跨机房cache同步问题?/p>
最q几q_业界不断涌现出很多各U各LNoSQL产品Q那么如何才能正地使用好这些品,最大化地发挥其长处Q是我们需要深入研I和思考的?题,实际归根l底最重要的是了解q些产品的定位,q且了解到每ƾ品的tradeoffsQ在实际应用中做到扬镉K短,M上这些NoSQL主要用于解决 以下几种问题
量数据存储Q高速读写访问。此cM品通过数据全部in-momery 的方式来保证高速访问,同时提供数据落地的功能,实际q正是Redis最主要的适用场景?/p>
量数据存储Q分布式pȝ支持Q数据一致性保证,方便的集节Ҏ?删除?/p>
q方面最具代表性的是dynamo和bigtable 2论文所阐述的思\。前者是一个完全无中心的设计,节点之间通过gossip方式传递集信息,数据保证最l一致性,后者是一个中心化的方案设计,通过 cM一个分布式锁服务来保证Z致?数据写入先写内存和redo logQ然后定期compat归ƈ到磁盘上Q将随机写优化ؓ序写,提高写入性能?/p>
Schema freeQauto-sharding{。比如目前常见的一些文档数据库都是支持schema-free的,直接存储json格式数据Qƈ且支持auto-sharding{功能,比如mongodb?/p>
面对q些不同cd的NoSQL产品,我们需要根据我们的业务场景选择最合适的产品?/p>
前面已经分析q,Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能Q但实际更多的是一个disk-backed 的功能,跟传l意义上的持久化有比较大的差别,那么可能大家׃有疑问,gRedis更像一个加强版的MemcachedQ那么何时?Memcached,何时使用Redis呢?
|络IO模型
Memcached是多U程Q非dIO复用的网l模型,分ؓ监听ȝE和worker子线E,监听U程监听|络q接Q接受请求后Q将q接 描述字pipe 传递给workerU程Q进行读写IO, |络层用libevent装的事件库Q多U程模型可以发挥多核作用Q但是引入了cache coherency和锁的问题,比如QMemcached最常用的stats 命oQ实际Memcached所有操作都要对q个全局变量加锁Q进行计数等工作Q带来了性能损耗?/p>
QMemcached|络IO模型Q?/p>
Redis使用单线E的IO复用模型Q自己封装了一个简单的AeEvent事g处理框架Q主要实Cepoll、kqueue?selectQ对于单U只有IO操作来说Q单U程可以速度优势发挥到最大,但是Redis也提供了一些简单的计算功能Q比如排序、聚合等Q对于这些操 作,单线E模型实际会严重影响整体吞吐量,CPU计算q程中,整个IO调度都是被阻塞住的?/p>
内存理斚w
Memcached使用预分配的内存池的方式Q用slab和大不同的chunk来管理内存,ItemҎ大小选择合适的chunk?储,内存池的方式可以省去甌/释放内存的开销Qƈ且能减小内存片产生Q但q种方式也会带来一定程度上的空间浪费,q且在内存仍然有很大I间Ӟ新的?据也可能会被剔除Q原因可以参考Timyang的文章:http://timyang.net/data/Memcached-lru-evictions/
Redis使用现场甌内存的方式来存储数据Qƈ且很用free-list{方式来优化内存分配Q会在一定程度上存在内存?片,Redis跟据存储命o参数Q会把带q期旉的数据单独存攑֜一Pq把它们UCؓ临时数据Q非临时数据是永q不会被剔除的,即便物理内存不够Q导?swap也不会剔除Q何非临时数据Q但会尝试剔除部分时数据)Q这点上Redis更适合作ؓ存储而不是cache?/p>
数据一致性问?/p>
Memcached提供了cas命oQ可以保证多个ƈ发访问操作同一份数据的一致性问题?Redis没有提供cas 命oQƈ不能保证q点Q不qRedis提供了事务的功能Q可以保证一?命o的原子性,中间不会被Q何操作打断?/p>
存储方式及其它方?/p>
Memcached基本只支持简单的key-value存储Q不支持枚DQ不支持持久化和复制{功?/p>
Redis除key/value之外Q还支持list,set,sorted set,hash{众多数据结构,提供了KEYS
q行枚D操作Q但不能在线上用,如果需要枚丄上数据,Redis提供了工具可以直接扫描其dump文gQ枚丑և所有数据,Redisq同时提供了持久化和复制{功能?/p>
关于不同语言的客L支持
在不同语a的客L斚wQMemcached和Redis都有丰富的第三方客户端可供选择Q不q因为Memcached发展的时间更久一 些,目前看在客户端支持方面,Memcached的很多客L更加成熟E_Q而Redis׃其协议本w就比Memcached复杂Q加上作者不断增加新 的功能等Q对应第三方客户端跟q速度可能会赶不上Q有时可能需要自己在W三方客L基础上做些修Ҏ能更好的使用?/p>
Ҏ以上比较不难看出Q当我们不希望数据被t出Q或者需要除key/value之外的更多数据类型时Q或者需要落地功能时Q用Redis比用Memcached更合适?/p>
Redis除了作ؓ存储之外q提供了一些其它方面的功能Q比如聚合计、pubsub、scripting{,对于此类功能需要了解其实现原理Q清 楚地了解到它的局限性后Q才能正的使用Q比如pubsub功能Q这个实际是没有M持久化支持的Q消Ҏq接闪断或重q之间过来的消息是会全部丢失的, 又比如聚合计和scripting{功能受Redis单线E模型所限,是不可能辑ֈ很高的吞吐量的,需要}慎用?/p>
ȝ来说Redis作者是一位非常勤奋的开发者,可以l常看到作者在试着各种不同的新鲜想法和思\Q针对这些方面的功能p求我们需要深入了解后再用?/p>
Redis使用最x式是全部数据in-memory?/p>
Redis更多场景是作为Memcached的替代者来使用?/p>
当需要除key/value之外的更多数据类型支持时Q用Redis更合适?/p>
当存储的数据不能被剔除时Q用Redis更合适?/p>
后箋关于Redis文章计划Q?/p>
Redis数据cd与容量规划?/p>
如何Ҏ业务场景搭徏E_Q可靠,可扩展的Redis集群?/p>
Redis参数Q代码优化及二次开发基实践?/p>
关于作?/strong>
田琪Q目前负责新微博^台底层架构与研发工作Q之前曾担Q搜狐白社会实时游戏^台核心架构工作,主要xwebgame, 分布式存储,nosql ?erlang {领域,目前主要从事mysql源代码的一些深入研I工作,微博:http://weibo.com/bachmozart?/p>
理 Redis 服务?/p>
理 Redis data favorite
理 Redis 数据
?Redis 数据Qstring, list, hash, set, sorted set
重命?Redis 数据
删除 Redis 数据
更新 Redis 数据
剪切Q复Ӟ_脓 Redis 数据
导入Q导?Redis 数据
搜烦 Redis 数据
通过关键字,数据cdQ大来排序 Redis 数据
D历史
RedisClient ? Redis 客户?GUI 工具Q?Java swt 和jedis ~写。它redis数据以windows资源理器的界面风格呈现l用P可以帮助redis开发h员和l护人员方便的徏立,修改Q删除,查询redis数据Q可以让用户方便的编辑数据,可以剪切Q拷贝,_脓redis数据Q可以导入,导出redis数据Q可以对redis数据排序?/p>
实际MySQL是适合q行量数据存储的,通过Memcached热Ҏ据加载到cacheQ加速访问,很多公司都曾l用过q样的架构,但随着业务数据量的不断增加Q和讉K量的持箋增长Q我们遇C很多问题Q?/p>
1.MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容Q扩容和l护工作占据大量开发时间?/p>
2.Memcached与MySQL数据库数据一致性问题?/p>
3.Memcached数据命中率低或down机,大量讉K直接IK到DBQMySQL无法支撑?/p>
4.跨机房cache同步问题?/p>
众多NoSQL百花齐放Q如何选择
最q几q_业界不断涌现出很多各U各LNoSQL产品Q那么如何才能正地使用好这些品,最大化地发挥其长处Q是我们需要深入研I和思考的问题Q实 际归根结底最重要的是了解q些产品的定位,q且了解到每ƾ品的tradeoffsQ在实际应用中做到扬镉K短,M上这些NoSQL主要用于解决以下?U问?/p>
1.量数据存储Q高速读写访问。此cM品通过数据全部in-momery 的方式来保证高速访问,同时提供数据落地的功能,实际q正是Redis最主要的适用场景?/p>
2.量数据存储Q分布式pȝ支持Q数据一致性保证,方便的集节Ҏ?删除?/p>
3.q方面最具代表性的是dynamo和bigtable 2论文所阐述的思\。前者是一个完全无中心的设计,节点之间通过gossip方式传递集信息,数据保证最l一致性,后者是一个中心化的方案设计,通过cM一个分布式锁服务来保证Z致?数据写入先写内存和redo logQ然后定期compat归ƈ到磁盘上Q将随机写优化ؓ序写,提高写入性能?/p>
4.Schema freeQauto-sharding{。比如目前常见的一些文档数据库都是支持schema-free的,直接存储json格式数据Qƈ且支持auto-sharding{功能,比如mongodb?/p>
面对q些不同cd的NoSQL产品,我们需要根据我们的业务场景选择最合适的产品?/p>
Redis适用场景Q如何正的使用
前面已经分析q,Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能Q但实际更多的是一个disk-backed的功 能,跟传l意义上的持久化有比较大的差别,那么可能大家׃有疑问,gRedis更像一个加强版的MemcachedQ那么何时?Memcached,何时使用Redis?
如果单地比较Redis与Memcached的区别,大多数都会得C下观点:
1 Redis不仅仅支持简单的k/vcd的数据,同时q提供listQsetQzsetQhash{数据结构的存储?br style="padding: 0px; margin: 0px;" />
2 Redis支持数据的备份,即master-slave模式的数据备份?br style="padding: 0px; margin: 0px;" />
3 Redis支持数据的持久化Q可以将内存中的数据保持在磁盘中Q重启的时候可以再ơ加载进行用?br style="padding: 0px; margin: 0px;" />
抛开q些Q可以深入到Redis内部构造去观察更加本质的区别,理解Redis的设计?br style="padding: 0px; margin: 0px;" />
在Redis中,q不是所有的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。Redis只会~存所有的 key的信息,如果Redis发现内存的用量过了某一个阀|触发swap的操作,RedisҎ“swappability = age*log(size_in_memory)”?出哪些key对应的value需要swap到磁盘。然后再这些key对应的value持久化到盘中,同时在内存中清除。这U特性得Redis可以 保持过其机器本w内存大的数据。当Ӟ机器本n的内存必要能够保持所有的keyQ毕竟这些数据是不会q行swap操作的。同时由于Redis内?中的数据swap到磁盘中的时候,提供服务的主U程和进行swap操作的子U程会共享这部分内存Q所以如果更新需要swap的数据,Redis阻塞这?操作Q直到子U程完成swap操作后才可以q行修改?br style="padding: 0px; margin: 0px;" />
使用RedisҎ内存模型前后的情况对比:
VM off: 300k keys, 4096 bytes values: 1.3G used
VM on: 300k keys, 4096 bytes values: 73M used
VM off: 1 million keys, 256 bytes values: 430.12M used
VM on: 1 million keys, 256 bytes values: 160.09M used
VM on: 1 million keys, values as large as you want, still: 160.09M used
?从Redis中读取数据的时候,如果d的key对应的value不在内存中,那么Redis需要从swap文g中加载相应数据,然后再返回给h斏V?q里存在一个I/OU程池的问题。在默认的情况下QRedis会出现阻塞,卛_成所有的swap文g加蝲后才会相应。这U策略在客户端的数量较小Q进?扚w操作的时候比较合适。但是如果将Redis应用在一个大型的|站应用E序中,q显然是无法满大ƈ发的情况的。所以Redisq行我们讄I/OU程 池的大小Q对需要从swap文g中加载相应数据的dhq行q发操作Q减阻塞的旉?br style="padding: 0px; margin: 0px;" />
如果希望在v量数据的环境中用好RedisQ我怿理解Redis的内存设计和d的情冉|不可~少的?/p>
补充的知识点Q?/strong>
memcached和redis的比?/p>
1 |络IO模型
Memcached是多U程Q非dIO复用的网l模型,分ؓ监听ȝE和worker子线E,监听U程监听|络q接Q接受请求后Q将q接描述?pipe 传递给workerU程Q进行读写IO, |络层用libevent装的事件库Q多U程模型可以发挥多核作用Q但是引入了cache coherency和锁的问题,比如QMemcached最常用的stats 命oQ实际Memcached所有操作都要对q个全局变量加锁Q进行计数等工作Q带来了性能损耗?/p>
(Memcached|络IO模型)
Redis使用单线E的IO复用模型Q自己封装了一个简单的AeEvent事g处理框架Q主要实Cepoll、kqueue和selectQ对于单U?只有IO操作来说Q单U程可以速度优势发挥到最大,但是Redis也提供了一些简单的计算功能Q比如排序、聚合等Q对于这些操作,单线E模型实际会严重 影响整体吞吐量,CPU计算q程中,整个IO调度都是被阻塞住的?/p>
2.内存理斚w
Memcached使用预分配的内存池的方式Q用slab和大不同的chunk来管理内存,ItemҎ大小选择合适的chunk存储Q内存池的方 式可以省ȝ?释放内存的开销Qƈ且能减小内存片产生Q但q种方式也会带来一定程度上的空间浪费,q且在内存仍然有很大I间Ӟ新的数据也可能会被剔 除,原因可以参考Timyang的文章:http://timyang.net/data/Memcached-lru-evictions/
Redis使用现场甌内存的方式来存储数据Qƈ且很用free-list{方式来优化内存分配Q会在一定程度上存在内存片QRedis跟据存储 命o参数Q会把带q期旉的数据单独存攑֜一Pq把它们UCؓ临时数据Q非临时数据是永q不会被剔除的,即便物理内存不够Q导致swap也不会剔除Q何非 临时数据(但会试剔除部分临时数据)Q这点上Redis更适合作ؓ存储而不是cache?/p>
3.数据一致性问?/p>
Memcached提供了cas命oQ可以保证多个ƈ发访问操作同一份数据的一致性问题?Redis没有提供cas 命oQƈ不能保证q点Q不qRedis提供了事务的功能Q可以保证一?命o的原子性,中间不会被Q何操作打断?/p>
4.存储方式及其它方?/p>
Memcached基本只支持简单的key-value存储Q不支持枚DQ不支持持久化和复制{功?/p>
Redis除key/value之外Q还支持list,set,sorted set,hash{众多数据结构,提供了KEYS
q行枚D操作Q但不能在线上用,如果需要枚丄上数据,Redis提供了工具可以直接扫描其dump文gQ枚丑և所有数据,Redisq同时提供了持久化和复制{功能?/p>
5.关于不同语言的客L支持
在不同语a的客L斚wQMemcached和Redis都有丰富的第三方客户端可供选择Q不q因为Memcached发展的时间更久一些,目前看在?L支持斚wQMemcached的很多客L更加成熟E_Q而Redis׃其协议本w就比Memcached复杂Q加上作者不断增加新的功能等Q对?W三方客L跟进速度可能会赶不上Q有时可能需要自己在W三方客L基础上做些修Ҏ能更好的使用?/p>
Ҏ以上比较不难看出Q当我们不希望数据被t出Q或者需要除key/value之外的更多数据类型时Q或者需要落地功能时Q用Redis比用Memcached更合适?/p>
关于Redis的一些周边功?/strong>
Redis除了作ؓ存储之外q提供了一些其它方面的功能Q比如聚合计、pubsub、scripting{,对于此类功能需要了解其实现原理Q清楚地 了解到它的局限性后Q才能正的使用Q比如pubsub功能Q这个实际是没有M持久化支持的Q消Ҏq接闪断或重q之间过来的消息是会全部丢失的,又比 如聚合计和scripting{功能受Redis单线E模型所限,是不可能辑ֈ很高的吞吐量的,需要}慎用?/p>
ȝ来说Redis作者是一位非常勤奋的开发者,可以l常看到作者在试着各种不同的新鲜想法和思\Q针对这些方面的功能p求我们需要深入了解后再用?/p>
ȝQ?/strong>
1.Redis使用最x式是全部数据in-memory?/p>
2.Redis更多场景是作为Memcached的替代者来使用?/p>
3.当需要除key/value之外的更多数据类型支持时Q用Redis更合适?/p>
4.当存储的数据不能被剔除时Q用Redis更合适?/p>