某大厂技术面试之一(二)

接上文:某大厂技术面试之一(一) 记录7-19题   

题目:

7、#{} 和 ${}的区别

8、equals和”==“的区别

9、MQ的一对一,一对多怎么实现的 代码或者配置

10、&和&&的区别

11、redis的数据类型 

12、多线程IO密集型和CPU密集型的区别

13、left join和right join、inner join。

14、线程池参数:核心线程数和最大线程数的区别

15、mybatis的标签有那些

16、mybatis分页实现

17、Java8中lamda表达式的写法

18、表锁、行锁、页锁 的区别

19、Redis的淘汰策略

========================================================

7、#{} 和 ${}的区别

相同点:都可以获取map或者JavaBean中的信息、以及传入的值

不同点:

#{}

1.#是预编译处理(是什么)
2.mybatis在处理#的时候,会将sql中的#{}替换为?号,调用预编译语句(PreparedStatement)中的set注入参数(这也是为什么变成问号的原因)
3.会在sql中加上' '单引号,所以会相对安全,不会有sql注入问题
4.支持基本数据类型(八大数据类型,包装类,BigDecimal等等)

5.#方式能够很大程度防止sql注入
————————————————
${}

1.$是字符串替换(是什么)
2.mybatis在处理$的时候,会将$的变量,原原本本的赋值到sql里面
3.直接作为SQL本身,不会加单引号,所以有sql注入问题(案例:以前可以通过sql注入盗取qq)
4.不支持基本数据类型
怎么选取,要通过安全性去考虑
总的来说,作为排序或者分组字句获取参数值时使用$
其他作为子句获取参数值使用#

5.MyBatis排序时使用order by 动态参数时需要注意,用$而不是#

对于第4点的解释(支持基本数据类型的意思)
例如传入单个参数比如: void get(Long id),#{}取值时,可以使用任意的字符串,#{aaa},#{id}都可以取到 void get(Long id)参数的值, 但是${}取值就不能随意的字符串,必须要是id -${id}
简单的说就是:
#能识别void get(Long id)中的Long类型和参数名id,sql里面换了名字,#通过类型(Long)还能找到值
而$只能识别void get(Long id)中的参数名id,sql里面换了名字就不认识了

6..$方式一般用于传入数据库对象,例如传入表名.
————————————————

另外:什么是SQL注入

也就是假如有不法分子,知道了你这个地方是用$接值的,他直接可以在参数当中传入自己想要执行的sql,在管理员不知情的情况下实现非法操作。

 8、equals和”==“的区别

1、“==” 和equals都是用来判断两个对象是否相等的,返回值都是Boolean值

2、区别:

      1、基本类型的数据比较则使用“==”来比较,比较的是值。因为8种基本数据类型的变量,变量直接存储的是“值”,因此在用关系操作符==来进行比较时,比较的就是 “值” 本身。要注意浮点型和整型都是有符号类型的,而char是无符号类型的(char类型取值范围为0~2^16-1).

     2、当用"==" 来比较封装类型的数据时,比较的是存储对象的地址。

     3、对于equals方法,注意:equals方法不能作用于基本数据类型的变量

     4、当equals用来比较封装类型(类类型)的数据,比较的是变量本身的值。

         equals方法是基类Object中的方法,因此对于所有的继承于Object的类都会有该方法。为了更直观地理解equals方法的作用,直接看Object类中equals方法的实现。

     5、String常量存放在常量池中,Java虚拟机处于优化的考虑,会让内容一致的对象共享内存块,但变量是存放在堆空间中的,new定义的不同变量内存地址不会相同。

     6、String常量连接常量,还是常量,依然用常量池管理。变量连接常量则是变量。

总结来说:

  1)对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;

    如果作用于引用类型的变量,则比较的是所指向的对象的地址

  2)对于equals方法,注意:equals方法不能作用于基本数据类型的变量

    如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

    反之,诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

9、MQ的一对一,一对多怎么实现的 代码或者配置

 

10、&和&&的区别

java中&叫做按位与,&&叫做短路与,它们的区别是:

& :(1)按位运算符; (2)逻辑运算符

   & 既是位运算符又是逻辑运算符,&的两侧可以是int(按位运算),也可以是boolean(逻辑运算)表达式,当&两侧是int时,要先把运算符两侧的数转化为二进制数再进行运算,而短路与(&&)的两侧要求    必须是布尔表达式

   作为逻辑运算符时,&左右两端条件式有一个为假就会不成立,但是两端都会运行,比如(1+2)=4 &(1+2)=3;1+2=4即使为假也会去判断1+2=3是否成立。

