这是我参与更文挑战的第10天,活动详情查看: 更文挑战
四、MySQL排他锁&共享锁
mysql锁机制分表级锁和行级锁。共享锁和排他锁都属于行级锁。
- 共享锁:读锁,简称S锁。共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
- 排他锁:写锁,简称X锁。排他锁就是不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
对于共享锁大家可能很好理解,就是多个事务只能读数据不能改数据,对于排他锁大家的理解可能就有些差别,我当初就犯了一个错误,以为排他锁锁住一行数据后,其他事务就不能读取和修改该行数据,其实不是这样的。
排他锁指的是一个事务在一行数据加上排他锁后,其他事务不能再在其上加其他的锁。mysql InnoDB引擎默认的修改数据语句,update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select ...for update语句,加共享锁可以使用select ... lock in share mode语句。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select ...from...查询数据,因为普通查询没有任何锁机制。
五、MySQL事务隔离级别
1. 未提交读(Read uncommitted)
允许脏读,也就是可能读取到其他会话中未提交事务修改的数据(数据库一般不用)
2. 已提交读(Read committed)
只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)
数据的读取都是不加锁的,但是数据的写入、修改和删除是需要加锁的
3. 可重复读(Repeatable read)
可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读(幻读)
4. 可串行化(Serializable )
完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。解决幻读
5. 不可重复读和幻读的区别
- 在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复读了
- 但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免
- 需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。
六、使用Redis缓存可能带来的问题
1、缓存穿透
定义
大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层(比如:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库)
解决方法
-
缓存失效key
如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间。
但是不能从根本上解决问题,如果黑客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存大量无效的 key
-
布隆过滤器
但是,布隆过滤器可能会存在误判的情况。总结来说就是: 布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
当一个元素加入布隆过滤器中的时候,会进行哪些操作:
- 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
- 根据得到的哈希值,在位数组中把对应下标的值置为 1。
当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行哪些操作:
- 对给定元素再次进行相同的哈希计算;
- 得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
2、缓存雪崩
定义
缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求
解决方法
-
针对 Redis 服务不可用的情况:
- 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
- 限流,避免同时处理大量的请求。
-
针对热点缓存失效的情况:
- 设置不同的失效时间比如随机设置缓存的失效时间。
- 缓存永不失效。