标签:介绍 red ons obj 清理 img 启动命令 部分 可用性
本机IP地址: 192.168.126.174
说明:如果有大量的用户请求,如果频繁的读取数据库,这样的效率依然很低,一般大型公司都会采用缓存机制.当前最流行的缓存服务器Redis.
说明:如果有大量的用户请求,如果频繁的读取数据库,这样的效率依然很低,一般大型公司都会采用缓存机制.当前最流行的缓存服务器Redis.
缓存机制:缓存机制能够有效的降低用户访问物理设备的访问频次.缓存中的数据就是数据库中的数据.当数据库数据发现更新操作时,也应该及时更新缓存.保证数据的一致性.
实现缓存需要解决的问题:
Redis是一个开源(BSD许可),采用key-value结构进行数据存储,用作数据库,缓存和消息队列。它支持数据结构,如字符串,散列,列表,集合,带有范围查询的排序集,位图,超级日志,具有半径查询和流的地理空间索引。Redis具有内置复制,Lua脚本,LRU算法,事务和不同级别的磁盘持久性,并通过Redis Sentinel提供高可用性并使用Redis Cluster自动分区.
速度:读写速度平均10万/秒
http://www.redis.cn/download.html
Make:编译环境.
安装指令: make install
命令 |
说明 |
案例 |
set |
添加key-value |
set username admin |
get |
根据key获取数据 |
get username |
strlen |
获取key的长度 |
strlen key |
exists |
判断key是否存在 |
exists name 返回1存在 0不存在 |
del |
删除redis中的key |
del key |
Keys |
用于查询符合条件的key |
keys * 查询redis中全部的key keys n?me 使用占位符获取数据 keys nam* 获取nam开头的数据 |
mset |
赋值多个key-value |
mset key1 value1 key2 value2 key3 value3 |
mget |
获取多个key的值 |
mget key1 key2 |
append |
对某个key的值进行追加 |
append key value |
type |
检查某个key的类型 |
type key |
select |
切换redis数据库 |
select 0-15 redis中共有16个数据库 |
flushdb |
清空单个数据库 |
flushdb |
flushall |
清空全部数据库 |
flushall |
incr |
自动加1 |
incr key |
decr |
自动减1 |
decr key |
incrby |
指定数值添加 |
incrby 10 |
decrby |
指定数值减 |
decrby 10 |
expire |
指定key的生效时间 单位秒 |
expire key 20 key20秒后失效 |
pexpire |
指定key的失效时间 单位毫秒 |
pexpire key 2000 key 2000毫秒后失效 |
ttl |
检查key的剩余存活时间 |
ttl key |
persist |
撤销key的失效时间 |
persist key |
说明:可以用散列类型保存对象和属性值
例子:User对象{id:2,name:小明,age:19}
命令 |
说明 |
案例 |
hset |
为对象添加数据 |
hset key field value |
hget |
获取对象的属性值 |
hget key field |
hexists |
判断对象的属性是否存在 |
HEXISTS key field 1表示存在 0表示不存在 |
hdel |
删除hash中的属性 |
hdel user field [field ...] |
hgetall |
获取hash全部元素和值 |
HGETALL key |
hkyes |
获取hash中的所有字段 |
HKEYS key |
hlen |
获取hash中所有属性的数量 |
hlen key |
hmget |
获取hash里面指定字段的值 |
hmget key field [field ...] |
hmset |
为hash的多个字段设定值 |
hmset key field value [field value ...] |
hsetnx |
设置hash的一个字段,只有当这个字段不存在时有效 |
HSETNX key field value |
hstrlen |
获取hash中指定key的长度 |
HSTRLEN key field |
hvals |
获取hash的所有值 |
HVALS user |
说明:Redis中的List集合是双端循环列表,分别可以从左右两个方向插入数据.
List集合可以当做队列使用,也可以当做栈使用
队列:存入数据的方向和获取数据的方向相反
栈:存入数据的方向和获取数据的方向相同
命令 |
说明 |
案例 |
lpush |
从队列的左边入队一个或多个元素 |
LPUSH key value [value ...] |
rpush |
从队列的右边入队一个或多个元素 |
RPUSH key value [value ...] |
lpop |
从队列的左端出队一个元素 |
LPOP key |
rpop |
从队列的右端出队一个元素 |
RPOP key |
lpushx |
当队列存在时从队列的左侧入队一个元素 |
LPUSHX key value |
rpushx |
当队列存在时从队列的右侧入队一个元素 |
RPUSHx key value |
lrange |
从列表中获取指定返回的元素 |
LRANGE key start stop Lrange key 0 -1 获取全部队列的数据 |
lrem |
从存于 key 的列表里移除前 count 次出现的值为 value 的元素。 这个 count 参数通过下面几种方式影响这个操作:
|
LREM list -2 “hello” 会从存于 list 的列表里移除最后两个出现的 “hello”。 需要注意的是,如果list里没有存在key就会被当作空list处理,所以当 key 不存在的时候,这个命令会返回 0。 |
Lset |
设置 index 位置的list元素的值为 value |
LSET key index value |
说明:redis中操作可以添加事务的支持.一项任务可以由多个redis命令完成,如果有一个命令失败导致入库失败时.需要实现事务回滚.
命令 |
说明 |
案例 |
multi |
标记一个事务开始 |
127.0.0.1:6379> MULTI OK |
exec |
执行所有multi之后发的命令 |
127.0.0.1:6379> EXEC OK |
discard |
丢弃所有multi之后发的命令 |
|
条件:访问一个不存在的数据
说明:当访问一个不存在的数据时,因为缓存中没有这个key,导致缓存形同虚设.最终访问后台数据库.但是数据库中没有该数据所以返回null.
隐患:如果有人恶意频繁查询一个不存在的数据,可能会导致数据库负载高导致宕机.
总结:业务系统访问一个不存在的数据,称之为缓存穿透.
条件:当缓存key失效/过期/未命中时,高并发访问该key
说明:如果给一个key设定了失效时间,当key失效时有一万的并发请求访问这个key,这时缓存失效,所有的请求都会访问后台数据库.称之为缓存击穿.
场景:微博热点消息访问量很大,如果该缓存失效则会直接访问后台数据库,导致数据库负载过高.
前提:高并发访问,缓存命中较低或者失效时
说明:假设缓存都设定了失效时间,在同一时间内缓存大量失效.如果这时用户高并发访问.缓存命中率过低.导致全部的用户访问都会访问后台真实的数据库.
场景:在高并发条件下.缓存动态更新时
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<!--添加spring-datajar包 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
前提:关闭防火墙 service iptables stop
/**
* 1.通过IP和端口可以连接Redis
* 2.操作方法就是命令
* **/
@Test
public void StringTest() {
Jedis jedis =
new Jedis("192.168.126.174", 6379);
String result = jedis.set("1811","今天周四");
System.out.println("保存后的返回值为:"+result);
System.out.println("获取数据:"+jedis.get("1811"));
}
//操作hash
@Test
public void testHash() {
Jedis jedis = new Jedis("192.168.126.174", 6379);
jedis.hset("dog", "name","二哈");
jedis.hset("dog", "age", "7");
Map<String,String> map =
jedis.hgetAll("dog");
System.out.println(map);
}
/**操作List集合,改数据类型,不能长期保存数据,
数据最终会被消费.*/
@Test
public void testList() {
Jedis jedis = new Jedis("192.168.126.174", 6379);
jedis.rpush("list","1","2","3");
String a = jedis.rpop("list");
System.out.println(a);
}
把redis当做数据库使用时,才会使用事务.
//控制redis事务问题
@Test
public void tx() {
Jedis jedis = new Jedis("192.168.126.174", 6379);
//开启事务
Transaction transaction = jedis.multi();
try {
transaction.set("kk", "kk");
transaction.set("ww", "ww");
int a = 1/0;
transaction.exec();
} catch (Exception e) {
e.printStackTrace();
transaction.discard();
}
}
说明:应该将不变的数据,添加到缓存中.如果数据频繁的修改不适合添加缓存.
public class ObjectMapperTest {
@Test
public void objectToJSON() throws IOException {
User user = new User();
user.setId(100);
user.setName("测试");
user.setAge(10000);
user.setSex("男");
ObjectMapper objectMapper = new ObjectMapper();
String json =
objectMapper.writeValueAsString(user);
System.out.println(json);
//将json串转化为对象
User user2 =
objectMapper.readValue(json, User.class);
//get方法获取属性值. set方法为属性赋值.
System.out.println(user2.toString());
}
@SuppressWarnings("unchecked")
@Test
public void listToJSON() throws IOException {
List<User> userList = new ArrayList<User>();
User user1 = new User();
user1.setId(1);
user1.setName("杯子");
user1.setAge(19);
user1.setSex("男");
userList.add(user1);
ObjectMapper objectMapper = new ObjectMapper();
String result =
objectMapper.writeValueAsString(userList);
System.out.println(result);
//将JSON转化为List集合
List<User> uList =
objectMapper.readValue(result,ArrayList.class);
System.out.println(uList);
}
}
<!--1.创建objectMapper -->
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"></bean>
<!--2.创建Jedis对象 -->
<bean id="jedis" class="redis.clients.jedis.Jedis">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port}"/>
</bean>
public class ObjectMapperUtil {
private static ObjectMapper mapper = new ObjectMapper();
public static String toJSON(Object object) {
String json = null;
try {
json = mapper.writeValueAsString(object);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
return json;
}
@SuppressWarnings("unchecked")
public static <T> T toObject(String json,Class<?> targetClass) {
T t = null; //定义泛型对象
try {
t = (T) mapper.readValue(json,targetClass);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
return t;
}
}
#编辑后台管理服务器
server {
listen 80;
server_name manage.jt.com;
location / {
#代理请求url路径
#proxy_pass http://jtLinux;
#proxy_pass http://jt;
proxy_pass http://localhost:8091;
proxy_connect_timeout 3;
proxy_read_timeout 3;
proxy_send_timeout 3;
}
}
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/jtdb?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
/**
* 1.生成key
* 2.查询redis缓存
* null: 则调用业务层方法获取数据,
* 利用工具类封装为JSON,保存到缓存中
* !null:
* 将缓存转化为对象返回
*/
@SuppressWarnings("unchecked")
@Override
public List<EasyUITree> findCacheItemCat(Long parentId) {
String key = "ITEM_CAT_" + parentId;
String json = jedis.get(key);
List<EasyUITree> treeList = new ArrayList<>();
if(StringUtils.isEmpty(json)) {
treeList = findItemCatList(parentId);
String result =
ObjectMapperUtil.toJSON(treeList);
jedis.set(key, result);
System.out.println("查询数据库!!!!!");
}else {
treeList = ObjectMapperUtil.toObject(json,ArrayList.class);
System.out.println("查询缓存!!!!!!!");
}
return treeList;
}
说明:单台redis中使用的内存大小有限.默认的内存的大小为10M,如果使用时内存不足,如何处理.
修改内存大小:
最大的内存大小不要超过1G 512M-1024M
准备多台redis.实现内存扩容
mkdir shards
[root@localhost redis-3.2.8]# cp redis.conf shards/redis-6379.conf
[root@localhost redis-3.2.8]# cp redis.conf shards/redis-6380.conf
[root@localhost redis-3.2.8]# cp redis.conf shards/redis-6381.conf
[root@localhost shards]# vim redis-6380.conf
[root@localhost shards]# vim redis-6381.conf
[root@localhost shards]# redis-server redis-6379.conf
[root@localhost shards]# redis-server redis-6380.conf
[root@localhost shards]# redis-server redis-6381.conf
ps -ef |grep redis
@Test
public void test01() {
List<JedisShardInfo> shards =
new ArrayList<JedisShardInfo>();
JedisShardInfo info1 =
new JedisShardInfo("192.168.126.174",6379);
JedisShardInfo info2 =
new JedisShardInfo("192.168.126.174",6380);
JedisShardInfo info3 =
new JedisShardInfo("192.168.126.174",6381);
shards.add(info1);
shards.add(info2);
shards.add(info3);
ShardedJedis jedis = new ShardedJedis(shards);
jedis.set("1811","分片测试");
System.out.println(jedis.get("1811"));
//问:数据保存到了哪个redis节点??如何存储??
}
角色: node(节点) key
内存:node*n 实现内存扩容.
说明:尽可能的让节点均匀的保存数据.
问题:如果没有均衡性算法,则会导致数据负载不均.
解决方法:引入虚拟节点的概念.通过虚拟节点动态的平衡数据.
说明:当节点新增时,节点信息会动态的划分,实现数据的挂载.
原则:如果节点新增时,尽肯能的保证原有的数据保持不变!只平衡部分数据.
单调性中描述节点只能新增,不能减少,如果节点个数少了.则内存缺失.分片不能使用.
Redis高级
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
//反射???? 不加参数??? 方法名称 find
public @interface CacheAnno {
String key(); //定义key值
int index(); //参数定义位置
Class targetClass(); //定义目标类型
CACHE_TYPE cacheType() default CACHE_TYPE.FIND;
//定义泛型类型
enum CACHE_TYPE{
FIND, //定义查找
UPDATE //定义更新
}
}
@Component //交给spring容器管理
@Aspect //标识切面
public class CacheAspect {
//当执行时才注入,后期将该对象换为集群对象
@Autowired(required=false)
private Jedis jedis;
/**
* 控制用户是否查询数据库 目标方法
* @param joinPoint
* @param cacheAnno
* @return
*/
//利用环绕通知 拦截所有的缓存注解
@Around("@annotation(cacheAnno)")
public Object around(ProceedingJoinPoint joinPoint,CacheAnno cacheAnno) {
return getObject(joinPoint,cacheAnno);
}
public Object getObject(ProceedingJoinPoint joinPoint, CacheAnno cacheAnno) {
//获取参数
CACHE_TYPE cacheType = cacheAnno.cacheType();
String key = cacheAnno.key();
int index = cacheAnno.index();
Class<?> targetClass = cacheAnno.targetClass();
//根据位置获取参数
Long id = (Long) joinPoint.getArgs()[index];
//拼接参数 ITEM_CAT_0
String redisKey = key + id;
Object object = null;
switch (cacheType) {
case FIND: //表示查询缓存
object = findObject(joinPoint,redisKey,targetClass);
break;
case UPDATE: //表示更新缓存
object = updateObject(joinPoint,redisKey);
break;
}
return object;
}
private Object findObject(ProceedingJoinPoint joinPoint, String key, Class<?> targetClass) {
//检查缓存中是否有数据
String result = jedis.get(key);
Object object = null;
try {
if(StringUtils.isEmpty(result)) {
//表示缓存中没有数据,则查询数据库
object = joinPoint.proceed();
String json = ObjectMapperUtil.toJSON(object);
jedis.set(key, json);
System.out.println("AOP查询真实数据库!!");
}else {
//表示缓存中有数据,查询缓存
object = ObjectMapperUtil.toObject(result, targetClass);
System.out.println("AOP查询缓存!!");
}
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException();
}
return object;
}
private Object updateObject(ProceedingJoinPoint joinPoint, String redisKey) {
//更新缓存,删除即可
Object object = null;
try {
jedis.del(redisKey);
object = joinPoint.proceed();
System.out.println("AOP缓存删除");
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException();
}
return object;
}
}
redis.host=192.168.126.174
redis.port.a=6379
redis.port.b=6380
redis.port.c=6381
<!--value如果存储字符串/基本类型-->
<!--ref存储对象-->
<!--2.redis分片 -->
<bean id="shardJedis" class="redis.clients.jedis.ShardedJedis">
<constructor-arg name="shards">
<list>
<ref bean="info1"/>
<ref bean="info2"/>
<ref bean="info3"/>
</list>
</constructor-arg>
</bean>
<bean id="info1" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.a}"/>
</bean>
<bean id="info2" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.b}"/>
</bean>
<bean id="info3" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.c}"/>
</bean>
1.前提:数据必须同步,搭建redis主从.
2.哨兵只会监听主机的状态,通过心跳机制进行检测 ping-pong
3.当哨兵发现主机3次都没有响应时,这时认为主机宕机.内部进行推选.
4.当哨兵通过读取主机的配置文件,发现当前的主机中有2个从机.所以哨兵推选一台从机当做现在的主节点.
5.当哨兵成功推选了从机当主节点时.哨兵会修改另外节点的配置文件.重新定义主从结构.
cp -r shards sentinel
[root@localhost sentinel]# redis-server redis-6379.conf
[root@localhost sentinel]# redis-server redis-6380.conf
[root@localhost sentinel]# redis-server redis-6381.conf
命令: info replication
redis-cli -p 6380
SLAVEOF 192.168.126.174 6379
cp sentinel.conf sentinel
sentinel monitor mymaster 192.168.126.174 6379 1
mymaster: 表示主机的变量名称.
IP:端口 主机的IP和端口
2:几票同意,即选举成功!!!
redis-sentinel sentinel.conf
说明:首先将redis主机宕机.之后等待10秒后,检查哨兵是否进行推选.
如果哨兵完成推选.并且将其余的机器更新主从结构,则哨兵测试成功!
将配置文件全部删除.
/**
* 参数说明
* 1.masterName 主机的变量名称
* 2.sentinels 哨兵的信息
* String=IP:端口
*/
@Test
public void testSentinel() {
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.126.174:26379");
JedisSentinelPool pool =
new JedisSentinelPool("mymaster", sentinels);
Jedis jedis = pool.getResource();
jedis.set("aa", "abc");
System.out.println("获取redis数据:"+jedis.get("aa"));
jedis.close();
}
redis.masterName=mymaster
redis.node1=192.168.126.174:26379
<bean id="jedisSentinelPool" class="redis.clients.jedis.JedisSentinelPool">
<constructor-arg name="masterName" value="${redis.masterName}"/>
<constructor-arg name="sentinels">
<set>
<value>${redis.node1}</value>
</set>
</constructor-arg>
</bean>
@Service
public class RedisService {
//使用时注入
@Autowired(required=false)
private JedisSentinelPool pool;
//set get
public void set(String key,String value) {
Jedis jedis = pool.getResource();
jedis.set(key, value);
jedis.expire(key, 7*24*3600);
jedis.close();
}
public String get(String key) {
Jedis jedis = pool.getResource();
String result = jedis.get(key);
jedis.close();
return result;
}
}
编辑完成后,将Common打包!!
save :立即执行持久化
bgsave :开启另外的线程进行持久化.
save 900 1 #在900秒内,执行一次set操作,则持久化一次
save 300 10 #在300秒内,执行10次set操作,则持久化一次
save 60 10000 #在60秒内,执行10000次set操作,则持久化一次
3.持久化策略
# appendfsync always 每次执行set操作,则持久化一次
appendfsync everysec 每秒持久化一次.
# appendfsync no 由操作系统决定何时持久化一般默认15分钟
4.AOF文件保存的路径
说明:作为一个优秀的缓存服务器,应该定义内存的大小,同时应该进行动态的维护.如果数据只增不减,.那么redis内存很快会被占满,如果后续有set操作时,则必然会报错.
策略:
内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。
关于操作系统的内存管理,如何节省利用容量不大的内存为最多的进程提供资源,一直是研究的重要方向。而内存的虚拟存储管理,是现在最通用,最成功的方式—— 在内存有限的情况下,扩展一部分外存作为虚拟内存,真正的内存只存储当前运行时所用得到信息。这无疑极大地扩充了内存的功能,极大地提高了计算机的并发度。虚拟页式存储管理,则是将进程所需空间划分为多个页面,内存中只存放当前所需页面,其余页面放入外存的管理方式。
然而,有利就有弊,虚拟页式存储管理增加了进程所需的内存空间,却也带来了运行时间变长这一缺点:进程运行过程中,不可避免地要把在外存中存放的一些信息和内存中已有的进行交换,由于外存的低速,这一步骤所花费的时间不可忽略。因而,采取尽量好的算法以减少读取外存的次数,也是相当有意义的事情。
volatile-lru :在设定了超时时间的数据中采用LRU方式删除数据.
allkeys-lru :在所有的key中采用LRU算法删除数据.
volatile-random :在设定了超时时间的key中随机删除.
allkeys-random :所有的key随机删除.
volatile-ttl : 在设定了超时时间的key中根据ttl(显示存活的剩余时间),将马上要超时的数据提前删除.
noeviction :该配置为默认值.当内存占满时,报错返回.
说明:
Redis集群是通过节点内部进行推选,不需要依赖第三方.
搭建集群时,主机的个数是奇数个.
标签:介绍 red ons obj 清理 img 启动命令 部分 可用性
原文地址:https://www.cnblogs.com/douguihun/p/10546927.html