&&:逻辑运算符

   &&也叫做短路运算符,因为只要左端条件式为假直接不成立,不会去判断右端条件式。

相同点:只要有一端为假,则语句不成立。总的来说&&执行效率要比&高一些,也更准确一些,所在在编写程序时,&&的使用率要比&高。

11、redis的数据类型以及一些常用命令

  • string 字符串(可以为整形、浮点型和字符串,统称为元素)
  • list 列表(实现队列,元素不唯一,先入先出原则)
  • set 集合(各不相同的元素)
  • hash hash散列值(hash的key必须是唯一的)
  • sort set 有序集合
string类型的常用命令:

自加:incr
自减:decr
加: incrby
减: decrby

list类型支持的常用命令:

lpush:从左边推入
lpop:从右边弹出
rpush:从右变推入
rpop:从右边弹出
llen:查看某个list数据类型的长度

 

 

 

 

set类型支持的常用命令:

sadd:添加数据
scard:查看set数据中存在的元素个数
sismember:判断set数据中是否存在某个元素
srem:删除某个set数据中的元素

 

 

 

hash数据类型支持的常用命令:

hset:添加hash数据
hget:获取hash数据
hmget:获取多个hash数据

 

 

 

sort set和hash很相似,也是映射形式的存储:

zadd:添加
zcard:查询
zrange:数据排序

 

几个基本的命令:

 

函数 说明
keys * 获得当前数据库的所有键
exists key [key ...] 判断键是否存在,返回个数,如果key有一样的也是叠加数
del key [key ...] 删除键,返回删除的个数
type key 获取键值的数据类型(string,hash,list,set,zset)
flushall 清空所有数据库
config [get、set] redis配置

12、多线程IO密集型和CPU密集型的区别

————————————————
参考自:https://blog.csdn.net/weixin_38399962/article/details/82622009

————————————————

IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。

I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力。

如果是一个磁盘或网络为主的程序(IO密集型)。一个线程处在IO等待的时候,另一个线程还可以在CPU里面跑,有时候CPU闲着没事干,所有的线程都在等着IO,这时候他们就是同时的了,而单线程的话此时还是在一个一个等待的。我们都知道IO的速度比起CPU来是慢到令人发指的。所以开多线程,比方说多线程网络传输,多线程往不同的目录写文件,等等。

此时 线程数等于IO任务数是最佳的。

=============================================

CPU密集型(CPU-bound)

CPU密集型也叫计算密集型(一个计算为主的程序),指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading很高。

在多重程序系统中,大部份时间用来做计算、逻辑判断等CPU动作的程序称之CPU bound。例如一个计算圆周率至小数点一千位以下的程序,在执行的过程当中绝大部份时间用在三角函数和开根号的计算,便是属于CPU bound的程序。

CPU bound的程序一般而言CPU占用率相当高。这可能是因为任务本身不太需要访问I/O设备,也可能是因为程序是多线程实现因此屏蔽掉了等待I/O的时间。

多线程跑的时候,可以充分利用起所有的cpu核心,比如说4个核心的cpu,开4个线程的时候,可以同时跑4个线程的运算任务,此时是最大效率。

但是如果线程远远超出cpu核心数量 反而会使得任务效率下降,因为频繁的切换线程也是要消耗时间的。

因此对于cpu密集型的任务来说,线程数等于cpu数是最好的了。
————————————————

CPU密集型 vs IO密集型

我们可以把任务分为计算密集型和IO密集型。

计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。

计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。

第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。

IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。

总之,计算密集型程序适合C语言多线程,I/O密集型适合脚本语言开发的多线程。13、left join和right join、inner join的区别

