Linux非关系型数据库NoSQL-Redis入门
文章目录
概述
互联网项目开发模式
互联网用户访问的数据来源
-
前台页面做静态化
-
后台关系型数据库转非关系型数据库
redis负载均衡
redis意义
NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题,redis就是属于NoSQL数据库类型
Redis安装
本篇Blog在Linux系统中演示,在Linux下的安装详见
Redis启动关闭
前端方式启动
直接运行redis/bin/redis-server
后端模式启动
修改redis.conf配置文件, daemonize yes
执行以下命令
cd /usr/local/redis
./bin/redis-server ./conf/redis.conf
redis默认使用6379端口
如需更改端口号,可在redis.conf文件中进行修改
关闭
强行终止Redis进程可能会导致redis持久化数据丢失。正确停止Redis的方式应该是向Redis服务发送SHUTDOWN命令,方法为:
cd /usr/local/redis
./bin/redis-cli shutdown save
连接Redis客户端
redis是服务端,要操作服务端(存取数据)是通过客户端
在redis的安装目录中有redis的客户端,即redis-cli(Redis Command Line Interface),它是Redis自带的基于命令行的Redis客户端
./redis.cli -h 192.168.31.128 -p 6379
发送ping
Redis提供了PING命令来测试客户端与Redis的连接是否正常,如果连接正常会收到回复PONG
set/get
使用set和get可以向redis设置数据、获取数据
Redis多数据库
redis实例
一个redis进程就是一个redis实例,一台服务器可以同时有多个redis实例,不同的redis实例提供不同的服务端口对外提供服务,每个redis实例之间互相影响。每个redis实例都包括自己的数据库,数据库中可以存储自己的数据
一个redis实例最多可提供16个数据库,下标从0到15,客户端默认连接第0号数据库,也可以通过select选择连接哪个数据库,如
注意:redis不支持修改数据库的名称,只能通过select 0、select 1…选择数据库
原则上:
不同的应用系统要使用不同的redis实例而不是使用同一个redis实例下的不同数据库
Jedis
Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。
在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。
Jedis同样也是托管在github上,地址:https://github.com/xetorthio/jedis
导包
单例连接
package com;
import redis.clients.jedis.Jedis;
class Main{
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.31.128", 6379);
jedis.set("name1", "bar");
String name = jedis.get("name1");
System.out.println(name);
jedis.close();
}
}
连接池连接
package com;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
class Main{
public static void main(String[] args) {
JedisPoolConfig config = new JedisPoolConfig();
//最大连接数
config.setMaxTotal(30);
//最大连接空闲数
config.setMaxIdle(2);
JedisPool pool = new JedisPool(config, "192.168.31.128", 6379);
Jedis jedis = null;
try {
jedis = pool.getResource();
jedis.set("name", "lisi");
String name = jedis.get("name");
System.out.println(name);
}catch(Exception ex){
ex.printStackTrace();
}finally{
if(jedis != null){
//关闭连接
jedis.close();
}
}
}
}
Redis_Hash
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
赋值与取值
HSET key field value 一次只能设置一个字段值
HGET key field 一次只能获取一个字段值
HMSET key field value [field value ...] 一次可以设置多个字段值
HMGET key field [field ...] 一次可以获取多个字段值
其他操作_通过jedis
package com;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.*;
class Main{
public static void main(String[] args) {
System.out.println("==Hash==");
Jedis jedis = new Jedis("192.168.31.128", 6379);
try {
Map<String, String> pairs = new HashMap<String, String>();
pairs.put("name", "Akshi");
pairs.put("age", "2");
pairs.put("sex", "Female");
jedis.hmset("kid", pairs);
List<String> name = jedis.hmget("kid", "name");// 结果是个泛型的LIST
System.out.println(name);
jedis.hdel("kid","age"); //删除map中的某个键值
System.out.println(jedis.hmget("kid", "pwd")); // 因为删除了,所以返回的是null
System.out.println(jedis.hlen("kid")); // 返回key为user的键中存放的值的个数
System.out.println(jedis.exists("kid"));// 是否存在key为user的记录
System.out.println(jedis.hkeys("kid"));// 返回map对象中的所有key
System.out.println(jedis.hvals("kid"));// 返回map对象中的所有value
Iterator<String> iter = jedis.hkeys("kid").iterator();
while (iter.hasNext()) {
String key = iter.next();
System.out.println(key + ":" + jedis.hmget("kid", key));
}
List<String> values = jedis.lrange("messages", 0, -1);
values = jedis.hmget("kid", new String[] { "name", "age", "sex" });
System.out.println(values);
Set<String> setValues = jedis.zrange("hackers", 0, -1);
setValues = jedis.hkeys("kid");
System.out.println(setValues);
values = jedis.hvals("kid");
System.out.println(values);
pairs = jedis.hgetAll("kid");
System.out.println(pairs);
// 清空数据
System.out.println(jedis.flushDB());
// 添加数据
jedis.hset("hashs", "entryKey", "entryValue");
jedis.hset("hashs", "entryKey1", "entryValue1");
jedis.hset("hashs", "entryKey2", "entryValue2");
// 判断某个值是否存在
System.out.println(jedis.hexists("hashs", "entryKey"));
// 获取指定的值
System.out.println(jedis.hget("hashs", "entryKey")); // 批量获取指定的值
System.out.println(jedis.hmget("hashs", "entryKey", "entryKey1"));
// 删除指定的值
System.out.println(jedis.hdel("hashs", "entryKey"));
// 为key中的域 field 的值加上增量 increment
System.out.println(jedis.hincrBy("hashs", "entryKey", 123l));
// 获取所有的keys
System.out.println(jedis.hkeys("hashs"));
// 获取所有的values
System.out.println(jedis.hvals("hashs"));
} catch (Exception e) {
e.printStackTrace();
} finally {
jedis.close();
}
}
}
Redis_List
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段
列表类型内部是使用**双向链表(double linked list)**实现的,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的
向列表两端增加元素
LPUSH key value [value ...]
RPUSH key value [value ...]
从列表两端弹出元素
LPOP key
RPOP key
LPOP命令从列表左边弹出一个元素,会分两步完成,第一步是将列表左边的元素从列表中移除,第二步是返回被移除的元素值
获取列表片段
LRANGE key start stop
LRANGE命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回start、stop之间的所有元素(包含两端的元素),索引从0开始。索引可以是负数,如:“-1”代表最后边的一个元素
其他操作,jedis实现
package com;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.*;
class Main{
public static void main(String[] args) {
System.out.println("==Hash==");
Jedis jedis = new Jedis("192.168.31.128", 6379);
try {
Map<String, String> pairs = new HashMap<String, String>();
jedis.del("messages");
jedis.rpush("messages", "Hello how are you?");
jedis.rpush("messages", "Fine thanks. I'm having fun with redis.");
jedis.rpush("messages", "I should look into this NOSQL thing ASAP");
// 再取出所有数据jedis.lrange是按范围取出,
// 第一个是key,第二个是起始位置,第三个是结束位置,jedis.llen获取长度 -1表示取得所有
List<String> values = jedis.lrange("messages", 0, -1);
System.out.println(values);
// 清空数据
System.out.println(jedis.flushDB());
// 添加数据
jedis.lpush("lists", "vector");
jedis.lpush("lists", "ArrayList");
jedis.lpush("lists", "LinkedList");
// 数组长度
System.out.println(jedis.llen("lists"));
// 排序
//System.out.println(jedis.sort("lists"));
// 字串
System.out.println(jedis.lrange("lists", 0, 3));
// 修改列表中单个值
jedis.lset("lists", 0, "hello list!");
// 获取列表指定下标的值
System.out.println(jedis.lindex("lists", 1));
// 删除列表指定下标的值
System.out.println(jedis.lrem("lists", 1, "vector"));
// 删除区间以外的数据
System.out.println(jedis.ltrim("lists", 0, 1));
// 列表出栈
System.out.println(jedis.lpop("lists"));
// 整个列表值
System.out.println(jedis.lrange("lists", 0, -1));
} catch (Exception e) {
e.printStackTrace();
} finally {
jedis.close();
}
}
}
Redis_set
在set集合中的每个元素都是不同的,且没有顺序
集合类型和列表类型的对比:
集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)
Redis还提供了多个集合之间的交集、并集、差集的运算
增加删除元素
SADD key member [member ...]
SREM key member [member ...]
获得集合中的所有元素
SMEMBERS key
Redis_设计
在互联网项目中我们需要提高数据的访问速度,关系型数据库就满足不了我们的要求,所有我们需要使用非关系型数据库来提高查询速度
将DB关系型数据库的数据同步到Redis数据库中,用户直接查询Redis‘数据库,以提高我们的访问速度
但是我们该如何同步数据库到redis中呢
示例数据模型
我们需要把关系型数据库变成键值对存储
- 球队的存储
Key的设计:在key中我们可以使用list来存储所有的球队的id
结构类型 | Key | value |
---|---|---|
list | team:team_id | 1 2 3 4… |
team:team_id : [1, 2, 3, 4….]
- 每一支球队的存储使用hash
结果类型 | Key | Key_attr | Value |
---|---|---|---|
hash | team:[team_id值] | name | 公牛 |
hash | team:[team_id值] | addr | 芝加哥 |
hash | team:[team_id值] | team_id | 101 |
team:101 : [{team_id:101},{name : 公牛},{addr:芝加哥}]
team:102 : [{team_id:102},{name : 骑士},{addr:克利夫兰}]
- 每一个雇员使用hash
结果类型 | Key | Key_attr | Value |
---|---|---|---|
hash | emp:[emp_id值] | emp_id | 1 |
hash | emp:[emp_id值] | name | 张三 |
hash | emp:[emp_id值] | birthday | 2016-12-12 |
hash | emp:[emp_id值] | gender | 101 |
hash | emp:[emp_id值] | team_id | 101 |
emp:001 : {emp_id:001, name:张三, birthday:2016-12-12, gender:1, team_id:101}
emp:002 : {emp_id:002, name:李四, birthday:2016-12-13, gender:1, team_id:101}
- 从球队方向关注与雇员的关系:是一对多的关系,多的一端如何获取多的一端:
一对多存储类型 | Key | Value |
---|---|---|
list | team:[team_id值]:emp | [001, 002,……] |
team:101:emp : [001, 002]
- 多对一关系可以直接在员工的hash结构中来获得一的一端
: {emp_id:001, name:张三, birthday:2016-12-12, gender:1, team_id:101}
emp:002 : {emp_id:002, name:李四, birthday:2016-12-13, gender:1, team_id:101}
- 从球队方向关注与雇员的关系:是一对多的关系,多的一端如何获取多的一端:
一对多存储类型 | Key | Value |
---|---|---|
list | team:[team_id值]:emp | [001, 002,……] |
team:101:emp : [001, 002]
- 多对一关系可以直接在员工的hash结构中来获得一的一端