redis 研究笔记汇总

redis 研究笔记汇总

背景

  1. nosql 是为了解决高并发,高可扩展,高可用,高写入产生的数据库解决方案
  2. NoSql就是Not Only sql。Nosql是非关系型数据库,它是关系型数据库的良好补充,而不能替代关系型数据库
  3. Nosql数据库分类

键值(Key-Value)存储数据库
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
数据模型: 一系列键值对
优势: 快速查询
劣势: 存储的数据缺少结构化

列存储数据库
相关产品:Cassandra, HBase, Riak
典型应用:分布式的文件系统
数据模型:以列簇式存储,将同一列数据存在一起 (列族,可以简单理解为schema,可以在列族中添加很多的列)
优势:查找速度快,可扩展性强,更容易进行分布式扩展
劣势:功能相对局限

文档型数据库
相关产品:CouchDB、MongoDB
典型应用:Web应用(与Key-Value类似,Value是结构化的)
数据模型: 一系列键值对
优势:数据结构要求不严格
劣势: 查询性能不高,而且缺乏统一的查询语法

图形(Graph)数据库
相关数据库:Neo4J、InfoGrid、Infinite Graph
典型应用:社交网络
数据模型:图结构
优势:利用图结构相关算法。
劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案

1. redis是什么

  1. redis是用C语言开发的高性能的键值对存储的Nosql数据库。redis是一个内存nosql数据库,redis中也是存储key-value形式的数据.redis中的key-value相比hbase等数据库来说,redis的value比较强大,它的value可以不仅仅是一个byte[] .
  2. redis的value可以有结构:可以是一个list,也可以是一个hash,也可以是set…
  3. redis存储的数据类型有五种:字符(string)、散列(hash)、列表(list)、集合(set)、有序集合(sorted set).所以redis经常被称作为:数据结构服务器
  4. 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等等)
应用排行榜
网站访问统计

2. 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系统进行安装
上传redis的压缩包到linux系统

解压redis压缩包
[root@doit-04 ~]# tar -zxvf redis-3.2.8.tar.gz -C apps/

编译解压缩之后的redis文件
[root@doit-04 ~]# cd apps/redis-3.2.8
[root@doit-04 redis-3.2.8]# make
在这里插入图片描述
在这里插入图片描述

在linux中安装C语言环境
[root@doit-04 ~]# yum -y install gcc gcc-c++
重新编译:
如果报错:在这里插入图片描述
# make MALLOC=libc

安装redis 安装编译之后的库和可执行文件到系统中,指定目录为/usr/local/redis
[root@doit-04 redis-3.2.8]# make install PREFIX=/usr/local/redis
切换到/usr/local/redis目录,发现以下信息,则说明安装成功。在这里插入图片描述

  1. 启动

使用redis-server命令,则可以进行前台启动:
# cd /usr/local/redis/bin
# ./redis-server
# make在这里插入图片描述
默认监听端口是6379
前台启动,一旦启动redis的客户端关闭,则redis也关闭。
退出:ctrl+c

后端启动
第一步:将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
# vi redis.conf在这里插入图片描述
或者 # 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

3. redis如何使用

3.1 客户端

3.1.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 数据库编号在这里插入图片描述

3.1.2. java客户端(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

pom

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.8.1</version>
</dependency>

单实例连接redis
在这里插入图片描述
使用连接池连接redis
在这里插入图片描述

3.2 Redis数据类型

在这里插入图片描述

Redis中存储数据是通过key-value存储的,对于value的类型有以下几种:
字符串 Map<String, String>
Hash类型 Map<String, Map<String, String>>
List Map<String, List>
Set Map<String, HasSet>
SortedSet(zset) Map<String, TreeSet>
在redis中的命令语句中,命令是忽略大小写的,而key是不忽略大小写的

删除所有的数据 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
  2. “v1”
  3. “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

如何存储对象在这里插入图片描述

适用场景:

自增主键:
商品编号、订单号采用string的递增数字特性生成。
定义商品编号key:items:id
192.168.101.3:7003> INCR items:id
(integer) 2
192.168.101.3:7003> INCR items:id
(integer) 3

Hash

hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:在这里插入图片描述

命令

赋值取值: 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
  4. “age”
  5. “30”
  6. “name”
  7. “zhangsan”
  8. “sex”
  9. “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
  10. “age”
  11. “name”
    127.0.0.1:6379> hvals user
  12. “20”
  13. “lisi”
    获取长度 HLEN key
    127.0.0.1:6379> hlen user
    (integer) 2

适用场景

存储商品信息
商品字段
【商品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")

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

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

    // 关闭连接
    jedis.close()

List

Redis的list使用的是linkedlist,linkedlist有两种方式:队列、堆栈。
在linkedlist中的头插法 和尾插法
队列中的名称: 入栈 push,出栈(弹栈) pop

命令

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

适用场景

商品评论列表
思路:
在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}’

Set

List和set的区别:
List是有序且可重复
Set是无序唯一

命令

赋值 取值
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 有

运算命令

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

差集

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的元素构成的集合。在这里插入图片描述

交集

属于A且属于B的元素构成的集合
在这里插入图片描述
127.0.0.1:6379> sinter set1 set2

  1. “2”
  2. “3”

并集

属于A或者属于B的元素构成的集合
在这里插入图片描述
127.0.0.1:6379> sunion set1 set2

  1. “1”
  2. “2”
  3. “3”
  4. “4”

sortedset(zset)

有序集合和set以及list的区别
Zset是唯一且有序的。
Zset是通过score 来进行排序的。

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

应用场景

需求:根据商品销售量对商品进行排行显示
思路:定义商品销售排行榜(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

4. redis内部机制

  1. redis持久化

redis的数据是存储在内存中,如果一旦服务器挂掉,内存中的会丢失,所以需要对内存中的数据进行持久化。
持久化有两种方式:rdb、aof
Rdb是默认支持的。

  1. RDB持久化

RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
RDB是Redis默认采用的持久化方式。
在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方式进行持久化。

Aof持久化

通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。
如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化

开启aof持久化

默认情况下,aof持久化是不开启的
将redis.conf中的appendonly 改为yes ,即可开启aof持久化
在这里插入图片描述
重启redis在这里插入图片描述
如果使用aof模式,那么redis启动时不会从rdb文件中加载持久化数据,而是从aof文件中加载持久化数据

Redis的主从复制

Redis的主从复制是解决单点故障问题,可以通过redis的高可用(HA)。
主从复制,则需要两个redis实例。把两个redis实例放到两个服务器中。模拟实现,可以在一台服务中启动两个实例。

Redis主从实例准备

第一步:复制一个redis实例
# cd /usr/local/redis
# cp -r bin/ ./bin2
第二步:修改端口在这里插入图片描述其中:6379服务器会是主redis,而6380会是从redis

主redis

无需配置

将slaveof 指定主redis的ip和端口在这里插入图片描述

启动主redis和从redis
# bin/redis-server bin/redis.conf
# bin2/redis-server bin2/redis.conf在这里插入图片描述

总结

主redis中存储的数据,在从redis中会进行同步。
主redis可以进行写操作,而从redis只是只读的。在这里插入图片描述

5. 案例

生成访问数据
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("-----------------------------------------")
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值