NoSQL之redis初体验
一、NoSQL
1.1 NoSQL简介
NoSQL(Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,如:
- High performance - 对数据库高并发读写的需求
web2.0网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用动态页面静态化技术,因此数据库并发负载非常高,往往要达到每秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。其实对于普通的BBS网站,往往也存在对高并发写请求的需求,例如网站的实时统计在线用户状态,记录热门帖子的点击次数,投票计数等,因此这是一个相当普遍的需求。 - Huge Storage - 对海量数据的高效率存储和访问的需求
类似Facebook,twitter,Friendfeed这样的SNS网站,每天用户产生海量的用户动态,以Friendfeed为例,一个月就达到了2.5亿条用户动态,对于关系数据库来说,在一张2.5亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。再例如大型web网站的用户登录系统,例如腾讯,盛大,动辄数以亿计的帐号,关系数据库也很难应付。 - High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求
在基于web的架构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,数据库没有办法像web server和app server那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移。
NoSQL数据库的诞生就是为了解决大规模数据集合和多重数据种类带来的挑战,尤其是大数据应用难题。
1.2 主流NoSQL产品和分类
1.2.1 键值(Key-Value)存储数据库
-
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
-
典型应用: 内容缓存,主要用于处理大量数据的高访问负载
-
数据模型: 一系列键值对
-
优势: 快速查询
-
劣势: 存储的数据缺少结构化
1.2.2 列存储数据库
-
相关产品:Cassandra、HBase、Riak
-
典型应用:分布式的文件系统
-
数据模型:以列簇式存储,将同一列数据存在一起
-
优势:查找速度快,可扩展性强,更容易进行分布式扩展
-
劣势:功能相对局限
1.2.3 文档型数据库
-
相关产品:CouchDB、MongoDB
-
典型应用:Web应用(与Key-Value类似,Value是结构化的)
-
数据模型: 一系列键值对
-
优势:数据结构要求不严格
-
劣势: 查询性能不高,而且缺乏统一的查询语法
1.2.4 图形(Graph)数据库
-
相关数据库:Neo4J、InfoGrid、Infinite Graph
-
典型应用:社交网络
-
数据模型:图结构
-
优势:利用图结构相关算法。
-
劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案
1.3 NoSQL的特点
- 易扩展:数据之间无关系,这样就非常容易扩展,也无形之间,在架构的层面上带来了可扩展的能力
- 大数据量,高性能:NoSQL数据库都具有非常高的读写性能,尤其在大数据量下
- 灵活的数据模型:NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式
- 高可用:NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如Cassandra,HBase模型,通过复制模型也能实现高可用
二、redis
2.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的用户。
VMware公司从2010年开始赞助Redis的开发, Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全职开发Redis。
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
-
字符串类型
string
-
散列类型
hash
-
列表类型
list
-
集合类型
set
-
有序集合类型
sorted set
官方提供测试数据:50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s(数据仅供参考,根据服务器配置会有不同结果)。
2.2 redis的应用场景
-
缓存(数据查询、短连接、新闻内容、商品内容等等)
-
聊天室的在线好友列表
-
任务队列(秒杀、抢购、12306等等)
-
应用排行榜
-
网站访问统计
-
数据过期处理(可以精确到毫秒)
-
分布式集群架构中的session分离
2.3 Redis命令
2.3.1 通用命令
- 切换数据库 select 索引(redis中有默认的十六个数据库,索引0 ~ 15)
dbsize
查看key的个数flushdb
清空当前数据flushall
清空所有数据库,16个库全删(删的是数据)del key
根据key删除keys *
查看所有keytype key
查询key的类型,根据类型 就可以使用不同的apittl key
查看key的有效时间 1 永不超时; -2 没有这个数据或者这个数据已经过时了;正数: 还有多少秒过期expire key s
设置数据的过期时间 单位秒 例如:expire name 20 表示key为name的缓存20秒后过期
2.3.2 hash类型
hset key filed value
存入一个字段,一个值hmset key field value field value
存入多个字段多个值hget key filed
取出对应key的值hdel key flied
删除keyhgetall key
查询所有key
key:{fieldname:fieldvalue,fieldname:fieldvalue}
user: {username:zhangsan,gender:male,age:18}
2.3.3 list类型
lpush key value value value
左添加rpush key value value value
右添加lrange key startIndex endIndex
获取,startIndex为0时,表示从左开始开始查询;-1表示从右开始lpop key
左移除,从左弹出数据rpop key
右移除,从右弹出数据
key:[91,93,95]
2.3.4 set类型
sadd key value value value
存入smembers key
获取srem key value value
移除
和list基本一致,唯一区别就是不能重复
2.3.5 sorted set类型
zadd key socre value socre value socre value
存入zscore key member
获得指定成员的分数zrange key start end [withscores]
升序查询数据zrevrange key start end [withscores]
降序查询数据zrem key member
删除指定的成员
在set的基础上进行了一定的增强,有序不重复
三、在Java中操作redis
导入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
demo
import redis.clients.jedis.Jedis;
public class JedisTest {
public static void main(String[] args) {
//1.创建jedis对象
Jedis jedis = new Jedis("localhost",6379);
//2.调用api,存入redis
jedis.set("username","张三");
//3.调用api从redis获取
String username = jedis.get("username");
//4.输出
System.out.println(username);
//5.释放资源
jedis.close();
}
}
JedisPool Demo
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolTest {
public static void main(String[] args) {
//1.创建jedis配置对象
JedisPoolConfig config = new JedisPoolConfig();
//2.设置参数
config.setMinIdle(3);//最小空闲数
config.setMaxWaitMillis(3000); //最大等待时间
config.setMaxTotal(30);//最大连接数
//3.创建jedis连接池
JedisPool jedisPool = new JedisPool(config,"localhost",6379);
//4.获取连接
Jedis jedis = jedisPool.getResource();
//5.执行操作
System.out.println(jedis.keys("*"));
//6.释放
jedis.close();
jedisPool.close();
}
}
抽取工具类
jedis.maxTotal=30
jedis.minIdle=3
jedis.maxWaitMillis=3000
jedis.host=127.0.0.1
jedis.port=6379
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.Properties;
import java.util.ResourceBundle;
/**
* 工具类
* 目标提供jedis连接对象
*/
public class JedisUtils {
//定义池对象
private static JedisPool pool = null;
private static ResourceBundle bundle = null;
static{
/**
* ResourceBundle 专门用来操作properties
* ResourceBundle.getBundle("jedis"); 获得配置文件 (此处不需要后缀)
*/
//读取配置文件
bundle = ResourceBundle.getBundle("jedis");
int minIdle= Integer.valueOf( bundle.getString("jedis.minIdle") );
int maxWaitMillis = Integer.valueOf( bundle.getString("jedis.maxWaitMillis") );
int maxTotal = Integer.valueOf( bundle.getString("jedis.maxTotal") );
int port = Integer.valueOf( bundle.getString("jedis.port") );
String host = bundle.getString("jedis.host");
//创建配置
JedisPoolConfig config = new JedisPoolConfig();
//创建池
pool = new JedisPool( config,host ,port );
}
/**
* 提供jedis连接对象
* @return
*/
public static Jedis getJedis(){
return pool.getResource();
}
public static void main(String[] args) {
Jedis jedis = JedisUtils.getJedis();
System.out.println(jedis);
}
}
四、redis持久化
4.1 RDB持久化
该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘。
RDB优势:
- 一旦采用该方式,那么整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,可以非常容易的进行恢复
- RDB可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上
- 性能最大化,对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程中执行IO操作了
RDB劣势:
-
如果想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失
-
由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟
配置说明Snapshotting(快照)
-
save 900 1 #每900秒(15分钟)至少有1个key发生变化,则dump内存快照。
-
save 300 10 #每300秒(5分钟)至少有10个key发生变化,则dump内存快照
-
save 60 10000 #每60秒(1分钟)至少有10000个key发生变化,则dump内存快照
4.2 AOF持久化
该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据是完整的。
AOF优势:
- AOF机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失;而每修改同步,可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中,这种方式在效率上是最低的
- 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。例如,如果本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前可以通过redis-check-aof工具来帮助我们解决数据一致性的问题
- 如果日志过大,Redis可以自动启用rewrite机制,即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行,因此在进行rewrite切换时可以更好的保证数据安全性
AOF劣势:
- 对于相同数量的数据集而言,AOF文件通常要大于RDB文件
- 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效
配置AOF
重写AOF:若不满足重写条件时,可以手动重写,命令:bgrewriteaof
注意:
redis如果希望启动日志机制,需要以配置文件方式启动redis
数据恢复演示:
-
flushall操作 清空数据库
-
及时关闭redis服务器(防止dump.rdb)。 shutdown nosave
-
编辑aof文件,将日志中的flushall命令删除并重启服务即可