Redis:Nosql数据库

Redis

Nosql技术

 

  1. redis介绍
    1. 什么是NoSql

NoSql是为了解决高并发、高可扩展、高可用以及高写入而产生的数据库解决方案。

 

NoSql就是Not Only sql。Nosql是非关系型数据库,它是关系型数据库的良好补充,而不能替代关系型数据库。

 

    1. Nosql数据库分类(了解)
  1. 键值(Key-Value)存储数据库

相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB

典型应用: 内容缓存,主要用于处理大量数据的高访问负载。

数据模型: 一系列键值对

优势: 快速查询

劣势: 存储的数据缺少结构化

 

  1. 列存储数据库

相关产品:Cassandra, HBase, Riak

典型应用:分布式的文件系统

数据模型:以列簇式存储,将同一列数据存在一起 (列族,可以简单理解为schema,可以在列族中添加很多的列)

优势:查找速度快,可扩展性强,更容易进行分布式扩展

 劣势:功能相对局限

  1. 文档型数据库

相关产品:CouchDB、MongoDB

典型应用:Web应用(与Key-Value类似,Value是结构化的)

数据模型: 一系列键值对

 优势:数据结构要求不严格

 劣势: 查询性能不高,而且缺乏统一的查询语法

  1. 图形(Graph)数据库

相关数据库:Neo4J、InfoGrid、Infinite Graph

典型应用:社交网络

数据模型:图结构

优势:利用图结构相关算法。

劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。

 

    1. 什么是redis

Redis是用C语言开发的高性能的键值对存储的Nosql数据库。

redis是一个内存nosql数据库

redis中也是存储key-value形式的数据

redis中的key-value相比hbase等数据库来说,redis的value比较强大,它的value可以不仅仅是一个byte[]

redis的value可以有结构:可以是一个list,也可以是一个hash,也可以是set.....

Redis存储的数据类型有五种:字符(string)、散列(hash)、列表(list)、集合(set)、有序集合(sorted set)

所以redis经常被称作为:数据结构服务器

 

    1. redis历史发展

       2008年,意大利的一家创业公司Merzia推出了一款基于MySQL的网站实时统计系统LLOOGG,然而没过多久该公司的创始人 Salvatore Sanfilippo便 对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身定做一个数据库,并于2009年开发完成,这个数据库就是Redis。 不过Salvatore Sanfilippo并不满足只将Redis用于LLOOGG这一款产品,而是希望更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Noordhuis一起继续着Redis的开发,直到今天。

         Salvatore Sanfilippo自己也没有想到,短短的几年时间,Redis就拥有了庞大的用户群体。Hacker News在2012年发布了一份数据库的使用情况调查,结果显示有近12%的公司在使用Redis。国内如新浪微博、街旁网、知乎网,国外如GitHub、Stack Overflow、Flickr等都是Redis的用户。(GitHub  代码托管平台)

         VMware公司从2010年开始赞助Redis的开发, Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全职开发Redis。

 

    1. redis的应用场景

缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用

分布式集群架构中的session分离。

聊天室的在线好友列表。

任务队列。(秒杀、抢购、12306等等)

应用排行榜。

网站访问统计。

 

  1. redis的安装启动
    1. 下载redis

因为redis一般会在linux系统进行安装,所以下载时要下载linux系统的安装包。

官网地址:http://redis.io/

下载地址:http://download.redis.io/releases/redis-3.2.8.tar.gz

 

 

编译工具:

make                 BuildFile

ant                    build.xml

maven              pom.xml

 

    1. 安装

在linux系统进行安装

  1. 上传redis的压缩包到linux系统

 

 

  1. 解压redis压缩包

[root@hdp-04 ~]# tar -zxvf redis-3.2.8.tar.gz  -C  apps/

 

  1. 编译解压缩之后的redis文件

[root@hdp-04 ~]# cd apps/redis-3.2.8

[root@hdp-04 redis-3.2.8]# make

 

 

本地yum源的可安装包:

 

 

  1. 在linux中安装C语言环境

