Redis01

Redis介绍

redis是一个K-V存储系统,它是内存数据库,主要是用于做数据的缓存,同时在大数据领域上,主要是做数据结果的存储,因为它是一个内存数据数据库,读写效率高,每秒吞吐量达到11W左右,支持多种数据类型(String,list,set等等),C语言开发的K-V数据库

应用场景:Java居多,用于缓存页面,大数据领域,主要是在做实时流处理统计的时候,使用Redis进行数据的存储

Nosql数据库:MongoDB、Hbase、Redis

Nosql优势:非关系数据库,K-V存储,查询效率高,数据存储容量大(Redis除外),可以按照数据的K-V结构处理当前存储的数据

注意:3.0以下版本不支持集群模式,3.0以上支持集群模式

安装Redis

Redis数据类型

String类型 字符串

Hash类型 散列

List类型 列表

Set类型 集合

Zset类型 有序集合

Redis-API连接

添加redis的java依赖

 <dependency>   
 		<groupId>redis.clients</groupId>  
 		<artifactId>jedis</artifactId>   
 		<version>2.9.0</version> 
 </dependency>
资源设置和使用
序号参数名含义默认值使用建议
1maxTotal资源池中最大连接数8设置建议见下节
2maxIdle资源池允许最大空闲的连接数8设置建议见下节
3minIdle资源池确保最少空闲的连接数0设置建议见下节
4blockWhenExhausted当资源池用尽后,调用者是否要等待。只有当为true时,下面的maxWaitMillis才会生效true建议使用默认值
5maxWaitMillis当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)-1:表示永不超时不建议使用默认值
6testOnBorrow向资源池借用连接时是否做连接有效性检测(ping),无效连接会被移除false业务量很大时候建议设置为false(多一次ping的开销)。
7testOnReturn向资源池归还连接时是否做连接有效性检测(ping),无效连接会被移除false业务量很大时候建议设置为false(多一次ping的开销)。
8jmxEnabled是否开启jmx监控,可用于监控true建议开启,但应用本身也要开启
2.空闲资源监测

空闲Jedis对象检测,下面四个参数组合来完成,testWhileIdle是该功能的开关。

序号参数名含义默认值使用建议
1testWhileIdle是否开启空闲资源监测falsetrue
2timeBetweenEvictionRunsMillis空闲资源的检测周期(单位为毫秒)-1:不检测建议设置,周期自行选择,也可以默认也可以使用下面JedisPoolConfig中的配置
3minEvictableIdleTimeMillis资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除1000 60 30 = 30分钟可根据自身业务决定,大部分默认值即可,也可以考虑使用下面JeidsPoolConfig中的配置
4numTestsPerEvictionRun做空闲资源检测时,每次的采样数3可根据自身应用连接数进行微调,如果设置为-1,就是对所有连接做空闲监测

Redis 持久化

RDB(Redis DataBase): 优点在于效率很高,不会频繁的刷入数据到磁盘,到达一定量的时候刷入磁盘,减少IO操作,缺点是数据安全没保障,如果某一时刻数据库挂掉了,那么没有达到持久化要求,此时的数据不会被刷入磁盘存储,所以下次启动后,无法回复未持久化的数据

AOF(Append Only File): 优点是数据安全,缺点效率低,并且频繁执行存储,IO过大,而且内部存储的是命令,不是数据,如果进行恢复,那么需要执行命令恢复数据。

总结:RDB适用于对数据安全性低的操作,而AOF恰恰相反,适用于数据安全要求性高的。

Redis集群

Redis集群,首先保证最少3个主节点,三个从节点,保证某一节点宕机半数以上节点存活,同时,每个槽位都是固定的,如果当一个节点宕机,那么从节点之间顶替主节点工作,当主节点没问题的时候,从节点负责同步数据即可

案例解析

注意redis的序列化问题

package com.qf.SparkStreaming

import org.apache.commons.pool2.impl.GenericObjectPoolConfig
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Seconds,StreamingContext}
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}
import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}
import redis.clients.jedis.{ JedisPool, JedisPoolConfig}


/**
  * @Author potpof
  * @Date 2019/11/20 16:07
  * @Version 1.0
  */
class HomeWork1120 {

}

// 序列化创建redis连接
object RedisClient extends Serializable {
  val redisHost = "10.36.151.79"
  val redisPort = 6379
  val redisTimeout = 30000
  lazy val pool = new JedisPool(new GenericObjectPoolConfig(), redisHost, redisPort, redisTimeout)

  lazy val hook: Thread = new Thread {
    override def run(): Unit = {
      println("Execute hook thread: " + this)
      pool.destroy()
    }
  }
  sys.addShutdownHook(hook.run())
}

/**
  * 从kafka读数据
  */
