线程池,--其实就是一个 容纳多个线程的容器 ,其中的线程可以反复使用,省去了频繁创建线程对象的操作 ,--无需反复创建线程而消耗过多资源。 创建销毁线程是一个非常消耗性能的。 如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,--需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。 java自带线程池的最大线程数为Integer.MAX_VALUE,直接有多少任务来创建多少线程来执行(源码里面可以看到)会直接导致CPU使用率飙升,导致卡死等
1、悲观锁和乐观锁 悲观锁:当前线程去操作数据的时候,总是认为别的线程会去修改数据,所以每次操作数据的时候都会上锁,别的线程去操作数据的时候就会 阻塞,比如synchronized; 乐观锁:当前线程每次去操作数据的时候都认为别人不会修改,更新的时候会判断别人是否会去更新数据,通过版本来判断,如果数据被修改了就拒绝更新,例如cas是乐观锁,但是严格来说并不是锁,通过原子性来保证数据的同步,例如数据库的乐观锁,通过版本控制来实现,cas不会保证线程同步,乐观的认为在数据更新期间没有其他线程影响 总结:悲观锁适合写操作多的场景,乐观锁适合读操作多的场景,乐观锁的吞吐量会比悲观锁高 2、公平锁和非公平锁 公平锁:有多个线程按照申请锁的顺序来获取锁,就是说,如果一个线程组里面,能够保证每个线程都能拿到锁,例如:ReentrantLock(使用的同步队列FIFO) 非公平锁:获取锁的方式是随机的,保证不了每个线程都能拿到锁,会存在有的线程饿死,一直拿不到锁,例如:synchronized,ReentrantLock 总结:非公平锁性能高于公平锁,更能重复利用CPU的时间 3、可重入锁和不可重入锁 可重入锁:也叫递归锁,在外层使用锁之后,在内层仍然可以使用,并且不会产生死锁 不可重入锁:在当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞 总结:可重入锁能一定程度的避免死锁,例如:synchronized,ReentrantLock 4、自旋锁 自旋锁:一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环,任何时刻最多只能有一个执行单元获得锁 总结:不会发生线程状态的切换,一直处于用户态,减少了线程上下文切换的消耗,缺点是循环会消耗CPU 5、共享锁和独享锁 共享锁:也叫读锁,可以查看数据,但是不能修改和删除的一种数据锁,加锁后其他的用户可以并发读取,但不能修改、增加、删除数据,该锁可被多个线程持有,用于资源数据共享 独享锁:也叫排它锁、写锁、独占锁、独享锁,该锁每一次只能被一个线程所持有,加锁后任何线程试图再次加锁都会被阻塞,直到当前线程解锁。例如:线程A对data加上排它锁后,则其他线程不能再对data加任何类型的锁,获得互斥锁的线程既能读数据又能修改数据
服务发现和服务健康监测 动态配置服务 动态 DNS服务 服务及其元数据管理
Seata是阿里开发的一个用于微服务架构的高性能零侵入易使用的 分布式事务框架。 Seata处理一个全局事务的流程如下: 1.TM向TC请求发起一个全局事务,TC返回一个代表这个全局事务的XID。 2.XID在rpc中传播给每一个调用链中的服务。 3.每个RM拿到XID后向TC发起一个分支事务,TC返回一个代表这个分支事务的XID。 4.RM完成本地分支的业务,提交本地分支,并且报告给TC。 5.全局事务调用链处理完毕,TM根据有无异常向TC发起全局事务的提交或者回滚。 回滚: 6.假设某个RM本地事务失败。该RM自身驱动本地事务回滚,并且报告给TC。 7.TM检测到了某个分支事务失败,向TC发起全局事务回滚。 TC给每一个RM发送消息,通知它们全部回滚。 9.TC将全局事务回滚的结果发送给TM。 全局事务结束。
1、静态方法属于整个类所有,因此调用它不需要实例化,可以直接调用(类.静态方法())。实例方法必须先实例化,创建一个对象,才能进行调用(对象.实例方法())。 2、静态方法只能访问静态成员,不能访问实例成员;而实例方法可以访问静态成员和实例成员。 3、在程序运行期间,静态方法是一直存放在内存中,因此调用速度快,但是却占用内存。实例方法是使用完成后由回收机制自动进行回收,下次再使用必须再实例化。 4、一般来说,公共的函数、经常调用的可以写成静态方法,比如数据连接等(SqlHelper)。
1、在开发中,虽然springboot简化了配置,但只不过是编写的方式变得简单了,和SSM整合的方式还是有些差异; 2、使用SSM开发时,多数会选择Jsp作为视图,但是springboot不推荐使用jsp。
java的动态机制 允许我们在程序运行期间获取任意类的名称 包的信息 所有属性 能调用任意对象的属性方法 并且能改变对象的属性实例化任意一个类
jdk1.7是数组加链表 1.8后是数组链表红黑树 链表长度大于8变为红黑树 小于6变为链表 hashmap的底层是 数组+链表 在向hashMap存值时,会计算key的hash值,并根据数组的长度进行取模【默认是16】,从而确定将这个键值对放到数组的哪个位置,比如map.put("userName","zhangsan");这样就会计算userName的hash值,比如是18,接着用18和16取模,得2;那么就会在arr[2]的位置放入 userName:zhangsan这么一个键值对。 hashmap中可能会出现hash冲突(hash碰撞)的情况,也就是两个key不一样,但他们的hash值一样,就会导致他们出现在数组的同一个地方,这个时候就可以通过链表的方式,将这个键值对放到数组元素对应的链表里。但使用链表可能会导致一个问题,就是链表长度过长,查询起来性能低,所以在jdk1.8的时候做了优化,就是当链表达到一定长度后就会把它转换为红黑树,从而提高查询性能。 在get的时候,也是根据key计算hash值,和数组长度取模后,得到数组中的位置,如果发现这个位置挂了一个链表,那就根据key找到链表中和该key对应的键值对。 hashmap的扩容,默认情况数组长度是16,负载因子是0.75,所以当数组中存入数组的长度达到12以后就会进行成倍扩容,从16变为32。扩容后就会对原有hashmap中的数据进行重新的hash取模运算,得到新的位置。所以可以根据数据量的多少,更改hashmap的默认容量,从而避免频繁扩容,导致性能降低。
HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对
在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种情况的发生。它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。
新建 就绪 运行 阻塞 死亡
困难:性能问题
困难:第三方库兼容性问题
困难:复杂的并发问题
困难:安全漏洞
如果在项目中遇到无法解决的问题,即使在百度等搜索引擎上找不到答案,可以采取以下步骤:
咨询同事或上级: 请教有经验的同事或上级,分享问题并寻求他们的建议和见解。
在社区或论坛提问: 参与开发者社区或专业论坛,向其他开发者提问,可能会获得新的思路和解决方案。
深入分析和调试: 使用调试工具和日志来深入分析问题,可能会找到问题的根本原因。
考虑重构或重新设计: 如果问题非常复杂且无法解决,可能需要考虑对代码进行重构或重新设计,以避免问题的出现。
常见的Linux命令示例:
复制目录的方法:
要复制目录及其内容,可以使用cp
命令的-r
选项(递归复制)。例如,要将/source/dir
目录复制到/destination
目录,可以执行以下命令:
cp -r /source/dir /destination
-r
选项表示递归复制,它会将源目录及其所有内容复制到目标目录。
关于grep管道命令:
grep
是用于在文件中搜索指定模式的命令,通常与管道一起使用,用于在输出中筛选出符合条件的行。
例如,要在一个文件中搜索包含关键词"error"的行,可以使用以下命令:
grep "error" filename
如果要在一个命令的输出中搜索,可以将该命令的输出通过管道传递给grep
。例如,要查找包含关键词"error"的正在运行的进程,可以使用以下命令:
ps aux | grep "error"
这将首先列出所有正在运行的进程,然后通过管道将输出传递给grep
,以便仅显示包含"error"的行。
请注意,grep
支持各种选项和正则表达式,使其在文本搜索和筛选方面非常强大。
项目部署通常涉及以下步骤:
准备环境: 在目标服务器上准备适当的操作系统、依赖库和运行环境。这可能涉及安装所需的软件和配置。
获取代码: 从版本控制系统(如Git)中获取项目代码。可以使用命令行或图形界面工具进行操作。
构建项目: 如果项目需要编译、打包或构建,执行适当的构建步骤以生成可执行文件或部署包。
配置应用: 配置应用程序的设置,如数据库连接、API密钥等。这通常在配置文件中完成。
数据库迁移: 如果项目使用数据库,可能需要执行数据库迁移以确保数据库模式与应用程序版本匹配。
部署代码: 将构建好的代码和相关文件部署到目标服务器。可以使用FTP、SCP、rsync等工具进行文件传输。
启动应用: 在目标服务器上启动应用程序。可以使用命令行工具或服务管理工具(如systemd)来管理应用程序的生命周期。
监控和测试: 确保应用程序在部署后正常运行。进行端到端测试、性能测试和监控,以确保应用程序稳定且具有良好的性能。
反向代理和域名绑定: 如果需要,设置反向代理服务器(如Nginx或Apache)来处理流量分发和SSL加密,并将域名指向应用程序。
持续集成/持续部署(CI/CD): 如果采用CI/CD实践,部署可以自动化,每次代码提交都会触发自动构建和部署流程。
需要注意的是,不同项目可能会有不同的部署需求和步骤。部署流程可能会因技术栈、应用类型和团队实践而异。在实际项目中,可能还需要处理数据库备份、安全性、容灾等问题。
1、抽象类可以有构造方法,接口中不能有构造方法。 2、接口的方法默认是 public,所有方法在接口中不能有实现,而抽象类可以有非抽象的方法。 3、接口中除了 static、final 变量,不能有其他变量,而抽象类中则不一定。 4、一个类可以实现多个接口,但只能继承一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。 5、接口方法默认修饰符是 public,抽象方法可以有 public、protected 和 default 这些修饰符(不能使用 private 关键字修饰!)。 6、从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
1.继承关系2.实现关系3.依赖关系4.关联关系5.聚合关系6.组合关系
1、 getClass():获取类的class对象。 2、 hashCode:获取对象的hashCode值 3、 equals():比较对象是否相等,比较的是值和地址,子类可重写以自定义。 4、 clone():克隆方法。 5、 toString():如果没有重写,应用对象将打印的是地址值。 6、 notify():随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。 7、 notifyall():解除所有那些在该对象上调用wait方法的线程的阻塞状态。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。 8、 wait():导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。 9、 finalize():对象回收时调用 ...
java.lang.NullPointerException 空指针异常 :调用了未经初始化的对象或者是不存在的对象 java.lang.ClassNotFoundException 指定的类找不到 :类的名称和路径加载错误;通常都是程序试图通过字符串来加载某个类时可能引发异常 java.lang.NumberFormatException 字符串转换为数字异常 :字符型数据中包含非数字型字符 java.lang.IndexOutOfBoundsException 数组下标越界异常 :常见于操作数组对象时发生 java.lang.IIIegalArgumentException 方法传递参数错误 java.lang.ClassCastException 数据类型转换异常 java.lang.NoClassDefFoundException 未找到类定义错误 SQLException SQL异常 :常见于操作数据库时的SQL语句错误 java.lang.InstantiationException 实例化异常 java.lang.NoSuchMethodException 方法不存在异常 ...
重写(Override)是父类与子类之间的多态性,实质是对父类的函数进行重新定义,如果在子类中定义某方法与其父类有相同的名称和参数则该方法被重写,不过子类函数的访问修饰权限不能小于父类的;若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法,如需父类中原有的方法则可使用 super 关键字。
重载(Overload)是让类以统一的方式处理不同类型数据的一种手段,实质表现就是多个具有不同的参数个数或者类型的同名函数(返回值类型可随意,不能以返回类型作为重载函数的区分标准)同时存在于同一个类中,是一个类中多态性的一种表现(调用方法时通过传递不同参数个数和参数类型来决定具体使用哪个方法的多态性)。
重写是子类方法重写父类的方法,重写的方法名不变,而类的构造方法名必须与类名一致,假设父类的构造方法如果能够被子类重写则子类类名必须与父类类名一致才行,所以 Java 的构造方法是不能被重写的。而重载是针对同一个的,所以构造方法可以被重载
。
由于参加秒杀的商品,一般都是“抢到就是赚到”,所以成功下单后却不付款的情况比较少,再加上卖家对秒杀商品的库存有严格限制,所以秒杀商品采用“下单减库存”更加合理。另外,理论上由于“下单减库存”比“预扣库存”以及涉及第三方支付的“付款减库存”在逻辑上更为简单,所以性能上更占优势。
“下单减库存”在数据一致性上,主要就是保证大并发请求时库存数据不能为负数,也就是要保证数据库中的库存字段值不能为负数,一般我们有多种解决方案:一种是在应用程序中通过事务来判断,即保证减后库存不能为负数,否则就回滚;另一种办法是直接设置数据库的字段数据为无符号整数,这样减后库存字段值小于零时会直接执行 SQL 语句来报错;再有一种就是使用 CASE WHEN 判断语句
SpringBoot是用来简化Spring应用的初始搭建以及开发过程的全新框架,该框架使用特定方式来进行配置,从而使开发人员不再需要定义样板化的配置。
SpringBoot具有如下优势:
1.创建独立的Spring应用程序,通过mian方法进行运行。
2.内置嵌入的Tomcat,可选择部署jar文件,也可部署war文件。
3.简化Maven配置,SpringBoot的父工程帮我们管理了大量的maven依赖,我们可以不用关系版本号,只需要继承引用即可。
4.自动配置Spring,根据核心application.properties配置文件+代码注解使用,就能自动配置Spring,无需要再繁琐的去配置xml文件。
sql优化面试题链接
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
查询SQL尽量不要使用select *,而是具体字段 避免在where子句中使用or来连接条件 使用varchar代替char 尽量使用数值替代字符串类型 查询尽量避免返回大量数据 使用explain分析你SQL执行计划 是否使用了索引及其扫描类型 创建name字段的索引 优化like语句: 字符串怪现象 索引不宜太多,一般5个以内 索引不适合建在有大量重复数据的字段上 where限定查询的数据 避免在索引列上使用内置函数 避免在where中对字段进行表达式操作 避免在where子句中使用!=或<>操作符 去重distinct过滤字段要少 where中使用默认值代替null
序号 | 数据类型 | 存储需求 | 大小/位 | 封装类 | 默认值 | 可表示数据范围 |
---|---|---|---|---|---|---|
1 | byte(位) | 1字节 | 8位 | Byte | 0 | -128~127 |
2 | short(短整数) | 2字节 | 16位 | Short | 0 | -32768~32767 |
3 | int(整数) | 4字节 | 32位 | Integer | 0 | -2147483648~2147483647 |
4 | long(长整数) | 8字节 | 64位 | Long | 0 | -9223372036854775808~9223372036854775807 |
5 | float(单精度) | 4字节 | 32位 | Float | 0.0f | 1.4E-45~3.4028235E38 |
6 | double(双精度) | 8字节 | 64位 | Double | 0.0d | 4.9E-324~1.7976931348623157E308 |
7 | char(字符) | 2字节 | 16位 | Character | 空 | 0~65535 |
8 | boolean(布尔值) | 1字节 | 8位 | Boolean | false | true或false |
1)、length() 返回字符串中的字符数。
eg:int a=str.length(); //str是上面例中中的str,a等于11.
2)、charAt(index) 返回字符串s中指定位置的字符
eg:char a1=str.charAt(2); //a1为'l'
3)、toUpperCase() //返回一个新字符串,其中所有字母大写
eg:String b1=str.toUpperCase(); //b1为 HELLO WORLD。
4)、toLowerCase() //返回一个新字符串,其中所有字母小写。
eg:String b2=b1.toLowerCase(); //b2为 hello world。
5)、concat(s1) //将本字符串和字符串s1连接,返回一个新字符串。等价"+"
eg:String c1=b1.concat(b2); //c1为 HELLO WORLDhello world
6)、trim() //返回一个新字符串,去掉两边的空白字符
eg:"\t Good Night \n".trim() //返回一个新字符串 Good Night。
7)、equals(s1) //如果该字符串内容等于字符串s1,返回true。
eg:b1.equals(b2) //返回false
8)、equalsIgnoreCase(s1) //如果该字符串内容等于字符串s1,返回true。不区分大小写!
eg:b1.equalsIgnoreCase(b2) //返回true
9)、compareTo(s1) //返回一个大于零、等于零、小于零的整数,表明一个字符串是否大于、等于、或小于s1.
如果 s1与 s2 相等,那么该方法返回值 0; 如果按字典顺序 s1小于 s2, 那么方法返回值小于 0;如果按字典顺序 s1大于 s2,方法返回值大于 0。
eg:s1为"abc",s2 为"abg", 那 么s1compareTo(s2)返回-4
10)、compareToIgnoreCase(s1) //和compareTo一样,除了比较是区分大小写的之外。
eg:s1为"abc",s2 为"ABg", 那 么s1compareTo(s2)返回-4
11)、startsWith(prefix) //如果字符串以特定前缀开始,返回true
eg:"Welcome to Java".startsWith("We") //返回 true.
12)、endsWith(suffix) //如果字符串以特定的后缀结束,返回 true
eg: "Welcome to java".endsWith("va") //返回 true.
13)、contains(s1) //如果s1是该字符串的子字符串,返回true
eg:"Welcome to Java".contains("to") //返回 true
14)、substring(beginIndex) //返回字符串,从特定位置beginindex开始到字符串结尾
15)、substring(beginindex,endIndex) //返回字符串从从下标beginIndex到endIndex-1的下标位置
16)、indexOf(ch) //返回字符串中出现第一个ch的下标,如果没有返回-1
eg:"Welcome to Java".indexOf(‘W’) //返回 0.
17)、indexOf(ch,fromIndex) //返回字符串中fromIndex之后出现的第一个ch下标,如果没有返回-1
eg:"Welcome to Java".indexOf('o', 5) //返回9.
18)、lastindexOf(ch) //返回字符串中出现最后一个ch的下标,如果没有返回-1
eg:"Welcome to Java".lastindexOf(‘o’) //返回 9.
19)、lastIndexOf(s,fromindex) //返回字符串中fromIndex之前出现的第一个ch下标,如果没有返回-1
eg:"Welcome to Java".lastindexOf('o', 5) //返回4.
20)、Integer.parselnt () //将数值型字符串转换成int数值
eg:int str1= Integer.parseInt(intString); //例如intString是字符串"123" 则结果为int型123
21)、Double.parseDouble () //将数值型字符串转换成double型数值
eg:int str2= Double.parseDouble (doubleString); //例如doubleString是字符串"123.321" 则结果为double型123.321
若要将数值转换成字符串 只需要简单的字符串连接操作符即可。
eg:String s=123+"";
使用StringBuilder或者StringBuffer来做字符串拼接。StringBuilder是非线程安全的,效率更高。StringBuffer是线程安全的,效率低一些,但安全。,StringBuffer的append、delete、replace、length等方法前都加了synchronized关键字保证线程安全
HashMap线程不安全,HashTable线程安全其内部使用synchronized关键字进行加锁
在JDK1.7的时候是数组+链表;在JDK1.8的时候是数组+链表+红黑树;当链表的长度大于8的时候,就会变为红黑树。小于6变为链表
常量名一般使用大写字符下划线分割 方法首字母小写驼峰命名法
SpringIOC控制反转 SpringAOP面向切面编程 IOC: IOC中最基本的技术就是“反射(Reflection)”编程 AOP:动态代理;Spring采用两种方法来实现动态代理,分别是JDK动态代理技术和CGLIB动态代理技术。JDK动态代理技术要求我们实现InvocationHandler接口,当我们的代理类调用方法时,会被其invoke方法拦截,进行方法的增强,但它必须基于接口来实现。CGLIB底层采用的是字节码技术来生成代理类,也是在我们调用代理类方法时,被拦截,进行方法的增强,但它可以不基于接口来实现,而采用继承的方式来实现。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
eg:
Class c = Class.forName("com.seleflearn.Person");
// 使用无参构造方法创建Person的实例
Person p = (Person) c.newInstance();
// 设置名称和年龄
p.setName("张三");
p.setAge(20);
---------------------------------------
// 得到Class
Class c = Class.forName("com.seleflearn.Person");
// 获取有参数构造方法,获取有参构造方法的参数类型,需要使用class形式传递
Constructor cs = c.getConstructor(String.class, int.class);
// 通过有参构造方法创建Person的实例
Person p = (Person) cs.newInstance("张三", 20);
对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。 查询SQL尽量不要使用select *,而是具体字段 避免在where子句中使用or来连接条件 使用varchar代替char 尽量使用数值替代字符串类型 查询尽量避免返回大量数据 使用explain分析你SQL执行计划 是否使用了索引及其扫描类型 创建name字段的索引 优化like语句: 字符串怪现象 索引不宜太多,一般5个以内 索引不适合建在有大量重复数据的字段上 where限定查询的数据 避免在索引列上使用内置函数 避免在where中对字段进行表达式操作 避免在where子句中使用!=或<>操作符 去重distinct过滤字段要少 where中使用默认值代替null
一级缓存是默认开启的,是SqlSession级别的缓存, 在操作数据库时需要构造sqlSession对象,不同的sqlSession之间的缓存数据区域是互相不影响的,不同的sqlSession中的缓存是互相不能读取。 二级缓存需要手动开启,在xml中进行配置,是namespace(命名空间)级别的缓存基于多个SqlSession 可以共用二级缓存,二级缓存是跨SqlSession的。
Redis集群搭建(非常详细)_yygr的博客-CSDN博客
用户量不大, 访问量月均10万左右,成交量在1-2W笔订单一台服务器qps一两千 三台服务器
系统初始化 把商品库存数加载到redis
每次点击秒杀按钮,先从服务器获取动态拼接而成的秒杀地址。
Redis以缓存用户ID和商品ID为Key,秒杀地址为Value缓存秒杀地址
用户请求秒杀商品的时候,要带上秒杀地址进行校验
防止恶意脚本抢购
使请求时间分散
收到请求 redis原子操作预减库存(在内存里加一个map,key为商品ID,value为是否有库存,这样当库存没有之后,直接通过内存中的值判断是否还有库存,减少对Redis的访问。)库存不足 直接返回
请求入队 立即返回排队中(购买请求加入消息队列,异步下单(前端显示排队中),增强用户体验)
请求出对 生成订单 减少库存(服务端)
客户端轮询,是否秒杀成功(客户端)和4同步,得到结果刷新结果显示
接口限流防刷
使用计数法,在拦截器做限制请求频率。利用Redis缓存的有效期(以用户ID拼接Url作为key,以访问次数为value),指定缓存有效期为1秒,访问接口每次将value+1,到达阈值跳转全局异常。
优化:使用拦截器+自定义注解,减少对业务代码的侵入
1.在秒杀开始前指定的时间,Redis缓存预热,将每个sku参与秒杀的库存数保存在Redis中 而且为了避免有人恶意访问还可以生成一个随机码,也保存在Redis中,用于验证是否为正常链接购买秒杀商品 2.在每个批次秒杀开始前,将本批次所有秒杀商品的spuid保存在布隆过滤器中,减少缓存穿透的情况 秒杀开始,用户在秒杀商品的规定时间内可以查询秒杀商品详情 所有秒杀商品spu查询时,都先查询布隆过滤器是否包含这个spuId,如果包含允许访问,如果不包含抛出异常,也要考虑布隆过滤器误判的情况, 每当业务中查询spu和sku时,都需要先检查redis中是否包含这个数据,如果包含直接从redis中获得,如果不包含再从数据库中查,但是同时也注意,查询完了要保存到Redis中,以便之后的查询直接从redis中获取,在保存到Redis时,为了减少缓存雪崩的几率,我们为每个Spu和Sku对象都添加了过期时间随机数 查询返回前,可以在判断一下当前时间是否在可秒杀该商品的时间段内,如果不在秒杀时间段内,抛出异常 只有返回了完整信息,前端才可能获得包含随机码的提交路径,否则是无法完成正常连接购买的 在用户购买秒杀商品时,保证用户登录的前提下 验证用户是否重复秒杀(业务要求秒杀相同商品只能购买一次),我们使用userId和skuId,向Redis中保存一个key,如果没有这个key就是用户没有秒杀过,否则发生异常提示 我们要保证用户购买时,这个商品有库存,减少库存后,获得剩余库存信息 只要剩余库存不小于0,就可以为当前用户生成订单,否则发生异常 生成订单直接Dubbo调用Order模块完成的生成订单的方法即可 订单提交后,还需要修改秒杀sku库存数和生成秒杀成功记录保存在数据库 但是这个业务非迫切运行,我们可以将信息发送给消息队列,削峰填谷 然后再编写接收消息队列的代码,完成修改秒杀库存和生成秒杀成功记录的操作 在控制层方法上添加注解实现Sentinel的限流,保证这个业务在非常大的并发下,也能稳定运行 控制器方法中还要判断用户请求路径中的随机码,是否和Redis中保存的随机码一致,防止非正常连接购买
自定义权限认证 客户端向认证服务申请令牌token,成功后,携带token访问资源服务器。 认证服务生成token,通常使用uuid生成随机字符串,并且保存到redis中。 资源服务器使用filter过滤器拦截需要认证的API,并校验token。校验的大概业务如下: 当前api是否无需权限,根据url规格匹配。 判断当前服务调用是否是内部服务调用,根据内部服务调用的服务名和密码认证。 验证是否剔除前一次登录状态。 根据token获取用户相关信息进行认证。
区别一,定长和变长 char 表示定长,长度固定,varchar表示变长,即长度可变。char如果插入的长度小于定义长度时,则用空格填充;varchar小于定义长度时,还是按实际长度存储,插入多长就存多长。 因为其长度固定,char的存取速度还是要比varchar要快得多,方便程序的存储与查找;但是char也为此付出的是空间的代价,因为其长度固定,所以会占据多余的空间,可谓是以空间换取时间效率。varchar则刚好相反,以时间换空间。 区别之二,存储的容量不同 对 char 来说,最多能存放的字符个数 255,和编码无关。 而 varchar 呢,最多能存放 65532 个字符。varchar的最大有效长度由最大行大小和使用的字符集确定。整体最大长度是 65,532字节。 优缺点: 1. char类型存储时,相比varchar类型存储效率更高, 不需要判断字符长度,直接分配磁盘空间 2.varchar类型,相比char类型,按需分配空间。 选择? 2. 从原则上来讲,将来字符串长度不固定的话,选择varchar类型, 字符串长度固定不变则选择char类型 3. 实际上我们生产中在考虑性能问题的方面, 需要有大量插入(insert)操作的应用中,我们可以考虑使用char去代替varchar。 4. 如果我们业务中,大量是查询类操作的应用中, 数据量级又比较大情况下,变长长度数据类型,可以考虑采用varchar, 一方面节省空间,可以有效的减少***索引树***的高度, 从而提高索引的优化查询的效果。
1. 如果条件中有or,即使其中有条件带索引也不会使用 2. 对于多列索引,不是使用的第一部分,则不会使用索引 3. like查询是以%开头 4. 如果列类型是[字符串](https://so.csdn.net/so/search?q=字符串&spm=1001.2101.3001.7020),那一定要在条件中将数据使用引号引用起来,否则不使用索引 5. 如果mysql估计使用全表扫描要比使用索引快,则不使用索引
方式一:写一个类,继承Thread类,重写run方法。 方式二:写一个类,实现Runnable接口,chonrun方法,创建线程的时候传入实现了Runnable接口的实现类。 方式三:写一个类,实现Callable接口,结合FutureTask使用,可以拿到异步处理的结果。 方式四:使用线程池创建。
事务四大属性 分别是 :原子性、一致性、隔离性、持久性。 1、原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。 2、一致性(Consistency) 一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。举例来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账、转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。 3、隔离性(Isolation) 隔离性是当多个用户并发访问数据库时,比如同时操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。 4、持久性(Durability) 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。 数据库事务的隔离级别 有4个,由低到高依次为Read uncommitted(未授权读取、读未提交)、Read committed(授权读取、读已提交)、Repeatable read(可重复读取)、Serializable(可串行化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。 - Read uncommitted(未授权读取、读未提交): 如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。这样就避免了更新丢失,却可能出现脏读。也就是说事务B读取到了事务A未提交的数据。 - Read committed(授权读取、读已提交): 读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。 - Repeatable read(可重复读取): 可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,即使第二个事务对数据进行修改,第一个事务两次读到的的数据是一样的。这样就发生了在一个事务内两次读到的数据是一样的,因此称为是可重复读。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。这样避免了不可重复读取和脏读,但是有时可能出现幻象读。(读取数据的事务)这可以通过“共享读锁”和“排他写锁”实现。 - Serializable(可串行化): 提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。
set方法 构造方法 注解注入
类加载子系统 垃圾回收器 运行时数据区 运行时数据区:线程私有 线程共有 线程私有:程序计数器 虚拟机栈 本地方法栈 线程共有:堆 方法区
类加载的过程分为:加载、验证、准备、解析和初始化五个阶段。
通过双亲委派类加载机制,保证同一个类只能被加载一次,同时也是对类资源的一种保护
ArrayList底层是数组 查询速度快 增删速度慢 LinkList底层是链表 查询速度慢 增删速度快
jdk1.8之前 数组加链表 1.8之后数组链表红黑树 链表长度大于8时变红黑树 红黑树不追求“完全平衡 ”,它只会求部分达到平衡要求,降低了对旋转的要求,从而提高性能。
map.get(Key)的实现原理(取) 1、底层会调用key的hashcode()方法,通过hash函数将hash值转换为数组下标,通过数组下标快速定位到数组的指定位置上,如果这个位置上没有任何元素,那么返回null。 2、如果这个位置上有单向链表(该位置上有元素),那么会拿着我们get(key)中的key和单向链表中的每个节点的key进行equals,如果说所有的equals都返回false,那么这个get方法返回false。 3、只要其中有一个节点的key和参数key的equals对比的结果返回true,那么这个节点的value就是我们想要找的value,get方法返回这个value. map.put(Key,value)的实现原理(存) 1、先将key和value封装到Node节点中 2、底层会调用key的hashcode()方法,通过hash函数将hash值转换为数组下标,下标位置上如果没有任何元素,就把该Node添加到该位置上(该下标处)
#java自带四种线程池:固定线程数线程池 固定只有一条线程的线程池 固定频率执行的线程池 动态线程池 一般都不用 用自定义线程池 参数: 核心线程数 最大线程数 空闲线程保留时间 保留时间单位 工作阻塞队列 线程工厂 拒绝策略 核心线程建议设置为cpu核心数*2+1 默认拒绝策略 ThreadPoolExceutor.AbortPolicy : 丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务 ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
SpringAOP面向切面 底层原理:动态代理 1.jdk动态代理:实现InvocationHandler接口 当我们的代理类调用方法时,会被其invoke方法拦截,进行方法的增强,必须基于接口来实现 2.CGLIB动态代理:底层是字节码技术生成代理类 调用方法时被invoke方法拦截进行增强 可以基于继承实现
基于缓存机制 一级缓存创建完毕的bean对象SingletonObjects 二级缓存是半成品(没有初始化属性)的bean对象EarlySingletonObject
Mysql:使用Limit关键字 MySQL 分页 (利用LIMIT关键字) 计算参数为 开始序号(startNum),要查的总条数 (totalNum)select * from stu limit m, n; //m = (startPage-1)*pageSize,n = pageSize (1)第一个参数值m表示起始行,第二个参数表示取多少行(页面大小) (2)m= (2-1)*10+1,n=10 ,表示 limit 11,10从11行开始,取10行,即第2页数据。 (3)m、n参数值不能在语句当中写计算表达式,写到语句之前必须计算好值。 Oracle使用rownum分页 Oracle 分页 (利用自带的rownum) 计算参数为 开始序号(startNum) , 结束序号 (endNum)select * from ( select rownum rn, a.* from ( select * from table_name order by XXX ) a where ruwnum <= y // 结束行,y = startPage*pageSize ) where rn > x; // 起始行,x = (startPage-1)*pageSize (1)>= y,<= x表示从第y行(起始行)~x行(结束行) 。 (2)rownum只能比较小于,不能比较大于,因为rownum是先查询后排序的,例如你的条件为rownum>1,当查询到第一条数据,rownum为1,则不符合条件。第2、3...类似,一直不符合条件,所以一直没有返回结果。所以查询的时候需要设置别名,然后查询完成之后再通过调用别名进行大于的判断。
Select name from student s,corse c where s.id=c.id group by s.id having count(corse)>3;
中软二面:
负载均衡(Load Balance) ,简单点说就是将用户的请求平摊分配到多个服务器上运行,以达到扩展服务器带宽、增强数据处理能力、增加吞吐量、提高网络的可用性和灵活性的目的。 常见的负载均衡方式有两种: 服务端负载均衡 客户端负载均衡
负载均衡算法
(1)轮转调度(Round-Robin Scheduling)算法 (2)加权轮转调度(Weighted Round-Robin Scheduling)算法 (3)随机均衡调度(Random Scheduling)算法 (4)加权随机均衡调度(Weighted Random Scheduling)算法 (5)最小连接调度(Least-Connection Scheduling)算法 (6)加权最小连接调度(Weighted Least-Connection Scheduling)算法 (7)目标地址散列调度(Destination Hashing Scheduling)算法 (8)源地址散列调度(Source Hashing Scheduling)算法 (9)基于局部性的最少链接调度(Locality-Based Least ConnectionsScheduling)算法 (10)带复制的基于局部性最少链接调度(Locality-Based Least Connectionswith Replication Scheduling)算法 (11)响应速度均衡调度(Response Time Scheduling)算法 (12)处理能力均衡调度(Processing Capacity Scheduling)算法 (13)DNS均衡调度(DNS Scheduling)算法
多线程之间相互等待临界资源而造成彼此无法继续执行。(1)系统中存在多个临界资源且临界资源不可抢占;(2)每个线程需要多个临界资源才能继续执行。 最常用的一种死锁阻止技术就是 锁排序 . 假设有 N 个线程尝试获取 M 把锁 , 就可以针对 M 把锁进行编号 (1, 2, 3...M). N 个线程尝试获取锁的时候 , 都按照固定的按编号由小到大顺序来获取锁 . 这样就可以避免环路等待 。
HashMap是线程不安全 HashTable是线程安全 底层采用Synchronize关键字加锁
LinkList底层链表增删快查询慢 ArrayList底层是数组增删慢查询快
类加载过程一共分三个阶段,第一个阶段是加载,然后是链接,最后是初始化
如果一个类加载器收到一个类加载的请求,它首先不会自己加载,而是把这个请求委派给父类加载器,只有父类无法完成加载时子类加载器才会尝试加载
首先判断对象是否为垃圾 两种算法 1引用计数器法:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题; 2可达性分析算法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。 再收集清除垃圾对象 算法主要有: A、标记清除法:最基础的收集算法,分为“标记”,“清除”二个阶段; B、复制算法; C、标记整理法; D、分代收集 算法:当前JVM的垃圾收集大都采用分代收集算法。即根据对象的新老特点,采取合适的垃圾收集算法。 将Java堆对象分为新生代和老年代,在新生代中,每次收集都会有大量对象死去,故可以采用“复制算法”。 在老年代中,对象的存活率高,故可以采取“标记清除法”和“标记整理算法”。
查询SQL尽量不要使用select *,而是具体字段 避免在where子句中使用or来连接条件 使用varchar代替char 尽量使用数值替代字符串类型 查询尽量避免返回大量数据 使用explain分析你SQL执行计划 是否使用了索引及其扫描类型 创建name字段的索引 优化like语句: 字符串怪现象 索引不宜太多,一般5个以内 索引不适合建在有大量重复数据的字段上 where限定查询的数据 避免在索引列上使用内置函数 避免在where中对字段进行表达式操作 避免在where子句中使用!=或<>操作符 去重distinct过滤字段要少 where中使用默认值代替null
1、在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。 2、使用连接(JOIN)来代替子查询(Sub-Queries) 3、使用联合(UNION)来代替手动创建的临时表 4、事务 5、锁定表 6、使用外键 7、使用索引 8、优化的查询语句
都是面向对象的语言,都支持封装、继承和多态 Java不提供指针来直接访问内存,程序内存更加安全 Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。 Java有自动内存管理机制,不需要程序员手动释放无用内存
单点登录SSO,说的是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。 使用独立登录系统 一般来说,大型应用会把授权的逻辑和用户信息的相关逻辑独立成一个应用,称为用户中心。用户中心不处理业务逻辑,只是处理用户信息的管理以及授权给第三方应用。第三方应用需要登录的时候,则把用户的登录请求转发给用户中心进行处理,用户处理完毕后返回凭证,第三方应用验证凭证,通过后就登录用户。
一个jwt token由三部分组成,header、payload与signature,以点隔开
1.Header(头) 作用:记录令牌类型、签名算法等 例如:{“alg":"HS256","type","JWT}
2.Payload(有效载荷)作用:携带一些用户信息 例如{"userId":"1","username":"mingzi"}
3.Signature(签名)作用:防止Token被篡改、确保安全性 例如 计算出来的签名,一个字符串
### 1. 定时器任务 使用Scheduled作为定时器,通过cron语法指定运行时间,每3小时运行一次
成员变量: 1、成员变量定义在类中,在整个类中都可以被访问。 2、成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中。 3、成员变量有默认初始化值。 局部变量: 1、局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。 2、局部变量存在于栈内存中,作用的范围结束,变量空间会自动释放。 3、局部变量没有默认初始化值 在使用变量时需要遵循的原则为:就近原则 首先在局部范围找,有就使用;接着在成员位置找。
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。简单的说:就是用基类的引用指向子类的对象。除了代码的复用性外,还可以解决项目中紧偶合的问题,提高程序的可扩展性
java中异常处理机制是使用try - catch语句,在catch语句中定义捕获异常后的操作,用try语句定义可能会出现异常的语句块。。也可以直接向外抛异常,并在方法声明里声明这个方法可能会跑出的异常提醒调用者做相应的异常处理 java中Throwable这个类可以被作为异常抛出的类,继承它的分为异常Exception和错误Error. Exception表示程序需要捕捉和处理的的异常; Error表示系统级别的错误和程序无需处理的。 我们所需要关心的是Exception. Execption可以分为java标准定义的异常和程序员自定义异常2种. (1)一种是当程序违反了java语规则的时候,JAVA虚拟机就会将发生的错误表示为一个异常.这里语法规则指的是JAVA类库内置的语义检查。 (2)另一种情况就是JAVA允许程序员扩展这种语义检查,程序员可以创建自己的异常,并自由选择在何时用throw关键字引发异常。所有的异常都是Thowable的子类。 异常处理是与程序执行是并行的. Try{ //可能发现异常的语句块 }catch(异常类型,e){ //发生异常时候的执行语句块 }finnally{ //不管是否发生异常都执行的语句块 }
lamda表达式是一种函数式接口,可以使代码更加简洁。 何为函数式接口,若一个接口只包含唯一一个抽象方法,那么它就是一个函数式接口。 (参数)->{方法语句} 只有一个参数的时候参数可以不用括号; 只有一个语句的时候大括号可以不用; 只有一个语句且是return的时候可以省略return,直接写需要返回的值(表达式)
消费型接口 Consumer相当于只进不出; 供给型接口 Supplier相当于只出不进; 函数型接口 Function相当于有进有出,且一进一出; 断定型接口 Predicate相当于给了他参数之后,他给你返回一个true或false。
1 xml配置在springweb项目工程web.xml中 ContextLoaderListener或者DispatcherServlet的初始参数contextConfigLocation指定spring配置文件位置,可以在该配置文件中直接定义bean,或者在该配置文件中import专门配置三方bean的xml文件。springboot工程,可以在启动类上面加注解@ImportResource。@ImportResource注解可以通过value指定需要扫描的xml文件,value是字符串数组可以指定多个xml配置文件。 2 @Configuration和@Bean 可以通过直接直接向spring容器注入bean。在开启注解扫描的情况下,在扫描的包路径中添加具有@Configuration注解的类,在该类的定义返回某个实例的方法,这个方法加上@Bean的注解,那么这个方法的返回值将注入spring容器,这个bean的名称是这个方法的名称。 3 @Import @Import可以直接向容器中导入第三方包中bean。在开启注解扫描的情况下,可以在有@Controller,@Service,@Component,@Repository,@Configuration注解的类上面添加@Import注解,@Import注解有一个类数组类型的属性,类数组中的类有以下四种情况
1、单例模式:作用:保证类只有一个实例。JDK中体现:Runtime类。 2、静态工厂模式:作用:代替构造函数创建对象,方法名比构造函数清晰。JDK中体现:Integer.valueOf、Class.forName 3、抽象工厂:作用:创建某一种类的对象。JDK中体现:Java.sql包。 4、原型模式:clone();原型模式的本质是拷贝原型来创建新的对象,拷贝是比new更快的创建对象的方法,当需要大批量创建新对象而且都是同一个类的对象的时候考虑使用原型模式。 一般的克隆只是浅拷贝(对象的hash值不一样,但是对象里面的成员变量的hash值是一样的)。有些场景需要深拷贝,这时我们就要重写clone方法,以ArrayList为例: 5、适配器模式:作用:使不兼容的接口相容。JDK中体现:InputStream、OutputStream。 6、装饰器模式:作用:为类添加新的功能,防止类继承带来的类爆炸。JDK中体现:io类、Collections、List。 7、外观模式:作用:封装一组交互类,一直对外提供接口。JDK中体现:logging包。 8、享元模式:作用:共享对象、节省内存。JDK中体现:Integer.valueOf、String常量池。 9、代理模式:作用:(1)透明调用被代理对象,无须知道复杂实现细节;(2)增加被代理类的功能;JDK中体现:动态代理。 10、迭代器模式:作用:将集合的迭代和集合本身分离。JDK中体现:Iterator 11、命令模式:作用:封装操作,使接口一致。JDK中体现:Runable、Callable、ThreadPoolExecutor。 线程安全问题 分情况
1. 静态的饿汉式单例模式是线程安全的 2. 在方法前面加synchronized的懒汉单例模式是线程安全的,但是效率低 3. 双重校验DCL模式的懒汉单例模式不是线程安全的 会重排序 4. 双重校验的volatile DCL模式是线程安全的 5. 静态内部类实现的单例模式是线程安全的
通过JavaScript屏蔽提交按钮(不推荐) 给数据库增加唯一键约束(简单粗暴): 在数据库建表的时候在ID字段添加主键约束,用户名、邮箱、电话等字段加唯一性约束。确保数据库只可以添加一条数 据。 数据库加唯一性约束sql: alter table tableName_xxx add unique key uniq_xxx(field1, field2) 利用Session防止表单重复提交(推荐) 实现原理: 服务器返回表单页面时,会先生成一个subToken保存于session,并把该subToen传给表单页面。当表单提交时会带上 subToken,服务器拦截器Interceptor会拦截该请求,拦截器判断session保存的subToken和表单提交subToken是否一 致。若不一致或session的subToken为空或表单未携带subToken则不通过。 首次提交表单时session的subToken与表单携带的subToken一致走正常流程,然后拦截器内会删除session保存的 subToken。当再次提交表单时由于session的subToken为空则不通过。从而实现了防止表单重复提交。 使用AOP自定义切入实现 实现原理: 自定义防止重复提交标记(@AvoidRepeatableCommit)。 对需要防止重复提交的Congtroller里的mapping方法加上该注解。 新增Aspect切入点,为@AvoidRepeatableCommit加入切入点。 每次提交表单时,Aspect都会保存当前key到reids(须设置过期时间)。 重复提交时Aspect会判断当前redis是否有该key,若有则拦截。
Git——三大分区【工作区 / 暂存区 / 版本库】 远程仓库 工作区暂 add 暂存区 commit 版本库 push 远程仓库
一、目录操作 pwd 查看当前工作目录 clear 清除屏幕 cd ~ 当前用户目录 cd / 根目录 cd - 上一次访问的目录 cd .. 上一级目录 查看目录内信息 ll 查看当前目录下内容(LL的小写) 创建目录 mkdir aaa 在当前目录下创建aaa目录,相对路径; mkdir ./bbb 在当前目录下创建bbb目录,相对路径; mkdir /ccc 在根目录下创建ccc目录,绝对路径; 递归创建目录(会创建里面没有的目录文件夹) mkdir -p temp/nginx 搜索命令 find / -name 'b' 查询根目录下(包括子目录),名以b的目录和文件; find / -name 'b*' 查询根目录下(包括子目录),名以b开头的目录和文件; 重命名 mv 原先目录 文件的名称 mv tomcat001 tomcat 剪切命令(有目录剪切到制定目录下,没有的话剪切为指定目录) mv /aaa /bbb 将根目录下的aaa目录,移动到bbb目录下(假如没有bbb目录,则重命名为bbb); mv bbbb usr/bbb 将当前目录下的bbbb目录,移动到usr目录下,并且修改名称为bbb; mv bbb usr/aaa 将当前目录下的bbbb目录,移动到usr目录下,并且修改名称为aaa; 复制目录 cp -r /aaa /bbb 将/目录下的aaa目录复制到/bbb目录下,在/bbb目录下的名称为aaa cp -r /aaa /bbb/aaa 将/目录下的aa目录复制到/bbb目录下,且修改名为aaa; 强制式删除指定目录 rm -rf /bbb 强制删除/目录下的bbb目录。如果bbb目录中还有子目录,也会被强制删除,不会提示; 删除目录 rm -r /bbb 普通删除。会询问你是否删除每一个文件 rmdir test01 目录的删除 查看树状目录结构 tree test01/
检查是否已经安装过 服务器的防火墙mysql端口3306是否开放
从硬件层面:cpu、内存、磁盘读写效率、网卡流量 数据库引擎 服务器操作系统 linux参数设置 数据库参数设置 数据库表的设计SQL语句的执行效率(影响最大的):慢查询是性能问题的罪魁祸首,不合理的数据库表结构设计和不合理的索引是影响数据库查询性能的重要因素; 数据库架构:高并发下读写分离、分库分表、多级缓存、搜索引擎
1、尽量使用缓存技术, 2、同步转异步, 3、合并多个同类型请求为一个请求, 4、数据库方面,搭建数据库集群, 一、对于被频繁调用,更新频率较低的页面,可以采用HTML静态化技术 二、图片服务器分离 三、数据库集群和库表散列 mysql主从。m-m-s-s-s...(2个主,多个从。多个从使用负载均衡。主写入数据,从读取数据) 四、缓存。众多的缓存框架 五、负载均衡。nginx,lvs,F5 六、搜索用单独的服务器,搜索框架 七、使用MQ服务器
当前主流的方案有两种: 1)Redis 的 set 加锁+lua 脚本解锁方案,至于是不是用守护线程续命可以结合自己的场景去决定, 2)Zookeeper 方案 通常情况下,对于数据的安全性要求没那么高的,可以采用 Redis 的方案,对数据安全性要求比较高的可以采用 Zookeeper 的方案。
NoSQL,泛指非关系型的数据库
1.String.StringBuilder和StringBuffer有了解吗?
2.为什么不建议使用String做字符串拼接
3.equals和hashcode有了解吗?
4.equals为false,hashcode能为true吗?
5.有了解反射吗?
6.怎么进行sql调优?
7.索引类型有哪些?全文索引有使用过吗?
8.索引失效的情况?模糊查询都回导致索引失效吗?
9.redis的使用场景
10.redis使用的数据类型?
11.什么是spring?什么是springboot?
12.springboot中将对象交给容器管理的注解有了解吗?
13.如何保证消息得可靠投递?
14.使用队列遇到过哪些问题?
15.分布式事务使用了哪些?
16.微服务和传统服务得优缺点?
17.两个Integer类型赋值1,equals结果是什么?赋值200呢?
new integer(1)呢?
18.有了解过泛型吗?泛型可以用作参数列表吗?
1.讲一哈jdk,jre,jvm以及他们的区别
jdk:java开发环境 jre:java运行环境 jvm:java虚拟机 1.三者联系: JVM不能单独搞定class的执行,解释class的时候JVM需要调用解释所需要的类库lib。在JDK下面的的jre目录里面有两个文件夹bin和lib,在这里可以认为bin里的就是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib和起来就称为jre。JVM+Lib=JRE。总体来说就是,我们利用JDK(调用JAVA API)开发了属于我们自己的JAVA程序后,通过JDK中的编译程序(javac)将我们的文本java文件编译成JAVA字节码,在JRE上运行这些JAVA字节码,JVM解析这些字节码,映射到CPU指令集或OS的系统调用。 2.三者区别: a.JDK和JRE区别:在bin文件夹下会发现,JDK有javac.exe而JRE里面没有,javac指令是用来将java文件编译成class文件的,这是开发者需要的,而用户(只需要运行的人)是不需要的。JDK还有jar.exe, javadoc.exe等等用于开发的可执行指令文件。这也证实了一个是开发环境,一个是运行环境。 b.JRE和JVM区别:JVM并不代表就可以执行class了,JVM执行.class还需要JRE下的lib类库的支持,尤其是rt.jar。
2.你常用的集合有哪些?list集合中ArrayList和linklist的区别,你最常用的是哪个
3.Springioc是什么,有什么注入方式,最常用的注入方式是什么?说说你经常用的注解注入有哪些
4.springAop是什么?实现原理是什么,动态代理技术有哪些
5.怎么进行数据库中的优化的?mysql中有哪些索引?如果索引失效怎么办?
6.#{}和${}的区别,一般用哪个
7.==和equal的区别
8.HashMap和ConcurrentHashMap的区别,ConcurrentHashMap怎么实现线程安全的
9.你了解哪些锁,知道java中有哪些加了锁的类吗?
10.讲一讲微服务中的各个组件有什么作用
11.在项目中有没用到redis?在哪用到了?
12.讲一哈你了解的linux虚拟机的基础命令
1.以你的项目为背景,介绍你的项目详情,你在项目中的工作与贡献,并取得了什么成绩以及遇到了什么难题,是怎么解决的;
2.项目遇见的并发访问或同步控制的场景,一般是怎么解决的;
分布式锁
3.什么时候用synchronize好,什么时候用redis好;
单体项目使用synchronize 分布式项目使用redis
4.遇到多线程场景时,是怎么管理线程的;
线程池
5.平时在工作中会使用什么数据结构;
数组 链表 红黑树
6.介绍一下hasmap,谈谈你对他的理解以及应用;
7.为什么JDK1.8会修改成数组+链表+红黑树;
8.介绍一下JAVA类的加载机制;
9.平时用的中间件有哪些,介绍一下你对kafka的了解;
10.Redis在项目中能够解决哪些问题。
南天:
1.常用的mybatis标签及作用
一. 定义 sql 语句 select 标签 ,insert 标签 ,delete 标签 ,update 标签 二. 配置 JAVA 对象属性与查询结果集中列名对应关系:resultMap 标签 建立 SQL 查询结果字段与实体属性的映射关系信息 查询的结果集转换为 java 对象,方便进一步操作。 将结果集中的列与 java 对象中的属性对应起来并将值填充进去 三. 动态 sql 拼接: if 标签 通常用于 WHERE 语句、UPDATE 语句、INSERT 语句中,通过判断参数值来决定是否使用某个查询条件、判断是否更新某一个字段、判断是否插入某个字段的值。 foreach 标签主要用于构建 in 条件,可在 sql 中对集合进行迭代。也常用到批量删除、添加等操作中。 属性介绍: collection:collection 属性的值有三个分别是 list、array、map 三 种,分别对应的参数类型为:List、数组、map 集合。 item :表示在迭代过程中每一个元素的别名 index :表示在迭代过程中每次迭代到的位置(下标) open :前缀 close :后缀 separator :分隔符,表示迭代时每个元素之间以什么分隔 choose 标签 有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。MyBatis 提供了 choose 元素,按顺序判断 when 中的条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的 sql。类似于 Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。 if 是与(and)的关系,而 choose 是或(or)的关系。 四. 格式化输出 where 标签 当 if 标签较多时,这样的组合可能会导致错误 set 标签 没有使用 if 标签时,如果有一个参数为 null,都会导致错误。当在 update 语句中使用 if 标签时,如果最后的 if 没有执行,则或导致逗号多余错误。使用 set 标签可以将动态的配置 set关键字,和剔除追加到条件末尾的任何不相关的逗号。 trim 标签 六. 定义常量及引用 sql 标签 当多种类型的查询语句的查询字段或者查询条件相同时,可以将其定义为常量,方便调用。为求
2.url的使用规范(参数,项目中是怎么做的)
1、不能使用中文单词,最好使用有意义的英文单词,少用拼音。 2、层级不能超过三级。 例如:http://domain.com/xx/xx/xx/xx.html不被允许。 3、URL的参数不允许超过3个 4、URL全部小写 5、网站内部在链接到其他网页,尤其是主页时,只使用一种URL,即不允许同一个资源有多个URL。 6、不允许出现没有意义的下URL 例如:http://www.uxuexi.cn/123.html。谁也看不明白是什么意思。 7、如果是内容资源URL,不允许以参数的方法显示 例如:http://www.uxuexi.cn/user.html?userId=123 需要改成http://www.uxuexi.cn/user/123.html
3.#和$的区别
4.mysql的索引类型有哪些
MySQL目前主要有的索引类型为:普通索引、唯一索引、主键索引、组合索引、全文索引。
5.你对加班的看法
(另一个面试官)
6.lambda函数(排序,分组)
Collections.sort(userList,(u1,u2)->u2.getId()-u1.getId());
7.项目为什么要做成分布式
8.讲一下springboot的自动化配置
SpringBoot会读取classpath下的META-INF/spring.factories文件,其文件中我们可以添加配置好的类,SpringBoot会把这些类自动配置成bean加入的Spring容器中。
9.讲一下项目中redis用途
对数据库高并发读写的需求 对海量数据的高效率存储和访问的需求 对数据库的高可扩展性和高可用性的需求
10.MySQL索引的底层数据结构,你项目中是怎么设置索引
MySQL底层使用的是B+tree存储结构 非叶子节点存储索引和下一个子节点的地址 叶子结点存储所有的索引和数据
1.hashmap的底层数据结构,怎么确定数组位置,是否是线程安全的,哪些是线程安全的
jdk1.7数组加链表 1.8数组链表红黑树 hashcode值与数组长度取模 得到元素的数组位置
2.http请求和使用场景
GET: (1)get请求是用来获取数据的,就是说只是用来查询数据,不对服务器的数据进行任何的修改、添加、删除等操作,就可以使用get请求; (2)get请求会把请求的参数加在url后面,这样会产生安全问题,稍微懂点编程的人员都可以通过url来获取你网址的参数和值; (3)get请求的参数对于请求的url来说并没有大小限制,但是对于不同的浏览器可能会有不同的限制。 POST: (1)post请求一般是对服务器的数据的操作,常用来使用数据的提交,新增操作,这种请求会改变数据的种类等资源;几乎目前所有的提交操作都是通过post来实现; (2)post请求的请求参数一般都是放在请求体中,不会显示在url后面 (3)post请求的大小也会被服务器限制,这种限制的主要因素是服务器的处理能力。
3.使用where标签和直接用where的区别
md5是消息摘要算法
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分
CRUD 操作,更有强大的条件构造器,满足各类使用需求
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
常用注解:
1.@TableName添加@TableName(“t_user”),标识实体类对应的表,即可成功执行SQL语句 2.@TableId 在实体类中uid属性上通过@TableId将其标识为主键,即可成功执行SQL语句
@TableId的value属性\@TableId的type属性
详见Mybatis-Plus
1.继承Thread类重写run()方法
2.实现runnable接口重写run()方法
3.实现callable接口 结合futuretask使用 可以得到异步处理结果
4.线程池创建
4.ConcurrentHashMap底层怎么实现线程安全?在上什么地方加锁?看过它的源码吗?
mysql是典型的c/s(client/server)架构 存储引擎 分myisam和innodb myisam表级锁 不支持事务 innod支持行级锁 支持事务 他的底层是b+树
只有在你增删改查时匹配的条件字段带有索引时,innodb才会使用行级锁,在你增删改查时匹配的条件字段不带有索引时,innodb使用的将是表级锁。因为当你匹配条件字段不带有所引时,数据库会全表查询,所以这需要将整张表加锁,才能保证查询匹配的正确性。在生产环境中我们往往需要满足多人同时对一张表进行增删改查,所以就需要使用行级锁,所以这个时候一定要记住为匹配条件字段加索引。
join: 将两个表按照条件连接起来; left join: 以左边表为基准,看左边表的数据是否都包含在新生成的表中,若是:则和join一样;若不是:则用NULL和不包含的行组成新的行加入到新表中; right join: 以右边表为基准,看右边表的数据是否都包含在新生成的表中,若是:则和join一样;若不是:则用NULL和不包含的行组成新的行加入到新表中;
实例化 -> 属性赋值 -> 初始化 -> 销毁
1SpringBoot 可以看作是对 Spring 框架的扩展,可以快速搭建独立的 Spring 应用程序。
2它内嵌了 Tomcat 通过 Application 入口类快速运行整个项目
3提供了 Pom 简化 Maven 的配置 减少了大量的 XML 配置。SpringBoot 基于约定优于配置的理念,原先大量的配置都可以省去,并且不推荐 XML 配置,改为 Java 配置。主要的配置可以写在 apaplication.properties 文件中
4除了 application.properties 文件,还支持 yaml 格式的 application.yml 和 bootstrap.yml
差别在于配置文件的加载顺序不同。
Spring Boot 配置文件的加载顺序是 bootstrap.properties -> bootstrap.yml -> application.properties -> application.yml
微服务之间服务远程调用 dubbo大的三层分别为 Business(业务层)、RPC 层、Remoting,并且还分为 API 层和 SPI 层。
1.自定义一个注解,让它可用于 想要记录日志的方法上;
2.通过Aop 统一处理这些标记了自定义注解的类;
@Aspect
@Component
public class LogAspect {...}
3.在Aop通知中添加逻辑,获取操作日志想要记录的信息,最后添加到自己设计的操作日志表里去;功能完成;
动态代理 jdk动态代理(实现invocationhandler接口 代理类 调用方法被invoke()方法拦截进行方法增强)
CGLib动态代理(字节码技术可以不用接口 性能较高)
缓存 将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。 会话缓存 可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。 消息队列(发布/订阅功能) List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息。不过最好使用 Kafka、RabbitMQ 等消息中间件。 分布式锁实现 在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
redis持久化 AOF RDB AOF是指Redis每次接收到一条改变数据的命令时,它将把该命令写到一个AOF文件中(只记录写操作,不记录读操作) RDB是默认开启的,是指在指定的时间间隔内将内存中的数据集快照写入磁盘
笔试题
class PrintSuShu { public static void main(String[] args) { //方法一:根据素数的定义来遍历检查 //外层循环遍历被除数i(因为1既不是素数也不是和数,所以直接从2开始遍历) for (int i = 2; i <= 100; i++) { //定义一个逻辑值,初值为true boolean flag = true; //内层遍历除数j for (int j = 2; j < i; j++) { //判断是否存在j能整除i,若存在,则更改flag的值并跳出循环 if (0 == i % j) { flag = false; break; } } //根据flag的值判断是否输出i if (flag) { System.out.print(i + " "); } } System.out.println('\n' + "---------------------------");
2:统计整数二进制中1的个数.
public void count1{ public static void main(String[] args){ Scanner scan = new Scanner(System.in); int i = scan.nextInt(); String str = Integer.toBinaryString(i); str = str.replace("0",""); System.out.println(str.length()) } }
3:给定一个整数数组如:[2,5,8,9,3],实现快速排序算法实现升序排序,
public void sort{ public static void main(String[] args){ int[] arr = {2,5,8,9,3}; int i = for(int i=0;i4:一个楼梯有N个台阶,小明从台阶最底层地面上楼梯,小明一次可以最多跨三阶,问小明爬上顶一共有多少种步伐组合?
5:进程与线程的区别,?
6:内存分配时堆与栈的区别?
7:http状态码200,300,400,500,502的含义?
8:POST/PUT/PATCH请求方法区别?
9:简单写出一个哈希表的基础成员和方法
笔试: 1.list,map,set接口特点
List、Set是存储单列的数据集合,都继承与Collection接口。 Map是存储键值对这样的双列数据的集合,是个独立接口。 List中存储的数据是有序的,可以是重复的。 Set中存储的数据是无序的,且不允许重复。 Map中存储的数据是无序的,他的键是不允许重复的,值是可以重复的。2.String,String Buffer,String bulider区别
string不可变对象 每次改变String类型数据时,就会生成一个新的String对象String Buffer,String bulider可变 String bulider线程不安全效率高 String Buffer线程安全效率低3.java代码编写规范
类名首字母大写大驼峰 方法名首字母小写小驼峰 常量全大写用下划线分割 英文要做到见文知意4.BeanFactory和AppicationContoxt区别?
BeanFactory 仅提供了最基本的依赖注入支持,而 ApplicationContext 则扩展了BeanFactory ,提供了更多的额外功能。5.String mvc执行流程
第一步:发起请求到前端控制器 第二步:前端控制器请求HandlerMapping查找 Handler,可以根据xml配置、注解进行查找 第三步:处理器映射器HandlerMapping向前端控制器返回Handler 第四步:前端控制器调用处理器适配器去执行Handler 第五步:处理器适配器去执行Handler 第六步:Handler执行完成给适配器返回ModelAndView 第七步:处理器适配器向前端控制器返回ModelAndView,ModelAndView是springmvc框架的一个底层对象,包括Model和view 第八步:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名解析成真正的视图(jsp) 第九步:视图解析器向前端控制器返回View 第十步:前端控制器进行视图渲染,视图渲染将模型数据(在ModelAndView对象中)填充到request域 第十一步:前端控制器向用户响应结果6.数据库事务隔离级别?
读已提交 读未提交 可重复读 串行化7.什么是乐观锁,悲观锁?
乐观锁 当前线程每次去操作数据的时候都认为别人不会修改,更新的时候会判断别人是否会去更新数据,通过版本来判断,如果数据被修改了就拒绝更新 悲观锁 当前线程去操作数据的时候,总是认为别的线程会去修改数据,所以每次操作数据的时候都会上锁,别的线程去操作数据的时候就会 阻塞,比如synchronized;8.建立索引优缺点,以及为什么用索引?
建立索引的目的是为了减少磁盘IO的次数,加快查询的速率。索引是帮助MySQL高效获取数据的数据结构。9.手写线程安全单例模式?
public class Singleton { private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }面试:
1,数据库隔离级别及作用?
2.什么是脏读,幻读?
3,Spring boot自动装配
4,SQL调优(要提到实际情况来,不是说怎么调)
select尽量使用具体字段不要select*5.循环依赖怎么解决的?三级缓存来回答,循环依赖怎么出现的
虽说要初始化一个Bean,必须要注入Bean里的依赖,才算初始化成功,但并不要求此时依赖的依赖也都注入成功,只要依赖对象的构造方法执行完了,这个依赖对象就算存在了,注入就算成功了,至于依赖的依赖,以后再初始化也来得及(参考Java的内存模型)。
因此,我们初始化一个Bean时,先调用Bean的构造方法,这个对象就在内存中存在了(对象里面的依赖还没有被注入),然后把这个对象保存下来,当循环依赖产生时,直接拿到之前保存的对象,于是循环依赖就被终止了,依赖注入也就顺利完成了。
6.Spring bean生命周期
实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction
7,Spring AOP (这个回答要全面点,要把相关知识点全部回答上) 8.Spring ioc理解(回答ioc容器,控制反转思想) 9.业务场景:用户需要新增电话功能(要求从web端,接口,数据库三个方面来回答) 其他有些问题就记不住了,大概就这些
百汇世纪面试题
一创建线程有哪几种方式? 二当一个线程进入某个对象的一个synchronized的实例方法后,其它线程是否可进入此对象的其它方法?
1.其他方法前是否加了synchronized关键字,如果没加,则能。 2.如果这个方法内部调用了wait,则可以进入其他synchronized方法。 3.如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。 4.如果当前线程进入的synchronized方法是static方法,其他线程可以进入其他synchronized修饰的非静态方法;如果当前线程进入的synchronized方法是非static方法,其他线程可以进入其他synchronized修饰的静态方法。三 SpringBoot如何做全局异常处理
//新建一个GlobalExceptionHandler全局异常处理类,然后加上@ControllerAdvice注解即可拦截项目中抛出的异常 @ControllerAdvice @ResponseBody public class GlobalExceptionHandler { //打印log private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); //....... @ExceptionHandler(MissingServletRequestParameterException.class) @ResponseStatus(value= HttpStatus.BAD_REQUEST) public JsonResult handleHttpMessageNotReadableException(MissingServletRequestParameterException ex){ logger.error("缺少请求参数,{}",ex.getMessage()); return new JsonResult("400","缺少必要的参数"); } }四 RequestMapping和 GetMapping 的不同之处在哪里
@RequestMapping可以指定GET、POST请求方式 @GetMapping等价于@RequestMapping的GET请求方式 @GetMapping不支持consumes属性。consumes: 指定处理请求的提交内容类型(Content-Type), 1、目标范围不同 @RequestMapping 能注解在类(ElementType.Type)和方法(ElementType.Method)中 @GetMapping 只能注解方法(ElementType.Method) 2、功能不同 注解在方法时,@GetMapping 等价于 @RequestMapping(method= RequestMethod.GET)五 SpringBoot中如何读取配置
在 Spring Boot 中读取配置文件有以下 5 种方法: 使用 @Value 读取配置文件。 使用 @ConfigurationProperties 读取配置文件。 使用 @PropertySource 读取配置文件。 使用 Environment 读取配置文件。 使用原生方式读取配置文件。六 MyBatis 中${}取值和#{}取值的区别
$是字符串拼接会产生sql注入 #是预编译七 MyBatis一级缓存和二级缓存的区别
一级缓存是默认开启的,是SqlSession级别的缓存, 在操作数据库时需要构造sqlSession对象,不同的sqlSession之间的缓存数据区域是互相不影响的,缓存是互相不能读取。 二级缓存namespace级别手动开启在xml中进行配置 多个SqlSession可以共用二级缓存八 MyBatis 的动态SQL标签有哪些?
Mybatis 提供了 9 种动态 sql 标签:trim | where | set | foreach | if | choose| when | otherwise | bind。九讲一下你理解的 Redis,为什么 Redis很快
1数据基于内存操作 2网络请求模块和 数据操作模块是单线程的。3十怎么保证Redis和数据库的一致性
十一 Ribbon和Feign的区别
1.启动类使用的注解不同,Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients。 2.服务的指定位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。 3.调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。 Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可, 不需要自己构建http请求。不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致。十二 springcloud 断路器的作用
我们可以把Hystrix当成是一个“断路器”,当我们某个服务单元发生故障之后,通过断路器的故障监控 (类似熔断保险丝) ,向调用方返回一个服务预期的,可处理的备选响应 (FallBack) ,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。 服务降级 服务熔断 服务限流 接近实时的监控十三用代码实现一个生产者和消费者模式
成都坊间里网络科技有限公司面试题:
1.java中如何实现多线程?
2.线程的 run()和 start()有什么区别?
3.什么是线程安全?
4.怎么保证多线程的运行安全问题?
5.什么是springAOP?
6.java的基本数据类型
7.java里面的wait方法和sleep方法?
8.springboot的自动装配?
9.HashMap的扩容机制?
10.HashMap扩容后的时间复杂度?
11.redis是一个什么类型的数据库?
12.redis中的数据结构?
13.如何排查多个sql执行慢?
14.如何解决sql执行慢?
15.sql的三范式?
16.mysql中有哪几种索引?
17.什么情况下不走索引,直接进行全表查询?
18.Ajax在项目中做了什么?
19.mybatis中的二级缓存和三级缓存?
20.ES中怎么拆分索引?
21.Quartz在项目中做什么?
22.线程和进程的区别?
23.多线程的实现方式?
面向对象
面向对象,它是一种编程思想。聊到面向对象,我们需要聊一下面向过程的编程方式,因为面向对象是从面向过程过渡而来的。面相过程的思维方式,它更加注重这个事情的每一个步骤以及顺序。他比较直接高效,需要做什么可以直接开始干。面向对象的思维方式,它更加注重事情有哪些参与者,需求里面有哪些对象,这些对象各自需要做些什么事情。将其拆解成一个个模块和对象,这样会更易于维护和拓展。面向对象的三大特性:封装、继承和多态。 1 什么是封装呢? 通过访问修饰符(如 private)来修饰成员变量和成员方法,将不需要对外提供的内容都隐藏起来,提供公共方法对其访问。 封装的好处是: 隐藏实现细节,提供公共的访问方式 提高了代码的复用性 提高安全性 2 面向对象的第二大特性:继承 什么是继承呢? 在 Java 中子类使用关键词 extend 去继承父类的关系。 继承主要用途是将子类存在共性的东西,把它抽取出来放到父类里面,比如将共同拥有的属性和方法抽取出来放到父类里面。 继承的好处: 想要使用这些属性和方法的时候,可以直接去使用父类的,而不需要自己再重新去定义,更大程度的实现代码复用。 我们不需要写很多的冗余的代码,把共性的全部抽到父类,可以直接调用,如果需要个性化自定义子类的方法时,去重写父类的方法即可。 3 面向对象的第三大特性:多态 什么是多态呢? 多态是同一个行为具有多个不同表现形式或形态的能力。 多态就是同一个接口,使用不同的实例而执行不同操作 多态的理解稍微有点抽象,解释一下: 多态是和继承一脉相承的,多态存在的需要有三个必要条件:继承、重写、父类引用指向子类对象。防止超卖
第1种方案:使用mysql的事务加排他锁来解决,首先我们选择数据库的存储引擎为innoDB,使用的是排他锁实现的 对数据库的性能影响很大,导致数据库的压力很大。 第2种方案:使用文件锁实现。当用户抢到一件促销商品后先触发文件锁,防止其他用户进入,该用户抢到促销品后再解开文件锁,放其他用户进行操作。这样可以解决超卖的问题,但是会导致文件得I/O开销很大。 第3种方案:使用redis的setnx来实现锁机制。但是并发大的情况下,锁的争夺会变多,导致响应越来越慢。(与第四种方案类似) 第4种方案:redis的队列来实现。将要促销的商品数量以队列的方式存入redis中,每当用户抢到一件促销商品则从队列中删除一个数据,确保商品不会超卖。这个操作起来很方便,而且效率极高除了synchronized还知道哪些锁?详情https://blog.csdn.net/qq_35958391/article/details/124767267
乐观锁 CAS 悲观锁 synchronized 自旋锁 CAS 可重入锁 synchronized、Reentrantlock、Lock 读写锁 ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet 公平锁 Reentrantlock(true) 非公平锁 synchronized、reentrantlock(false) 共享锁 ReentrantReadWriteLock中读锁 独占锁 synchronized、ReentrantReadWriteLock中写锁 重量级锁 synchronized 轻量级锁 锁优化技术 偏向锁 锁优化技术 分段锁 concurrentHashMap(jdk7) 死锁 相互请求对方的资源 锁粗化 锁优化技术 锁消除 锁优化技术 线程生命周期
新建 就绪 运行 阻塞 死亡类的生命周期
加载、连接(验证 准备 解析)、初始化、使用、和卸载五个阶段什么是倒排索引:
在搜索引擎中,每个文档都有一个对应的文档 ID,文档内容被表示为一系列关键词的集合。例如,某个文档经过分词,提取了 20 个关键词,每个关键词都会记录它在文档中出现的次数和出现位置。那么,倒排索引就是 关键词到文档 ID 的映射,每个关键词都对应着一系列的文件,这些文件中都出现了该关键词。有了倒排索引,搜索引擎可以很方便地响应用户的查询。 要注意倒排索引的两个重要细节: 倒排索引中的所有词项对应一个或多个文档 倒排索引中的词项 根据字典顺序升序排列run()和start()有什么区别
start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
new 一个 Thread,线程进入了新建状态。调用 start() 方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。 总结: 调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。 如何实现分布式锁
基于数据库实现分布式锁; 方案1 表结构 对某字段做了唯一性约束,如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功。 基于缓存(Redis等)实现分布式锁; 获取锁使用命令: SET resource_name my_random_value NX PX 30000 基于Zookeeper实现分布式锁;堆中对象创建过程
1 通过new指令创建对象时 会先检查new指令参数在原空间中是否能定位到一个类的符号引用 定位到的话 检查这个符号参数代表的类是否被加载 解析 初始化过 没有则进行类加载 2 类加载检查通过后为新对象分配内存 在堆空间空闲内存划分一块区域(基于指针碰撞 空闲列表算法分配)CAS加失败重试算法保证线程安全 3 进行初始化 填充对象头 (是哪个类的实例 如何找到类的元数据信息 对象的哈希码 gc分代年龄信息) 4 执行new指令的init方法后才算创建完成常见面试题:Dubbo的注册发现流程
1.首先服务的提供者启动服务时,将自己的具备的服务注册到注册中心,启动包括当前提供者的ip地址和端口号等信息,Dubbo会同时注册该项目提供的远程调用的方法 2.消费者(使用者)启动项目,也注册到注册中心,同时从注册中心中获得当前项目具备的所有服务列表 3.当注册中心中有新的服务出现时,会通知已经订阅发现的消费者,消费者会更新所有服务列表 4.RPC调用,消费者需要调用远程方法时,根据注册中心服务列表的信息,只需服务名称,不需要ip地址和端口号等信息,就可以利用Dubbo调用远程方法了分布式和微服务有什么不同
简单的说,微服务是架构设计方式,分布式是系统部署方式,两者概念不同
多态的概念
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
什么时候用try..catch,什么时候用throw和throws
原则:如果该功能内部可以将问题处理,用 try,如果处理不了,交由调用者处理,这是用 throws 区别: 当前程序需要继续运行就 try 当前程序不需要继续运行就throwserror和exception的区别?
Error类和Exception类的父类都是Throwable类,他们的区别如下: ● Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。 ● Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。 ●Exception类又分为未检查异常(UnCheckedException)和受检查的异常(CheckedException)。运行时异常ArithmeticException,IllegalArgumentException编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而受检查的异常,要么用 try…catch 捕获,要么用[throws](https://so.csdn.net/so/search?q=throws&spm=1001.2101.3001.7020)字句声明抛出,交给它的父类处理,否则编译不会通过。单例模式线程安全吗?
分情况 单例模式分为 饿汉模式 懒汉模式 双验锁模式 饿汉模式 优点:线程安全,效率比懒汉快 缺点:它不管是是否被调用都会提前创建类的实例,所以资源利用率比较低,系统加载时间比较长 懒汉模式 优点:实现了延迟加载 缺点:线程不安全,需要克服多线程同时访问的问题,需要通过双检锁(双重检查锁定机制)来进行控制,导致性能受到一定影响。 双验锁模式 优点:线程安全 缺点:因为加锁了,性能受到影响Git——三大分区 工作流
Git——三大分区【工作区 / 暂存区 / 版本库】 远程仓库 工作区暂 add 暂存区 commit 版本库 push 远程仓库Session结构
Session的本质是一个MAP结构的数据,当客户端首次向服务器端提交请求时,服务器端会响应一个Session ID到客户端,客户端在后续的访问中,都会在请求中自动携带此Session ID,同时,服务器端的内存中会存在每个Session ID对应的Session数据,从而,每个客户端都可以访问到自己的此前存入的数据。关于Session与Token:
Session默认是保存在服务器的内存中的数据,会占用一定的服务器内存资源,并且,不适合集群或分布式系统(虽然可以通过共享Session来解决),客户携带的Session ID只具有唯一性的特点(理论上),不具备数据含义……而Token的本质是将有意义的数据进行加密处理后的结果,各服务器都只需要具有解析这个加密数据的功能即可获取到其中的信息含义,理论上不占用内存资源,更适用于集群和分布式系统,但是,存在一定的被解密的风险(概率极低)。
酷鲨商城前台业务总结
"我负责的功能"
登录,注册
显示商品分类(自关联三级分类树)
显示商品列表
显示商品详情
购物车管理(添加购物车,删除购物车,修改购物车数量)
生成订单(减少库存,删除购物车,新增订单,新增订单项,修改订单状态等)
搜索商品(使用ES完成根据关键字完成全文搜索)
商品秒杀功能(缓存预热库存\随机码\布隆过滤器,检查重复购买和防止超卖,生成订单,消息队列)
项目的模块和我负责的模块
分类信息模块和商品显示模块(front)
购物车和订单(order)
搜索模块(search)
秒杀模块(seckill)
单点登录SSO(passport)
没做的模块(不建议写):
支付模块,
物流模块,
客服模块,
评论模块
三级分类树
本项目使用固定的三级分类树
是自关联分类(所有分类信息在一张表中)
实现思路
1.一次性查询出所有分类对象(List集合)
2.遍历集合将当前分类对象以父分类id为Key,以当前对象作为值,保存在一个Map中,这个Map对象
的Key(父级分类ID)对应的value,会包含它的所有子分类对象
3.遍历所有分类对象,以当前分类对象id为key,从Map中获取它的子分类,关联到三级分类树对象中
最后返回包含三级分类树结构的集合
4.查询返回之前,将三级分类树保存到Redis,以便以后的请求高效获取
代码如下
@Override public List
listWithTree() { //因为集成了mp框架所提供的ServiceImpl类,并指定泛型为CategoryDao,所以我们可以直接使用 //查询所有的菜单 List categoryEntityList = baseMapper.selectList(null); System.out.println(categoryEntityList.size()); //找到所有的菜单 List level1Menus = categoryEntityList.stream() //过滤出父级一级菜单 .filter(categoryEntity -> categoryEntity.getParentCid() == 0) .map(categoryEntity -> { //获取当前一级菜单中的子菜单 List childList = getChild(categoryEntity, categoryEntityList); categoryEntity.setChild(childList); return categoryEntity; }) //进行排序 .sorted(Comparator.comparingInt(CategoryEntity::getSort)) .collect(Collectors.toList()); return level1Menus; } /** * 获取当前菜单中的子菜单 * * @param root 当前菜单 * @param all 需要遍历获取的菜单集合 * @return 子菜单列表 */ private List getChild(CategoryEntity root , List all) { List childList = all.stream() //这里有个坑要注意,如果两个比较的对象是Integer类型,最好还是要用equals来进行比较 .filter(categoryEntity -> root.getCatId().equals(categoryEntity.getParentCid())) .map(categoryEntity -> { categoryEntity.setChild(getChild(categoryEntity, all)); return categoryEntity; }) //排序 .sorted(Comparator.comparingInt(CategoryEntity::getSort)) .collect(Collectors.toList()); return childList; } 如何实现spu列表
可能是通过分类id查询出spu列表
也可能是搜索功能搜索出的spu列表
显示它们的注意事项就是分页(JsonPage)
分类id查询数据库,分页是PageHelper
搜索查询是ES,分页SpringData
如何显示一个商品的详情
商品详情页面有4个查询
1.SpuId查询spu表中信息显示的内容有默认价格\title\name\默认图片等
2.SpuId查询spu_detail表中信息,内容是商品详情大图片
3.根据SpuId查询商品的所有属性
是先用spuid关联到分类id,再由分类id关联到属性id,在获得属性id包含的所有属性,是一个关联查询
如果是一个智能手机分类下的spu,能够查询到例如内存\处理器\颜色等规格属性
4.根据spuId查询Sku列表
只有查询到Sku列表,才知道具体的真实价格\图片\库存的情况
当选择对应规格属性时,才能知道有货无货
如何实现购物车的管理
用户在商品详情页选择属性之后,能够确定sku
将用户选中的sku保存在购物车中,未登录前提下 服务器生成一个cookie 包含一个uuid 可以利用uuid去生成订单等待用户登录后在合并 利用cookie的uuid合并 检查登录后购物车有没有临时购物车中的商品 有的话就添加数量即可
身份认证在控制器方法前添加@PreAuthorize("hasRole('ROLE_user')") SpringSecurity单点登录
我们新增到购物车中的商品要检查是否已经在购物车中,如果不在新增到购物车,如果在的话,修改数量即可
删除或清空购物车功能就是按照购物车id进行操作即可
修改购物车中商品数量时,可以判断一下库存是否允许,如果没有库存就修改失败
生成订单功能如何实现
用户选好了商品,或勾选了购物车中购买的商品
就可以进行订单的生成了,
在用户已经登录的前提下
首先减少库存数,如果用户从购物车勾选,删除用户勾选购物车的商品
然后开始收集各种数据,使用Leaf生成唯一的id,
单价和购买的数量,生成订单对象同时也生成订单项对象
一个订单中可能包含多个商品,计算总价,包含运费和优惠的处理
所有数据收集完毕之后,新增到数据库
我们利用Dubbo去修改sku库存信息,其他修改都是本模块的功能
任何数据库操作失败都要抛出发生异常
我们可以利用分布式事务seata来根据运行状态决定最终要提交还是回滚
保证订单生成之后数据的完整性
搜索功能如何实现
我们使用Elasticsearch全文搜索引擎实现搜索功能
先创建\配置关联Es的实体类
我们可以使用logstash实现数据库和Es信息的同步
(也可以编写代码分页查询所有表中信息在分批增到ES中,只是后续同步数据比较麻烦)
搜索功能本身使用SpringDataElasticsearch实现
将用户输入的关键字获取到Es中进行查询
将查询到的Spu列表返回给前端即可
订单
1. 跳转订单确认
获取收货地址、获取用户选中的商品项及库存、获取会员积分、设置订单令牌。
2. 生成订单
校验防重令牌
创建订单
验证价格
保存订单
锁库存
消息队列-库存解锁
删除购物车
秒杀
1. 缓存预热
缓存活动信息
缓存商品信息
设置随机码
设置信号量
2. 开始秒杀
读取缓存的活动信息及商品信息
判断商品信息是否在秒杀时间段内
验证随机码
验证用户购买的数量是否超过秒杀商品的限制
减信号量,防止商品超卖
封装订单
调用订单服务生成订单
返回给前端一个响应
订单服务生成订单后通过websocket发送一条信息给前端,提示用户可支付订单。