[root@hdp-04 ~]# yum -y install gcc gcc-c++

重新编译:

# make

 

如果报错:

 

# make MALLOC=libc

 

 

  1. 安装redis 安装编译之后的库和可执行文件到系统中,指定目录为/usr/local/redis

[root@hdp-04 redis-3.2.8]# make install PREFIX=/usr/local/redis

 

切换到/usr/local/redis目录,发现以下信息,则说明安装成功。

 

 

 

    1. 启动
      1. 前台启动

使用redis-server命令,则可以进行前台启动:

# cd /usr/local/redis/bin

# ./redis-server

 

 

默认监听端口是6379

前台启动,一旦启动redis的客户端关闭,则redis也关闭。

退出:ctrl+c

 

      1. 后端启动

第一步:将redis.conf拷贝到bin目录下

# cp /root/apps/redis-3.2.8/redis.conf  /usr/local/redis/bin

第二步:修改redis.conf的配置:

修改redis绑定地址

将daemonize 改为yes,把redis以后台守护进程启动

# vi redis.conf

bind 192.168.8.14 127.0.0.1

daemonize yes

 

 

 

第三步:后端启动redis,指定启动命令使用修改后的redis.conf文件

[root@hdp-04 bin]# ./redis-server redis.conf

 

第四步:查看是否启动成功

# ps -ef | grep redis

 

或者 # netstat -natpl | grep 6379

 

 

解决yum安装命令失败:

# yum clean all

# yum repolist 

如果有包,就正确的,如果为0 ,就需要重新挂载

挂载命令:

# mount  /dev/cdrom /mnt/cdrom

电脑重启后,挂载失效。

可以修改配置文件,重启后依然生效

# cat  /etc/fatab

添加下面一行内容:(统一分隔符,统一使用tab键或者统一使用空格)

/dev/sr0        /mnt/cdrom      iso9660 ro      0       0

 

 

虚拟机需要配置:

 

 

 

要确保: 使用  ./redis-server ./redis.conf 

  1. Redis客户端
    1. Redis自带的客户端

# cd /usr/local/redis/bin

# ./redis-cli

127.0.0.1:6379> ping

PONG

 

指定启动参数:-h:指定主机IP   -p:指定主机端口

# ./redis-cli  -h 127.0.0.1 -p 6379

 

 

Redis安装成功之后,默认有16个数据库,每个库之间是互相独立的。

 

 

默认存储的数据是放到db0中的。

切换数据库的命令:select  数据库编号

 

 

    1. Java客户端(jedis)
      1. jedis介绍

         Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。

         在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。

Jedis同样也是托管在github上,地址:https://github.com/xetorthio/jedis

 

 

      1. 搭建maven工程

创建一个maven    project并导入jar包依赖。

jar包搜索地址: https://search.maven.org/

添加pom依赖:


<dependency>
    <groupId>redis.clientsgroupId>
    <artifactId>jedisartifactId>
    <version>2.8.1version>
dependency>

 

 

会自动引入相关依赖的jar包

 

 

      1. 单实例连接redis

 

 

      1. 使用连接池连接redis

 

 

 

  1. Redis数据类型

 

Redis中存储数据是通过key-value存储的,对于value的类型有以下几种:

  • 字符串 Map
  • Hash类型 Map>
  • List    Map
  • Set    Map
  • SortedSet(zset) Map

 

在redis中的命令语句中,命令是忽略大小写的,而key是不忽略大小写的。

 

    1. String数据类型操作

删除所有的数据  flushdb ,注意 慎用该命令 

127.0.0.1:6379> set name zhangsan

OK

127.0.0.1:6379> keys *

1) "name"

127.0.0.1:6379> get name

"zhangsan"

127.0.0.1:6379> set age 18

OK

127.0.0.1:6379> get age

"18"

自增 incr  自减 decr

127.0.0.1:6379> incr age

(integer) 19

127.0.0.1:6379> incr name

(error) ERR value is not an integer or out of range

自增指定数值(自增的步长) incrby decrby

