Redis作为号称读写性能百万每秒的nosql数据库,但是我们在实际使用的过程中却是无法达到这个效果的,那是什么原因呢?都有哪些因素影响了Redis的性能呢?
1.从机器性能上来看,CPU、网卡、磁盘等都会影响到读写的性能,就从网卡来说,你用100M的网卡去支持Redis200M/s的读写,这个肯定是会严重的影响到Redis性能的。
2.从网络来看,如果你的网络经过很多次跳转才能最终到达Redis服务器,那么网络跳转产生的时间也会产生数据的延迟,因此也可以被看作是影响Redis性能的原因。
3.从Redis的RESP协议来看,RESP协议定义的非常高效且可人读,因此这个提升了Redis的读写性能。
针对上面的一些影响因素,在官网提供了一些测试数据:
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (with pipelining)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -P 16 -q
SET: 552028.75 requests per second
GET: 707463.75 requests per second
LPUSH: 767459.75 requests per second
LPOP: 770119.38 requests per second
Intel(R) Xeon(R) CPU E5520 @ 2.27GHz (without pipelining)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 122556.53 requests per second
GET: 123601.76 requests per second
LPUSH: 136752.14 requests per second
LPOP: 132424.03 requests per second
Linode 2048 instance (with pipelining)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q -P 16
SET: 195503.42 requests per second
GET: 250187.64 requests per second
LPUSH: 230547.55 requests per second
LPOP: 250815.16 requests per second
Linode 2048 instance (without pipelining)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 35001.75 requests per second
GET: 37481.26 requests per second
LPUSH: 36968.58 requests per second
LPOP: 35186.49 requests per second
通过上面的测试分别在物理机和云上以及是否使用pipeline机制的时候,读写数据在性能上的差距是很大的(更多的测试数据可以看https://redis.io/topics/benchmarks),因此我们也可以看出如果针对大量的高并发读写,我们采用pipeline的机制在性能上面更好。在代码上看下实现方式
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 使用pipeline的方式
* @param batchSize
*/
public void pipeline(int batchSize) {
List<Object> results = stringRedisTemplate.executePipelined(
new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
for(int i=0; i< batchSize; i++) {
// set key1 value2
// set key2 value2
stringRedisConn.set("pipeline"+i, "xxx"+i);
}
return null;
}
});
System.out.println("pipeline over. results: "+results);
}
public void pipeline(int batchSize) {
Jedis jedis = new Jedis("192.168.1.5", 6379);
Pipeline p = jedis.pipelined();
List<Response<?>> list = new ArrayList<Response<?>>();
long s = System.currentTimeMillis();
for(int i=0; i< batchSize; i++) {
Response<?> r = p.get("pipeline"+i);
list.add(r);
}
System.out.println("write cost:"+(System.currentTimeMillis() - s));
p.sync();
list.forEach((e)->{
System.out.println(e.get());
});
System.out.println("read cost:"+(System.currentTimeMillis() - s));
try {
p.close();
} catch (IOException e1) {
e1.printStackTrace();
}
jedis.close();
}
在使用Redis的时候假如我们已经有100W的数据产生,这个时候需要把这些大量的数据导入。那我们有哪些方法呢?
1.打开一个txt文档,通过下面这种方式把要导入的数据写入txt文档
SET Key0 Value0
SET Key1 Value1
...
SET KeyN ValueN
然后通过下面命令进行导入
(cat data.txt; sleep 10) | nc localhost 6379 > /dev/null
但是这种方法不好的是它并不能确定数据是否已经完全导入,并且是否有错误产生。
2.仍然通过第一种的txt文档内容方式,但是采用下面这种命令
cat data.txt | redis-cli --pipe
通过pipe的方式会解决掉第一种方式的不足。
3.通过Redis的RESP协议内容的方式进行数据组装,如下所示
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"
或通过格式化的方式看起来好看些
*3\r\n
$3\r\n
SET\r\n
$3\r\n
key\r\n
$5\r\n
value\r\n
如果是新的Redis服务器,则可以直接通过以aof文件的方式读取到Redis中,否则可以通过代码的方式进行读取。
4.ruby的方式
def gen_redis_proto(*cmd)
proto = ""
proto << "*"+cmd.length.to_s+"\r\n"
cmd.each{|arg|
proto << "$"+arg.to_s.bytesize.to_s+"\r\n"
proto << arg.to_s+"\r\n"
}
proto
end
puts gen_redis_proto("SET","mykey","Hello World!").inspect
(0...1000).each{|n|
STDOUT.write(gen_redis_proto("SET","Key#{n}","Value#{n}"))
}
或者通过命令行方式执行ruby
$ ruby proto.rb | redis-cli --pipe
5.lua脚本,具体参考https://redis.io/commands/eval