mysql 隔离级别测试

Read UnCommited

可解决丢失更新

create table users (
    id int auto_increment not null primary key,
    name char(10) not null,
    state int not null
); 
insert into users values(1,'dd',0);

事务1

set autocommit = 0;
set global transaction isolation level read uncommitted ;
set session transaction isolation level read uncommitted ; 
start transaction;
update users set state=state+1 where id=1;
select sleep(10);
commit;

事务2

set autocommit = 0;
set global transaction isolation level read uncommitted ;
set session transaction isolation level read uncommitted ; 
start transaction;
update users set state=state+1 where id=1;
commit;

数据库的一级封锁锁(修改前加X锁,事务结束释放X锁,读不加锁)

事务1先执行修改,修改前申请持有X锁,事务结束释放X锁;事务2也执行修改操作,修改前也申请持有X锁

事务1,10秒才提交释放锁,事务2申请持锁需要等待,直到事务1结束才能获取到锁的持有权进行修改

这样对同一数据的修改会变成串行化的修改,所以不会出现因为并发只进行一次+1的情况,也就不会出现丢失修改的问题

RU 不能解决脏读和不可重复读

另一个事务回滚就会产生脏读

set autocommit = 0;
set global transaction isolation level read uncommitted ;
set session transaction isolation level read uncommitted ; 
start transaction;
update users set state=1 where id=1;
select sleep(5);
rollback;
set autocommit = 0;
set global transaction isolation level read uncommitted ;
set session transaction isolation level read uncommitted ; 
start transaction;
select * from users where id=1;
commit; 

事务2结果=1

事务1和事务2都执行结束时,再进行一次查询=0

事务1先修改,修改前申请持有X锁,

事务2读,不需要申请持锁,读出的数据是事务1修改后的,而此时事务1回滚,修改的数据被还原,产生脏读现象

发布于 | Tags mysql

mysql复制

5.6

引入GTID,server uuid(auto.cnf)和事务ID组成

并行只基于库(Schema)

  1. Crash Safe 不好做,可能后执行的事务由于并行复制先完成执行

  2. 性能不高单库多表比多库多表更常见

发布于 | Tags mysql

java singleton

  • 懒汉,线程不安全

适合单线程程序,多线程需要保护getInstance(),否则可能产生多个Singleton对象实例

public class Singleton {

    private Singleton() {
    }

    private static Singleton instance = null;

    public static Singleton getInstance() {
        if (Singleton.instance == null) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    }
}
发布于 | Tags java

os 5 io

Linux I/O体系七层:

  • VFS

适配各种文件系统,对外提供统一API

磁盘:EXT,XFS …

网络:NFS, CIFS …

特殊:/proc,裸设备

  • 磁盘缓存

磁盘数据驻留 RAM

  • Dentry cache
  • Page cache
  • Buffer cache

  • 映射层

确定数据在物理设备上的位置

  • 通用块层

绝大多数I/O操作是跟块设备打交道

下层对接各种不同属性的块设备,对上提供统一的Block IO请求标准

  • I/O调度层

管理块设备的请求队列,有利于减少磁盘寻址时间,提高全局吞吐量

大多数块设备都是磁盘设备

根据设备及应用特点设置不同的调度器

  • 块设备驱动

设备操作接口

  • 物理硬盘
发布于 | Tags os

分布式一致性

分布式系统面临的问题

  • 通信异常

延时,丢失

  • 网络分区(脑裂)

  • 组内都正常通讯,但组间通讯被阻隔

  • 三态

成功,失败和超时(请求者无法判断当前请求是否成功处理)

  • 节点故障
发布于 | Tags concurrent

mysql 锁 & 事务

锁作用

  • 保证共享资源一致性

  • 实现事务隔离性

锁粒度(等级)

  • 表级锁:开销小,加锁快; 不会死锁;锁定粒度大,发生锁冲突的概率最高 ,并发度最低
  • 行级锁:开销大,加锁慢;会出现死锁(锁是逐步获得的);锁定粒度最小,发生锁冲突的概率最低,并发度也最高
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

存储引擎各自实现锁机制,server层不感知存储引擎的锁实现

InnoDB锁模式

行锁

  • 共享锁(S)

  • 排他锁(X)

表锁

  • 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁

  • 意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁

允许行锁和表锁共存,实现多粒度锁机制

详解 MySql InnoDB 中意向锁的作用


隐式& 显式 锁定

隐式锁定

