1. Nosql介绍

1.1. 一类新出现的数据库(not only sql),它的特点
1.1.1. 不支持SQL语法
1.1.2. 存储结构跟传统关系型数据库中的那种关系表完全不同,nosql中存储的数据都是KV形式
1.1.3. NoSQL的世界中没有一种通用的语言,每种nosql数据库都有自己的api和语法,以及擅长的业务场景
1.2. NoSQL产品种类
1.2.1. Mongodb 文档型nosql数据库,擅长做CMS系统(内容管理系统) 1.2.2. Redis 内存数据库,数据结构服务器,号称瑞士军刀(精巧),只要你有足够的想象力,它可以还给你无限惊喜 1.2.3. Hbase hadoop生态系统中原生的一种nosql数据库,重量级的分布式nosql数据库,用于海量数据的场景 1.2.4. Cassandra hadoop生态系统中原生的一种分布式nosql数据库,后起之秀 1.3. NoSQL和SQL数据库的比较
1.3.1. 适用场景不同:sql数据库适合用于关系特别复杂的数据查询场景,nosql反之
1.3.2. “事务”特性的支持:sql对事务的支持非常完善,而nosql基本不支持事务
1.3.3. 两者在不断地取长补短,呈现融合趋势

2. Redis介绍

2.1. 简述
Redis是一个高性能的kv对缓存和内存数据库(存的不像mysql那样的表)Redis的存储结构就是key-value,
Redis中的value内部可以支持各种数据结构类型,比如可以存入一个普通的string,还可以存list,set,hashmap,sortedSet(有序的set)
2.2. Redis的特性
2.2.1. Redis数据访问速度快(数据在内存中)
2.2.2. Redis有数据持久化机制(持久化机制有两种:1、定期将内存数据dump到磁盘;2、aof(append only file)持久化机制——用记日志的方式记录每一条数据更新操作,一旦出现灾难事件,可以通过日志重放来恢复整个数据库
2.2.3. Redis支持集群模式(容量可以线性扩展)
2.2.4. Redis相比其他缓存工具(ehcach/memcached),有一个鲜明的优势:支持丰富的数据结构 2.3. Redis应用场景
2.3.1. 用来做缓存(ehcache/memcached)——Redis的所有数据是放在内存中的(内存数据库)
2.3.2. 可以在某些特定应用场景下替代传统数据库——比如社交类的应用
2.3.3. 在一些大型系统中,巧妙地实现一些特定的功能:session共享、购物车
只要你有丰富的想象力,Redis可以给你无限的惊喜…….

3. Redis的安装及配置

3.1. 到官网下载最新stable版 3.2. 解压源码并进入目录 tar -zxvf redis-2.8.19.tar.gz -C ./redis-src/
3.3. make
如果报错提示缺少gcc,则安装gcc : yum install -y gcc
如果报错提示:Newer version of jemalloc required
则在make时加参数:make MALLOC=libc

3.4. 安装redis,指定安装目录,如 /usr/local/redis
make PREFIX=/usr/local/redis install

3.5. 拷贝一份配置文件到安装目录下
切换到源码目录,里面有一份配置文件 redis.conf,然后将其拷贝到安装路径下
cp redis.conf /usr/local/redis/

3.6. 启动redis
cd /usr/local/redis
bin/redis-server redis.conf
3.7. 启动为后台服务
3.7.1. 方式一
nohup bin/redis-server ./redis.conf 1>/dev/null 2>&1 &
Nohup:控制台关闭或闲置超时,也不退出
1>/dev/null : 把程序的“1”——标准输出,重定向到文件/dev/null
2>&1 : 把程序的“2”——错误输出,重定向到“1”所去的文件
& : 把程序放到后台运行
3.7.2. 方式二
修改redis.conf 配置文件 daemonize为yes
3.8. 查看后台进程
ps -ef | grep redis
3.9. 查看端口号
netstat -nltp
3.10. 连接客户端
bin/redis-cli -h mini1 -p 6379

4. Redis常用API-使用文档

5. Redis的数据功能

5.1. String 类型的数据 5.1.1. 插入和读取一条string类型的数据
set sessionid-0001 “zhangsan”
5.1.2. 对string类型数据进行增减(前提是这条数据的value可以看成数字)
set sessionid-0001 “123”
decr sessionid-0001
incr sessionid-0001
decrby sessionid-0001 10 # 修改redis中的数据,一次减少10
incrby sessionid-0001 10
5.1.3. 一次性插入或者获取多条数据
MGET sessionid-0001 sessionid-0002
5.1.4. 在插入一条string类型数据的同时为它指定一个存活期限
setex sessionid-0001 10 weige # 单位时间为s (这里出现了一个错误 - 解决方案: )
5.2. List功能演示 5.2.1. 从头部(左边)插入数据
lpush list-001 a b c
lrange list-001 0 -1
结果: c b a
5.2.2. 从尾部(右边)插入数据
rpush list-002 a b c
lrange list-001 0 -1
结果: a b c
5.2.3. 从尾部(右边)读取数据
lrange key start end
lrange key 0 -1 # 读取整个list
5.2.4. 从头部弹出一个元素
lpop list-001
c 被弹了出来,原list-001 少了一个元素
5.2.5. 从尾部弹出一个元素
rpop list-002
c 被弹了出来,原list-002 少了一个元素
5.2.6. 从一个list的尾部弹出一个元素插入到另一个list
rpoplpush list-001 list-002 # 这是一个原子性操作
5.3. List的应用案例Demo
5.3.1. 需求描述
任务调度系统:
生产者不断产生任务,放入task-queue排队
消费者不断拿出任务来处理,同时放入一个tmp-queue暂存,如果任务处理成功,则清除tmp-queue,否则,将任务弹回task-queue
5.3.2. 设计思路

5.3.3. 代码实现
生产者

public class TaskProducer {

// 获取一个redis的客户端连接对象
public static Jedis getRedisConnection(String host, int port) {

	Jedis jedis = new Jedis(host, port);

	return jedis;

}

public static void main(String[] args) {

	Jedis jedis = getRedisConnection("mini1", 6379);

	Random random = new Random();
	// 生成任务
	while (true) {

		try {
			// 生成任务的速度有一定的随机性,在1-2秒之间
			Thread.sleep(random.nextInt(1000) + 1000);
			// 生成一个任务
			String taskid = UUID.randomUUID().toString();

			// 往任务队列"task-queue"中插入,第一次插入时,"task-queue"还不存在
			//但是lpush方法会在redis库中创建一条新的list数据
			jedis.lpush("task-queue", taskid);
			System.out.println("向任务队列中插入了一个新的任务: " + taskid);

		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

}

}

消费者

public class TaskConsumer {

public static void main(String[] args) {

	Jedis jedis = new Jedis("mini1", 6379);
	Random random = new Random();

	while (true) {

		try {
			// 从task-queue中取一个任务,同时放入"tmp-queue"
			String taskid = jedis.rpoplpush("task-queue", "tmp-queue");

			// 模拟处理任务
			Thread.sleep(1000);

			// 模拟有成功又有失败的情况
			int nextInt = random.nextInt(13);
			if (nextInt % 7 == 0) { // 模拟失败的情况

				// 失败的情况下,需要将任务从"tmp-queue"弹回"task-queue"
				jedis.rpoplpush("tmp-queue", "task-queue");
				System.out.println("-------任务处理失败: " + taskid);

			} else { // 模拟成功的情况

				// 成功的情况下,将任务从"tmp-queue"清除
				jedis.rpop("tmp-queue");
				System.out.println("任务处理成功: " + taskid);

			}

		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

}

5.4. Hash功能演示
5.4.1. 往redis库中插入一条hash类型的数据
hset user001:zhangsan iphone 6
hset user001:zhangsan xiaomi 7
hset user001:zhangsan meizu 8
5.4.2. 取出一条hash类型数据中所有field-value对
hgetall user001:zhangsan
5.4.3. 取出hash数据中所有fields
hkeys user001:zhangsan
5.4.4. 取出hash数据中所有的value
hvals user001:zhangsan
5.4.5. 取出hash数据中一个指定field的值
hget user001:zhangsan xiaomi
5.4.6. 为hash数据中指定的一个field的值进行增减
hincrby user001:zhangsan xiaomi 2 # 如果减,就填负数
5.4.7. 从hash数据中删除一个字段field及其值
hdel user001:zhangsan iphone
5.5. Set 数据结构功能
特点:无序、无重复元素
5.5.1. 插入一条set数据
sadd fields:ids 1 2 3 4 5 2 3 # 结果是5个元素
5.5.2. 获取一条set数据的所有members
smembers fields:ids
5.5.3. 判断一个成员是否属于某条指定的set数据
sismember fields:ids 4 #如果不是,则返回0
sismember fields:ids 7 #如果是,则返回1
5.5.4. 求两个set数据的差集
sadd fields:counts 4 5 6 7 8
sdiff fields:ids fields:counts
sdiff fields:counts fields:ids
sdiffstore fields:rets fields:ids fields:counts # 求差集,并将结果存入到另一个set
5.5.5. 求交集,求并集
sinter fields:ids fields:counts
sunion fields:ids fields:counts
注意:同样可以把求得的结果存入到一个新的集合当中
5.6. sortedSet(有序集合)数据结构
sortedset中存储的成员都有一个附带的分数值,而redis就可以根据分数来对成员进行各种排序(正序、倒序)
5.6.1. 往redis库中插入一条sortedset数据
zadd ranking 70 liudehua 90 huangbo 100 weixiaobao 60 zhangfei
5.6.2. 从sortedset中查询有序结果
zrange ranking 0 -1
5.6.3. 查询某个成员的名次
zrank ranking zhangfei
zrevrank ranking zhangfei # 在倒序榜中的名次
5.6.4. 修改成员的分数
zincrby ranking 300 zhangfei

6. Redis应用案例(待续)

Lol盒子英雄数据排行榜:
1、在redis中需要一个榜单所对应的sortedset数据
2、玩家每选择一个英雄打一场游戏,就对sortedset数据的相应的英雄分数+1
3、Lol盒子上查看榜单时,就调用zrange来看榜单中的排序结果

7. Redis应用案例(待续)

1、每来一个用户创建购物车,就对购物车中的每一件商品在redis中插入一条以商品名称为key的sortedset,成员为同时出现在购物车中的其他商品,分数为1
2、每新产生一个购物车,就对车中的商品更新redis中的sortedset,将同时出现的商品的分数增1
3、推荐时,用户放入一件商品到购物车,则从这件商品对应的sortedset中查询分数最高的同现商品,推荐给该用户