-left join(左联接) 返回包括 左表中的所有记录和右表中联结字段相等的记录 
-right join(右联接) 返回包括 右表中的所有记录和左表中联结字段相等的记录
-inner join(等值连接) 只返回两个表中联结字段相等的行 (注释:INNER JOIN 与 JOIN 是相同的。)
-FULL JOIN:只要其中一个表中存在匹配,则返回行(产生的结果是A和B的并集(如果没有相同的值会用null作为值))

  -FULL OUTER JOIN 关键字只要左表(table1)和右表(table2)其中一个表中存在匹配,则返回行.

  -FULL OUTER JOIN 关键字结合了 LEFT JOIN 和 RIGHT JOIN 的结果。

   SQL UNION 操作符合并两个或多个 SELECT 语句的结果。

   UNION 操作符用于合并两个或多个 SELECT 语句的结果集。

   请注意,UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同。

   SELECT column_name(s) FROM table1
   UNION
   SELECT column_name(s) FROM table2;

   注释:默认地,UNION 操作符选取不同的值。如果允许重复的值,请使用 UNION ALL。

   SQL UNION ALL 语法

    SELECT column_name(s) FROM table1
    UNION ALL
    SELECT column_name(s) FROM table2;

    注释:UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句中的列名。

left join以左表为参照物,返回包含左表中全部记录和右表中跟左表中字段对的上的所有数据,因此若是出现左表中有,但是右表中数据没有对应的情况,结果中也会将这部分数据展示出来,也就是说左表中的数据都会展示出来;

right join以右表为参照物,返回包含右表中的全部记录和左表中跟右表中字段对的上的所有数据,也就是说右表中的数据都会展示出来;
inner join是等值连接,返回的是两个表中能对应上的数据,不太恰当的比喻是,取的两表的交集.

自然连接(natural join)
自然连接将表中具有相同名称的列自动进行匹配,自然连接不必指定任何同等连接条件也不能认为指定哪些列需要被匹配,自然连接得到的结果表中,两表中名称相同的列只出现一次。
select * from employee natural join department;

内连接(inner join):产生的结果是A和B的交集(相同列里面的相同值)
内连接查询能将左表和右表中能关联起来的数据连接后返回,返回的结果就是两个表中所有相匹配的数据。
select * from TableA as A inner join TableB B on A.PK = B.PK;
select * from TableA as A inner join TableB B on A.PK > B.PK;

 交叉连接(cross join)
又称笛卡尔连接,交叉连接返回两个集合的笛卡尔积。
select * from TableA cross join TableB;

14、线程池参数:核心线程数和最大线程数的区别

Java线程池的构造函数如下:

public ThreadPoolExecutor(
  int corePoolSize, //线程池里的核心线程数量
  int maximumPoolSize, //线程池里允许有的最大线程数量
  long keepAliveTime, //空闲线程存活时间
  TimeUnit unit, //keepAliveTime的时间单位,比如分钟,小时等
  BlockingQueue<Runnable> workQueue, //缓冲队列
  ThreadFactory threadFactory, //线程工厂用来创建新的线程放入线程池
  RejectedExecutionHandler handler //线程池拒绝任务的处理策略,比如抛出异常等策略
) {
//... }

线程池策略

corePoolSize:核心线程数;maximunPoolSize:最大线程数
每当有新的任务到线程池时,
第一步: 先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在线程池中,不做销毁处理,若当前线程数量已达到corePoolSize,则进入下一步;

第二步: 判断工作队列(workQueue)是否已满,未满则将新的任务提交到工作队列中,满了则进入下一步;

第三步: 判断线程池中的线程数量是否达到了maxumunPoolSize,如果未达到,则新建一个工作线程来执行这个任务,如果达到了则使用饱和策略来处理这个任务。

注意: 在线程池中的线程数量超过corePoolSize时,每当有线程的空闲时间超过了keepAliveTime,这个线程就会被终止。直到线程池中线程的数量不大于corePoolSize为止。
(由第三步可知,在一般情况下,Java线程池中会长期保持corePoolSize个线程。)

饱和策略

当工作队列满且线程个数达到maximunPoolSize后所采取的策略
AbortPolicy:默认策略;新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。
CallerRunsPolicy:既不抛弃任务也不抛出异常,使用调用者所在线程运行新的任务。
DiscardPolicy:丢弃新的任务,且不抛出异常。
DiscardOldestPolicy:调用poll方法丢弃工作队列队头的任务,然后尝试提交新任务
自定义策略:根据用户需要定制。

15、mybatis的标签有那些

 

 

1. 定义sql语句
1.1 select 标签 
属性介绍:

id :唯一的标识符.
parameterType:传给此语句的参数的全路径名或别名 例:com.test.poso.User或user
resultType :语句返回值类型或别名。注意,如果是集合,那么这里填写的是集合的泛型,而不是集合本身(resultType 与resultMap 不能并用)
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="Object">
        select * from student where id=#{id}
</select>

1.2 insert标签
属性介绍:

id :唯一的标识符
parameterType:传给此语句的参数的全路径名或别名 
<insert id="insert" parameterType="Object">
        insert into student    <trim     prefix="("    suffix=")"    suffixOverrides="," >    
    <if test="name != null  "> NAME,  </if>    
    </trim>    <trim     prefix="values("    suffix=")"    suffixOverrides="," >
    <if test="name != null  ">  #{name},  </if>    
    </trim>
</insert>

1.3 delete标签
属性同 insert

<delete id="deleteByPrimaryKey" parameterType="Object">
        delete      from student where id=#{id}
</delete>

1.4 update标签
属性同 insert

2. 配置JAVA对象属性与查询结果集中列名对应关系
resultMap 标签的使用
基本作用:

建立SQL查询结果字段与实体属性的映射关系信息
查询的结果集转换为java对象,方便进一步操作。
将结果集中的列与java对象中的属性对应起来并将值填充进去
!注意:与java对象对应的列不是数据库中表的列名,而是查询后结果集的列名

<resultMap id="BaseResultMap" type="com.online.charge.platform.student.model.Student">
        <id property="id" column="id" />
        <result column="NAME" property="name" />
        <result column="HOBBY" property="hobby" />
        <result column="MAJOR" property="major" />
        <result column="BIRTHDAY" property="birthday" />
        <result column="AGE" property="age" />

</resultMap>
  <!--查询时resultMap引用该resultMap -->  
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="Object">
        select id,name,hobby,major,birthday,age from student where id=#{id}
</select> 

标签说明:

主标签:

id:该resultMap的标志
type:返回值的类名,此例中返回Studnet类
子标签:

id:用于设置主键字段与领域模型属性的映射关系,此处主键为ID,对应id。
result:用于设置普通字段与领域模型属性的映射关系

3. 动态sql拼接
3.1 if 标签
if标签通常用于WHERE语句、UPDATE语句、INSERT语句中,通过判断参数值来决定是否使用某个查询条件、判断是否更新某一个字段、判断是否插入某个字段的值。

<if test="name != null and name != ''">
         and NAME = #{name}
</if>
3.2 foreach 标签
foreach标签主要用于构建in条件,可在sql中对集合进行迭代。也常用到批量删除、添加等操作中。
 <!-- in查询所有,不分页 -->
    <select id="selectIn" resultMap="BaseResultMap">
        select name,hobby 
              from student where id in
        <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
            #{item}
        </foreach>
    </select>

属性介绍:

collection:collection属性的值有三个分别是list、array、map三种,分别对应的参数类型为:List、数组、map集合。
item :表示在迭代过程中每一个元素的别名
index :表示在迭代过程中每次迭代到的位置(下标)
open :前缀
close :后缀
separator :分隔符,表示迭代时每个元素之间以什么分隔

3.3 choose标签
  有时候我们并不想应用所有的条件,而只是想从多个选项中选择一个。MyBatis提供了choose 元素,按顺序判断when中的条件出否成立,如果有一个成立,则choose结束。当choose中所有when的条件都不满则时,则执行 otherwise中的sql。类似于Java 的switch 语句,choose为switch,when为case,otherwise则为default。

 if是与(and)的关系,而choose是或(or)的关系。

<select id="getStudentListChoose" parameterType="Student" resultMap="BaseResultMap">     
    SELECT * from STUDENT WHERE 1=1    
    <where>     
        <choose>     
            <when test="Name!=null and student!='' ">     
                   AND name LIKE CONCAT(CONCAT('%', #{student}),'%')      
            </when>     
            <when test="hobby!= null and hobby!= '' ">     
                    AND hobby = #{hobby}      
            </when>                   
            <otherwise>     
                    AND AGE = 15  
            </otherwise>     
        </choose>     
    </where>     
</select>   

4. 格式化输出
4.1 where标签
当if标签较多时,这样的组合可能会导致错误。 如下:
<select id="getStudentListWhere" parameterType="Object" resultMap="BaseResultMap">     
    SELECT * from STUDENT      
        WHERE      
        <if test="name!=null and name!='' ">     
            NAME LIKE CONCAT(CONCAT('%', #{name}),'%')      
        </if>     
        <if test="hobby!= null and hobby!= '' ">     
            AND hobby = #{hobby}      
        </if>     
</select> 
当name值为null时,查询语句会出现 “WHERE AND” 的情况,解决该情况除了将"WHERE"改为“WHERE 1=1”之外,还可以利用where标签。这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
<select id="getStudentListWhere" parameterType="Object" resultMap="BaseResultMap">     
    SELECT * from STUDENT      
       <where>   
         <if test="name!=null and name!='' ">     
            NAME LIKE CONCAT(CONCAT('%', #{name}),'%')      
         </if>     
         <if test="hobby!= null and hobby!= '' ">     
            AND hobby = #{hobby}      
         </if>  
       </where>        
</select>   
4.2 set 标签
没有使用if标签时,如果有一个参数为null,都会导致错误。当在update语句中使用if标签时,如果最后的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置set关键字,和剔除追加到条件末尾的任何不相关的逗号。
<update id="updateStudent" parameterType="Object">     
    UPDATE STUDENT   
       SET NAME = #{name},   
           MAJOR = #{major},
           HOBBY = #{hobby}
     WHERE ID = #{id};      
</update>   

<update id="updateStudent" parameterType="Object">     
    UPDATE STUDENT SET    
        <if test="name!=null and name!='' ">     
            NAME = #{name},      
        </if>     
        <if test="hobby!=null and hobby!='' ">     
            MAJOR = #{major},      
        </if> 
        <if test="hobby!=null and hobby!='' ">     
            HOBBY = #{hobby}    
        </if>          
    WHERE ID = #{id};      
</update>  
使用set+if标签修改后,如果某项为null则不进行更新,而是保持数据库原值。
<update id="updateStudent" parameterType="Object">     
    UPDATE STUDENT      
    <set>     
        <if test="name!=null and name!='' ">     
            NAME = #{name},      
        </if>     
        <if test="hobby!=null and hobby!='' ">     
            MAJOR = #{major},      
        </if> 
        <if test="hobby!=null and hobby!='' ">     
            HOBBY = #{hobby}    
        </if>     
    </set>     
    WHERE ID = #{id};      
</update>

4.3 trim标签
格式化输出,也可以通过trim标签设定或忽略前后缀来实现,详见我的另一博客

5. 配置关联关系
5.1 collection标签

5.2 association标签

6. 定义常量及引用
6.1 sql标签
当多种类型的查询语句的查询字段或者查询条件相同时,可以将其定义为常量,方便调用。为求<select>结构清晰也可将sql语句分解。

 <!-- 查询字段 -->
    <sql id="Base_Column_List">
        ID,MAJOR,BIRTHDAY,AGE,NAME,HOBBY
    </sql>

 <!-- 查询条件 -->
    <sql id="Example_Where_Clause">
        where 1=1
        <trim suffixOverrides=","> 
            <if test="id != null and id !=''">
                and id = #{id}
            </if>
            <if test="major != null and major != ''">
                and MAJOR = #{major}
            </if>
            <if test="birthday != null ">
                and BIRTHDAY = #{birthday}
            </if>
            <if test="age != null ">
                and AGE = #{age}
            </if>
            <if test="name != null and name != ''">
                and NAME = #{name}
            </if>
            <if test="hobby != null and hobby != ''">
                and HOBBY = #{hobby}
            </if>            
            <if test="sorting != null">
                order by #{sorting}
            </if>
            <if test="sort!= null  and sort != '' ">
                order by ${sort}   ${order}
            </if>

        </trim>
    </sql>

6.2 include标签
用于引用定义的常量

<!-- 查询所有,不分页 -->
    <select id="selectAll" resultMap="BaseResultMap">
        SELECT
        <include refid="Base_Column_List" />
        FROM
        student
        <include refid="Example_Where_Clause" />
    </select>
    
<!-- 分页查询 -->
    <select id="select" resultMap="BaseResultMap">
    
    
        select * from (
            select tt.*,rownum as rowno from 
                (
                    SELECT
                    <include refid="Base_Column_List" /> 
                    FROM
                    student
                    <include refid="Example_Where_Clause" />
                     ) tt 
                     <where>
                            <if test="pageNum != null and rows != null">
                            and  rownum  <![CDATA[<=]]>#{page}*#{rows}
                         </if>
                    </where>
             ) table_alias
            where table_alias.rowno>#{pageNum}
              
              
    </select>
<!-- 根据条件删除 -->
    <delete id="deleteByEntity" parameterType="java.util.Map">
    DELETE  FROM   student
    <include refid="Example_Where_Clause" />
    </delete>

16、mybatis分页实现原理

原理:

Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

 

MyBatis几种常见的分页实现方式
1、原始切分(最原始方法,不建议使用)----利用 List 的 subList() 方法对数据进行切片,控制层直接调用,即可完成分页

2、Limit 关键字 实现 查询数据数量count Pager.setPage() Pager.setSize()

3、interceptor plugin 实现 (拦截器插入) 若要使用 MyBatis 拦截器实现分页 必须使用 mybatis 3.2.0 以上版本 因为 3.2.0 版本以下 SystemMetaObject 类会报错,找不到该类。

4、RowBounds 实现分页
4.1、在DAO层(Mapper)定义好所要传输的分页信息,类型为RowBounds
List<DbStudents> selectListByPage (RowBounds rowBounds);
-------------------------------------------------------
4.2、DbStudentsMapper.xml 查询所有数据

<select id="selectListByPage" resultType="com.top.bean.DbStudents" >
select
<include refid="Base_Column_List"/>
from db_students
</select>
————————————————
4.3、业务层,将PageInfo信息封装成 RowBounds,调用DAO层方法
public List<DbStudents> selectListByPage(PageInfo info) {
return dbStudentsMapper.selectListByPage(new RowBounds(info.getPageNum(), info.getPageSize()));
}
————————————————
最后控制层直接调用即可实现分页查询。

5、PageHelper 实现
开源项目 PageHelper 实现,其实本质还是自己封装了个 Interceptor。

一、首先引入分页插件
引入有下面2种方式,推荐使用 Maven 方式。

1、引入 Jar 包
2、使用 Maven
<!-- 引入mybatis的 pagehelper 分页插件 -->

<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
————————————————
3、配置拦截器插件
特别注意,新版拦截器是 com.github.pagehelper.PageInterceptor。 com.github.pagehelper.PageHelper 现在是一个特殊的 dialect 实现类,是分页插件的默认实现类,提供了和以前相同的用法。
3.1、在 MyBatis 配置 xml 中配置拦截器插件
<!--
plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--reasonable:分页合理化参数,默认值为false,直接根据参数进行查询。
当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。-->
<!--<property name="reasonable" value="true"/>-->
</plugin>
</plugins>
————————————————
3.2、在 Spring 配置文件中配置拦截器插件
使用 spring 的属性配置方式,可以使用 plugins 属性像下面这样配置

 

17、Java8中的新特性

 

  • Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。

    •  Lambda表达式(也称为闭包)是Java 8中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理。

         Lambda表达式可以引用类成员和局部变量(会将这些变量隐式得转换成final的)

      • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
      • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
      • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
      • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

    • /**
       * 静态方法引用:ClassName::methodName
       * 实例上的实例方法引用:instanceReference::methodName
       * 超类上的实例方法引用:super::methodName
       * 类型上的实例方法引用:ClassName::methodName
       * 构造方法引用:Class::new
       * 数组构造方法引用:TypeName[]::new
       * Created by codecraft on 2016-02-05.
       */
  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。

  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。

  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。

    • /**
       * 主要接口
       * 1,predicate
       * 2,Unary/BinaryOperator:传入参数和返回值必然是同一种数据类型
       * 3,Int/Double/LongFunction/BiFunction:函数接口并不要求传入参数和返回值之间的数据类型必须一样
       * 4,Int/Long/DoubleConsumer/BiConsumer:消费数据
       * 5,Int/Long/DoubleSupplier:生产数据
       *
       * 主要方法:
       * 1,filter
       * 2,map
       * 3,reduce
       * 4,collect
       * 5,peek
       * -Djdk.internal.lambda.dumpProxyClasses
       * Created by codecraft on 2016-02-05.
       */
    • HashMap<String,String> map = list.stream().collect(Collectors.toMap(类::getId, e -> e, (k1, k2) -> k1));   将对象的List集合转化为map 
    • List<String> list = list1.stream.map(s->{ String s = "null";  return s;}).collect(Collectors.toList());
    • List<Field> uniqueFieldList = fieldList.stream().filter(v -> { Field feild = new Feild; return feild; }).collect(Collectors.toList());

    • list..stream().forEach(str->{});  流式遍历
  • Date Time API − 加强对日期与时间的处理。

  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

    • Optional.empty()
      • 参数:此方法不接受任何内容。

        返回值:此方法返回此Optional类的空实例。

    • Optional.ofNullable()   参数:此方法接受value作为类型T的参数,以使用此值创建Optional实例。 返回值:此方法返回具有指定类型的指定值的Optional类的实例。 异常:如果指定的值为null,则此方法将引发NullPointerException。
        1. public static <T> Optional<T> ofNullable(T value) { 
        2. return value == null ? empty() : of(value);
        3.  }
        相比较of(T value)的区别就是,当value值为null时,of(T value)会报NullPointerException异常;ofNullable(T value)不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。
    • boolean equals(Object obj) 判断其他对象是否等于 Optional。

    • Optional<T> filter(Predicate<? super <T> predicate)

      如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。

    • T get()

      如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException

    • int hashCode()

      返回存在值的哈希码,如果值不存在 返回 0。

    • void ifPresent(Consumer<? super T> consumer) 如果值存在则使用该值调用 consumer , 否则不做任何事情。

    • boolean isPresent()

      如果值存在则方法会返回true,否则返回 false。

    • Optional<T> of(T value)  返回一个指定非null值的Optional。
  • Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

  • 提升HashMaps的性能

    当hash冲突时,以前都是用链表存储,在java8里头,当节点个数>=TREEIFY_THRESHOLD - 1时,HashMap将采用红黑树存储,这样最坏的情况下即所有的key都Hash冲突,采用链表的话查找时间为O(n),而采用红黑树为O(logn)。

 

18、表锁、行锁、页锁 的区别

MySQL 按锁的粒度可以细分为行级锁、页级锁和表级锁。

参考自:http://c.biancheng.net/view/8096.html

我们可以将锁粒度理解成锁范围。

1)表级锁(table lock)

表级锁为表级别的锁定,会锁定整张表,可以很好的避免死锁,是 MySQL 中最大颗粒度的锁定机制。

一个用户在对表进行写操作(插入、删除、更新等)时,需要先获得写锁,这会阻塞其它用户对该表的所有读写操作。没有写锁时,其它读取的用户才能获得读锁,读锁之间是不相互阻塞的。

表级锁最大的特点就是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。当然,锁定颗粒度大带来最大的负面影响就是出现锁定资源争用的概率会很高,致使并发度大打折扣。

不过在某些特定的场景中,表级锁也可以有良好的性能。例如,READ LOCAL 表级锁支持某些类型的并发写操作。另外,写锁也比读锁有更高的优先级,因此一个写锁请求可能会被插入到读锁队列的前面(写锁可以插入到锁队列中读锁的前面,反之读锁则不能插入到写锁的前面)。

使用表级锁的主要是 MyISAM,MEMORY,CSV 等一些非事务性存储引擎。

尽管存储引擎可以管理自己的锁,MySQL 本身还是会使用各种有效的表级锁来实现不同的目的。例如,服务器会为诸如 ALTER TABLE 之类的语句使用表级锁,而忽略存储引擎的锁机制。

 

2)页级锁(page lock)

页级锁是 MySQL 中比较独特的一种锁定级别,在其他数据库管理软件中并不常见。

页级锁的颗粒度介于行级锁与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力同样也是介于上面二者之间。另外,页级锁和行级锁一样,会发生死锁。

页级锁主要应用于 BDB 存储引擎。

 

3)行级锁(row lock)

行级锁的锁定颗粒度在 MySQL 中是最小的,只针对操作的当前行进行加锁,所以行级锁发生锁定资源争用的概率也最小。

行级锁能够给予应用程序尽可能大的并发处理能力,从而提高需要高并发应用系统的整体性能。虽然行级锁在并发处理能力上面有较大的优势,但也因此带来了不少弊端。

由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也就更多,带来的消耗自然也就更大。此外,行级锁也最容易发生死锁。所以说行级锁最大程度地支持并发处理的同时,也带来了最大的锁开销。

行级锁主要应用于 InnoDB 存储引擎。

随着锁定资源颗粒度的减小,锁定相同数据量的数据所需要消耗的内存数量也越来越多,实现算法也会越来越复杂。不过,随着锁定资源颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也会随之提升。

 

  表级锁 行级锁 页级锁
开销 介于表级锁和行级锁之间
加锁 介于表级锁和行级锁之间
死锁 不会出现死锁 会出现死锁 会出现死锁
锁粒度 介于表级锁和行级锁之间
发度 一般

从上述特点可见,很难笼统的说哪种锁更好,只能具体应用具体分析。

从锁的角度来说,表级锁适合以查询为主,只有少量按索引条件更新数据的应用,如 Web 应用。而行级锁更适合于有大量按索引条件,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。

19、redis的淘汰策略

LRU(最近最少使用算法)是redis唯一支持的回收算法

当数据到达最大内存限制时(maxmemory),redis根据maxmemory-policy配置策略,来决定具体行为

noeviction:不删除策略,达到最大内存限制时刻,如果需要更多内存,直接返回错误信息

allkey-lru:所有key通用,优先删除最近最少使用的key(LRU)

volatile-lru:只限于设置了expire的部分,优先删除最近最少使用的key

allkey-random:所有key通用,随机删除一部分key

volatile-random:只限于设置了expire部分,随即删除一部分key

volatile-ttl:只设置了expire的部分;优先删除剩余时间短的key
在缓存的内存淘汰策略中有FIFO、LRU、LFU三种,其中LRU和LFU是Redis在使用的。

FIFO是最简单的淘汰策略,遵循着先进先出的原则(FIFO单向链表)

LRU算法
LRU(Least Recently Used)表示最近最少使用,该算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

LRU算法的常见实现方式为链表:

新数据放在链表头部 ,链表中的数据被访问就移动到链头,链表满的时候从链表尾部移出数据。

 

 

而在Redis中使用的是近似LRU算法,为什么说是近似呢?Redis中是随机采样5个(可以修改参数maxmemory-samples配置)key,然后从中选择访问时间最早的key进行淘汰,因此当采样key的数量与Redis库中key的数量越接近,淘汰的规则就越接近LRU算法。但官方推荐5个就足够了,最多不超过10个,越大就越消耗CPU的资源。

但在LRU算法下,如果一个热点数据最近很少访问,而非热点数据近期访问了,就会误把热点数据淘汰而留下了非热点数据,因此在Redis4.x中新增了LFU算法。
————————————————

在LRU算法下,Redis会为每个key新增一个3字节的内存空间用于存储key的访问时间;
LFU算法
LFU(Least Frequently Used)表示最不经常使用,它是根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。

LFU算法反映了一个key的热度情况,不会因LRU算法的偶尔一次被访问被误认为是热点数据。

LFU算法的常见实现方式为链表:

新数据放在链表尾部 ,链表中的数据按照被访问次数降序排列,访问次数相同的按最近访问时间降序排列,链表满的时候从链表尾部移出数据。
————————————————
https://blog.csdn.net/wsdc0521/article/details/106997623

 

 

过期删除策略
前面介绍的LRU和LFU算法都是在Redis内存占用满的情况下的淘汰策略,那么当内存没占满时在Redis中过期的key是如何从内存中删除以达到优化内存占用的呢? 

官网:https://redis.io/commands/expire#expire-accuracy 

在Redis中过期的key不会立刻从内存中删除,而是会同时以下面两种策略进行删除:

惰性删除:当key被访问时检查该key的过期时间,若已过期则删除;已过期未被访问的数据仍保持在内存中,消耗内存资源;
定期删除:每隔一段时间,随机检查设置了过期的key并删除已过期的key;维护定时器消耗CPU资源;
Redis每10秒进行一次过期扫描:

随机取20个设置了过期策略的key;
检查20个key中过期时间中已过期的key并删除;
如果有超过25%的key已过期则重复第一步;
这种循环随机操作会持续到过期key可能仅占全部key的25%以下时,并且为了保证不会出现循环过多的情况,默认扫描时间不会超过25ms;

AOF和RDB的过期删除策略
前面介绍了Redis的持久化策略RDB和AOF,当Redis中的key已过期未删除时,如果进行RDB和AOF的持久化操作时候会怎么操作呢?

在RDB持久化模式中我们可以使用save和bgsave命令进行数据持久化操作
在AOF持久化模式中使用rewriteaof和bgrewriteaof命令进行持久化操作
这四个命令都不会将过期key持久化到RDB文件或AOF文件中,可以保证重启服务时不会将过期key载入Redis。

为了保证一致性,在AOF持久化模式中,当key过期时候,会同时发送DEL命令给AOF文件和所有节点;

从节点不会主动的删除过期key除非它升级为主节点或收到主节点发来的DEL命令;
————————————————

====================

以上redis淘汰策略:参考自:https://blog.csdn.net/wsdc0521/article/details/106997623

posted @ 2021-03-23 21:20  Gentleman-cx  阅读(66)  评论(0编辑  收藏  举报