事务执行过程中,使用两阶段锁协议:

  • 随时都可以执行锁定,InnoDB会根据隔离级别在需要的时候自动加锁

  • 锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在 `同一时刻 被释放

显式锁定 :

select ... lock in share mode
select ... for update

乐观&悲观锁

乐观锁

select id,value,version from TABLE where id = #{id}

更新时,为防止发生冲突,需要这样操作

update TABLE
set value=2,version=version+1
where id=#{id} and version=#{version}

失败后重试或回滚

悲观锁

直接调用数据库的相关语句

悲观锁涉及的另外两个锁概念:共享锁与排它锁

共享锁(S):

SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE

确保自己查到的数据没有被其他的事务正在修改,也就是说确保查到的数据是最新的数据,并且不允许其他人来修改数据(获取数据上的排他锁)

自己不一定能够修改数据,因为有可能其他的事务也对这些数据 使用了 in share mode 的方式上了 S 锁


当前事务对读锁进行修改操作,很可能会造成死锁

create table innodb_lock(
id bigint primary key auto_increment,
v int
);

insert into innodb_lock (id,v) values(1,'dd');
Transaction A Transaction B
set autocommit=0;select * from innodb_lock where id=1 lock in share mode; set autocommit=0;select * from innodb_lock where id=1 lock in share mode;
update innodb_lock set v=2 where id=1;
等待
等待 update innodb_lock set v=2 where id=1;
Deadlock found when trying to get lock; try restarting transaction
Affected rows: 1

排他锁(X):

SELECT * FROM table_name WHERE ... FOR UPDATE

自己查到的数据确保是最新数据,并且查到后的数据只允许自己来修改

发布于 | Tags mysql

Redis内存淘汰策略

一是内存爆了要用 LRU、LFU、FIFO 清理,否则磁盘SWAP,性能急剧下降

二是超时键过期要删除,用主动或惰性的方法

通用过期策略

  • 定时过期:

每个设置过expire时间的key都要创建一个定时器,到期立即清除

该策略可以立即清除过期的数据,对内存很友好

但占用大量CPU资源处理过期数据,从而影响响应时间和吞吐量

  • 惰性过期:

访问key才会判断该key是否已过期,过期则清除

该策略可最大化节省CPU资源,却对内存非常不友好

极端情况可能出现大量过期key没有再次被访问,而不会被清除,占用大量内存

  • 定期过期:

每隔一定的时间,扫描一定数量expires字典中一定数量的key,并清除其中已过期的key

该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可在不同情况下使CPU和内存资源达到最优的平衡效果

(expires字典,key指向键的指针,value是该键的毫秒精度UNIX时间戳)

Redis同时使用惰性过期和定期过期两种

发布于 | Tags redis

grpc

grpc demo

发布于 | Tags go

os 4 线程

线程类型

  • 内核线程

创建 / 撤消由内核内部需求决定,不需和用户进程联系起来

共享内核全局数据,具有自己的内核堆栈

能单独被调度且用标准的内核同步机制,可被单独分配到一个处理器上运行

调度不需要经过态的转换并进行地址空间的重新映射,内核线程间上下文切换比在进程间做上下文切换快得多

  • 轻量级进程 LWP

内核支持的用户线程,在一个单独的进程中提供多线程控制

这些轻量级进程被单独的调度,可以在多个处理器上运行,每一个轻量级进程都被绑定在一个内核线程上

独立调度并且共享地址空间和进程中的其它资源

但每个 LWP 都应该有自己的程序计数器、寄存器集合、核心栈和用户栈

  • 用户线程

如通过线程库实现,提供同步和调度的方法

可在没有内核参与下创建、释放和管理,不消耗内核资源,省去大量系统开销

用户线程的上下文可以在没有内核干预的情况下保存和恢复

每个用户线程都可以有自己的用户堆栈,一块用来保存用户级寄存器上下文以及如信号屏蔽等状态信息的内存区

库通过保存当前线程的堆栈和寄存器内容载入新调度线程的那些内容来实现用户线程之间的调度和上下文切换

内核仍然负责进程的切换,因为只有内核具有修改内存管理寄存器的权力

用户线程不是真正的调度实体,内核对它们一无所知,而只是调度用户线程下的进程或者轻量级进程,这些进程再通过线程库函数来调度它们的线程

当一个进程被抢占时,它的所有用户线程都被抢占,当一个用户线程被阻塞时,它会阻塞下面的轻量级进程,如果进程只有一个轻量级进程,则它的所有用户线程都会被阻塞

pthread 实现

  • Linux2.4 前:LinuxThread

  • Linux2.5 后:NPTL


fork && clone

  • 函数: fork,拷贝父进程而创建一个新进程
  • 系统调用:clone,为进程创建提供

fork & pthread_create

fork & pthread_create 都调用的 clone

clone系统调用 ,参数包括如 CLONE_VM, CLONE_FILES, CLONE_SIGHAND 等,指定了克隆时需要拷贝的东西

  • fork

调用 clone 时不设置 CLONE_VM

内核看来就是产生了两个拥有不同内存空间的进程

  • pthread_create

调用 clone 时设置了 CLONE_VM

内核看来就是产生了两个拥有相同内存空间的进程

通过 clone 的参数共享进程资源,创建出的就是 LWP

所以用户态创建一个新线程,内核态就对应生成一个新进程

发布于 | Tags os

经典图片

抽象

abstract

毕加索的牛抽象过程,一步一步隐藏细节

发布于 | Tags dev