NoSQL数据库

NoSQL,泛指非关系型的数据库,它可以作为关系型数据库的良好补充。随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。

NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。noSql数据库说白了就是不能使用sql了,不是关系型数据库,解决高并发的问题。因为像mysql或oracle单机能够承受的并发数最多也就几千

主流NoSQL

NoSQL数据库的四大分类如下:

  • 键值(Key-Value)存储数据库(map)
  • 相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
    典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
    数据模型: 一系列键值对
    优势: 快速查询
    劣势: 存储的数据缺少结构化,需要自己设计
  • 列存储数据库
  • 相关产品:Cassandra, HBase, Riak
    典型应用:大数据下分布式的文件系统
    数据模型:以列簇式存储,将同一列数据存在一起
    优势:查找速度快,可扩展性强,更容易进行分布式扩展
    劣势:功能相对局限
  • 文档型数据库
  • 相关产品:CouchDB、MongoDB
    典型应用:Web应用(与Key-Value类似,Value是结构化的 bson) user1001 {“username”:”admin”,”age”:12}
    数据模型: 一系列键值对
    优势:数据结构要求不严格
    劣势: 查询性能不高,而且缺乏统一的查询语法
  • 图形(Graph)数据库
  • 相关数据库:Neo4J、InfoGrid、Infinite Graph
    典型应用:社交网络
    数据模型:图结构
    优势:利用图结构相关算法。
    劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。

什么是redis

Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:

  • 字符串类型 string
  • 散列类型 hash
  • 列表类型 list
  • 集合类型 set
  • 有序集合类型 sortedSet。

redis应用场景

  • 内容缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
  • 分布式集群架构中的session分离。
  • 聊天室的在线好友列表。
  • 任务队列。(秒杀、抢购、12306等等) 100件iphonex免费赠送,
  • 应用排行榜。
  • 网站访问统计。
  • 数据过期处理(可以精确到毫秒)

redis安装步骤

1:wget 在线下载tar包或者scp一份到centos7环境
2:tar -zxvf redis-4.0.9.tar.gz
3:cd redis-4.0.9
4:make
5:make PREFIX=/usr/app/redis install
6:cp /usr/app/redis-4.0.9/redis.conf /usr/app/redis/bin/
7: cd /usr/app/redis/bin
9:./redis-server redis.conf
10: ./redis-cli -h 127.0.0.1 -p 6379
11:set test libai
12:get test
13:del test

redis的三种连接方式
1:./redis-cli -h ip地址 -p 端口号
关闭redis 服务器
通过客户端发送shutdown的指令
2:通过可视化工具 进行连接(注意:远程连接需要修改redis.conf的配置)
1:将 bind 127.0.0.1 注释掉
2:将 protected-mode 改成no
修改数据库数量 databases 16 改成1
可以设置redis的访问密码 requirepass 密码
daemonize yes 修改成后台运行
3:通过java客户端(java端大多都使用jedis jar包
)也可以连接redis数据库

redis需要单机多开么?
redis是单线程的key value数据库,多开可以合理利用redis资源(redis集群的时候)

redis数据类型与命令

1.string

redis中没有使用C语言的字符串表示,而是自定义一个数据结构叫SDS(simple dynamic string)即简单动态字符串

//赋值与取值  set key value  get key value
set test 1234
get test 
Jedis jedis = new Jedis("192.168.192.3",6379);
jedis.set("test","123456");
System.out.println(jedis.get("test"));

//递增数字incr key(如果存的是数字) 
incr test 
jedis.incr("test");
//增加指定的整数incrby key number
incrby test 2
jedis.incrBy("test",2);
//减少整数decr key   
decr test 
jedis.decr("test");
//减少指定的数字decrby key  number
decrby key test 2    
jedis.decrBy("test",2);

//向尾部追加值append key value
append test "world"
jedis.set("test","hello");
jedis.append("test"," world!");
System.out.println(jedis.get("test"));

//获取字符串长度strlen key
strlen test
System.out.println(jedis.strlen("test"));

//同时设置/获取多个键值 mset key value key value  mget key key key
jedis.mset("1","1","2","2","3","3");
System.out.println(jedis.mget("1","2","3"));

//自增主键(自己拼接)
set its:id 1
its:1  2
its:2  2
String key=”its:”+request.getParameter(“id”);
String value=jedis.get(key);

2.hash

  • Map<String,HashMap<String,String>>

使用String出现的问题

User:1 {“id”:1,“username”:zhangsan,”age”:20}

