MySQL数据库引擎、事务、锁、索引

本文详细介绍了数据库的基本操作包括数据类型、增删改查等,深入探讨了事务处理的原理和实现方法,涵盖事务的ACID特性、锁机制、死锁处理及索引优化等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、数据库及引擎
(1)数据类型
(2)增删改查操作
(3)没有框架的情况下:JDBC数据库操作:
(4)关系型和非关系型数据库:
(5)Innodb引擎和MyIASM引擎:

2、事务

3、锁
(1)锁的类型与范围
(2)加锁操作:
(3)死锁

4、索引

5、Spring事务管理:



1、数据库及引擎

(1)数据类型

数据库常见的数据类型:

int

整数

varchar

指定最大多长的字符串(如密码等不知道有多长,指定最大长度)

datetime

时间的数据结构

float(m,d)

浮点类型

text,65535

比较大的文本

(2)增删改查操作

CRUD操作:增删改查操作

INSERT

INSERT INTO table_name(列1,列2,….) VALUES(值1,值2)

DELETE

SELECT */列名称 FROM 表名称

UPDATE

UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值

SELECT

DELETE FROM 表名称 WHERE 列名称  = 值

(3)没有框架的情况下:JDBC数据库操作:

  1. 加载驱动
  2. 创建数据库连接
  3. 创建Statement对象(表达式)
  4. 定义操作的SQL语句
  5. 执行数据库操作
  6. 获取并操作结果集
  7. 关闭对象,回收数据库资源(关闭结果集-->关闭数据库操作对象-->关闭连接)

(4)关系型和非关系型数据库:

非关系型数据库的优势:

1、NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高。

2、可扩展性同样也是基于键值对,数据之间没有耦合性,所以非常容易水平扩展。

关系型数据库的优势:

1、复杂查询可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。

2、事务支持使得对于安全性很高的数据访问要求得以实现。

(5)Innodb引擎和MyIASM引擎:

主要是因为InnoDB支持事务,NDB也支持事务但是主要用于集群中。MySQL能不做集群就不做集群,因为集群要考虑分布式的事务;

区别:
1、MyIASM是非事务安全的,而Innodb是事务安全的。
2、MyIASM是表级的,而InnoDB支持行级锁
3、MyIASM不支持外键,InnoDB支持外键
4、MyIASM支持全文类型(FullText)索引,而InnoDB不支持全文类型索
5、MyIASM保存了表的行数,InnoDB没有保存表的行数。
6、MyIASM相对简单,效率上要优于InnoDB,小型应用可以考虑使用MyIASM

应用场景:
1、InnoDB用于事务处理,具有ACID事务支持等特性,如果在应用中执行大量insertupdate操作,应该选择InnoDB
2、MyIASM管理非事务表,提供高速存储和检索以及全文搜索能力,如果在应用中执行大量select操作,应该选择MyIASM
3、对于一般的Web应用来说,应该选择MyIASM,效率更高,特定场景再用InnoDB。

2、事务

事务是由N步数据操作序列组成的逻辑执行单元,这系列操作要么全执行,要么全放弃执行;

事务的特性:(ACID)

原子性:事务是应用中不可再分的最小执行体

一致性:事务执行结果,需使数据从一个一致性状态,变为另一个一致性状态

隔离性:各个事务的执行互不干扰,任何事务的内部操作对其他的事务都是隔离的

持久性:事务一旦提交,对数据所做的任何改变都要记录到永久存储器中。

服务器是在一个多线程的环境中的:事务的隔离性:

--常见的并发异常(5种):

第一类丢失更新;第二类丢失更新;

脏读、不可重复读、幻读;

第一类丢失更新:某一个事务的回滚,导致另外一个事务已更新的数据丢失了;

第二类丢失更新:某一个数据的提交,导致另外一个事务已更新的数据丢失了;

脏读:某一个事务读取了另外一个事务未提交的数据;

不可重复读:对某一个事务,对同一个数据前后读取的结果不一致

幻读:对某一个事务,对同一个表前后查询的行数不一致;

--常见的隔离级别(4种):

Read Uncommitted:读取未提交的数据

Read Committed:读取已提交的数据

Repeatable Read:可重复读(InnoDB默认隔离级别)

Serializable:串行化(加锁,性能极不好)

大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read

隔离级别

第一类丢失更新

脏读

第二类丢失更新

不可重复读

幻读

Read Uncommitted

no

no

no

no

no

Read Committed

ok

ok

no

no

no

Repeatable Read

ok

ok

ok

ok

no

Serializable

ok

ok

ok

ok

ok

3、锁

(1)锁的类型与范围

悲观锁(数据库的锁):1)共享锁(S锁):事务A对某数据加了共享锁以后,其他事务只能对该数据加共享锁,但不能加排他锁。

2)排他锁(X锁):事务A对某数据加了排它锁以后,其他事务对该数据既不能加共享锁,也不能加排它锁。

间隙锁:行级,使用范围条件查询时,对范围内不存在的记录加锁。一是为了防止幻读,二是为了满足恢复和复制的需要。

乐观锁(自定义)使用版本号、时间戳等;在更新数据前,检查版本号是否发生变化。若变化则取消本次更新,否则就更新数据(版本号+1)。

乐观锁的实现:

1. 版本号机制:UPDATE … SET …,VERSION=#{version+1} WHERE … AND VERSION=${version}

2. CAS算法(Compare and swap):是一种无锁的算法,该算法涉及3个操作数(内存值V、旧值A、新值B),当V等于A时,采用原子方式用B的值更新V的值。该算法通常采用自旋操作,也叫自旋锁。它的缺点是:
1) ABA问题:某线程将A该为B,再改回A,则CAS会误认为A没被修改过。
2) 自旋操作采用循环的方式实现,若加锁时间长,则会给CPU带来巨大的开销。
3) CAS只能保证一个共享变量的原子操作。

