来源:http://t.cn/EwMvakT
1. Redis 介绍1.1 NoSQL 基本概念1.2 NoSQL 分类1.3 Redis 基本概念1.4 发展历史1.5 应用场景2. Redis 安装2.1 下载2.2 安装2.3 redis 启动3. Redis 客户端3.1 自带客户端3.2 图形界面客户端3.3 jedis 客户端4. 数据类型4.1 String 类型4.2 Hash 散列类型4.3 List 类型4.4 Set 类型4.5 Sortedset 类型
1. Redis 介绍
1.1 NoSQL 基本概念
为了解决高并发、高可用、高可扩展,大数据存储等一系列问题而产生的数据库解决方案,就是NoSql。
NoSql,叫非关系型数据库,它的全名Not only sql。它不能替代关系型数据库,只能作为关系型数据库的一个良好补充。
1.2 NoSQL 分类
- 键值(Key-Value)存储数据库 相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB 典型应用: 内容缓存,主要用于处理大量数据的高访问负载。 数据模型: 一系列键值对 优势: 快速查询 劣势: 存储的数据缺少结构化
- 列存储数据库 相关产品:Cassandra, HBase, Riak 典型应用:分布式的文件系统 数据模型:以列簇式存储,将同一列数据存在一起 优势:查找速度快,可扩展性强,更容易进行分布式扩展 劣势:功能相对局限
- 文档型数据库 相关产品:CouchDB、MongoDB 典型应用:Web应用(与Key-Value类似,Value是结构化的) 数据模型: 一系列键值对 优势:数据结构要求不严格 劣势: 查询性能不高,而且缺乏统一的查询语法
- 图形(Graph)数据库 相关数据库:Neo4J、InfoGrid、Infinite Graph 典型应用:社交网络 数据模型:图结构 优势:利用图结构相关算法。 劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
1.3 Redis 基本概念
Redis是使用c语言开发的一个高性能键值数据库。Redis可以通过一些键值类型来存储数据。 键值类型: String字符类型 map散列类型 list列表类型 set集合类型 sortedset有序集合类型
1.4 发展历史
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的用户。
VMware公司从2010年开始赞助Redis的开发, Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全职开发Redis。
1.5 应用场景
缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用) 分布式集群架构中的session分离。 聊天室的在线好友列表。 任务队列。(秒杀、抢购、12306等等) 应用排行榜。 网站访问统计。 数据过期处理(可以精确到毫秒)
2. Redis 安装
2.1 下载
官网地址:http://redis.io/ 下载地址:http://download.redis.io/releases/redis-3.0.0.tar.gz
2.2 安装
代码语言:javascript复制#sftp 上传安装包到linux
#解压
tar -zxvf redis.3.0.0.tar.gz
#安装c语言环境
sudo apt-get install gcc-c
#编译
cd redis-3.0.0
make
#安装
make install PREFIX = /usr/local/redis
# 查看
cd /usr/local/redis
ls
2.3 redis 启动
2.3.1 前端启动
- 前端启动命令
./redis-server
- 前端启动的关闭 强制关闭
ctrl c
正常关闭./redis-cli shutdown
tips:一旦客户端关闭,则redis服务也会停掉
2.3.2 后端启动
- 将 redis 源码包中的 redis.conf 文件拷贝至 bin 目录下
cp /root/redis-3.0.0/redis.conf
- 修改 redis.conf 文件,将 daemonize 改为 yes
vim redis.conf
- 使用后端命令启动 redis
./redis-server redis.conf
- 查看是否启动成功
ps -aux | grep redis
- 关闭后端启动的方式 强制关闭:
kill -9 进程号
正常关闭:./redis-cli shutdown
3. Redis 客户端
3.1 自带客户端
- 启动
./redis-cli -h 127.0.0.1 -p 6379
-h 指定访问 redis 服务器的 ip 地址 -p 指定访问的 redis 服务器的 port 端口 还可以写成 ./redis-cli 使用默认配置,默认ip 127.0.0.1,默认端口 6379 - 关闭 ctrl c 127.0.0.1:6379>quit
3.2 图形界面客户端
redis-desktop-manager 打开如下:
img
img
选择数据库方式: select 加上数据库的下标,就可以选择指定的数据库使用,下标从0开始。
代码语言:javascript复制127.0.0.1:6379> select 15
OK
127.0.0.1:6379[15]>
3.3 jedis 客户端
3.3.1 介绍
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
3.3.2 工程搭建
- 添加 jar commons-pool2-2.3.jar jedis-2.7.0.jar
3.3.2.1 单例连接 redis
img
3.3.2.2 使用连接池连接 redis
img
3.3.2.3 Spring 整合 jedisPool
添加 spring 的 jar 包 配置 spring 配置文件 applicationContext.xml
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="false" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!-- redis单机 通过连接池 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool"
destroy-method="close">
<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
<constructor-arg name="host" value="192.168.242.130" />
<constructor-arg name="port" value="6379" />
</bean>
</beans>
测试代码
代码语言:javascript复制 @Test
public void testJedisPool() {
JedisPool pool = (JedisPool) applicationContext.getBean("jedisPool");
Jedis jedis = null;
try {
jedis = pool.getResource();
jedis.set("name", "lisi");
String name = jedis.get("name");
System.out.println(name);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (jedis != null) {
// 关闭连接
jedis.close();
}
}
}
4. 数据类型
4.1 String 类型
赋值 set key value
代码语言:javascript复制127.0.0.1:6379> set test 123
OK
取值 get key
代码语言:javascript复制127.0.0.1:6379> get test
"123"
取值并赋值 getset key value
代码语言:javascript复制127.0.0.1:6379> getset test 321
"123"
127.0.0.1:6379> get test
"321"
设置获取多个键值 mset key value [key value…] mget key [key…]
代码语言:javascript复制127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> mget k1 k2
1) "v1"
2) "v2"
删除 del key
代码语言:javascript复制127.0.0.1:6379> del test
(integer) 1
数值增减
- 递增数字 当存储的字符串是整数时,Redis提供了一个实用的命令INCR,其作用是让当前键值递增,并返回递增后的值。 语法:incr key 127.0.0.1:6379> set num 1 OK 127.0.0.1:6379> incr num (integer) 2 127.0.0.1:6379> incr num (integer) 3 127.0.0.1:6379> incr num (integer) 4
- 增加指定的整数 incrby key increment 127.0.0.1:6379> incrby num 2 (integer) 8 127.0.0.1:6379> incrby num 2 (integer) 10
- 递减数值 decr key 127.0.0.1:6379> decr num (integer) 9 127.0.0.1:6379> decr num (integer) 8
- 减少指定的数值 decryby key decrement 127.0.0.1:6379> decrby num 2 (integer) 6 127.0.0.1:6379> decrby num 2 (integer) 4
向尾部追加值 APPEND的作用是向键值的末尾追加value。如果键不存在则将该键的值设置为value,即相当于 SET key value。返回值是追加后字符串的总长度。 语法:append key value
代码语言:javascript复制127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> append str "world"
(integer) 10
127.0.0.1:6379> get str
"helloworld"
获取字符串长度 STRLEN命令返回键值的长度,如果键不存在则返回0。 语法:strlen key
代码语言:javascript复制127.0.0.1:6379> strlen str
(integer) 10
应用
- 自增主键 商品编号、订单号采用 string 的递增数字特性生成
4.2 Hash 散列类型
4.2.1 使用 string 的问题
假设有User对象以JSON序列化的形式存储到Redis中,User对象有id,username、password、age、name等属性,存储的过程如下: 保存、更新:User对象 json(string) redis
如果在业务上只是更新age属性,其他的属性并不做更新我应该怎么做呢? 如果仍然采用上边的方法在传输、处理时会造成资源浪费,下边讲的hash可以很好的解决这个问题。
4.2.2 介绍
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
img
4.2.3 命令
赋值 HSET命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0。
- 一次只设置一个字段值 语法:hset key field value 127.0.0.1:6379> hset user username zhangsan (integer) 1
- 一次设置多个字段值 语法:hmset key field value [field value…] 127.0.0.1:6379> hmset user age 20 username lisi OK
- 当字段不存在时赋值,类似hset,区别在于如果字段存在,该命令不执行任何操作。 语法:hsetnx key field value 127.0.0.1:6379> hsetnx user age 30 (integer) 0
取值
- 一次获取一个字段值 语法:hget key field 127.0.0.1:6379> hget user username "lisi"
- 一次可以获取多个字段值 语法:hmget key field [field…] 127.0.0.1:6379> hmget user age username 1) "20" 2) "lisi"
- 获取所有字段值 语法:hgetall key 127.0.0.1:6379> hgetall user 1) "username" 2) "lisi" 3) "age" 4) "20"
删除字段 可以删除一个或多个字段,返回值是被删除的字段的个数。 语法:hdel key field [field…]
代码语言:javascript复制127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hdel user age username
(integer) 1
增加数字 语法:hincrby key field increment
代码语言:javascript复制127.0.0.1:6379> hincrby user age 2
(integer) 2
判断字段是否存在 语法:hexists key field
代码语言:javascript复制127.0.0.1:6379> hexists user age
(integer) 1
只获取字段名或字段值 语法: hkeys key hvals key
代码语言:javascript复制127.0.0.1:6379> hkeys user
1) "age"
获取字段数量 语法:hlen key
代码语言:javascript复制127.0.0.1:6379> hlen user
(integer) 1
应用 存储商品信息
代码语言:javascript复制127.0.0.1:6379> hlen user
(integer) 1
获取商品信息
代码语言:javascript复制127.0.0.1:6379> hgetall items:1001
1) "id"
2) "3"
3) "name"
4) "apple"
5) "price"
6) "5.00"
4.3 List 类型
4.3.1 ArrayList 和 LinkedList 的区别
Arraylist是使用数组来存储数据,特点:查询快、增删慢
Linkedlist是使用双向链表存储数据,特点:增删快、查询慢,但是查询链表两端的数据也很快。
Redis的list是采用来链表来存储的,所以对于redis的list数据类型的操作,是操作list的两端数据来操作的。
4.3.2 命令
向列表两端增加元素
- 向列表左边增加元素 语法:lpush key value [value…]
127.0.0.1:6379> lpush list:1 1 2 3
(integer) 3
- 向列表右边增加元素 语法:rpush key value [value…]
127.0.0.1:6379> rpush list:1 4 5 6
(integer) 6
查看列表 LRANGE命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回start、stop之间的所有元素(包含两端的元素),索引从0开始。索引可以是负数,如:“-1”代表最后边的一个元素。
语法:lrange key start stop
代码语言:javascript复制127.0.0.1:6379> lrange list:1 0 2
1) "3"
2) "2"
3) "1"
127.0.0.1:6379> lrange list:1 0 -1
1) "3"
2) "2"
3) "1"
4) "4"
5) "5"
6) "6"
从列表两端弹出元素 LPOP命令从列表左边弹出一个元素,会分两步完成:
- 第一步是将列表左边的元素从列表中移除
- 第二步是返回被移除的元素值。 语法: lpop key rpop key 127.0.0.1:6379> lpop list:1 "3" 127.0.0.1:6379> rpop list:1 "6"
获取列表中元素的个数 语法:llen key
代码语言:javascript复制127.0.0.1:6379> llen list:1
(integer) 4
删除列表中指定的值 LREM命令会删除列表中前count个值为value的元素,返回实际删除的元素个数。根据count值的不同,该命令的执行方式会有所不同:
- 当count>0时, LREM会从列表左边开始删除。
- 当count<0时, LREM会从列表后边开始删除。
- 当count=0时, LREM删除所有值为value的元素。
语法:lrem key count value
获得/设置指定索引的元素值
- 获得指定索引的元素值 语法:lindex key index 127.0.0.1:6379> lindex list:1 2 "4"
- 设置指定索引的元素值 语法:lset key index value 127.0.0.1:6379> lset list:1 2 2 OK
只保留列表指定片段 指定范围和 lrange 一致 语法:ltrim key start stop
代码语言:javascript复制127.0.0.1:6379> lrange list:1 0 -1
1) "2"
2) "1"
3) "2"
4) "5"
127.0.0.1:6379> ltrim list:1 0 2
OK
127.0.0.1:6379> lrange list:1 0 -1
1) "2"
2) "1"
3) "2"
向列表中插入元素 该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。 语法:linsert key before | after pivot value
代码语言:javascript复制127.0.0.1:6379> lrange list:1 0 -1
1) "2"
2) "1"
3) "2"
127.0.0.1:6379> linsert list:1 after 1 9
(integer) 4
127.0.0.1:6379> lrange list:1 0 -1
1) "2"
2) "1"
3) "9"
4) "2"
将元素从一个列表转移到另一个列表 语法:rpoplpush source destination
代码语言:javascript复制127.0.0.1:6379> lrange list:1 0 -1
1) "2"
2) "1"
3) "9"
4) "2"
127.0.0.1:6379> rpoplpush list:1 newlist
"2"
127.0.0.1:6379> lrange newlist 0 -1
1) "2"
127.0.0.1:6379> lrange list:1 0 -1
1) "2"
2) "1"
3) "9"
应用 在Redis中创建商品评论列表 用户发布商品评论,将评论信息转成json存储到list中。 用户在页面查询评论列表,从redis中取出json数据展示到页面。
定义商品评论列表key: 商品编号为1001的商品评论key【items: comment:1001】
4.4 Set 类型
集合类型:无序、不可重复 列表类型:有序、可重复
4.4.1 命令
增加/删除元素 语法:sadd key member [member…]
代码语言:javascript复制127.0.0.1:6379> sadd set a b c
(integer) 3
127.0.0.1:6379> sadd set a
(integer) 0
语法:srem key member [member…]
代码语言:javascript复制127.0.0.1:6379> srem set c
(integer) 1
获得集合中的所有元素 语法:smembers key
代码语言:javascript复制127.0.0.1:6379> smembers set
1) "b"
2) "a"
判断元素是否在集合中 语法:sismember key member
代码语言:javascript复制127.0.0.1:6379> sismember set a
(integer) 1
127.0.0.1:6379> sismember set h
(integer) 0
4.4.2 运算命令
集合的差集运算 A-B 属于 A 并且 不属于 B 的元素构成的集合
img
语法:sdiff key [key…]
代码语言:javascript复制127.0.0.1:6379> sadd setA 1 2 3
(integer) 3
127.0.0.1:6379> sadd setB 2 3 4
(integer) 3
127.0.0.1:6379> sdiff setA setB
1) "1"
127.0.0.1:6379> sdiff setB setA
1) "4"
集合的交集运算 属于A且属于B的元素构成的集合
img
语法:sinter key [key…]
代码语言:javascript复制127.0.0.1:6379> sinter setA setB
1) "2"
2) "3"
集合的并集运算 属于 A 或者 属于 B 的元素构成的集合
img
语法:sunion key [key…]
代码语言:javascript复制127.0.0.1:6379> sunion setA setB
1) "1"
2) "2"
3) "3"
4) "4"
获得集合中元素的个数 语法:scard key
代码语言:javascript复制127.0.0.1:6379> smembers setA
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> scard setA
(integer) 3
从集合中弹出一个元素 注意:由于集合是无序的,所有spop命令会从集合中随机选择一个元素弹出。 语法:spop key
代码语言:javascript复制127.0.0.1:6379> spop setA
"2"
4.5 Sortedset 类型
Sortedset 又叫 zset Sortedset 是有序集合,可排序的,但是唯一。 Sortedset 和 set 的不同之处,会给 set 中元素添加一个分数,然后通过这个分数进行排序。
4.5.1 命令
4.5.1.1 增加元素
向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。 语法:zadd key score member [score member…]
代码语言:javascript复制127.0.0.1:6379> zadd scoreboard 80 zhangsan 89 lisi 94 wangwu
(integer) 3
127.0.0.1:6379> zadd scoreboard 97 lisi
(integer) 0
4.5.1.2 获取元素分数
语法:zscore key member
代码语言:javascript复制127.0.0.1:6379> zscore scoreboard lisi
"97"
4.5.1.3 删除元素
移除有序集key中的一个或多个成员,不存在的成员将被忽略。 当key存在但不是有序集类型时,返回一个错误。
语法:zrem key member [member…]
代码语言:javascript复制127.0.0.1:6379> zrem scoreboard lisi
(integer) 1
4.5.1.4 获得排名在某个范围的元素列表
获得排名在某个范围的元素列表
- 按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素) 语法:zrange key start stop [withscores] 127.0.0.1:6379> zrange scoreboard 0 2 1) "zhangsan" 2) "wangwu"
- 按照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素) 语法:zrevrange key start stop [withscores] 127.0.0.1:6379> zrevrange scoreboard 0 2 1) "wangwu" 2) "zhangsan" 如果需要获得元素的分数可以在命令末尾加上 withscores 参数 ··· 127.0.0.1:6379> zrevrange scoreboard 0 2 withscores
- "wangwu"
- "94"
- "zhangsan"
- "80" ···
4.5.1.5 获取元素的排名
- 从小到大 语法:zrank key member
127.0.0.1:6379> zrank scoreboard zhangsan
(integer) 0
- 从大到小 语法:zrevrank key member
127.0.0.1:6379> zrevrank scoreboard zhangsan
(integer) 1
4.5.1.6 获得指定分数范围的元素
语法:zrangebyscore key min max [withscores][limit offset count]
代码语言:javascript复制127.0.0.1:6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES
1) "wangwu"
2) "94"
3) "lisi"
4) "97"
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2
1) "wangwu"
2) "lisi"
4.5.1.7 增加某个元素的分数
返回值是更改后的分数 语法:zincrby key increment member
代码语言:javascript复制127.0.0.1:6379> ZINCRBY scoreboard 4 lisi
"101“
4.5.1.8 获得集合中元素的数量
语法:zcard key
代码语言:javascript复制127.0.0.1:6379> zcard scoreboard
(integer) 3
4.5.1.9 获得指定分数范围内的元素个数
语法:zcount key min max
代码语言:javascript复制127.0.0.1:6379> zcount scoreboard 80 90
(integer) 1
4.5.1.10 按照排名范围删除元素
语法:zremrangebyrank key start stop
代码语言:javascript复制127.0.0.1:6379> zremrangebyrank scoreboard 0 1
(integer) 2
127.0.0.1:6379> zrange scoreboard 0 -1
1) "wangwu"
4.5.1.11 按照分数范围删除元素
语法:zremrangebyscore key min max
代码语言:javascript复制127.0.0.1:6379> zadd scoreboard 84 zhangsan
(integer) 1
127.0.0.1:6379> ZREMRANGEBYSCORE scoreboard 80 100
(integer) 1
4.5.1.12 应用
4.5.1.13 商品销售排行榜
需求:根据商品销售量对商品进行排行显示 思路:定义商品销售排行榜(sorted set集合),Key为items:sellsort,分数为商品销售量。
写入商品销售量:
- 商品编号1001的销量是9,商品编号1002的销量是10 192.168.101.3:7007> ZADD items:sellsort 9 1001 10 1002
- 商品编号1001的销量加1 192.168.101.3:7001> ZINCRBY items:sellsort 1 1001
- 商品销量前10名: 192.168.101.3:7001> ZRANGE items:sellsort 0 9 withscores