美文网首页
Redis与mybatis

Redis与mybatis

作者: Tommmmm | 来源:发表于2018-09-19 18:10 被阅读0次

一、传统的缓存方式:

1、在函数执行前,先检查缓存中是否存在数据,如果存在则返回缓存数据
2、如果不存在,就需要在数据库的数据查询出来。
3、最后把数据存放在缓存中,当下次调用此函数时,就可以直接使用缓存数据,减轻了数据库压力。

二、redis的基本概念

1、什么是Redis
Redis本质上是一个Key-Value类型的内存数据库,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存

由于它的这一特性,Redis也成为一种流行的数据缓存工具

2、redis的优点:

(1) 速度快,因为数据存在内存中,类似于HashMap, HashMap的优势就是查找和操作的时间复杂度都是O(1 )

(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写。

3、Redis的数据类型
redis是一种高级的key:value存储系统,其中value支持五种数据类型:
1.字符串(strings)
2.字符串列表(lists)
3.字符串集合(sets)
4.有序字符串集合(sorted sets)
5.哈希(hashes)

String—list—sets

Redis是单进程单线程的


三、Redis的相关问题

1、为什么redis需要把所有数据放到内存中? 
为了达到最快的读写速度,并通过异步的方式写入磁盘。如果不把数据放入内存,磁盘的IO速度会严重影响redis的性能。

2、乐观锁与悲观锁
悲观锁:
总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起

乐观锁:
每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。

version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值

update table set x=x+1, version=version+1 where id=#{id} and version=#{version};  

3、redis的持久化
持久化:
Redis为了内部数据的安全考虑,会把本身的数据以文件形式保存到硬盘中一份,在服务器重启之后会自动把硬盘的数据恢复到redis的里边。

1:redis自身的持久化:

快照机制:在指定的时间间隔内将内存中的数据集快照写入磁盘。
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存。

优势:一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这样非常方便进行备份。
劣势: 一旦发生故障停机, 可能会丢失好几分钟的数据。

AOF:redis会将每一个收到的写命令都通过write函数追加到文件中。
需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

实际开发的选择:应该同时使用两种持久化功能。

2、如何保证Redis的数据和MySQL中的数据一致?

读请求:
不要求强一致性的读请求,走redis,要求强一致性的直接从mysql读取

写请求:
数据首先都写到数据库,之后更新redis

可以在MySQL端定义CRUD触发器,在触发CRUD操作后写数据到Redis

4、MySQL中的数据超过Redis的容量
redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。

volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。

5、 Redis解决高并发问题:————-————待解决
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问

1.客户端角度,为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。

2.服务器角度,利用setnx实现锁。
注:对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用synchronized也可以使用lock;第二种需要用到Redis的setnx命令,但是需要注意一些问题。

、Redis分布式锁



在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应。

在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。

Mybatis中的#与$的区别

1 #{变量名}可以进行预编译、类型匹配等操作

select * from tablename where id = #{id}

假设id的值为12,其中如果数据库字段id为字符型,那么#{id}表示的就是'12',如果id为整型,那么id就是12.

${变量名}不进行数据类型匹配,直接替换。

使用#在很大程度上可以防止sql注入。

其它的一些JAVA基础问题:
1:IO与NIO

IO—面向流—阻塞IO—无
NIO—面向缓冲—非阻塞IO—选择器

2:hibernate访问数据库慢

1、Hibernate的并发机制
Hibernate的Session对象是非线程安全的,对于单个请求,单个会话,单个的工作单元,它通常只使用一次, 然后就丢弃。

解决方案:设置事务隔离级别
串行化:隔离级别最高 :别的事务在旁边看的机会都没有
可重复读:读的时候加锁,别的线程不能修改,所以可以重复读。但是这种方法锁不住insert的数据
已提交数据读: 等别的线程修改完了,才能读。
Read Uncommitted:未提交数据读。隔离级别最差
乐观锁和悲观锁

2、在数据库中查询速度很慢怎么解决:
1.建索引
2.减少表之间的关联
3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排在前面
4.简化查询字段,没用的字段不要

3、Hibernate的优化:
懒加载:
Customer c1 = session.load(Customer.class, 1);
// Customer c1 = session.get(Customer.class, 1);
load方法采用的策略是延迟加载;get方法采用的策略是立即加载

LOAD:
User user=session.load(User.class, "1");
当调用load()方法的时候会返回一个目标对象的代理对象,在这个代理对象中只存储了目标对象的ID值,只有当调用除ID值以外的属性值的时候才会发出SQL查询的。

如果查询的数据非常大,例如说它里面有一些大的字段,这个时候建议采用load方法,不要一上来就立即加载,把内存占满,这样可以让我们的性能得到一部分的提升。

3、线程池 java.util.concurrent.Executors

1、 Thread 类中的start() 和 run() 方法有什么区别?
start()内部调用了run()方法
当调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

2、如果你提交任务时,线程池队列已满。会时发会生什么?
事实上如果一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常。

牛逼

3、atomic
atomic可以保证+-的原子性操作

4、singleton
单例对象的类必须保证只有一个实例存在
线程安全的懒汉模式

public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){

    }
    public static synchronized SingletonDemo getInstance(){
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }
}

5、线程池
一个线程池包括以下四个基本组成部分:
1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

ExecutorService pool=Executors.newCachedThreadPool();
 //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
     
        Thread t1=new MyThread();
        Thread t2=new MyThread();
        //将线程放到池中执行;
        pool.execute(t1);
        pool.execute(t2);
        //关闭线程池
       pool.shutdown();

Mybatis配置多数据源:

6、Mybatis的缓存机制 
mybatis的一级缓存:
SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中有一个HashMap用于存储缓存数据,此HashMap是当前会话对象私有的,别的SqlSession会话对象无法访问

具体流程:
1.第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来

2.第二次执行select会从缓存中查数据,如果select相同并且传参数一样,那么就能从缓存中返回数据,不用去数据库了,从而提高了效率

mybatis的二级缓存:
mapper级别的缓存
也就是同一个namespace的mapper.xml,当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个二级缓存区域

不同的 SqlSession 两次执行相同的 namespace 下的 sql 语句,会执行相同的 sql,第二次查询只会查询第一次查询时读取数据库后写到缓存的数据,不会再去数据库查询

二级缓存默认是没有开启的。需要在setting全局参数中配置开启二级缓存


相关文章

网友评论

      本文标题:Redis与mybatis

      本文链接:https://www.haomeiwen.com/subject/ywctnftx.html