127.0.0.1:6379>incrby age 2

(integer) 21

127.0.0.1:6379> del name

(integer) 1

127.0.0.1:6379> del xxx

(integer) 0

 

同时设置 获取多个键值

语法:

MSET key value [key value …]

MGET key [key …]

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3

OK

127.0.0.1:6379> get k1

"v1"

127.0.0.1:6379> mget k1 k3

1) "v1"

2) "v3"

 

 

 

STRLEN命令返回键值的长度,如果键不存在则返回0。

语法:STRLEN key

127.0.0.1:6379> strlen str

(integer) 0

127.0.0.1:6379> set str hello

OK

127.0.0.1:6379> strlen str

(integer) 5

 

 

如何存储对象:

 

      1. 适用场景

自增主键:

商品编号、订单号采用string的递增数字特性生成。

定义商品编号key:items:id

192.168.101.3:7003> INCR items:id

(integer) 2

192.168.101.3:7003> INCR items:id

(integer) 3

    1. Hash

hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:

        

      1. 命令

赋值取值: hset  hget

127.0.0.1:6379> hset hash1 age 20

(integer) 1

127.0.0.1:6379> hget hash1 age

"20"

删除value中的key

127.0.0.1:6379> hdel hash1 age

(integer) 1

删除key

127.0.0.1:6379> del hash1

(integer) 0

增加数字

127.0.0.1:6379> hincrby hash1 age 2

(integer) 22

 

同时赋予多值,取多值

127.0.0.1:6379> hmset hash1 name zhangsan sex 1

OK

127.0.0.1:6379> hmget hash1 name sex age

1) "zhangsan"

2) "1"

3) "30"

获取所有键值

127.0.0.1:6379> hgetall hash1

1) "age"

2) "30"

3) "name"

4) "zhangsan"

5) "sex"

6) "1"

 

判断字段是否存在

语法:HEXISTS key field

127.0.0.1:6379> hexists user age          查看user中是否有age字段

(integer) 1

127.0.0.1:6379> hexists user name       查看user中是否有name字段

(integer) 0

 

只获取keys或者values

语法:HKEYS key  HVALS key

127.0.0.1:6379> hmset user age 20 name lisi

OK

127.0.0.1:6379> hkeys user

1) "age"

2) "name"

127.0.0.1:6379> hvals user

1) "20"

2) "lisi"

 

获取长度 HLEN key

127.0.0.1:6379> hlen user

(integer) 2

 

 

      1. 适用场景

存储商品信息

  • 商品字段

【商品id、商品名称、商品描述、商品库存、商品好评】

 

  • 定义商品信息的key

商品1001的信息在 Redis中的key为:[items:1001]

 

  • 存储商品信息

192.168.101.3:7003> HMSET items:1001 id 3 name apple price 999.9

OK

 

  • 获取商品信息

192.168.101.3:7003> HGET items:1001 id

"3"

192.168.101.3:7003> HGETALL items:1001

1) "id"

2) "3"

3) "name"

4) "apple"

5) "price"

6) "999.9"

 

    val jedis = new Jedis("hdp-04",6379)
   
// 赋值
   
jedis.hset("item:1000","price","200")

   
val hvalue1 = jedis.hget("item:1000","price")
    println(hvalue1)

   
// 自增
   
jedis.hincrBy("item:1000","price",10)

   
// 删除  0 false  1 true
   
println("hdel:"+jedis.hdel("item:1000","xxx"))

   
val hkeys: util.Set[String] = jedis.hkeys("item:1000")

   
// 这里会报错  解决方法 就是获取迭代器,然后使用javaapi进行迭代
//    val iterator = hkeys.iterator()
//    while (iterator.hasNext){
//      println("hkeys::"+iterator.next())
//    }

    //
直接导入scalajava的转换
   
import scala.collection.JavaConversions._
   
for(i<- hkeys){
      println(
s"key:${i}")
    }

   
// 关闭连接
   
jedis.close()

 

 

    1. List

Redis的list使用的是linkedlist,linkedlist有两种方式:队列、堆栈。