object ScalaKafkaStreaming1 {
  def main(args: Array[String]): Unit = {
    //  累加功能
    val func = (it: Iterator[(String, Seq[Int], Option[Int])]) => {
      it.map(x => {
        (x._1, x._2.sum + x._3.getOrElse(0))
      })
    }

    //    自定义方法将ip转换成数字
    def ip2Long(ip: String): Long = {
      val fragments = ip.split("[.]")
      var ipNum = 0L
      for (i <- 0 until fragments.length) {
        ipNum = fragments(i).toLong | ipNum << 8L
      }
      ipNum
    }

    //二分查找,找出ip在哪个范围,不存在则返回null
    def binarySearch(arr: Array[(String, String, String)], ip: Long): String = {
      var start = 0
      var end = arr.length - 1
      while (start <= end) {
        val middle = (start + end) / 2
        if ((ip >= arr(middle)._1.toLong) && (ip <= arr(middle)._2.toLong)) {
          return arr(middle)._3
        }
        if (ip < arr(middle)._1.toLong) {
          end = middle - 1
        } else {
          start = middle + 1
        }
      }
      "NULL"
    }

    val checkpointPath = "/ssc/1120_1"

    val conf = new SparkConf()
      .setAppName("ScalaKafkaStream")
      .setMaster("local[2]")
      .set("redis.host", "10.36.151.79")
    val sc = new SparkContext(conf)
    sc.setLogLevel("WARN")

    val ssc = new StreamingContext(sc, Seconds(3))
    ssc.checkpoint(checkpointPath)
    /**
      * 配置kafka连接
      */
    val bootstrapServers = "server:9092,client1:9092,client2:9092"
    val groupId = "sz1901"
    val topicName = "shop-topic"
    val maxPoll = 500
    val offset = "earliest"
    val tr = "true"
    val kafkaParams = Map(
      ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG -> bootstrapServers,
      ConsumerConfig.GROUP_ID_CONFIG -> groupId,
      ConsumerConfig.MAX_POLL_RECORDS_CONFIG -> maxPoll.toString,
      ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG -> classOf[StringDeserializer],
      ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG -> classOf[StringDeserializer],
      ConsumerConfig.AUTO_OFFSET_RESET_CONFIG -> offset,
      ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG -> tr
    )

    val kafkaTopicDS = KafkaUtils.createDirectStream(ssc, LocationStrategies.PreferConsistent,
      ConsumerStrategies.Subscribe[String, String](Set(topicName), kafkaParams))

    kafkaTopicDS.map(_.value()).map(_.toString.split(" ")).print()

    //问题1.计算出总的成交量总额(结果保存到redis中)
    val input1: DStream[(String, Int)] = kafkaTopicDS.map(_.value().split(" ")(4).toInt).map(("sum", _))
    val sum: DStream[(String, Int)] = input1.updateStateByKey(func, new HashPartitioner(ssc.sparkContext.defaultParallelism), rememberPartitioner = true)
    sum.print()
    //保存到redis,redis一定要继承序列化接口

    //错误示例
    //    input1.foreachRDD(rdd => {
    //      val jedis = jedisPool.getResource
    //      rdd.foreachPartition(tp => {
    //        tp.foreach(tp1 => {
    //          jedis.hincrBy("sum1120", tp1._1, tp1._2)
    //        })
    //        jedis.close()
    //      })
    //    })

    input1.foreachRDD(rdd => {
      rdd.foreachPartition(partitionOfRecords => {
        partitionOfRecords.foreach(pair => {
          val sum = pair._1
          val data = pair._2
          val jedis = RedisClient.pool.getResource
          jedis.hincrBy("sum1120", sum, data)
          jedis.close()
        })
      })
    })

    //问题2.计算每个商品分类的成交量(结果保存到redis中)
    val input2: DStream[(String, Int)] = kafkaTopicDS.map(_.value().split(" ")(2)).map((_, 1))
    val cate: DStream[(String, Int)] = input2.updateStateByKey(func, new HashPartitioner(ssc.sparkContext.defaultParallelism), rememberPartitioner = true)
    cate.print()

    input2.foreachRDD(rdd => {
      rdd.foreachPartition(partitionOfRecords => {
        partitionOfRecords.foreach(pair => {
          val cate = pair._1
          val data = pair._2
          val jedis = RedisClient.pool.getResource
          jedis.hincrBy("cate1120", cate, data)
          jedis.close()
        })
      })
    })
    //    问题3.计算每个省份的成交总额(结果保存到redis)
    val ipDict = sc.textFile("/ip.txt")
    val dict = ipDict.map(x => {
      val strings = x.split("\\|")
      (strings(2), strings(3), strings(6))
    }).collect()
    val area: DStream[(String, Int)] = kafkaTopicDS.map(_.value().split(" ")(1)).map(x => {
      val longIp = ip2Long(x)
      val arrInfo: Array[(String, String, String)] = dict
      val i: String = binarySearch(arrInfo, longIp)
      (i, 1)
    })
    val area1: DStream[(String, Int)] = area.updateStateByKey(func, new HashPartitioner(ssc.sparkContext.defaultParallelism), rememberPartitioner = true)
    area1.print()

    area.foreachRDD(rdd => {
      rdd.foreachPartition(partitionOfRecords => {
        partitionOfRecords.foreach(pair => {
          val area = pair._1
          val data = pair._2
          val jedis = RedisClient.pool.getResource
          jedis.hincrBy("area1120", area, data)
          jedis.close()
        })
      })
    })

    ssc.start()
    ssc.awaitTermination()
  }
}

//验证
object test1120 extends App {
  // 设置连接池的配置
  val config = new JedisPoolConfig
  config.setMaxTotal(20)
  config.setMaxIdle(10)
  config.setMaxWaitMillis(10)
  val pool = new JedisPool(config, "10.36.151.79", 6379)
  val jedis = pool.getResource
  println(jedis.hgetAll("sum1120"))
  println(jedis.hgetAll("cate1120"))
  println(jedis.hgetAll("area1120"))
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值