Redis DB
标签(空格分隔): Redis
1,概述:
Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
2,安装、编译Redis
安装、编译
[hadoop@bigdata01 sdb4]$ ls
apache app eclipse idea-141 lost+found new file redis-3.0.7.tar.gz test work
[hadoop@bigdata01 sdb4]$ chmod u+x redis-3.0.7.tar.gz
[hadoop@bigdata01 sdb4]$ tar -zxf redis-3.0.7.tar.gz
[hadoop@bigdata01 sdb4]$ ls
apache eclipse lost+found redis-3.0.7 test
app idea-141 new file redis-3.0.7.tar.gz work
[hadoop@bigdata01 redis-3.0.7]$ make
make[1]: Leaving directory `/sdb4/redis-3.0.7/src'
运行Redis服务(连接端口:6379)
[hadoop@bigdata01 redis-3.0.7]$ src/redis-server
24054:M 01 May 14:37:33.094 * The server is now ready to accept connections on port 6379
运行Redis客户端测试
[hadoop@bigdata01 redis-3.0.7]$ src/redis-cli
127.0.0.1:6379> set test msg
OK
127.0.0.1:6379> get test
"msg"
3,配置Redis参数,修改redis.conf文件
•设置本地数据库存放目录
[hadoop@bigdata01 redis-3.0.7]$ mkdir data
[hadoop@bigdata01 data]$ pwd
/sdb4/redis-3.0.7/data
-- 本地存放目录
dir /sdb4/redis-3.0.7/data
4,Redis数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
- String(字符串)是redis最基本的类型,一个键最大能存储512MB。
[hadoop@bigdata01 redis-3.0.7]$ src/redis-cli
127.0.0.1:6379> set string "test set string"
OK
127.0.0.1:6379> get string
"test set string"
-- 操作说明
在以上实例中使用了 Redis 的 SET 和 GET 命令。键为 string,对应的值为test set string
注意:一个键最大能存储512MB
- Hash(哈希)是一个键值对集合,是一个string类型的field和value的映射表,hash特别适合用于存储对象
127.0.0.1:6379> HMSET zhangsan addr "huizhou" tel 07521234567
OK
127.0.0.1:6379> HGETALL zhangsan
1) "addr"
2) "huizhou"
3) "tel"
-- 说明
以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。 实例中我们使用了 Redis HMSET, HGETALL 命令,zhangsan 为键值。
- List(列表)是简单的字符串列表,按照插入顺序排序;你可以添加一个元素导列表的头部(左边)或者尾部(右边)
127.0.0.1:6379> LPUSH tablename redis
(integer) 1
127.0.0.1:6379> LPUSH tablename hadoop
(integer) 2
127.0.0.1:6379> LPUSH tablename spark
(integer) 3
127.0.0.1:6379> LPUSH tablename redis
(integer) 4
127.0.0.1:6379> LPUSH tablename "scala"
(integer) 5
127.0.0.1:6379> LRANGE tablename 0 10
1) "scala"
2) "redis"
3) "spark"
4) "hadoop"
5) "redis"
-- 说明
在上述例子中的五个值被插入到redis的命名列表tablename 使用LPUSH命令
- 集合(Set)t是string类型的无序集合;集合成员是唯一的,这就意味着集合中不能出现重复的数据
127.0.0.1:6379> SADD keySet "redis" "spark"
(integer) 2
127.0.0.1:6379> SADD keySet "hdfs"
(integer) 1
127.0.0.1:6379> SMEMBERS keySet
1) "redis"
2) "hdfs"
3) "spark"
127.0.0.1:6379> SADD keySet "hdfs"
(integer) 0
127.0.0.1:6379> SMEMBERS keySet
1) "redis"
2) "hdfs"
3) "spark"
-- 说明
在以上实例中我们通过 SADD 命令向名为 keySet 的集合插入的三个元素
- 有序集合(sorted set)有序集合和集合一样也是string类型元素的集合,且不允许重复的成员;不同的是每个元素都会关联一个double类型的分数;redis正是通过分数来为集合中的成员进行从小到大的排序;有序集合的成员是唯一的,但分数(score)却可以重复
127.0.0.1:6379> ZADD keySet2 1 "redis"
(integer) 1
127.0.0.1:6379> ZADD keySet2 3 "spark"
(integer) 1
127.0.0.1:6379> ZADD keySet2 2 "hdfs"
(integer) 1
127.0.0.1:6379> ZRANGE keySet2 0 10 WITHSCORES
1) "redis"
2) "1"
3) "hdfs"
4) "2"
5) "spark"
6) "3"
-- 说明
在以上实例中我们通过命令 ZADD 向 redis 的有序集合中添加了三个值并关联上分数
ZADD key score1 member1 [score2 member2]
向有序集合添加一个或多个成员,或者更新已存在成员的分数
5,Redis安全,通过 redis的配置文件设置密码参数,这样客户端连接到 redis 服务就需要密码验证,这样可以让你的 redis 服务更安全
-- 1、过以下命令查看是否设置了密码验证:
127.0.0.1:6379> CONFIG get requirepass
1) "requirepass"
2) ""
-- 2、默认情况下 requirepass参数是空的,无需通过密码验证就可以连接到 redis 服务,通过以下命令来修改该参数:
127.0.0.1:6379> CONFIG set requirepass "123456"
OK
-- 3、设置密码后,客户端连接 redis服务就需要密码验证,否则无法执行命令;AUTH 命令基本语法格式如下:
127.0.0.1:6379> AUTH "123456"
OK
127.0.0.1:6379> CONFIG get requirepass
1) "requirepass"
2) "123456"
6,Kafka + SparkStreaming + Redis集成
参考:https://github.com/xetorthio/jedis/wiki/Getting-started
- SparkStreaming从Kafak获取数据、写入Redis
- pom.xml配置redis
<!-- Redis Client -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
- 代码实现、测试
package com.bigdata.spark.streaming.kafka
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkContext, SparkConf}
import redis.clients.jedis.{ShardedJedis, Jedis, JedisPoolConfig, JedisPool}
/**
- Created by hadoop on 5/1/16.
*/
object StreamingKafkaRedis {
def main(args: Array[String]) {
// Create SparkConf Instance
val conf = new SparkConf()
.setAppName("StreamingKafkaRedisApp")
.setMaster("local[2]")
// Create SparkContext Instance
val sc = new SparkContext(conf)
val ssc = new StreamingContext(sc, Seconds(20))
val topicMap = Map("sparkTopic" -> 1)
val kafkaStream = KafkaUtils.createStream(ssc, "bigdata01.ibeifeng.com:2181", "topicGroup", topicMap).map(_._2)
// val wordCountDStream = kafkaStream.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
// wordCountDStream.print()
val wordDStream = kafkaStream.flatMap(_.split(" "))
/**
* 1. Kafka Send Data to Spark Streaming
* 2. Spark Streaming Write Data to Redis
*/
/** write data type one **/
/**
wordDStream.foreachRDD(rdd => {
rdd.foreachPartition(partitionRecords => {
partitionRecords.foreach(record => {
// AUTH Authentication required.
// print
val jedis = new Jedis("bigdata01.ibeifeng.com",6379)
jedis.auth("123456")
println("record: %s".format(record))
jedis.lpush("kafkaKey", record)
jedis.close()
})
})
})
*/
// Save Redis
/** write data type two **/
/***************** Redis Operation Start *******************/
// To use it, init a pool:
val pool = new JedisPool(new JedisPoolConfig(), "bigdata01.ibeifeng.com")//127.0.0.1
// Jedis implements Closable. Hence, the jedis instance will be auto-closed after the last statement.
wordDStream.foreachRDD(rdd => {
rdd.foreachPartition(partitionRecords => {
partitionRecords.foreach(record => {
// print
// if the master is on the same PC which runs your code
// jedis.slaveof("127.0.0.1", 6397)
val jedis = new Jedis("bigdata01.ibeifeng.com",6379)
// AUTH Authentication required.
jedis.auth("123456")
println("record: %s".format(record))
jedis.lpush("kafkaKey2", record)
jedis.close()
})
})
})
// ... when closing your application:
pool.destroy()
/***************** Redis Operation End ********************/
ssc.start()
ssc.awaitTermination()
}
}
7,利用foreachRDD的设计模式
dstream.foreachRDD是一个强大的原语,发送数据到外部系统中。然而,明白怎样正确地、有效地用这个原语是非常重要的。下面几点介绍了如何避免一般错误经常写数据到外部系统需要建一个连接对象(例如到远程服务器的TCP连接),用它发送数据到远程系统。为了达到这个目的,开发人员可能不经意的在Spark驱动中创建一个连接对象,但是在Spark worker中 尝试调用这个连接对象保存记录到RDD中,如下:
- 经常写数据到外部系统需要建一个连接对象(例如到远程服务器的TCP连接),用它发送数据到远程系统。为了达到这个目的,开发人员可能不经意的在Spark驱动中创建一个连接对象,但是在Spark worker中 尝试调用这个连接对象保存记录到RDD中,如下:
dstream.foreachRDD(rdd => {
val connection = createNewConnection() // executed at the driver
rdd.foreach(record => {
connection.send(record) // executed at the worker
})
})
- 然而,这会造成另外一个常见的错误-为每一个记录创建了一个连接对象。例如:
dstream.foreachRDD(rdd => {
rdd.foreach(record => {
val connection = createNewConnection()
connection.send(record)
connection.close()
})
})
通常,创建一个连接对象有资源和时间的开支。因此,为每个记录创建和销毁连接对象会导致非常高的开支,明显的减少系统的整体吞吐量。一个更好的解决办法是利用rdd.foreachPartition方法。 为RDD的partition创建一个连接对象,用这个两件对象发送partition中的所有记录。
dstream.foreachRDD(rdd => {
rdd.foreachPartition(partitionOfRecords => {
val connection = createNewConnection()
partitionOfRecords.foreach(record => connection.send(record))
connection.close()
})
})
这就将连接对象的创建开销分摊到了partition的所有记录上了。
这是不正确的,因为这需要先序列化连接对象,然后将它从driver发送到worker中。这样的连接对象在机器之间不能传送。它可能表现为序列化错误(连接对象不可序列化)或者初始化错误(连接对象应该 在worker中初始化)等等。正确的解决办法是在worker中创建连接对象。