赞
踩
假设index(a, b, c)
Where子句 | 索引是否被使用 |
---|---|
where a = 3 | Y,使用到a |
where a = 3 and b = 5 | Y,使用到a,b |
where a = 3 and b = 5 and c = 4 | Y,使用到a,b,c |
where b = 3 或者 where b = 3 and c = 4 或者where c = 4 | N |
where a = 3 and c = 5 | 使用到a,但是c不可以,b的地方断了 |
where a = 3 and b > 4 and c = 5 | 使用到a和b,c不能用在范围之后,在b之后就已经断了 |
where a is null and b is not null | is null支持索引,但是is not null不支持,所以a可以使用索引,但是b不一定能使用上索引 (8.0上的情况) |
where a <> 3 | 不能使用索引 |
where abs(a) == 3 | 不能使用索引 |
where a = 3 and b like ‘kk%’ and c = 4 | Y,使用到a,b,c |
where a = 3 and b like ‘%kk’ and c = 4 | Y,只用到a |
where a = 3 and b like ‘%kk%’ and c = 4 | Y,只用到a |
where a = 3 and b like ‘k%kk%’ and c = 4 | Y,使用到a,b,c |
// 索引失效
select * from emp where left(emp.name, 3) = 'abc';
// 索引失效
select * from emp where name like '%ab%';
select sql_no_cache * from emp where emp.name <> 'abc';
// name是字符串类型的
select * from emp where name = 123;
Mysql页默认是16KB,假设主键加上页内链表指针一共是16字节(二者分别都是8字节),则一页可以保存1000条数据,三层就是1000 * 1000 * 1000 = 10亿 条数据。
第三层为叶子页,假设每页保存不低于10条,三层就可以容纳1000万数据。
https://www.cnblogs.com/JCpeng/p/15231338.html
https://juejin.cn/book/6844733769996304392/section/6844733770071801870
1NF:字段拆分到不可拆分为止
2NF:每一行数据的唯一性
3NF:表之间关联主键依赖
降低耦合度,节省存储空间
指的是各种starter,重点是pom.xml,其中包含了框架所需要的其他依赖和默认配置文件,不需要使用方手动配置了。
构造注入的方式下会产生循环依赖的问题。 AB循环依赖问题只要A的注入方式是Set注入,就不会有循环依赖问题。
Spring容器循环依赖报错是BeanCurrentlyInCreationException。
默认的单例场景是支持循环依赖的解决的,不会报错。 原型场景不支持循环依赖,会报错。
三级缓存就是DefaultSingletonBeanRegistry
类中的三个map。一级缓存(也叫单例池)singletonObjects:存放已经经历了完整生命周期的bean对象。二级缓存earlySingletonObjects,存放早期暴露出来的bean对象,Bean的生命周期未结束(属性还未填充完毕)。第三级缓存singletonFactories,存放可以生成bean的工厂。
1 A创建的过程中需要B,于是将A放到三级缓存中,去实例化B
2 B实例化的时候发现需要A,于是B先去查一级缓存,没有再查二级缓存,还没有,查询三级缓存,在三级缓存中找到了A,然后把三级缓存中的A挪到了二级缓存中。
3 B初始化完毕,将自己放到一级缓存中(此时B里面的A仍然是创建中的状态)。然后回来接着创建A,此时B已经创建结束,直接可以从单例池中获取到B,然后完成创建。并将A挪到一级缓存里面。
关键的四个方法:
getSingleton
doCreateBean
populateBean
addSingleton
详细过程的文字描述:
1 调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA
2 在getSingleton()方法中,从一级缓存中查找,没有就返回null
3 doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
4 在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的createBean方法。
5 进入AbstractAutowireCapableBeanFactory#doCreateBean,先反射调用构造器创建出beanA的实例,然后判断:是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否在第4步的集合中)。判断为true则将beanA添加到【三级缓存】中(缓存中放的是FactoryBean)
6 对beanA进行属性填充,此时检测到beanA依赖beanB,于是开始查找beanB
7 调用doGetBean()方法,和beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性。
8 此时beanB依赖beanA,调用getSingleton()获取beanA,依次从一级、二级和三级缓存中查找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面流程中doCreateBean()方法中实例化的beanA
9 这样beanB就可以依赖beanA了,beanB顺利完成了初始化,并将beanA从三级缓存中升级到了二级缓存。
10 beanA继续完成它的属性填充工作,此时也获取到了beanB,beanA完成了创建,回到getSingleton方法中继续执行,将beanA从二级缓存中移动到一级缓存中。
【Spring4版本,对应SpringBoot 1.x,正常业务逻辑执行】
环绕通知(前)
Befor通知
业务逻辑
环绕通知(后)
Atfter通知
AtfterReturning通知
【Spring4版本,对应SpringBoot 1.x,出现异常的情况】
环绕通知
Before
After
AfterThrowing
【Spring5版本,对应SpringBoot 2.x,正常业务逻辑执行】
环绕通知
before
业务逻辑
AtfterReturning通知
After
环绕通知
【Spring5版本,对应SpringBoot 2.x,异常业务逻辑执行】
环绕通知
Before
AfterThrowing
After
try {
@Before
method.invoke(obj, args);
@AfterReturning
} catch () {
@AfterThrowing
} finaly {
@After
}
ConcurrentHashMap不允许插入为null的key\value,get也不可以,会包nullpointerException。
hashtable,concurrenthashmap它们是用于多线程的,并发的 ,如果map.get(key)得到了null,不能判断到底是映射的value是null,还是因为没有找到对应的key而为空,而用于单线程状态的hashmap却可以用containKey(key) 去判断到底是否包含了这个null。
hashtable为什么就不能containKey(key) ??因为一个线程先get(key)再containKey(key),这两个方法的中间时刻,其他线程怎么操作这个key都会可能发生,例如删掉这个key
public class Singeton { private Singeton() { } private static Singeton singeton = null; public static Singeton getInstance() { if (singeton == null) return new Singeton(); else return singeton; } }
/** * 利用静态构造函数 */ public class Singeton { private Singeton() { } private static Singeton singeton = new Singeton(); public Singeton getSingeton() { return singeton; } }
wait和notify方法必须要在同步块或者方法里面使用,并且要成对出现。 否则会爆出IllegalMonitorStateException
先wait后notify才ok。
await和signal方法也是有着同样的问题。
线程必须要先获得并持有锁,必须在锁块(synchronized或者lock)中;
必须要先等待后唤醒。
通过park和unpark方法实现阻塞和唤醒线程的操作。 所有的方法都是静态方法。调用的都是Unsafe类里面的native方法。
LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于是1,0的开关,默认是0;调用一次unpark就会++变成1;调用一次park就会消费permit,permit–,同时park方法返回;如果调用park的时候,permit已经是0了,会阻塞。
每个线程都有一个相关的permit,permit最大值为1,重复调用unpark也不会累积凭证。
因为unpark获得了凭证,之后再调用park方法,park方法可以名正言顺地凭证消费,故不会阻塞。
因为凭证数量最多为1,连续调用两次unpark和调用一次unpark的效果是一样的,只会增加一个凭证;但是调用两次park需要消费两个凭证,证不够,不能放行。
【查看版本】
redis-server -v
进入到redis里面可以使用info
命令
【大小写】
redis命令是不区分大小写的,但是key是区分大小写的。
【查询使用】
help@类型名
【String】
set key value
, get key
mset key value key value ...
meget key key ...
数值增减
获取字符串长度
分布式锁
应用场景
商品编号、订单号采用incr命令生成。
点赞、喜欢等功能。
【hash】
此种数据结构类似于java中的Map<String, Map<Object, Object>>
简单的购物车可以用这个搞定。
【list】
一个人订阅的微信公众号的列表
【set】
抽奖程序
微信朋友圈点赞
微博好友关注的社交关系
QQ推送可能认识的人
【zset】
对商品的销售情况进行排序显示
抖音热搜
sychronized:一直要等到锁,容易导致线程的积压。
Lock:有tryLock的功能,更加灵活。
private static String REDIS_LOCK = "xxx";
String value = UUID.randomUUID().toString()+Thread.currentThread().getName();
// 此语句本身就具有原子性。
Boolean lockSucess = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value, 10L, TimeUnit.SECONDS);
finally {
if (stringRedisTemplate.opsForValue().get(REDIS_LOCK).equalsIgnoreCase(value)) {
stringRedisTemplate.delete(REDIS_LOCK);
}
}
Jedis jedis = RedisUtils.getJedis();
String script = "xxx, 这个脚本要从Redis的官网上面去找";
try {
Object o = jedis.eval(script, Collections.singletonList(REDIS_LOCK), Collections.singletonList(value));
if ("1".equals(o.toString())) {
System.out.println("----------delete redis lock ok.");
} else {
System.out.println("----------delete redis lock error.");
}
} finally {
if (jedis != null) {
jedis.close();
}
}
可以使用Redis自身的事务
while (true) { // 设置哨兵 stringRedisTemplate.watch(REDIS_LOCK); if (stringRedisTemplate.opsForValue().get(REDIS_LOCK).equalsIgnoreCase(value)) { // 启用事务 stringRedisTemplate.setEnableTransactionSupport(true); // 开始事务 stringRedisTemplate.multi(); stringRedisTemplate.delete(REDIS_LOCK); // 执行事务,返回事务的执行结果 List<Object> list = stringReidsTemplate.exec(); if (list = null) { continue; } } stringRedisTemplate.unWatch(REDIS_LOCK); break; }
如何确保lock的存续时间确实大于业务逻辑的执行时间
Redis锁如何续期的问题。
Redis集群是一个AP类型的集群,一致性得不到保证。
最终可以使用redlock的RedisSon
但是解锁的时候需要注意,再度做一些判断。
if (redissonLock.isLocked()) {
if (redissonLock.isHeldByCurrentThread()) {
redissonLock.unlock();
}
}
【查看和设置Redis的最大占用内存】
查看配置文件redis.conf
;也可以通过info memory
命令进行查看
如果要设置最大内存,可以修改配置文件maxmemory 104857600
; 也可以通过命令进行设置config set maxmemory xxx
.
如果没有配置最大内存,或者最大内存设置的是0,那么在64为操作系统上不限制内存大小,如果在32位机上最多使用3GB内存。
一般推荐设置为物理内存的四分之三大小。
【Redis内存用满了】
会出现OOM的报错。
【删除策略】
定时删除:对CPU不友好,用时间换取空间。
惰性删除:在访问数据的时候进行数据的删除,如果访问的时候数据已经过期了,就删除掉。 对内存不友好,用空间换取时间。
定期删除:
【内存淘汰策略】
noeviction: 不会驱逐任何key
allkeys-lru:对所有的key通过lru算法进行删除
volatile-lru:对所有设置了过期时间的key使用lru算法进行删除。
allkeys-random:对所有的key随机删除
volatile-random:对所有设置了过期时间的key随机删除
volatile-ttl:删除马上要过期的key
allkeys-lfu:对所有的key使用lfu算法进行删除。
volatile-lfu:对所有设置了过期时间的key使用lfu算法进行删除。
两个维度:过期键中筛选;所有key中筛选
四个方面:LRU、LFU、random和ttl
命令修改
配置文件修改
【LRU算法】
leetcode lru-cache
现有的数据结构:LinkedHashMap
status_code 200; // 请求成功
serverList = {server1, server2, server3}; retryCount = 0; retryMax = 10; maxRequestTime = 200ms; public Object getXXOO() { try { // 尝试请求数据 callData(serverList); } catch (Exception ex) { // 拦截器记录日志 serverList.remove(请求过的服务器); if (retryCount == retryMax) { return error; } retryCount++; // 递归调用本方法 getXXOO(); } }
“虽然定义了boolean这种数据类型,但是只对它提供了非常有限的支持。在Java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达式所操作的boolean值,在编译之后都使用Java虚拟机中的int数据类型来代替,而boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位”。这样我们可以得出boolean类型占了单独使用是4个字节,在数组中又是1个字节。
显然第三条是更准确的说法,那虚拟机为什么要用int来代替boolean呢?为什么不用byte或short,这样不是更节省内存空间吗。大多数人都会很自然的这样去想,我同样也有这个疑问,经过查阅资料发现,使用int的原因是,对于当下32位的处理器(CPU)来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),具有高效存取的特点。
public class StringPoolDemo {
public static void main(String[] args) {
String str1 = new StringBuilder("58").append("tongcheng").toString();
System.out.println(str1);
System.out.println(str1.intern());
System.out.println(str1 == str1.intern());
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2);
System.out.println(str2.intern());
// java 1.8输出false,open jdk 17 输出为true
System.out.println(str2 == str2.intern());
}
}
jdk 1.8:
public final class System { /* Register the natives via the static initializer. * * VM will invoke the initializeSystemClass method to complete * the initialization for this class seperated from clinit. * Note that to use properties set by the VM, see the constriants * described in the initializeSystemClass method. */ private static native void registerNatives(); static { registerNatives(); } /** Don't let anyone instantiate this class */ private System() { } }
这个是openjdk 17:
public final class System {
/* Register the natives via the static initializer.
*
* The VM will invoke the initPhase1 method to complete the initialization
* of this class separate from <clinit>.
*/
private static native void registerNatives();
static {
registerNatives();
}
/** Don't let anyone instantiate this class */
private System() {
}
}
JDK 1.8:
...
sun.misc.Version.init();
...
这个是openjdk 17:
/** * Initialize the system class. Called after thread initialization. */ private static void initPhase1() { // register the shared secrets - do this first, since SystemProps.initProperties // might initialize CharsetDecoders that rely on it setJavaLangAccess(); // VM might invoke JNU_NewStringPlatform() to set those encoding // sensitive properties (user.home, user.name, boot.class.path, etc.) // during "props" initialization. // The charset is initialized in System.c and does not depend on the Properties. Map<String, String> tempProps = SystemProps.initProperties(); VersionProps.init(tempProps); // others ... }
public class Version {
private static final String launcher_name =
"java";
}
这个是openjdk 17:
class VersionProps {
private static final String launcher_name =
"openjdk";
由此观之,在17上,会有如下效果:
String str3 = new StringBuilder("open").append("jdk").toString();
System.out.println(str3);
System.out.println(str3.intern());
// 这个的输出是false,VersionProps 加载的时候,openjdk就已经加载到常量池里面了。
System.out.println(str3 == str3.intern());
T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oAdgMfmp-1687163385995)(null)]
产生并发不一致性问题主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。
隔离级别 | 脏读 | 不可重复读 | 幻影读 | 加锁读 |
---|---|---|---|---|
未提交读 | 是 | 是 | 是 | 否 |
提交读 | 否 | 是 | 是 | 否 |
可重复读 | 否 | 否 | 是 | 否 |
可串行化 | 否 | 否 | 否 | 是 |
#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #user_id#,如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”.
$将传入的数据直接显示生成在sql中。如:order by u s e r i d user_id userid,如果传入的值是111,那么解析成sql时的值为order by user_id, 如果传入的值是id,则解析成的sql为order by id.
#方式能够很大程度防止sql注入。$方式无法防止Sql注入。
5.KaTeX parse error: Expected 'EOF', got '#' at position 32: …传入表名. 6.一般能用#̲的就别用.
MyBatis排序时使用order by 动态参数时需要注意,用$而不是#
500 内部错误 — 因为意外情况,服务器不能完成请求。
304 未修改 — 未按预期修改文档。
302 已找到 — 请求的数据临时具有不同 URI。
不能自己写以"java."开头的类,其要么不能加载进内存,要么即使你用自定义的类加载器去强行加载,也会收到一个SecurityException。
public class BubbleSort { public static void main(String[] args) { int[] arr = {1,1,2,0,9,3,12,7,8,3,4,65,22}; bubbleSort(arr,arr.length); printArr(arr); } public static void printArr(int[] a) { for (int element : a) { System.out.print( element + " "); } System.out.println(); } public static void bubbleSort(int[] a,int n) { for(int i=0;i<n;i++) { for (int j=1;j<n-i;j++) { if(a[j-1] > a[j]) { int temp = a[j-1]; a[j-1] = a[j]; a[j] = temp; } } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。