NoSql :全称 not only sql ,非关系型数据库。他采用简单、高效的数据格式(例如key-value等)将数据存储于内存中,极大地提高了数据的访问速度。可以作为关系型数据库的一个很好的补充但不能代替关系型数据库。
传统关系型数据库一般用来存储重要信息,应对普通的业务是没有问题的。但是,随着互联网的高速发展,传统的关系型数据库在应付超大规模,超大流量以及高并发的时候力不从心。
为了解决高并发、高可扩展(集群)、高可用(不能宕机)、大数据存储问题而产生的数据库解决方案,就是NoSql数据库。
Nosql数据库相较于传统关系型数据库,最大的优点就是存储简单、访问速度快
根据它的特点主要有以下几点应用:
缓存
分布式集群架构中的session分离
任务队列(秒杀、抢购、12306等等)
应用排行榜(SortedSet)
网站访问统计
数据过期处理(expire)
接下来的整篇文章,都在介绍如何使用。
存储方式 | 存储结构 | 存储规范 | 存储扩展 | 查询方式 | 事务 | 性能 | |
---|---|---|---|---|---|---|---|
关系 | 二维表 | 结构化数据(预先定义列结构) | 存储于表中(避免重复、结构清晰) | 纵向扩展(操作的性能瓶颈可能涉及多个表,需要通过提高计算机的性能来克服) | 结构化的查询方式(SQL) | 支持-遵循ACID规则 | 低(维护数据的一致性付出了巨大的代价) |
非关系 | 数据集(键值,文档等) | 动态结构(适应数据类型、和结构变化) | 存储于数据集中(可重复、易读写) | 横向扩展(天然分布式) | key-value查询(类似map) | 支持-遵循BASE原则 | 高(数据松散且存储于内存中) |
键值(Key-Value)存储数据库
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB。
典型应用:内容缓存,主要用于处理大量数据的高访问负载。
数据模型:一系列键值对
优势:快速查询
劣势:存储的数据缺少结构化
列存储数据库
相关产品:Cassandra, HBase, Riak
典型应用:分布式的文件系统
数据模型:以列簇式存储,将同一列数据存在一起
优势:查找速度快,可扩展性强,更容易进行分布式扩展
劣势:功能相对局限
文档型数据库
相关产品:CouchDB、MongoDB
典型应用:Web应用(与Key-Value类似,Value是结构化的)
数据模型:一系列键值对
优势:数据结构要求不严格
劣势:查询性能不高,而且缺乏统一的查询语法
图形(Graph)数据库
相关数据库:Neo4J、InfoGrid、Infinite Graph
典型应用:社交网络
数据模型:图结构
优势:利用图结构相关算法。
劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库(nosql),应用在缓存。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型有5种。
如下:
字符串类型 (String)
散列类型(hash)
列表类型(List)
集合类型(set)
有序集合类型(SortedSet)
1>下载
官网地址:http://redis.io/
2.安装c语言编译环境gcc
yum -y install gcc-c++
3.上传tar包,解压缩 tar-zxvf 目录
4.进行编译 : cd到解压目录 ,输入命令 make
5.执行安装(指定安装目录)
make install PREFIX=/usr/local/redis
服务端-前端启动:
bin目录下: ./redis-server
这种启动方式后我们就不能进行其他的操作了, Ctrl+C 即可退出。
服务端-后台启动(推荐):
第一步:把解压目录中的/redis.conf复制到安装目录/usr/local/redis/bin目录下
第二步:使用vim命令修改redis.conf配置文件 将daemonize no修改为daemonize yes
第三步:输入启动命令 (bin目录下)
./redis-server redis.conf
第四步:检查redis进程
ps -ef|grep redis
客户端的启动
1> 使用Redis-cli建立连接:
bin目录下: ./redis-cli
默认连接localhost运行在6379端口的redis服务。
完整命令如下:./redis-cli -h 192.168.25.131 -p 6379
2>使用redis的桌面程序建立连接
下载安装Redis-Deskop-manager即可,这里就不演示了。
退出客户端cli 连接
第一种:
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> quit
第二种:
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> exit
第三种:CTR+C
退出redis服务
第一种:通过连接上客户端进行关闭,使用shutdown 命令。
第二种:使用 kill 命令。
找到对应的redis的进程id 然后使用命令:(pid为进程id) kill -9 pid
命令 | 格式 |
---|---|
设置值 | set key value |
获取值 | get key |
设置获取值(返回该值的原始值) | getset key value |
删除数据(通用) | del key |
递增1 | incr key |
递增指定长度 | incrby key 递增值 |
递减1 | decr key |
递增指定长度 | decrby key 递增值 |
追加字符串(返回追加后的长度) | append key value |
查询所有key(通用) | keys * |
判断key是否存在(1,0) | exists key |
命令 | 格式 |
---|---|
设置值 [多个] | hset key field value [field1 value1 field2 valued…] |
获取值 | hget key field |
获取值 [多个] | hmget key field [field1 field2…] |
获取所有的field value | hgetall key |
删除指定field | hdel key field |
删除指定key(通用) | del key |
递增/减(num的正负) | hincr key field num |
判断field是否存在 | hexits key field |
获取key中field的个数 | hlen key |
获取key中所有的field | hkeys key |
获取key中所有的value | hvals key |
List是有顺序可重复(数据结构中的:双链表,队列)
可作为链表 ,从左添加元素 也可以从右添加元素。
命令 | 格式 |
---|---|
插入(左/右) | lpush/rpush key value[value1 value2 …] |
查看 | lrange key start end (start:从0开始,end可以为负数,表示倒数第几个,0 -1表示查看所有) |
弹出(删除) | lpop/rpop key (从左/右删除,并返回被删除的元素) |
获取个数 | llen key |
添加如果key存在 | lpushx/rpushx key value [value1 value2 …] |
删除(从哪开始删除几个value) | lrem key num value 例如:lrem key 2 1 表示从左边开始删除2个1,若num为0则表示全部删除、num为负数 表示从右开始删除] |
插入到指定位置 | lset key index value (注意从0开始) |
命令 | 格式 |
---|---|
添加 | sadd key value1 [value2 value3…] |
删除 | srem key value1 [value2 value3…] |
查看 | smembers key |
判断是否存在 | sismember key value |
差级运算 | sdiff key1 key2 (key2相对于key1的差, 结果和key1、key2顺序有关) |
交集运算 | sinter key1 key2 |
并集运算 | sunion key1 key2 |
得到元素的数量 | scard key |
随机返回指定数量成员 | srandmember key [count] (不写count默认返回一个) |
设置新集合保存差值、并集、交集 | sdiffstore/sinterstore/sunionstore key key1 key2 |
有顺序,不能重复,如果重复会覆盖,适合做排行榜
命令 | 格式 |
---|---|
添加 | zadd key 分数1 值1 分数2 值2 … |
获取分数 | zscore key value |
获取value的数量 | zcard key |
范围查找 | zrange/zrevrange key start end [withscores] (分数可选择是否显示,range从小到大,rerange 从大到小) |
删除 | zrem key value1 [value2 value3 …] |
按照排名的顺序删除 | zremrangebyrank key start end |
按照分数范围删除 | zremrangebyscore key 分数1 分数2 |
统计分数之间的value个数 | zcount key 分数1 分数2 (注意分数1>=分数2) |
添加分数 | zincrby key 分数 value |
命令 | 格式 |
---|---|
获取所有的key | keys * |
判断key是否存在 | exists key |
key重命名 | rename old new |
删除key | del key [key1 key2 …] |
设置key的过期时间 | expire key time (time : 0 负数,可以理解为立刻删除) |
查看当前key所剩时间 | ttl key (-1表示永久,-2表示不存在) |
持久化key | persist key (ttl查看时间为-1) |
查看当前key的类别 | type key |
清空当前库 | full all |
Redis中共有16个数据库,索引从0开始,默认是选择第0个
切换数据库: select index[0-15]
移动到某库:move key index
相比于传统的关系型数据库来说,Redis的事务并不突出,所以了解即可。
开启:multi
提交:exec
回滚:discard
Redis的高性能源于其数据保存于内存中,所以存取都是相当快,但是当断电时,内存中的数据便会丢失,此时我们需要将数据持久化 内存->硬盘,下次启动的时候,redis会依据持久化的信息做数据恢复。
1.RDB 快照形式 (定期将当前时刻的数据保存磁盘中)会产生一个dump.rdb文件
特点:会存在数据丢失,性能较好,数据备份。
2.AOF append only file ,日志形式 (所有对redis的操作命令记录在aof文件中),恢复数据,重新执行一遍即可。
特点:每秒保存,数据比较完整,耗费性能。
RDB:快照模式默认开启,默认会将dump.rdb文件保存在redis安装目录根目录下。
优点:
1.只包含一个文件
2.性能最大化
缺点:
1.指定间隔保存,宕机时总有来不及保存的文件。
2.如果指定间隔时间过长,保存的停顿时间很大(Reids是单线程的)
在redis.conf中配置RDB的保存时间间隔:
AOF:是采取日志记录的方式、可以每秒,每次修改,进行数据持久化。
优势:
1.数据安全性高,数据丢失概率小。
2.采用追加写入,日志过大,自动重写(瘦身)
3.格式清晰,易于修改日志文件
劣势:
文件大,持久化次数频繁、运行效率低于RDB
修改redis.conf 配置文件,开启AOF
主要是修改:
1.开启aof appendobly yes
2.appendsync 选中同步策略, always或者eyerysec
修改后需要停掉redis,重启服务。
注意:默认使用rdb方式,但是如果两都开启了,会使用aof
Jedis即:Redis的Java客户端API,使用方式也很简单
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>xxxxxxxversion>
dependency>
// string类型操作
@Test
public void string_test() {
// 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
// 添加设置字符串
jedis.set("hello", "word");
String result = jedis.get("hello");
System.out.println(result); // world
// 添加数字,自增长
jedis.set("id", "1");
String id = jedis.get("id");
System.out.println(id); // 1
System.out.println(jedis.incr("id")); // 2
System.out.println(jedis.get("id")); // 2
// 第三步:关闭Jedis
jedis.close();
}
//hash类型的操作
@Test
public void hash_test() {
// 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
// 添加map的redis的hash中
Map<String, String> map = new HashMap<>();
map.put("name", "faker");
map.put("grade", "100");
jedis.hmset("skt", map);
// 获取指定field值
System.out.println(jedis.hget("skt", "name")); // faker
// 获取个field对应的值
jedis.hmget("skt", "name", "grade").forEach(System.out::println); // faker 100
// 设置增长
System.out.println(jedis.hincrBy("skt", "grade", 20)); // 120
jedis.close();
}
// list类型的操作
@Test
public void list_test() {
// 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
// 分别从左右添加
jedis.lpush("list", "1", "2", "3");
jedis.rpush("list", "x", "y", "z");
// lrange 获取指定范围的值
jedis.lrange("list", 0, -1).forEach(System.out::println); // 3 2 1 x y z
// pop 弹出,删除指定值
System.out.println(jedis.rpop("list")); // z
// lset 在指定位设置指定值
jedis.lset("list", 0, "first");
jedis.lrange("list", 0, -1).forEach(System.out::println); // first 2 1 x y
jedis.close();
}
// set类型的操作
@Test
public void set_test() {
// 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
// sadd添加值
jedis.sadd("set", "1", "faker", "2");
// smembers获取所有值
jedis.smembers("set").forEach(System.out::println); // 2 faker 1
// srem 删除指定value
jedis.srem("set", "1");
// sismember判断指定vlaue是否存在
System.out.println(jedis.sismember("set", "1")); // false
jedis.close();
}
// zset类型的操作
@Test
public void zset_add() {
// 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
Jedis jedis = new Jedis("192.168.25.131", 6379);
// 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
Map<String, Double> zset = new HashMap<>();
zset.put("faker", 100d);
zset.put("clearlove", 99d);
zset.put("uzi", 99d);
// 向zset中插入数据
jedis.zadd("zset", zset);
// 获取zset中的所有数据
Set<Tuple> withScores = jedis.zrangeWithScores("zset", 0, -1);
withScores.forEach(e -> {
System.out.println(e.getElement() + "-------" + e.getScore());
// clearlove-------99.0 uzi-------99.0 faker-------100.0
});
// 获取指定value的分数
System.out.println(jedis.zscore("zset", "faker")); // 100.0
jedis.close();
}
通过上述代码可以看出,使用Jedis操作Reids首先要获取连接对象(Jedis),这和传统的数据库获取connection对象是一样的,Jedis也为我们提供了连接池的方式,在程序中更推荐大家使用连接池方式。
@Test
public void testJedisPool() throws Exception {
// 第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。
JedisPool jedisPool = new JedisPool("192.168.25.131", 6379);
// 第二步:从JedisPool中获得Jedis对象。
Jedis jedis = jedisPool.getResource();
// 第三步:使用Jedis操作redis服务器。
jedis.set("jedis", "test");
String result = jedis.get("jedis");
System.out.println(result); //jedispool
// 第四步:操作完毕后关闭jedis对象,连接池回收资源。
jedis.close();
// 第五步:关闭JedisPool对象。
jedisPool.close();
}
Redis和Spring的整合十分的简单,我们只需要使用Spring容器管理JedisPool就可以了
1.首先加入相关的spring、jedis依赖
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>4.3.16.RELEASEversion>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.9.0version>
dependency>
2.配置
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="192.168.25.131">constructor-arg>
<constructor-arg name="port" value="6379">constructor-arg>
bean>
3.测试
@Test
public void redis_spring_test() {
// 1.首先加载spring的配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
// 2.获取连接池对象
JedisPool pool = applicationContext.getBean(JedisPool.class);
// 3.获取Jedis对象
Jedis jedis = pool.getResource();
jedis.set("hello", "world");
System.out.println(jedis.get("hello")); // world
jedis.close();
pool.close();
}