更新id为1的用户,年龄更改为22

User对象---JSON(string)----redis

如果只修改age,不修改其他属性会造成资源的浪费

  • hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。

User:1 username zhangsan

Password 123456

Age 23

//hset 一次设置一个值     hget一次获取一个值
//hmset 一次设置多个值		hmget一次获取多个值
//赋值与取值 hmset key key1 value key2 value  hmget key key1 key2
hmset a name zhangsan age 20
hmget a name age
Map map =  new HashMap();
map.put("name","zhangsan");
map.put("age","20");
jedis.hmset("a",map);
System.out.println(jedis.hmget("a","name","age"));
//获取所有属性 hgetall key
System.out.println(jedis.hgetAll("a"));

//判断字段是否存在 hexists key key1
hexists a age
System.out.println(jedis.hexists("a","age"));

//当字段不存在时赋值 hsetnx key key1 value1
hsetnx a age 22
jedis.hsetnx("a","age","22");
System.out.println(jedis.hget("a","age"));

//增加数字hincrby key key1 number
hincrby a age 2
hget a age
jedis.hincrBy("a","age",2);
System.out.println(jedis.hget("a","age"));

//删除字段 删除一个或多个,返回值是被删除的字段个数
//hdel key key1 key2
jedis.hdel("a","name");

//只获取字段名或字段值hkeys key  hvals key
hkeys a 
hvals a
System.out.println(jedis.hkeys("a"));
System.out.println(jedis.hvals("a"));
//获取字段数量hlen key
hlen a
System.out.println(jedis.hlen("a"));
  • 应用 存放商品信息

3.list

  • ArrayList与linkedList的区别
  • ArrayList使用数组方式存储数据,开辟存储空间时,需要连续的存储空间,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
  • LinkedList使用双向链接方式存储数据,不需要连续的存储空间,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。
//向列表两端增加元素lpush key value1 value2   rpush key value1 value2
jedis.lpush("b","1","2","3");
jedis.rpush("b","1","2","3");
//获取列表片段 lrange list startindex endindex
System.out.println(jedis.lrange("b",0,-1));

//从列表两端弹出元素lpop key     rpop key
jedis.lpop("b");
jedis.rpop("b");
System.out.println(jedis.lrange("b",0,-1));

//获取列表中元素的个数 llen key
System.out.println(jedis.llen("b"));

//删除列表中指定的值 krem key count value
//当count>0时, LREM会从列表左边开始删除。 
//当count<0时, LREM会从列表后边开始删除。 
//当count=0时, LREM删除所有值为value的元素。
jedis.lrem("b",2,"2");
System.out.println(jedis.lrange("b",0,-1));

//获取/设置指定索引的元素值lindex key index lset key index value
System.out.println(jedis.lindex("b",2));
jedis.lset("b",2,"3");

//只保留列表指定片段,指定范围和lrange一致 ltrim key start stop


//向列表中插入元素 linsert key before|after pivot value
该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面

    
//将元素从一个列表转移到另一个列表中 rpoplpush list newlist
  • 应用商品评论列表

还可以实现抢购秒杀的场景:充当一个队列(先进先出) lpush rpop

还可以做用户的商品评论.

思路:

在redis中创建商品评论列表

用户发布商品评论,将评论信息转成json存储到list中。

用户在页面查询评论列表,从redis中取出json数据展示到页面。

Items:商品id hash类型

Items:comments:商品id '{"id":1,"name":"商品不错,很好!!","date":1430295077289}'

'{"id":2,"name":"垃圾!!","date":1430295077289}'

定义商品评论列表key:

商品编号为1001的商品评论key:items: comment:1001

192.168.101.3:7001> LPUSH items:comment:1001 '{"id":1,"name":"商品不错,很好!!","date":1430295077289}'

4.set

Java当中的set是一个接口,无序不可重复

集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)。

Redis还提供了多个集合之间的交集、并集、差集的运算。

//增加删除元素 sadd key member member

//获得集合中所有元素 smember key

//判断元素是否在集合中 sismember key member

5.sorted set Zset

在集合类型的基础上有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。

在某些方面有序集合和列表类型有些相似。

1、二者都是有序的。

2、二者都可以获得某一范围的元素。

但是,二者有着很大区别:

1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。

2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。

3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)

4、有序集合要比列表类型更耗内存。

//增加元素向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。zadd key score member

//获取元素的分数zscore key member

//获得排名在某个范围的元素列表 zrange key start stop withscores