在linkedlist中的头插法 和尾插法

队列中的名称: 入栈 push,出栈(弹栈) pop

      1. 命令

lpush :  插入到队首

rpush: 插入到队尾

127.0.0.1:6379> lpush list1 4 5 6

(integer) 6

127.0.0.1:6379> rpush list1 a b

(integer) 10

-1 表示获取最后一个

127.0.0.1:6379> lrange list1 0 -1

 1) "6"

 2) "5"

 3) "4"

4) "a"

 5) "b"

 

127.0.0.1:6379> lrange list1 1 3

 

弹出列表,则表示从列表中删除

127.0.0.1:6379> lpop list1

"6"

127.0.0.1:6379> lpop list1

"5"

 

获取列表长度

127.0.0.1:6379> llen list1

(integer) 8

 

 

 

      1. 适用场景

商品评论列表

思路:

在Redis中创建商品评论列表

用户发布商品评论,将评论信息转成json存储到list中。

用户在页面查询评论列表,从redis中取出json数据展示到页面。

 

定义商品评论列表key:

商品编号为1001的商品评论key【items: comment:1001】

192.168.101.3:7001> LPUSH items:comment:1001 '{"id":1,"name":"商品不错,很好!!","date":1430295077289}'

 

    1. Set

List和set的区别:

List是有序且可重复

Set是无序唯一。

      1. 命令

赋值 取值

127.0.0.1:6379> sadd set1 1 2 2 3 3 4 5

(integer) 5

127.0.0.1:6379> smembers set1

1) "1"

2) "2"

3) "3"

4) "4"

5) "5"

 

删除元素

127.0.0.1:6379> srem set1 3

(integer) 1

判断元素是否存在

127.0.0.1:6379> sismember set1 3

(integer) 0

127.0.0.1:6379> sismember set1 4

(integer) 1

 

0没有 1 有

 

 

      1. 运算命令

在set中可以进行差集、交集、并集的运算

        1. 差集

127.0.0.1:6379> sadd set1 1 2 3

(integer) 1

127.0.0.1:6379> sadd set2 2 3 4

(integer) 3

127.0.0.1:6379> sdiff set1 set2

1) "1"                       

 

 

属于A并且不属于B的元素构成的集合。

 

 

        1. 交集

属于A且属于B的元素构成的集合。

 

 

127.0.0.1:6379> sinter set1 set2

1) "2"

2) "3"

        1. 并集

属于A或者属于B的元素构成的集合

 

 

127.0.0.1:6379> sunion set1 set2

1) "1"

2) "2"

3) "3"

4) "4"

 

    1. sortedset(zset)

有序集合和set以及list的区别

Zset是唯一且有序的。

Zset是通过score 来进行排序的。

      1. 基本操作

127.0.0.1:6379> zadd zset1 1 haha 3 hehe 2 heihei

(integer) 3

 

实际存储在redis中的 数据顺序为:haha 、 heihei 、 hehe (通过分数升序排序)

127.0.0.1:6379> zrange zset1 0 1

1) "haha"

2) "heihei"

 

降序排序

127.0.0.1:6379> zrevrange zset1 0 1

1) "hehe"

2) "heihei"

 

127.0.0.1:6379> zrem zset1 haha

(integer) 1

 

127.0.0.1:6379> zscore zset1 hehe

"3"

 

升序,查看 元素及值

zrange zset1 0 1 withscores

 1) "haha"

 2) "1"

 3) "heihei"

 4) "2"

 

返回值是更改后的分数

增加某元素的分数,返回值是更改后的分数

语法:ZINCRBY  key increment member

127.0.0.1:6379> ZINCRBY scoreboard 4 lisi

"101“

 

获取元素的排名

升序,从小到大

语法:ZRANK key member

127.0.0.1:6379> ZRANK scoreboard lisi

(integer) 0

 

降序,从大到小

语法:ZREVRANK key member

127.0.0.1:6379> ZREVRANK scoreboard zhangsan

(integer) 1

 

 

      1. 应用场景