实例:
1.数据库表设计task有三个字段,分别是id、value、version
2.实现:
1)先task表的数据(实际上这个表只有一条记录),得到version的值为versionValue
2)每次更新task表中的value字段时,为了防止发生冲突,需要这样操作:
update task set value = newValue,version =  versionValue +1  where version = versionValue;
只有这条语句执行了,才表明本次更新value字段的值成功。假设有两个节点A和B都要更新task表中的value字段值,差不多在同一时刻,A节点和B节点从task表中读到的version值为2,那么A节点和B节点在更新value字段值的时候,都操作 update task set value = newValue,version =  3   where version = 2; 实际上只有1个节点执行该SQL语句成功,假设A节点执行成功,那么此时task表的version字段的值是3,B节点再操作update task set value = newValue,version =  3   where version = 2;这条SQL语句是不执行的,这样就保证了更新task表时不发生冲突

(2)加锁操作:

增加行级锁之前,InnoDB会自动给表加意向锁

执行DML语句时,InnoDB会自动给数据加排它锁;DML语句是指:增删改表中的数据;

执行DQL语句时的加锁:DQL语句是指:查询表中的记录:

共享锁:select…from…where…lock in share mode

排他锁:select…from…where…for update

间隙锁:上述SQL语句采用范围条件时,InnoDB对不存在的记录自动增加间隙锁

(3)死锁

解决方案:

1、一般InnoDB会自动检测,并使一个事务回滚,另一个事务继续;

2、设置超时等待参数:innodeb_lock_wait_timeout

应该在程序中尽量避免死锁的发生

1、不同业务并发访问多个表时,应约定以相同的顺序来访问这些表

2、以批量的方式处理数据,应事先对数据排序,保证线程按固定的顺序来处理数据

3、在事务中,如果要更新记录,应直接申请足够级别的锁,即排它锁

二级索引&非唯一索引,如果某个叶子节点(若不存在则假设它存在)被添加了排它锁,那么就会在该叶子节点周围添加间隙锁,如果刚好其它会话要在间隙锁范围内创建索引,那么则会被挂起。主键索引& 唯一索引,如果要添加排它锁的节点存在,不会触发间隙锁,如果不存在,则触发间隙锁。
幻读和不可重复读从结果来看,都是针对一个事务中,多次读取的结果与目标结果不一致。从解决的目的上,是区分不了幻读和不可重复读的。那么从范围上去划分。从范围上区划分,分为指定行多次读取结果不一致,和范围读取结果不一致。指定行多次读取,为了解决结果不一致的问题,最简单的办法是加锁(悲观锁)和多版本控制(MVCC)(乐观锁)来实现。我(个人理解)称指定行多次读取结果不一致,叫做不可重复读。范围多次读取,这样简单的添加行锁是不可能解决多次读取结果不一致的问题,因此需要在范围添加锁。我(个人理解)称范围多次读取结果不一致的问题,叫做幻读。从索引的概念来讲,可以明确锁定某个索引的,且该索引值不会出现重复的,就是不可重复读的问题,不能明确锁定某个索引、或者某个索引值是可重复的,那么就会触发间隙锁,也是幻读的问题。

4、索引

见我的另一篇分析文章:https://blog.youkuaiyun.com/liuzewei2015/article/details/99699015

5、Spring事务管理:

声明式事务:通过XMl配置,声明某方法的事务特征;通过注解,声明某方法的事务特征。

编程式事务:通过TransactionTemplate管理事务,并通过它执行数据库的操作。

    // REQUIRED: 支持当前事务(外部事务),如果不存在则创建新事务.
    // REQUIRES_NEW: 创建一个新事务,并且暂停当前事务(外部事务).
    // NESTED: 如果当前存在事务(外部事务),则嵌套在该事务中执行(独立的提交和回滚),否则就会REQUIRED一样.
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public Object save1() {
        // 新增用户
        User user = new User();
        user.setUsername("alpha");
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
        user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
        user.setEmail("alpha@qq.com");
        user.setCreateTime(new Date());
        userMapper.insertUser(user);

        // 新增帖子
        DiscussPost post = new DiscussPost();
        post.setUserId(user.getId());
        post.setTitle("Hello");
        post.setContent("新人报道!");
        post.setCreateTime(new Date());
        discussPostMapper.insertDiscussPost(post);

        Integer.valueOf("abc");//故意写一个错误的语句,这条语句报错;事务回滚,对数据库仍然保证一致性

        return "ok";
    }

    public Object save2() {
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        return transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus status) {
                // 新增用户
                User user = new User();
                user.setUsername("beta");
                user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
                user.setPassword(CommunityUtil.md5("123" + user.getSalt()));
                user.setEmail("beta@qq.com");
                user.setCreateTime(new Date());
                userMapper.insertUser(user);

                // 新增帖子
                DiscussPost post = new DiscussPost();
                post.setUserId(user.getId());
                post.setTitle("你好");
                post.setContent("我是新人!");
                post.setCreateTime(new Date());
                discussPostMapper.insertDiscussPost(post);

                Integer.valueOf("abc");

                return "ok";
            }
        });
    }
    @Autowired
    private AlphaService alphaService;

    @Test
    public void testSave1() {
        Object obj = alphaService.save1();
        System.out.println(obj);
    }

    @Test
    public void testSave2() {
        Object obj = alphaService.save2();
        System.out.println(obj);
    }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值