1、mysql事务:
一、事务的基本要素(ACID)
1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
二、事务的并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
三、MySQL事务隔离级别
1、mysql默认的事务隔离级别为repeatable-read
2、mysql设置事务隔离级别:
set session transaction isolation level 事务隔离级别(例如:SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 当前用户登录期间有效)
start transaction;
数据库语句
rollback(或者commit);
3、查看事务隔离等级
SELECT @@tx_isolation;
4、实例:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
START TRANSACTION;
INSERT INTO c_dish VALUES(
NULL,"fish",20.0,0.8 ,NULL
)
ROLLBACK;
2、Java HashMap 的底层实现:
HashMap特点及原理分析
特点
HashMap是java中使用最为频繁的map类型,其读写效率较高,但是因为其是非同步的,即读写等操作都是没有锁保护的,所以在多线程场景下是不安全的,容易出现数据不一致的问题。在单线程场景下非常推荐使用。
原理
HashMap的整体结构,如下图所示:
根据图片可以很直观的看到,HashMap的实现并不难,是由数组和链表两种数据结构组合而成的,其节点类型均为名为Entry的class(后边会对Entry做讲解)。采用这种数据结果,即是综合了两种数据结果的优点,既能便于读取数据,也能方便的进行数据的增删。
每一个哈希表,在进行初始化的时候,都会设置一个容量值(capacity)和加载因子(loadFactor)。容量值指的并不是表的真实长度,而是用户预估的一个值,真实的表长度,是不小于capacity的2的整数次幂。加载因子是为了计算哈希表的扩容门限,如果哈希表保存的节点数量达到了扩容门限,哈希表就会进行扩容的操作,扩容的数量为原表数量的2倍。默认情况下,capacity的值为16,loadFactor的值为0.75(综合考虑效率与空间后的折衷)。
数据写入。以HashMap(String, String)为例,即对于每一个节点,其key值类型为String,value值类型也为String。在向哈希表中插入数据的时候,首先会计算出key值的hashCode,即key.hashCode()。关于hashCode方法的实现,有兴趣的朋友可以看一下jdk的源码(之前看到信息说有一次面试中问到了这个知识点)。该方法会返回一个32位的int类型的值,以int h = key.hashCode()为例。获取到h的值之后,会计算该key对应的哈希表中的数组的位置,计算方法就是取模运算,h%table.length。因为table的长度为2的整数次幂,所以可以用h与table.length-1直接进行位与运算,即是,index = h & (table.length-1)。得到的index就是放置新数据的位置。
如果插入多条数据,则有可能最后计算出来的index是相同的,比如1和17,计算的index均为1。这时候出现了hash冲突。HashMap解决哈希冲突的方式,就是使用链表。每个链表,保存的是index相同的数据。
数据读取。从哈希表中读取数据时候,先定位到对应的index,然后遍历对应位置的链表,找到key值和hashCode相同的节点,获取对应的value值。
数据删除。 在hashMap中,数据删除的成本很低,直接定位到对应的index,然后遍历该链表,删除对应的节点。哈希表中数据的分布越均匀,则删除数据的效率越高(考虑到极端场景,数据均保存到了数组中,不存在链表,则复杂度为O(1))
3、如何理解面向对象
1、面向对象包括哪些特性,怎么理解的?
(1)封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
(2)继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
(3)多态:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当 A系统访问B系统提供的服务时,B 系统有多种提供服务的方式,但一切对 A 系统来说都是透明的。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做三件事:
第一:继承
第二:重写
第三:父类引用指向子类对象
(4)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
4、Vue 生命周期
5、vuex
1、vuex核心概念
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
// 提供唯一公共数据源,所有共享数据要放到state中
count: 0
},
mutations: {
// 修改state中count的方法,获取方法见组件Addition addOne方法中
// 不要再mutations中执行异步操作
add (state) {
state.count++
},
addN (state, step) {
state.count += Number(step)
}
},
actions: {
// 异步操作要放在actions中,但还是通过触发mutations中的方法来操作
addSync (context) {
setTimeout(() => {
// 通过形参获取上下文对象mutations,通过commit获取mutations中的函数,触发这个函数详见Subtraction组件handSync方法
context.commit('add')
}, 1000)
},
addStepSync (context, step) {
// actions携带参数
setTimeout(() => { context.commit('addN', step) }, 1000)
}
},
getters: {
// getter用于对store中的数据加工形成新的数据,getter类似于计算属性,会响应式的根据state中的数据变化,
// 使用getter的方式详见Subtraction组件中的
showNum (state) {
return 'state中的count值为' + state.count
}
},
modules: {
}
})
2、使用
<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
data: () => {
return {
input: 0,
getter: '111111111'
}
},
computed: {
// ...展开运算符将全局共享数据映射为本组件的展开运算符
...mapState(['count']),
// 使用getters的第二种方式:
...mapGetters(['showNum'])
},
methods: {
// 通过引入mapMutataions来获取mutations中的方法,引入的方法直接当做组件的普通方法使用即可
...mapMutations(['add', 'addN']),
// 触发actions的第二种方法
...mapActions(['addSync', 'addStepSync']),
addN () {
// 获取vuex中mutations的第一种方法:commit
this.$store.commit('addN', this.input)
},
handSync () {
// 触发actions的第一种方法
this.$store.dispatch('addSync')
},
handStepSync () {
// 触发actions携带参数
this.$store.dispatch('addStepSync', this.input)
},
handStepSyncAn () {
// 使用触发actions的第二种方法
this.addStepSync(this.input)
},
handleGetters () {
// 使用getters的第一种方式
this.getter = this.$store.getters.showNum
}
},
mounted () {
console.log(this.count)
}
}
</script>