一只程序猿的随笔
JAVA小世界
spring的IOC
三级缓存:
循环依赖使用三级缓存完成过程
IOC(控制反转):组件类(比如service实现类)的依赖管理不是由组件类自身实现,而是由spring容器去管理,控制反转IoC是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:依赖注入和依赖查找,spring主要使用注入依赖。
DI(依赖注入):组件定义各自的依赖项,并且组件在创建期间,由spring容器将这些依赖联系起来。分为接口注入(Interface Injection),Setter方法注入(Setter Injection)和构造器注入(Constructor Injection)三种方式,其中接口注入由于在灵活性和易用性比较差,现在从Spring4开始已被废弃。
循环依赖问题:spring容器对单例模式用set方式注入,是可以解决循环依赖的,但是构造方法注入或者property类别是无法解决的。
依赖查找(Dependency Lookup): 依赖查找也有两种类型:依赖拖拽(DP)和上下文化依赖查找(CDL)。
上下文依赖查找(Contextualized Dependency Lookup)
在某些方面跟依赖拖拽类似,但是上下文依赖查找中,查找的过程是在容器管理的资源中进行的,而不是从集中注册表中,并且通常是作用在某些设置点上。
依赖拖拽 (Dependency Pull)
依赖拖拽:注入的对象如何与组件发生联系,这个过程就是通过依赖拖拽实现 。(较少有使用)而通常对注入对象的配置可以通过一个 xml 文件完成。依赖拖拽就是通过这种方式对对象进行集中管理。
Bean作用域
Bean生命周期
产生以下问题待编写
1.setter注入和构造器注入的区别:
答:
setter注入是类中写一个set方法,spring容器注入时会把实例通过set方法注入,而autowired注解默认就是setter方式注入。
构造器注入就是根据构造方法的参数类型,把实例注入到这个类里面。在构建期间就创建了一个完整的实例。
2.依赖注入的原理:
答:
依赖注入是beanFactory生产bean时为了解决bean之间的依赖的一种技术,一般都不直接用BeanFactory,而是用它的实现类ApplicationContext,这个类会自动解析我们配置的applicationContext.xml,或者注解配置的bean,然后根据我们配置的bean来new对象,将new好的对象放进一个Map中,键就是我们bean的id,值就是new的对象。
3.springAOP4和5的区别:
4.spring初始化过程
springBoot
SQL知识
1.索引的数据结构是B+树结构
1.1 慢查询排查
table:代表表名
type:索引类型,好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL
1.2 索引失效
1.查询时联合索引要满足最佳左侧法则
2.索引字段使用计算,函数,类型转换(包含隐式转换,比如name=‘123’ ,写成name=123)
3.范围查询时,如果记录百分比太高会失效
4.使用不等于(!=、<>)
5.like匹配以%开头
6.or前后存在非索引列
7.数据库与表字符集不一致,导致不同字符集比较进行类型转换
1.3 优化慢查询
查询是否开启慢SQL日志: show variables like ‘slow_query_log%’
1.explain查看分析SQL的执行计划
2.避免索引失效场景
3.优化SQL查询逻辑,比如分段查询,拆分SQL等,先缩小查询范围
4.分库分表
2.事务的四个隔离级别
未提交读
已提交读
可重复读
可序列化
3.事务的特性
原子性 (atomicity):强调事务的不可分割,要么一起成功,要么一起失败。
一致性 (consistency):事务的执行时,前后数据的完整性保持一致。
隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰。
持久性(durability) :事务一旦结束,数据就持久到数据库。
4.事务的传播特性
多个事物存在是怎么处理的策略
1)PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
2)PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务,如果当前没有事务,就以非事务方式执行。
3)PROPAGATION_MANDATORY:如果存在一个事务,支持当前事务,如果当前没有事务,就抛出异常。
4)PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
5)PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6)PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7)PROPAGATION_NESTED:支持当前事务,新增 Savepoint 点,与当前事务同步提交或回滚。
5.分布式事务
1.CAP:
C:Consistency 强一致性
A:Availability 可用性
P:Partition tolerance 分区容错性
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,
最多只能同时较好的满足两个。
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。------传统oracle数据库
CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。--------------redis,MongoDB
AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。------------大多数网站架构
事务并发问题:
1.脏读:事务A读取了事务B回滚前的数据。
2.不可重复读:事务A多次读同一数据过程中,事务B对数据做了修改,导致事务A多次读取结果不一致。(在一个事务中多次读取同一个数据时,结果出现不一致。)
3.幻读:事务A修改一批记录的过程中,事务B增删了一条记录,事务A发现有记录没修改,像幻觉。(在一个事务中使用相同的 SQL 两次读取,第二次读取到了其他事务新插入的行。)
2PC
2PC 是一种尽量保证强一致性的分布式事务,因此它是同步阻塞的,而同步阻塞就导致长久的资源锁定问题,总体而言效率低,并且存在单点故障问题,在极端条件下存在数据不一致的风险。
3PC
3PC 的出现是为了解决 2PC 的一些问题,相比于 2PC 它在参与者中也引入了超时机制,并且新增了一个阶段使得参与者可以利用这一个阶段统一各自的状态。
TCC
本地消息表
利用了 各系统本地的事务来实现分布式事务,将业务的执行和将消息放入消息表中的操作放在同一个事务中.
如果调用失败也没事,会有 后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。
消息事务
RocketMQ 就很好的支持了消息事务,让我们来看一下如何通过消息实现事务。
第一步先给 Broker 发送事务消息即半消息,半消息不是说一半消息,而是这个消息对消费者来说不可见,然后发送成功后发送方再执行本地事务。
再根据本地事务的结果向 Broker 发送 Commit 或者 RollBack 命令。
并且 RocketMQ 的发送方会提供一个反查事务状态接口,如果一段时间内半消息没有收到任何操作请求,那么 Broker 会通过反查接口得知发送方事务是否执行成功,然后执行 Commit 或者 RollBack 命令。
如果是 Commit 那么订阅方就能收到这条消息,然后再做对应的操作,做完了之后再消费这条消息即可。
如果是 RollBack 那么订阅方收不到这条消息,等于事务就没执行过。
可以看到通过 RocketMQ 还是比较容易实现的,RocketMQ 提供了事务消息的功能,我们只需要定义好事务反查接口即可。
最大努力通知
其实我觉得本地消息表也可以算最大努力,事务消息也可以算最大努力。
就本地消息表来说会有后台任务定时去查看未完成的消息,然后去调用对应的服务,当一个消息多次调用都失败的时候可以记录下然后引入人工,或者直接舍弃。这其实算是最大努力了。
事务消息也是一样,当半消息被commit了之后确实就是普通消息了,如果订阅者一直不消费或者消费不了则会一直重试,到最后进入死信队列。其实这也算最大努力。
所以最大努力通知其实只是表明了一种柔性事务的思想:我已经尽力我最大的努力想达成事务的最终一致了。
适用于对时间不敏感的业务,例如短信通知。
线程安全问题
1、常见问题
JMM:java内存模型
1.可见性
2.原子性
3.有序性:禁止指令重排
1.volatile关键字保证可见性和有序性,不保证原子性,所以是线程不安全的。
2.AtomicInteger(原子更新整型):线程安全的,原理是底层使用unsafe类的cas方法(CPU并发原语,执行过程不允许被中断,因此不存在数据不一致的问题),并且操作的时候使用的是自旋锁,如下图。
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
3.AtomicStampedReference时间戳原子引用,能够解决ABA(狸猫换太子)问题,因为加上了版本号。
4.Unsafe的含义
在上面我们主要是讲解了CAS的含义,CAS修饰在Unsafe上面。那这个Unsafe是什么意思呢?
Unsafe是位于sun.misc包下的一个类,Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。
这里说一句题外话,在jdk1.9中,对Usafe进行了删除,所以因为这,那些基于Usafe开发的框架慢慢的都死掉了。
在这里也就是说,Usafe再进行getAndAddInt的时候,首先是先加1,然后对底层对象的地址做出了更改。
5.ArrayList底层是Object数组,是线程不安全的,ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常。
解决方法:1.使用Vector:因为底层加了synchronized方法
2.使用Collections.synchronizedList(new ArrayList)
3.使用CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet。
6.HashSet底层是hashMap,线程不安全的:
解决方法:
1.使用Collections.synchronizedList(new HashSet)
2.使用CopyOnWriteArraySet.
7:hashMap是线程不安全的
解决方法:1.HashTablex线程安全的,但是策略实现代价却太大了,简单粗暴,get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁
2.ConcurrentHashMap采用了非常精妙的"分段锁"策略,ConcurrentHashMap的主干是个Segment数组。
2.锁
synchronized和lock的区别
1.原始构成.
synchronize是关键字属于JVM层面.
monitorenter(底层是通过monitor对象来完成,其实wait/notify等方法也依赖与monitor对象,只有在同步块或方法中才能调用wait/notify等方法.
monitorexit.
lock视具体的类(java.util.concurrent.locks.Lock) 是api层面的锁. jdk1.5
1.公平锁和非公平锁:synchronized和lock默认是非公平锁,但是lock初始的时候new ReentrantLock(true)便是公平锁;
2.可重入锁(递归锁):同一线程获得外层函数的锁,那么同时也获得了内层函数的锁,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
3.自旋锁: cas中的比较并交换,
4.独占锁:写锁
5.共享锁:读锁
6.互斥锁:
7.ReentrantReadWriteLock:读写锁,读写分离,写的时候会阻塞读。
8.countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。(做减法,秦等待一统六国)
9.CyclicBarrier:(做加法,人到齐开会)CyclicBarrier cyclicBarrier = new CyclicBarrier(7, ()->{System.out.println(“召唤神龙”);}) ;
10.Semaphore : 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。(争车位,多辆车,抢多个车位)
3.阻塞队列
BlockQueue
4.线程池
1.生产者消费者模式,新旧版对比
注意:Thread不能start两次,因为start调用native方法,一个线程只有一个状态,两次调用会提示异常,源码如下
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();//异常处
。。。
}
注意:高并发下的判断要用while循环判断,如果用if可能有虚假唤醒。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
*
* @author Administrator
* 线程a打印5次,通知线程b打印10次,再通知线程c打印15次
* 循环10次
*/
class KongTiao {
private int number = 1;
private Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
public void print5() {
lock.lock();
try {
while(number!=1) {
c1.await();
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+"打印"+i+"次");
}
number = 2;
c2.signal();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
public void print10() {
lock.lock();
try {
while(number!=2) {
c2.await();
}
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"打印"+i+"次");
}
number = 3;
c3.signal();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
public void print15() {
lock.lock();
try {
while(number!=3) {
c3.await();
}
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName()+"打印"+i+"次");
}
number = 1;
c1.signal();
} catch (Exception e) {
// TODO: handle exception
}finally {
lock.unlock();
}
}
}
public class LockCondition {
public static void main(String[] args) {
KongTiao kongTiao = new KongTiao();
for (int i = 1; i <=10; i++) {
final int tmp = i;
new Thread(()->{
try {
kongTiao.print5();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
}
},"AA").start();
}
for (int i = 1; i <=10; i++) {
final int tmp = i;
new Thread(()->{
try {
kongTiao.print10();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
}
},"BB").start();
}
for (int i = 1; i <=10; i++) {
final int tmp = i;
new Thread(()->{
try {
kongTiao.print15();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
}
},"CC").start();
}
}
}
AA打印1次
AA打印2次
AA打印3次
AA打印4次
AA打印5次
BB打印1次
BB打印2次
BB打印3次
BB打印4次
BB打印5次
BB打印6次
BB打印7次
BB打印8次
BB打印9次
BB打印10次
CC打印1次
CC打印2次
CC打印3次
CC打印4次
CC打印5次
CC打印6次
CC打印7次
CC打印8次
CC打印9次
CC打印10次
CC打印11次
CC打印12次
CC打印13次
CC打印14次
CC打印15次
AA打印1次
AA打印2次
AA打印3次
AA打印4次
AA打印5次
BB打印1次
BB打印2次
BB打印3次
BB打印4次
BB打印5次
BB打印6次
BB打印7次
BB打印8次
BB打印9次
BB打印10次
CC打印1次
CC打印2次
CC打印3次
CC打印4次
CC打印5次
CC打印6次
CC打印7次
CC打印8次
CC打印9次
CC打印10次
CC打印11次
CC打印12次
CC打印13次
CC打印14次
CC打印15次
。。。。。。
5.线程池实际使用
底层原理,线程池底层的类 ThreadPoolExecutor pool
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
Executors.defaultThreadFactory(),
defaultHandler
);
}
实现方式:
/**执行长期的任务,性能好很多**/
ExecutorService executor01 = Executors.newFixedThreadPool(5);//一池固定线程数
/**一个任务一个任务的执行场景**/
ExecutorService executor01 = Executors.newSingleThreadExecutor();//一池1线程
/**执行很多短期异步的小程序,或负载较轻的服务器**/
ExecutorService executor01 = Executors.newCachedThreadPool();//一池多线程
线程工作原理:
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
怎么配置合理的最大线程数:
1.cpu密集型:cup最大核数+1;
Runtime.getRuntime().availableProcessors();//获取cpu核数
2.io密集型
1.由于IO密集型任务并不是一直执行,所以应配置尽可能多的线程,比如 cpu核数*2;
2.cpu核数/(1-阻塞系数),如下图:
package com.luoyi.cn.javaBase.thread;
import java.util.concurrent.*;
/**
* @author luoyi
* @create 2021/5/1-17:36
* 线程池,第四种获取线程的方式
* 线程池底层的类 ThreadPoolExecutor pool
**/
public class ThreadPool02 {
/**执行长期的任务,性能好很多**/
//static ExecutorService executor01 = Executors.newFixedThreadPool(10000);//一池固定线程数
/**一个任务一个任务的执行场景**/
//static ExecutorService executor01 = Executors.newSingleThreadExecutor();//一池1线程
/**执行很多短期异步的小程序,或负载较轻的服务器**/
// static ExecutorService executor01 = Executors.newCachedThreadPool();//一池多线程
/**一般使用自定义,因为队列用队列是无限大的,容易造成oom**/
static ExecutorService executor01 = new ThreadPoolExecutor(0,1,
2L,TimeUnit.SECONDS,
new LinkedBlockingDeque<>(50000000),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
//new ThreadPoolExecutor.AbortPolicy(); //超过就抛出异常
//new ThreadPoolExecutor.CallerRunsPolicy(); //超过的抛弃并返回给调用线程(这里是mian线程)
//new ThreadPoolExecutor.DiscardOldestPolicy();
//new ThreadPoolExecutor.DiscardPolicy();
public static void main(String[] args) {
/**
* Array Arrays
* Collection Collections
* Executor Executors
*/
//查看cpu同时支持的线程数
System.out.println(Runtime.getRuntime().availableProcessors());
System.out.println(Runtime.getRuntime().freeMemory());
/**
* 模拟10个用户请求池
*/
try {
for(int i = 1;i<=1000000;i++){
final int tmp = i;
executor01.submit(()->{
System.out.println(Thread.currentThread().getName()+"办理业务=:"+tmp);
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace();}
});
//try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace();}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("结束");
//executor01.shutdown();
}
}
}
redis知识梳理
常用5大数据类型,以及使用场景
1.string:
2.hash:对应java泛型为:Map<String ,Map<Object ,Object> >
3.list:有序列表类型,可重复
4.set:(集合类型)
5.zset:
新加的4类数据类型(目前是6.2.3版本)
6.bitmap:位图
7.hypeLogLogs:统计,存算法,使用的是概率算法
8.geo:地理信息
9.stream:流
注意事项
1.命令不区分大小写,但是key 区分大小写
2.help @。。,比如help @string 常用命令查看,可用于内网查询
1.maxmemory-policy 最大缓存过期清除使用策略
LRU:是最近最少使用页面置换算法(Least Recently Used),也就是首先淘汰最长时间未被使用的页面!
LFU:是最近最不常用页面置换算法(Least Frequently Used),也就是淘汰一定时期内被访问次数最少的页!
LRU关键是看页面最后一次被使用到发生调度的时间长短;
而LFU关键是看一定时间段内页面被使用的频率!
(1)volatile-lru:使用LRU算法移除key,只对设置了过期时间的键
(2)allkeys-lru:对所有key使用LRU算法移除key(常用)
(3)volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
(4)allkeys-random:对所有key,移除随机的key
(5)volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key
(6)noeviction:不进行移除。针对写操作,只是返回错误信息
(7)allkeys-lfu:对所有key进行luf算法
(8)volatile-lfu:对设置了过期时间的key进行luf算法
参数说明
redis.conf 配置项说明如下:
1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no
2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
3. 指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
port 6379
4. 绑定的主机地址
bind 127.0.0.1
5.当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300
6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
loglevel verbose
7. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout
8. 设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
databases 16
9. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
save
Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
10. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
11. 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
12. 指定本地数据库存放目录
dir ./
13. 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
slaveof
14. 当master服务设置了密码保护时,slav服务连接master的密码
masterauth
15. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码,默认关闭
requirepass foobared
16. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 128
17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory
18. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no
19. 指定更新日志文件名,默认为appendonly.aof
appendfilename appendonly.aof
20. 指定更新日志条件,共有3个可选值:
no:表示等操作系统进行数据缓存同步到磁盘(快)
always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)
everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec
21. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
vm-enabled no
22. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap
23. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-max-memory 0
24. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-page-size 32
25. 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-pages 134217728
26. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
vm-max-threads 4
27. 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
glueoutputbuf yes
28. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
29. 指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)
activerehashing yes
30. 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf
2.两种备份方式
1:RDB(Redis DataBase)
2: AOF(Append Only File)
3.主从备份
常用命令小技巧
1.github搜索常用命令
功能(不区分大小写) | 命令 |
---|---|
1.按项目名称/仓库名称搜索 | in:name 项目名 |
2.按照readme搜索 | in:readme 信息 |
3.按照描述description搜索 | in:description 描述 |
4.筛选stars/forks数量大于x的项目 | stars:> 1000 |
5.采用了哪种编程语言 | language:java |
6.最新的更新时间 | pushed:>2020-04-16 |
组合查询 | in:name 项目名 in:description 描述 stars:> 1000 |
组合查询 | in:description 秒杀 language:java stars:>1000 |