NoSQL的概述
什么是NoSQL
- Not Only Sql
- 非关系型数据库
为什么需要NoSQL
- High performance — 高并发读写
- Huge Storage — 海量数据的高效率存储与访问
- High Scalability && High Availablity — 高可扩展和高可用
NoSQL产品
- Redis
- MongoDB
- membase
NoSQL数据库的四大分类
- 键值(K-V)存储:快速查询,数据没有结构化
- 列存储(Hbase):查找速度快,扩展强,功能局限
- 文档数据库(MongoDB):数据结构要求不严格,查询性能不是很高,缺少统一查询的语法
- 图形数据库(Neo4j,infinite):利用图结构的算法,需要的整个图做计算才能拿到结果,不利于分布式的部署
NoSQL特点
- 易扩展,非关系型
- 灵活的数据模型
- 大量数据,高性能
- 高可用
Redis
高性能键值对数据库,使用C语言开发,支持的数据类型如下:
- 字符串类型
- 列表类型
- 有序集合类型
- 散列类型
- 集合类型
读写性能:50个并发程序执行10万个请求,读速度:100000次/秒 写速度:81000次/秒
Redis应用场景
- 缓存
- 任务队列
- 网站访问统计
- 数据过期处理
- 应用排行榜
- 分布式集群架构中的session分离
redis安装
自行google或者百度
Jedis入门
什么是jedis ?
Jedis官方github,readme介绍如下:
Jedis is a blazingly small and sane Redis java client.
Jedis was conceived to be EASY to use.
Jedis is fully compatible with redis 2.8.x and 3.x.x.
Jedis是一个超级炫酷轻量和智能与一体的Redis的Java客户端,大家都觉得Jedis非常的好用,并且完全的兼容Redis2.8.x and 3.x.x.与此同时,Jedis也是Redis官方推荐使用的Redis的Java客户端。
Jedis的基本使用
@Test
/**
* jedis 单例测试
*/
public void demo1() {
// 1.设置ip地址和端口
Jedis jedis = new Jedis("127.0.0.1", 6379);
// 2.保存数据
jedis.set("name", "hello");
// 3.获取数据
String v = jedis.get("name");
System.out.println(v);
// 4.释放资源
jedis.close();
}
@Test
/**
* 使用jedis连接池
*/
public void demo2() {
//创建jedis连接池配置对象:
JedisPoolConfig config = new JedisPoolConfig();
//配置jedisPoolConfig
config.setMaxTotal(30);
config.setMaxIdle(10);
//创建jedis连接池
JedisPool jedisPool = new JedisPool(config, "127.0.0.1", 6379);
//使用连接池获取核心对象:
Jedis jedis = null;
try {
//获取对象
jedis = jedisPool.getResource();
//设置数据
jedis.set("name", "hashMap");
//获取数据
String value = jedis.get("name");
System.out.println(value);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
//释放资源
if (jedis != null) {
jedis.close();
}
if (jedisPool != null) {
jedisPool.close();
}
}
}
Redis数据结构
- 字符串(String)常用
- 哈希(hash)常用
- 字符串列表(list)
- 字符串集合(set)
- 有序字符串集合(sorted list)
key的定义注意点:
- 不要太长,占用内容大,且查找效率降低
- 不要太短,否则降低可读性
存储String
以二进制的形式进行存储操作的,意味着String的存储和获取的结果是相同的
最多容纳的value:512M
存储String的常用命令:
- 赋值
- set key value
- getset key newvalue
–> 先获取key的值然后将key对应的value设置为newvalue
- 取值
- get key
–> 获取这个key对应的value
- 删除
- del key
–> 返回一个(interger)1表示删除成功,之后get key
–> 会得到(nil)表示这个key没有value
- 数值增减
incr num
–> 如果num不存在初始化为0然后加1,num变为1
–> 如果num存在并且可以转为整形,num直接加1
–> 如果num存在但是不能转为整形,报出错误信息decr num
–> 如果num不存在初始化为0然后减1,num变为-1
–> 如果num存在并且可以转为整形,num直接减1
–> 如果num存在但是不能转为整形,报出错误信息
- 扩展命令
incrby num increment
–> 如果num不存在初始化为0然后减increment
–> 如果num存在并且可以转为整形,num直接减increment
–> 如果num存在但是不能转为整形,报出错误信息decrby num decrement
–> 如果num不存在初始化为0然后减decrement
–> 如果num存在并且可以转为整形,num直接减decrement
–> 如果num存在但是不能转为整形,报出错误信息append num value
–> 如果num不存在新创建一个num value
–> 如果num存在,以字符串形式在num后面追加一个value,得到追加之后的长度,结果是”numvalue”
存储Hash
hash类型可以看做是String key 和String value的map容器,非常适合存储值对象的信息,比如同户名,密码,年龄等…
存储hash常用的命令:
- 赋值
hset myset key value
–> 设置 myset 指定的哈希集中指定字段key的值。
–> 如果 myset 指定的哈希集不存在,会创建一个新的哈希集并与 myset 关联。
–> 如果字段在哈希集中存在,它将被重写hmset myset key value [key value …]
–> 设置 myset 指定的哈希集中指定字段的值。该命令将重写所有在哈希集中存在的字段
–> 如果 myset 指定的哈希集不存在,会创建一个新的哈希集并与 myset 关联
- 取值
hget myset key
–> 返回 myset 指定的哈希集中该key所关联的值hmget myset key [key …]
–> 设置 myset 指定的哈希集中指定key的值。该命令将重写所有在哈希集中存在的字段。
–> 如果 myset 指定的哈希集不存在,会创建一个新的哈希集并与 myset 关联hgetall myset
–> 返回 key 指定的哈希集中所有的字段和值。返回值中,每个字段名的下一个是它的值,所以返回值的长度是哈希集大小的两倍
- 删除
hdel myset key [key …]
–> 从 key 指定的哈希集中移除指定的域。在哈希集中不存在的域将被忽略
–> 如果 key 指定的哈希集不存在,它将被认为是一个空的哈希集,该命令将返回0del myset
–> 直接删除整个哈希集
- 增加数字
- hincreby myhash key increment
增加 myhash 指定的哈希集中指定字段的数值。如果 myhash不存在,会创建一个新的哈希集并与 myhash 关联。如果字段不存在,则字段的值在该操作执行前被设置为 0
HINCRBY 支持的值的范围限定在 64位 有符号整数
- 自学命令
hexists myhash key
判断key是否在myhash中存在。存在返回1,否则返回0hlen myhash
返回 myhash 指定的哈希集包含的字段的数量。hkeys myhash
返回 myhash 指定的哈希集中所有字段的key。hvals myhash
返回 myhash 指定的哈希集中所有字段的value。
存储list:
- ArrayList使用数字方式存储
- LinkedList使用双向链表方式存储
- 双向链表中增加数据
- 双向链表中删除数据
存储list常用的命令:
- 两端添加
- lpush
- rpush
- 查看链表
- lrange
- 两端弹出
- lpop
- rpop
- 获取列表元素个数
- llen
- 扩展命令
- lpushx
- rpushx
- lrem
- lset
- linsert
- rpoplpush –> 消息队列
存储Set
- 和list类型不同的是,Set集合中不允许出现重复的元素
- Set可以包含的元素数量是4294967295
存储Set常用的命令:
- 添加删除元素
- sadd myset 1 2 3
- srem myset 2
- 获取集合中的元素
- smembers myset –> 获取所有元素
- sismember myset 3 -> 判断某个元素时候属于集合
- 集合中的差集运算
- sdiff
- 集合中的并集运算
- sunion
- 集合中的交集运算
- sinter
- 扩展命令
- scard -> 获取元素个数
- srandmember myset -> 随机返回myset中的某个成员
- sdiffstore my1 my2 my3-> 把my2 my3差集存在my1中
- sinterstore my1 my2 my3 -> 把my2 my3 交集存在my1中
- sunionstore my1 my2 my3 -> 把my2 my3 并集存入my1中
存储Set使用的场景
- 跟踪一些唯一性数据
- 用于维护数据对象之间的关联关系,可以做一些并集交集的运算
存储Sorted-Set
- 与Set的区别:
- Sorted-Set成员有序,时间复杂度是元素个数的对数O(logn)
- Sorted-Set中的成员在集合中的位置是有序的
存储Sorted-Set常用的命令:
- 添加元素
- zadd mysort 50 funi 60 sorw
- 删除元素
- zrem mysort funi sorw -> 删除sorted-set中的funi sorw记录
- 获取元素
- zscore mysort funi -> 获取具体元素的分数
- zcard mysort -> 获取mysort的元素个数
- 范围查询
- zrange mysort 0 -1 -> 显示所有元素(不显示分数)
- zrange mysort 0 -1 withscores -> 显示所有元素并显示分数,由小到大排序
- zrevrange mysort 0 -1 withscores -> 显示所有元素并显示分数,由大到小排序
- zremrangebyrank mysort 0 4 -> 按照下标顺序删除元素
- zremrangebyscore mysort 80 100 -> 按照分数的范围删除元素
- 扩展命令
- zrangebyscore mysort 0 100 [withscores][limit 0 2]-> 显示分数为0到100并按照从小到大排序显示,withscore显示分数,limit显示从第0个到第2个
- zincrby mysort 3 li -> 给li的分数加3
- zcount mysort 80 90 -> 显示分数是80到90的元素个数
Sorted-Set使用场景
- 大型在线游戏积分排行榜
先使用zadd添加,然后zrange获取 - 构建索引数据
Keys的通用操作
- keys * -> 获取所有的key
- del -> 删除key
- exists -> 判断是否存在
- get key -> 获取这个key的value
- rename company newcompany -> 重命名company
- expire newcompany 1000 -> 设置1000s的过期时间
- ttl newcompany -> 过期时间剩余
- type key -> 获取这个key的数据类型
Redis特性:
- 多数据库
- 一个redis实例最多可以提供十六个数据库,就像是mysql的数据库一样,客户端可以指定连接那个数据库,redis的数据库下标是从0 – 15。
- 可以使用select 1 -> 来选择几号数据库。默认使用的是0号数据库
- 可以使用move myset 1 -> 将myset移动到1号数据库
- 支持事务
Redis使用multi exec discard来执行事务,事务中所有的命令都会被串行化执行,事务执行期间,redis不会为其他客户端提供服务,保证事务的原子性。与关系型数据库不同的是,redis事务中,如果某个命令执行失败,事务不会终止,而是继续执行后面的命令
- 可以使用multi 来开启是一个事务 相当于start transaction
- 通过exec相当于提交 相当于commit
- 通过discard相当于回滚 相当于roolback
Redis的持久化
- Redis的高效率源于Redis把数据都存储在内存当中,为了保证Redis在服务重启之后数据不丢失,就需要把Redis中的数据进行持久化(数据从内存同步到硬盘)。
持久化方式:
- RDB方式
- AOF方式
持久化使用的方式:
RDB方式
默认支持的,不需要设置。RDB方式就是在默认的时间间隔之后,将内存中的数据集快照写入到磁盘
AOF方式:
以日志的方式记录服务器所处理的每一个操作,在Redis服务器启动之初,会读取这个文件,然后重新构建,保证数据是完整的
无持久化:
通过配置禁用Redis持久化功能,这个时候Redis就是一个缓存的功能
同时使用RDB和AOF的方式:’
数据集快照+日志
Redis持久化的RDB方式
优势:
- 只有一个文件,对于文件备份而言很简单。
- 灾难恢复,RDB可以压缩文件之后转移个另一个服务器上
- 性能最大化,分叉出一些子进程,然后使用子进程来做持久化操作,可以极大的避免IO的影响
- 相比于AOF如果数据集很大,启动效率更高
劣势:
- 如果想保证数据的高可用性,是数据尽可能的不丢失RDB不是很好的选择,因为如果在系统定时持久化之前宕机,还没有来得及写数据,就会导致数据的丢失
- 由于RDB使用fork分叉来使用子进程完成数据的持久化操作,因此如果数据集很大,会导致整个服务器停止几百毫秒来等待持久化操作的完成。
配置:redis.conf
save 900 1 //表示每900s有一个key发生变化就会之久化一次
save 300 10 //每300s有10个key发生变化,会往硬盘上写一次
save 60 10000 //每60s有10000个key发生变化会写一次
保存在./dump.rdb
Redis持久化的AOF方式
优势:
- 更高的数据安全性。redis中提供了三种同步策略:每秒同步,每修改同步,和不同步;
- 对日志写入的过程是append追加的模式。因此就算宕机,也不会破坏已经写入的日志。如果写入一半的时候出现问题,也可以使用redis-cheak-aof这个工具来帮助我们解决数据一致性的问题
- 如果日志过大,redis会自动启动数据重写机制,以append的模式不断的写入老的磁盘文件当中,同时redis还会产生一个文件来记录此期间产生的修改命令。因此重写切换的时候还是可以保证数据的安全性。
- AOF包含的是格式清晰的日志文件,用于记录所有的修改操作。甚至可以通过这个文件来重建数据
劣势:
- AOF的文件比RDB的文件更大
- 同步策略的不同导致AOF运行的效率低于RDB
配置:redis.conf
appendonly no //默认不启动AOF
appendonly.aof //AOF文件
//同步方式:
# appendfsync always //每修改同步
# appendfsync everysec //每秒同步
# appendfsync no //不同步
如果要使用AOF方式,应该首先修改第1行的appendonly 为yes;然后appendfsync为always保证数据的安全
参考:
https://github.com/xetorthio/jedis
https://redis.io/
https://redis.io/documentation
http://www.redis.cn/commands.html
https://www.imooc.com/learn/839