需求:根据商品销售量对商品进行排行显示

思路:定义商品销售排行榜(sorted set集合),Key为items:sellsort,分数为商品销售量。

 

写入商品销售量

  • 商品编号1001的销量是9,商品编号1002的销量是10

127.0.0.1:6379> ZADD items:sellsort 9 1001 10 1002

 

  • 商品编号1001的销量加1

127.0.0.1:6379> ZINCRBY items:sellsort 1 1001

 

  • 商品销量前10名:

127.0.0.1:6379> ZRANGE items:sellsort 0 9 withscores

 

 

  1. 综合案例
    1. LOL英雄出场次数
 
生成访问数据
val heros = Array("易大师", "盖伦", "金克斯", "奥巴马", "瞎子", "安妮", "光辉", "石头")

  val random = new Random()

  // 获取连接

  val jedis = new Jedis("hdp-04", 6379)

  while (true) {

  // 挑一个英雄

  val hero = heros(random.nextInt(heros.length))

  // 更新redis中的英雄出场次数

  jedis.zincrby("chuchangbang", 1, hero)

  // 玩

  System.out.println("敌人30秒后将到达战场.... 人在塔在....")

  Thread.sleep(200)

}

 

排行榜数据更新

 

// 连接redis读取数据

  val jedis = new Jedis("hdp-04", 6379)

  while (true) {

  val zrevrangeWithScores = jedis.zrevrangeWithScores("chuchangbang", 0, -1)

  // java 代码转换为scala代码,需要导入转换

  import scala.collection.JavaConversions._

  for (tuple <- zrevrangeWithScores) {

    System.out.println(tuple.getElement + " : " + tuple.getScore)

  }

  Thread.sleep(2000)

  System.out.println("-----------------------------------------")

}

 

 

    1. 电商推荐系统
  1. redis持久化

redis的数据是存储在内存中,如果一旦服务器挂掉,内存中的会丢失,所以需要对内存中的数据进行持久化。

持久化有两种方式:rdb、aof

Rdb是默认支持的

    1. RDB持久化

RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘

RDBRedis默认采用的持久化方式。

 

  • 在redis.conf配置文件中默认有此下配置:

save 开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系。

“save 900 1”表示15分钟(900秒钟)内至少1个键被更改则进行快照。

“save 300 10”表示5分钟(300秒)内至少10个键被更改则进行快照。

save 900 1

save 300 10

save 60 10000

 

  • 在redis.conf中:

配置dir指定rdb快照文件的位置

# Note that you must specify a directory here, not a file name.

dir ./

 

配置dbfilenam指定rdb快照文件的名称

# The filename where to dump the DB

dbfilename dump.rdb

 

         Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20~30秒钟。

如果需要对数据进行完整持久化,那么需要使用aof方式进行持久化。

 

    1. Aof持久化

通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。

如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化

      1. 开启aof持久化

默认情况下,aof持久化是不开启的。

 

将redis.conf中的appendonly 改为yes ,即可开启aof持久化。

 

重启redis

 

 

如果使用aof模式,那么redis启动时不会从rdb文件中加载持久化数据,而是从aof文件中加载持久化数据。

 

  1. Redis的主从复制

Redis的主从复制是解决单点故障问题,可以通过redis的高可用(HA)。

 

主从复制,则需要两个redis实例。把两个redis实例放到两个服务器中。模拟实现,可以在一台服务中启动两个实例。

    1. Redis主从实例准备

第一步:复制一个redis实例

# cd /usr/local/redis

# cp -r bin/ ./bin2

第二步:修改端口

 

其中:6379服务器会是主redis,而6380会是从redis

    1. 主redis

无需配置

    1. 从redis

将slaveof 指定主redis的ip和端口

 

 

启动主redis和从redis

# bin/redis-server bin/redis.conf

# bin2/redis-server bin2/redis.conf

 

    1. 总结

主redis中存储的数据,在从redis中会进行同步。

主redis可以进行写操作,而从redis只是只读的。

 

 

你可能感兴趣的:(redis)