mysql-2

第六天


mysql索引

mysql索引类型

  • btree
  • rtree
  • hash
  • fulltext
  • gis

btree种类

  • b-tree
  • b+tree
  • btree*:增强的b+tree

mysql b+tree索引构建过程

  1. 聚簇索引btree结构(innodb独有)

    • 区===簇

      1. 建表时,指定主键列,mysql innodb会将主键作为聚簇索引列,比如id not null primary key
      2. 没有指定主键,自动选择唯一键(unique)的列,作为聚簇索引
      3. 以上都没有,生成隐藏聚簇索引

      作用:有了聚簇索引之后,将来插入的数据行,在同一个区内,都会按照id值的顺序,有序在磁盘存储数据。

  2. 辅助索引btree结构

    • 使用普通列作为条件构建的索引。需要人为创建

    • 优化非聚簇索引之外的查询条件的优化

    • ==alter table table_name add index idx(column_name);==

    • 分类

      • 单列索引

      • 联合索引(多列)

        • ==alter table table_name add index idx(column_name1,column_name2);==

        • 使用多列组合一个索引,但是要最左原则

          1. 查询条件中,必须要包含最左列
          2. 建立联合索引时,一定要选择重复值少的列,作为最左列
      • 前缀索引

        • 如果我们所选择的索引值长度多长,会导致索引树高度增高,会导致索引应用时,需要读取更多的索引数据页,所以可以选择大字段的前面部分字符作为索引生成条件
        • ==alter table table_name add index idx_d(column_name(5));==
  3. mysql中建议索引树高度==3-4层==


b+tree索引树高度影响因素

  1. 索引字段较长:解决用前缀索引
  2. 数据行过多:解决分区表,归档表(pt-archive),分布式架构(大企业)
  3. 数据类型:选择合适的数据类型

索引的管理命令

  • 什么时候创建索引

    • 按照业务语句的需求创建合适的索引,并不是将所有列都建立索引,不是索引越多越好

    • 将索引建立在,经常where ,group by ,order by, join on的条件

      1. 如果冗余索引过多,表的数据行变化的时候,很有可能会导致索引频繁更新。会阻塞很多正常的业务更新的请求
      2. 索引过多,会导致优化器选择出现偏差

管理命令

  1. 查询表的索引情况

    • ==desc table_name;==

    • key:

      • pri 聚簇索引
      • mul辅助
      • uni唯一索引
    • ==show index from table_name;==

  2. 建立索引

    • ==alter table table_name add index idx(column_name);==
      • idx可以子自己定义(索引名)
  3. 删除索引

    • ==alter table table_name drop index index_name;==

==知识回答==

数据库中的表长成什么样

  1. mysql用来存储数据行的逻辑结构,表的数据行最终存储到了很多的page
  2. innodb存储引擎,会按照聚簇索引,有序的组织存储表数据到各个区的连续页上
  3. 这些连续的数据页,成为了聚舱索引的叶子节点。你可以认为聚簇索引就是原表数据
  4. 回表即是,回聚簇索引

什么是回表

  1. 辅助索引:将辅助索引列值+id主键值,构建辅助索引b树结构
  2. 用户使用,辅助索引列作为条件查询时,首先扫描辅助索引的b树
    • 如果辅助索引能够完全覆盖我们的查询结果时,就不需要回表
    • 如果不能完全覆盖,只能通过得出的id主键值,回到聚簇索引(回表扫描,最终)得到想要的结果

回表会带来什么影响

  1. io量级变大
  2. iops会增大
  3. 随机io会增大

怎么减少回表

  1. 将查询尽可能用id主键查询
  2. 设计合理的辅助索引(联合索引)
  3. 更精确的查询条件+联合索引
  4. 优化器的算法:MRR---???

更新数据时,会对索引有影响吗,数据的变化会使索引实时更新吗

  1. 对于聚簇索引会立即更新
  2. 对于辅助索引,不是实时更新的
  3. 在innodb内存结果中,加入insert buffer(会话级别),现在版本叫change。change buffer功能是临时缓冲辅助索引需要的数据更新
  4. 当我们需要查询新insert的数据,会在内存中进行merge(合并)操作,此时辅助索引就是最新的

压力测试

### 导入测试表
mysql> source t100w.sql

### 压测命令(用户名为root,密码123,可以自己修改)
mysqlslap --defaults-file=/etc/my.cnf \
--concurrency=100 --iterations=1 --create-schema='test' \
--query="select * from test.t100w where k2='FGCD'" engine=innodb \
--number-of-queries=2000 -uroot -p123 -verbose

第七天


执行计划

==查看执行计划==

  • ==explain sql语句==
  • ==desc sql语句==

执行计划显示结果的分析

  • table:此次查询涉及到的表
  • ==type:查询类型==
    • 全表扫:不用任何的缩影。ALL
    • ==索引扫:index<range<ref<eq_ref<const(system)==
      • index:全索引扫
      • range:索引范围查询
      • ref:辅助索引等值查询
      • eq_ref:多表链接中,非驱动表链接条件是主键或唯一键
      • const:聚簇索引等值查询
  • possibe_keys:可能用到的索引
  • key:最后选择的索引
  • key_len:索引覆盖长度(==列的最大储值字节长度==)
    • ==联合索引覆盖长度==
      • 此次查询,可以帮助我们判断,走了联合索引的几部分
  • rows:此次查询需要扫描的行数
  • extra:额外的信息
    • using filesort
      • 表示此次查询使用到了文件排序
      • 说明在查询中的排序操作:order by ,group by ,distinct

==建索引原则==(一些注意)

  • 必须要有主键,如果没有可以做为主键条件的列,创建无关列
  • 经常做为where条件列 order by group by join on, distinct 的条件(业务:产品功能+用户行为)
  • 最好使用唯一值多的列作为索引,如果索引列重复值较多,可以考虑使用联合索引
  • 列值长度较长的索引列,我们建议使用前缀索引.
  • 降低索引条目,一方面不要创建没用索引,不常使用的索引清理,percona toolkit( xxxxx)
  • 索引维护要避开业务繁忙期

一些注意:

  • 查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,,/,! 等),就会导致索引失效*

  • 隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.

  • <> (不等于!),not in 不走索引(针对的是辅助索引)

  • 单独的>,<,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit

  • or或in 尽量改成union(条件:要重复值不多的列)

  • like "%_" 百分号在最前面不走索引

    • %linux%类的搜索需求,可以使用elasticsearch+mongodb 专门做搜索服务的数据库产品

查询结果集是原表中的大部分数据,应该是25%以上。

查询的结果集,超过了总数行数25%,优化器觉得就没有必要走索引了。

假如:tab表 id,name id:1-100w ,id列有(辅助)索引
select * from tab where id>500000;
如果业务允许,可以使用limit控制。
怎么改写 ?
结合业务判断,有没有更好的方式。


如果没有更好的改写方案
尽量不要在mysql存放这个数据了。放到redis里面。


索引本身失效,统计数据不真实

索引有自我维护的能力。
对于表内容变化比较频繁的情况下,统计信息不准确,过旧。有可能会出现索引失效。
一般是删除重建

现象:
有一条select语句平常查询时很快,突然有一天很慢,会是什么原因
select? --->统计数据不真实,导致索引失效。
DML ? --->锁冲突


统计数据不真实

mysql库中的两个表:

  • innodb_index_stats
  • innodb_table_stats

解决方法:

  • optimize table table_name;
  • alter table table_name engine=innodb;
  • 删除重建

==优化器针对索引的算法==

  1. ==mysql索引的自优化-AHI(自适应hash索引)==

    • mysql的innodb引擎,只能创建btree
    • ==AHI的作用:==
      • 自动评估热的内存索引page,生成hash索引表
      • 帮助innodb快速读取索引页,加快索引读取的速度
      • ==相当于索引的索引==
  2. ==mysql索引的自优化-change buffer==

    • 对于聚簇索引会立即更新
    • 对于辅助索引,不是实时更新的
    • 在innodb内存结果中,加入insert buffer(==会话级别==),现在版本叫change buffer。change buffer功能是临时缓冲辅助索引需要的数据更新
    • 当我们需要查询新insert的数据,会在内存中进行merge(合并)操作,此时辅助索引就是最新的
  3. ICP(索引下推)

  4. MRR

    • ==set global optimizer_switch='mrr=on,mrr_cost_based=off';==
    • 辅助索引---》==sort id==---》回表---》聚簇索引
  5. NLJ---多表联合查询

  6. BKA---多表联合查询

优化器算法

  • ==show variable like '%switch%';==

  • ==select @@optimizer_switch;==

  • 默认的优化器算法如下:

    • index_condition_pushdown=on---ICP
    • mrr=on
    • mrr_cost_based=on
    • block_netsted_loop=on
    • batched_key_access=off

如何修改?

  • ==my.cnf==

  • ==set global optimizer_switch='batched_key_access=on';==

  • hints

    • SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1
        FROM t3 WHERE f1 > 30 AND f1 < 33;
      SELECT /*+ BKA(t1) NO_BKA(t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
      SELECT /*+ NO_ICP(t1, t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
      SELECT /*+ SEMIJOIN(FIRSTMATCH, LOOSESCAN) */ * FROM t1 ...;
      EXPLAIN SELECT /*+ NO_ICP(t1) */ * FROM t1 WHERE ...;
      SELECT /*+ MERGE(dt) */ * FROM (SELECT * FROM t1) AS dt;
      INSERT /*+ SET_VAR(foreign_key_checks=OFF) */ INTO t2 VALUES(2);
      

https://mariadb.com/kb/en/multi-range-read-optimization/


第八天


mysql存储引擎

种类

  • oracle mysql
    • 可以对不同的表,设定不同的存储引擎
    • ==show engines;==
    • 默认存储引擎:innodb(5.5版本以后)
      • innodb
      • myisam
      • csv
      • memory
  • percona
    • 默认存储引擎:xtradb
  • mariadb
    • 默认存储引擎:innodb

其他引擎(percona、mariadb)

  • tokudb
    • 适合于,业务中有大量插入或者删除操作的场景
    • 应用于,数据量较大的业务
  • myrocks

环境: zabbix 3.2 mariaDB 5.5 centos 7.3
现象 : zabbix卡的要死 , 每隔3-4个月,都要重新搭建一遍zabbix,存储空间经常爆满.
问题 :

  1. zabbix 版本
  2. 数据库版本
  3. zabbix数据库500G,存在一个文件里ibdata1,手动删除1个月之前的数据,空间不释放

优化建议:

  1. 数据库版本升级到5.7(percona)(mariadb 10.1)版本,zabbix升级更高版本
  2. 存储引擎改为tokudb
  3. 监控数据按月份进行切割(二次开发:zabbix 数据保留机制功能重写,数据库分表
  4. 关闭binlog和双1
  5. 参数调整....

优化结果:
监控状态良好


为什么?

  1. 原生态支持TokuDB,另外经过测试环境,5.7要比5.5 版本性能 高 2-3倍
  2. TokuDB:insert数据比Innodb快的多,数据压缩比要Innodb高
  3. 监控数据按月份进行切割,为了能够truncate每个分区表,立即释放空间
  4. 关闭binlog ----->减少无关日志的记录.
  5. 参数调整...----->安全性参数关闭,提高性能.

https://www.jianshu.com/p/898d2e4bd3a7

https://mariadb.com/kb/en/installing-tokudb/

https://www.percona.com/doc/percona-server/5.7/tokudb/tokudb_installation.html


innodb核心特性

介绍

  • MVCC:多版本并发控制

  • 多缓冲区池

  • 事务

  • 行级锁(粒度)

  • 外键

  • 更多复制特性

  • 支持热备

  • 自动故障恢复


  • clustered index:聚簇索引

  • change buffer

  • 自适应hash索引---AHI


InnoDB和MyISAM存储引擎的替换

环境: centos 5.8 ,MySQL 5.0版本,MyISAM存储引擎,网站业务(LNMP),数据量50G左右
现象问题: 业务压力大的时候,非常卡;经历过宕机,会有部分数据丢失.
问题分析:
1.MyISAM存储引擎表级锁,在高并发时,会有很高锁等待
2.MyISAM存储引擎不支持事务,在断电时,会有可能丢失数据
职责
1.监控锁的情况:有很多的表锁等待
2.存储引擎查看:所有表默认是MyISAM
解决方案:
1.升级MySQL 5.6.10版本

2.迁移所有表到新环境

3.开启双1安全参数


以上是小项目


存储引擎的管理命令

  • 使用select确认会话存储引擎

    • ==select @@default_storage_engine;==
  • 修改存储引擎

    • 会话级别

      • set default_storage_engine=innodb;
    • 全局级别

      • set global default_storage_engine=innodb;
    • 永久生效

      • 写入配置文件
        vim /etc/my.cnf
        [mysqld]
        default_storage_engine=innodb

  • 确认每个表的存储引擎

    • ==show create table table_name;==
    • ==show table status like 'table_name';==
  • INFORMATION_SCHEMA 确认每个表的存储引擎

    • ==select table_schema,table_name ,engine from information_schema.tables where table_schema not in ('sys','mysql','information_schema','performance_schema');==
  • 修改一个表的存储引擎

    • ==alter table table_name engine=innodb;==
  • ==注意:==此命令我们经常使用他,进行innodb表的碎片整理


知识问答2

1.平常处理过的MySQL问题--碎片处理

环境:centos7.4,MySQL 5.7.20,InnoDB存储引擎
业务特点:数据量级较大,经常需要按月删除历史数据.
问题:磁盘空间占用很大,不释放
处理方法:
以前:将数据逻辑导出,手工drop表,然后导入进去
现在:
对表进行按月进行分表(partition,中间件),或者归档表(pt-archive)
业务替换为truncate方式


2.2亿行的表,想要删除其中1000w,你们公司都怎么做的?假如按照时间列条件

  1. 如果2亿行数据表,还没有生成,建议在设计表时,采用分区表的方式(按月range),然后删除truncate
  2. 如果2亿行数据表,已经存在,建议使用==pt-archive工具进行归档表==,并且删除无用数据

==3.如何批量修改==

### 需求1:将zabbix库中的所有表,innodb替换为tokudb
select concat("alter table zabbix.",table_name," engine tokudb;") from
information_schema.tables where table_schema='zabbix' into outfile '/tmp/tokudb.sql';

### 需求2:将所有非innodb业务表查询出来,并修改为innodb
select concat('alter table ',table_schema,'.',table_name,' engine=innodb;')
from information_schema.tables
where engine!='InnoDB'
and table_schema not in ('sys','performance_schema','information_schema','mysql')
into outfile '/tmp/alter.sql';

宏观结构

  • myisam

    • table_name.frm:数据字典信息(列的定义和属性)
    • table_name.myd:数据行
    • table_name.myi:索引
  • innodb

    • 用户(独立)表空间:table_name.ibd

      • 数据行,索引
    • 用户的数据字典信息:table_name.frm

      • 数据字典信息(列的定义和属性)
    • 系统(共享)的表空间:ibdata1

      • 包含了所有用户的数据字典信息

      • undo(事务回滚日志)

      • double write磁盘区域

      • change buffer磁盘区域

      • 5.5:ibdata1中还会存储临时表空间+用户数据(数据行+索引)

        5.6:还有临时表空间

        5.7:取消了临时表空间

        8.0:取消了存储的数据字典信息、undo独立了、double write磁盘区域

    • 事务日志文件:ib_logfile0,ib_logfile1,.......,ib_logfileN

      • 事务重做日志
    • 临时表空间:ibtmp1

      • 排序,分组,多表连接,子查询,逻辑备份等
    • ib_buffer_pool

      • 正常关库的时候,存储缓冲区的热数据
    • InnoDB architecture diagram showing in-memory and on-disk structures.
    • [站外图片上传中...(image-4f1c4d-1612244203500)]

    • InnoDB architecture diagram showing in-memory and on-disk structures. In-memory structures include the buffer pool, adaptive hash index, change buffer, and log buffer. On-disk structures include tablespaces, redo logs, and doublewrite buffer files.

微观结构

表空间

  • 为了解决存储空间扩展(类比lvm)的问题,5.5版本引入了共享表空间模式

  • mysql表空间类型

    • the system tablespace:共享表空间

      • 5.5版本引入共享表空间(ibdata1),作为默认存储方式
        • 用来存储:系统数据、日志、undo、临时表、用户数据和索引
    • file-per-table tablespaces:独立表空间

      • 5.6版本默认独立表空间模式。单表单表空间
    • general tablespaces:普通表空间

    • undo tablespaces:undo表空间

      • 存储undo logs(回滚日志)
    • the temporary tablespace:临时表空间

      • 5.7默认独立
  • 表空间管理

    • 用户数据默认的存储方式,独立表空间模式。独立表空间和共享表空间可以互相切换
    • 查看默认表空间模式和切换
      • ==select @@innodb_file_per_table;==
      • ==set global innodb_file_per_table=on;==
      • /etc/my.cnf

如何扩展共享表空间大小和个数?

select @@innodb_data_file_path;

### 1.初始化之前,需要在/etc/my.cnf加入以下配置即可:
innodb_data_file_path=ibdata1:1G;ibdata2:1G:autoextend

### 2.已运行的数据库上扩展多个ibdata文件
innodb_data_file_path=ibdata1:1G;ibdata2:1G;ibdata3:1G:autoextend

### 查看mysql 的err  log
cat /xxx/`hostname`.err  |grep   -i  error

事务日志(redo log重做日志)

  • 文件:ib_logfile0~ib_logfileN
  • 控制参数:(show variables like '%innodb_log';)
    • ==innodb_log_file_size=50331648==
    • ==innodb_log_files_in_group=2==
    • ==innodb_log_group_home_dir=./==
  • 功能
    • 用来存储,mysql在做修改类(DML)操作时的==数据页变化过程(变化日志)及版本号(LSN)==,属于==物理日志==
    • 默认两个文件存储redo,是循环覆盖使用

undo logs(回滚日志)

  • 文件:ibdataN,ibtmp1
  • 控制参数:(show variables like '%undo%';)
    • innodb_undo_directory=./
    • show variables like '%segments%';
      • ==innodb_rollback_segments=128==
      • segments这个是段,回滚段有128个,其中96个在ibdata中,剩下在ibtmp1中
    • 功能
      • 用来存储回滚日志,可以理解为记录了每次操作的反操作,属于==逻辑日志==
      • 使用快照功能,提供innodb多版本读写
      • 通过记录的反操作,提供回滚功能

==内存==

  • 数据内存区域(缓冲区
    • 共享内存区域
      • ==buffer pool:缓冲区池==
      • ==select @@innodb_buffer_pool_size;==
      • 缓冲数据页+索引页
    • 会话内存区域(独有,独占)
      • join_buffer_size
      • key_buffer_size
      • read_buffer_size
      • read_rnd_buffer_size
      • sort_buffer_size
  • ==日志(log buffer)==---共享
    • ==innodb_log_buffer_size=16777216==
    • 功能:负责redo日志的缓冲

==mysql总共使用内存=共享内存+会话内存*会话个数+额外的内存使用(文件系统缓存)==


==故障案列==

背景:

硬件及软件环境:
1.联想服务器(IBM) 磁盘500G 没有raid centos 6.8
2.mysql 5.6.33 innodb引擎 独立表空间
3.备份没有,日志也没开

4.开发用户专用库:jira(bug追踪) 、 confluence(内部知识库) ------>LNMT

故障描述:

断电了,启动完成后“/” 只读
fsck 重启,系统成功启动,mysql启动不了。
结果:confulence库在 , jira库不见了

办法(==表空间迁移==):

### 表空间迁移:
create table xxx
alter table  confulence.t1 discard tablespace;
alter table confulence.t1 import tablespace;
### 虚拟机测试可行。

处理问题思路

### confulence库中一共有107张表。
1、创建107和和原来一模一样的表。
他有2016年的历史库,我让他去他同时电脑上 mysqldump备份confulence库

mysqldump -uroot -ppassw0rd -B  confulence --no-data >test.sql

拿到你的测试库,进行恢复
到这步为止,表结构有了。

2、表空间删除。

select concat('alter table ',table_schema,'.'table_name,' discard tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql';
source /tmp/discard.sql

执行过程中发现,有20-30个表无法成功。主外键关系
很绝望,一个表一个表分析表结构,很痛苦。

set foreign_key_checks=0 跳过外键检查。

把有问题的表表空间也删掉了。

3、拷贝生产中confulence库下的所有表的ibd文件拷贝到准备好的环境中

select concat('alter table ',table_schema,'.'table_name,' import tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/import.sql';
source /tmp/import.sql

4、验证数据
表都可以访问了,数据挽回到了出现问题时刻的状态

第九天


事务

什么是事务

事务是伴随着交易类的业务场景出现的工作机制。保证交易的‘和谐’

事务ACID的特性

  • atomicity:原子性
    • 在一个事务工作单元中,所有标准事务语句(DML),要么全成功,要么全回滚
  • consistency:一致性
    • 事务发生前,中,后都应该保证数据始终一致状态
    • 所有的特性,都是最终保证一致性
  • isolation:隔离性
    • mysql可以支持多事务并发工作的系统
    • 在某个事务工作的时候,不能受到其他事务的影响
  • durability:持久性
    • 当事务提交(commit命令执行成功后),此次事务操作的所有数据“落盘”,都要永久保存下去
    • 不会因为数据实例发生故障,导致数据失效

事务生命周期管理

  • 标准事务控制语句

    • ==begin/start transaction;==:开启事务
    • ==commit;==:提交事务
    • ==rollback;==:回滚事务
  • 标准的事务语句(DML)

    • insert
    • update
    • delete
  • select

  • 自动提交功能

    • ==select @@autocommit;==

    • ==set autocommit=0;==

    • ==set global autocommit=0;==

    • ### vi /etc/my.cnf
      autocommit=0
      
    • autocommit=1,一般适合于非交易类的业务场景

  • 隐式事务控制

    • 隐式提交

      • 设置了autocommit=1

      • ==DDL,DCL等非DML语句时,会触发隐式提交==

      • 用于隐式提交的 SQL 语句:
        begin
        a
        b
        begin


        SET AUTOCOMMIT = 1


        ==导致提交的非事务语句==:
        DDL语句: (ALTER、CREATE 和 DROP)
        DCL语句: (GRANT、REVOKE 和 SET PASSWORD)
        锁定语句:(LOCK TABLES 和 UNLOCK TABLES)


        导致隐式提交的语句示例:
        TRUNCATE TABLE
        LOAD DATA INFILE
        SELECT FOR UPDATE

    • 隐式回滚

      • 会话关闭
      • 数据库宕机
      • 事务语句执行失败

==innodb事务的ACID如何保证(概念)==

  • 重做日志
    • redo log
      • 重做日志 ib_logfile0~1 48M , 轮询使用
      • 记录的是数据页的变化
    • redo log buffer :redo内存区域
  • 数据页存储位置
    • ibd :存储 数据行和索引
    • buffer pool :缓冲区池, 数据页和索引的缓冲
  • ==LSN : 日志序列号==
    • 磁盘数据页, redo文件, buffer pool, redo buffer
    • ==MySQL 每次数据库启动, 都会比较磁盘数据页和redo log的LSN, 必须要求两者LSN一致数据库才能正常启动==
  • ==WAL : write ahead log 日志优先写的方式实现持久化==
  • 脏页: 内存脏页, 内存中发生了修改, 没回写入到磁盘之前, 我们把内存页称之为脏页.
  • CKPT:Checkpoint, 检查点, 就是将脏页刷写到磁盘的动作
  • TXID: 事务号, InnoDB会为每一个事务生成一个事务号, 伴随着整个事务生命周期.
  • undo:ibdata1,存储了事务工作过程中的回滚信息

==innodb事务的工作流程==

  • redo log

    • redo,顾名思义“重做日志”,是事务日志的一种

    • 作用

      • 在事务ACID过程中,实现的是D持久化的作用。对于AC也有相应的作用
    • 日志位置

      • ib_logfile0~N
    • ==redo buffer==

      • 数据页的变化信息(变化日志)+数据页当时的LSN号
      • LSN:日志序列号 磁盘数据页、内存数据页、redo buffer、redolog
    • redo的刷新策略

      • commit
      • 刷新当前事务的redo buffer 到磁盘
      • 还会顺便将一部分redo buffer中没有提交的事务日志也刷新到磁盘
    • ==补充(CSR)==

      • redo 存储的是在事务工作流程中,数据页变化

        commit时会立即写入磁盘(默认),日志落盘成功commit才成功。

        正常mysql工作过程中,主要的工作室提供快速D的功能

        mysql出现crash异常宕机时,主要提供的是前滚功能(CSR)


    • ==select @@innodb_flush_log_at_trx_commit;==

      • innodb_flush_log_at_trx_commit=0/1/2

        1:在每次事务提交时,会立即刷新redo buffer到磁盘,commit才能成功(默认为1)

        0:每秒刷新redo buffer到os cache,再fsync()到磁盘,异常宕机时,会有可能导致丢失1s内的事务

        2:每次事务提交,都立即刷新redo buffer到os cache,再每秒fsync()磁盘,异常宕机时,会有可能导致丢失1s内的事务

    • redo buffer还和操作系统缓存机制有关,所以刷写策略可能和==innodb_flush_method==参数有一定关系

      redo也有group commit;可以理解为,每次刷新已提交的redo时,顺便可以将一些未提交的事务redo也一次性刷写到磁盘。此时为了区分不同状态的redo,会加一些比较特殊的标记(是否提交标记

  • undo logs

    • 作用

      • 在事务ACID过程中,实现的是“A” 原子性的作用
      • 另外CI也依赖于Undo
      • 在rolback时, 将数据恢复到修改之前的状态
      • 在CSR实现的是, 将redo当中记录的未提交的时候进行回滚.
        • ==CSR=REDO+UNDO(先前滚或回滚)==
      • undo提供快照技术 保存事务修改之前的数据状态. 保证了MVCC, 隔离性, mysqldump的热备
        • 每个事务开启时(begin),都会通过undo生成一个一致性的快照
      • ==MVCC(一致性快照读取/一致性非锁定锁)==---每次开启一个全新的事务窗口(begin),都会生成当前最新的一致性(事务)快照(undo),直到事务commit或者rollback。此次事务,都会在此快照中进行操作
        • 可以大大提高事务的并发能力
      • undo在生成过程中,也会被记录在redo信息里
  • 隔离级别

    • 作用
      • 主要是提供I的特性,另外对于C的特性也有保证
    • 事务隔离性介绍(transaction isolation
      • RU:读未提交
      • RC:读已提交
      • RR:可重复读(默认级别)
      • SR:可串行化
      • 这里的读不是sql层的数据行的select,而指的是存储引擎的读,是page的读取
    • ==隔离级别说明==
      • RU(READ UNCOMMITTED):读未提交
        • 出现的问题:脏页读,不可重复读,幻读
      • RC(READ COMMITTED):读已提交
        • 出现的问题:不可重复读,幻读
      • RR(REPEATABLE READ ):可重复读(默认级别)
        • 出现的问题:有可能出现幻读
        • ==防止不可重复读现象:利用的就是undo的一致性快照读。MVCC重要功能==
        • 通过RR,已经可以解决99%以上的幻读,为了更加严谨,加入了==GAP(间隙锁)和next-lock(下一键锁)==锁定功能来预防幻读
      • SR(SERIALIZABLE):可串行化
        • 串行化事务。以上问题都能规避,但是不利于事务的并发
    • 参数修改
      • ==select @@transaction_isolation;==
      • ==set global transaction_isolation='read-uncommitted';==
      • vi /etc/my.cnf
  • 锁机制

    • 作用
      • 保证事务之间的隔离性(主要提供写隔离),也保证了数据的一致性
      • 保证资源不被争用。锁是属于资源的,不是某个事务的特性
      • 每次事务需要资源的时候,需要申请持有资源的锁
    • 锁类型
      • 资源
        • 内存锁:mutex,latch,保证内存数据页资源不被争用,不被置换
        • ==对象锁==:
          • mdl(元数据锁):修改元数据时。DDL---》alter、备份
          • table_lock:表锁,DDL,备份(FTWRL全局表锁),==lock table table_name read==
          • record(low)lock:行锁,索引锁,锁定聚簇索引
          • GAP:间隙锁,RR级别,普通辅助索引间隙锁
          • Next-lock:下一键锁,GAP+record lock,普通辅助索引的范围锁
      • 对象锁的粒度
        • mdl(元数据锁):修改元数据时。DDL---》alter、备份
        • table_lock:表锁,DDL,备份(FTWRL全局表锁),==lock table table_name read==,可能由下面的锁变成表锁
          • IS
          • IX
          • ==如果都没有索引,update语句就是表锁==
        • record(low)lock:行锁,索引锁,锁定聚簇索引
        • GAP:间隙锁,RR级别,普通辅助索引间隙锁
        • Next-lock:下一键锁,GAP+record lock,普通辅助索引的范围锁
      • 功能分类
###  transaction_isolation='read-uncommitted'
#### 脏读(两个会话)
##### A session
begin;
update city set name='xxx' where id=2;
##### B session
begin;
select  * from  city;
##### 在session B事务中读取到了,session A未提交数据的脏数据

#### 不可能重复读
##### session B中的事务中,执行相同查询命令时,读到了session A正在发生变化的数据

#### 幻读
#### 在一个事务窗口中,更新操作,出现了别的插入数据的幻行

==innodb的事务总结==

==ACID---redo+undo+隔离级别(读隔离)+mvcc(防止不可重复读)+锁(写隔离)==

A:原子性---undo ,redo(CSR,uncommited)---写

D:持久性---redo(commit的数据,WAL)---写

I:隔离性---隔离级别,锁,MVCC(undo的一致性快照/一致性非锁定锁)---读和写

C:保证事务工作前,中,后,数据的状态都是完整的,一致的

写一致性:undo,redo,lock

读一致性:隔离级别(isolation level),MVCC(undo)

数据页一致性:double write buffer(磁盘区域)

Double write 是InnoDB在 tablespace上的128个页(2个区)是2MB
其原理:
为了解决 partial page write 问题 ,当mysql将脏数据flush到data file的时候, 先使用memcopy 将脏数据复制到内存中的double write buffer ,之后通过double write buffer再分2次,每次写入1MB到共享表空间,然后马上调用fsync函数,同步到磁盘上,避免缓冲带来的问题,在这个过程中,doublewrite是顺序写,开销并不大,在完成doublewrite写入后,在将double write buffer写入各表空间文件,这时是离散写入。
如果发生了极端情况(断电),InnoDB再次启动后,发现了一个Page数据已经损坏,那么此时就可以从doublewrite buffer中进行数据恢复了。

img
img

==存储引擎核心参数==

==innodb_flush_log_at_trx_commit=0/1/2==

1,每次事物的提交都会引起日志文件写入、flush磁盘的操作,确保了事务的ACID;flush  到操作系统的文件系统缓存  fsync到物理磁盘.

0,表示当事务提交时,不做日志写入操作,而是每秒钟将log buffer中的数据写入文件系统缓存并且秒fsync磁盘一次;

2,每次事务提交引起写入文件系统缓存,但每秒钟完成一次fsync磁盘操作。

--------
The default setting of 1 is required for full ACID compliance. Logs are written and flushed to disk at each transaction commit.

With a setting of 0, logs are written and flushed to disk once per second. Transactions for which logs have not been flushed can be lost in a crash.

With a setting of 2, logs are written after each transaction commit and flushed to disk once per second. Transactions for which logs have not been flushed can be lost in a crash.
-------
img

==Innodb_flush_method=fsync/O_DIRECT/O_DSYNC==

https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_method

img
O_DIRECT  :数据缓冲区写磁盘,不走OS buffer
fsync :日志和数据缓冲区写磁盘,都走OS buffer
O_DSYNC  :日志缓冲区写磁盘,不走 OS buffer

### 生产建议使用O_DIRECT,最好是配合固态盘使用

==innodb_buffer_pool_size==

https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_buffer_pool_size

show engine innodb status\G;
一般建议最多是物理内存的 75-80%
作用:数据缓冲区的总大小。缓冲数据页和索引页。是mysql最大的内存区域
### 默认128M

第十天


工具日志管理

错误日志

  • 作用

    • 记录mysql从启动后,所有的状态、警告、错误
    • 为我们定位数据库问题,提供帮助
  • 默认:开启

  • select @@log_error;
    show variables like 'log_error';
    
  • 定制方法

    • vim /etc/my.cnf
      log_error=/tmp/mysql.log
      

binlog二进制日志

  • 实际上是我们数据恢复时配合备份一起恢复数据的手段

  • 作用

    • 数据恢复,主从复制中应用
    • 主要记录数据库变化(DDL,DCL,DML)性质的日志。是==逻辑层==性质日志
  • 配置

    • 默认:8.0版本之前,没有开启。建议生产开启

    • vi /etc/my.cnf
          server_id=xxx   #主机编号。
          log_bin=/data/binlog/mysql-bin  #日志存放目录/日志名前缀,例如:mysql-bin.000001 mysql-bin.000002
          sync_binlog=1   #binlog日志刷盘策略,双一中的的第二个1.每次事务提交立即刷写binlog到磁盘
          binlog_format=row   #binlog的记录格式为row模式
          #expire_logs_days
          #一定要和数据盘分开
      
      mkdir -p /data/binlog
      chown  -R  mysql.mysql  !$
      /etc/init.d/mysqld  restart
      ls -l  /data/binlog
      
      
  • 记录内容是啥?

    • binlog是==SQL层==的功能。记录的是变更SQL语句,不记录查询语句。
    • 种类
      • DDL :原封不动的记录当前DDL(statement语句方式)。
      • DCL :原封不动的记录当前DCL(statement语句方式)。
      • DML :只记录已经提交的事务DML(insert,update,delete)
    • DML三种记录方式
      • binlog_format(binlog的记录格式)参数影响
      • statement(5.6默认)SBR(statement based replication) :语句模式原封不动的记录当前DML。
      • ROW(5.7 默认值) RBR(ROW based replication) :记录数据行的变化(用户看不懂,需要工具分析)
      • mixed(混合)MBR(mixed based replication)模式 :以上两种模式的混合
  • event(事件)

    • 二进制日志的最小记录单元

    • 对于DDL,DCL,一个语句就是一个event

    • 对于DML语句来讲,只记录已提交的事务

    • 组成

      • 事件的开始标识(position)
      • 事件内容
      • 事件的结束标识
    • 标识

      • 某个事件在binlog中的相对位置号
      • 位置号的作用是什么?
      • 为了方便我们截取事件
  • binlog的查看

    • 查看开启情况
      • ==select @@log_bin_basename;==
      • ==select @@log_bin;==
      • ==show variables like '%log_bin%';==
    • 事件查看命令
      • ==show binary logs;==
      • ==show master status;==
      • ==show binlog events in 'mysql-bin.000002';==
      • ==mysql -e "show binlog events in 'mysql-bin.000004'" |grep drop==
    • 内容查看命令
      • ==mysqlbinlog /data/mysql/mysql-bin.000006==
      • ==mysqlbinlog --base64-output=decode-rows -vvv /data/binlog/mysql-bin.000003==
      • mysqlbinlog -d binlog /data/binlog/mysql-bin.000003
        • ==-d database_name==
      • mysqlbinlog --start-datetime='2019-05-06 17:00:00' --stop-datetime='2019-05-06 17:01:00' /data/binlog/mysql-bin.000004 /data/binlog/mysql-bin.000005
  • 日志截取恢复

    • ==flush logs;==
      • 滚动一个新的binlog日志
    • ==mysqlbinlog --start-position=219 --stop-position=1347 /data/binlog/mysql-bin.000003 >/tmp/bin.sql==
    • set sql_log_bin=0/1;
  • ==维护操作==

    • 日志滚动
      • ==flush logs;==
      • ==mysqladmin -uroot -pxxx flush-logs==
      • ==select @@max_binlog_size;==
        • 默认:1G
      • mysqldump -F
      • 重启数据库
    • 日志删除
      • 不能用rm
      • ==select @@expire_logs_days;==
        • 自动删除机制
        • 默认:0天,代表永不删除
        • 一般生产建议最少2个全备周期+1
      • ==purge binary logs to ‘mysql-bin.000005’;==
        • 手工删除
        • ==purge binary logs befor '2021-02-01 22:46:46';==
      • ==reset master;==
        • 全部清空

slow慢日志

  • 作用

    • 记录mysql运行过程中较慢的语句,通过一个文本的文件记录下来的
    • 帮助我们进行语句优化工具日志
  • 如何配置

    • 默认没有开启
    • ==select @@slow_query_log;==
    • ==select @@slow_query_log_file;==
    • ==select @@long_query_time;==
      • 默认:10S
    • ==select @@log_queries_not_using_indexes;==
  • ==配置参数例子==

    • vi /etc/my.cnf
      #开关:
      slow_query_log=1 
      #文件位置及名字 
      slow_query_log_file=/data/mysql/slow.log
      #设定慢查询时间:
      long_query_time=0.1
      #没走索引的语句也记录:和上面是或的关系
      log_queries_not_using_indexes=1
      

SBR与RBR模式的对比

STATEMENT:可读性较高,日志量少,但是不够严谨
ROW :可读性很低,日志量大,足够严谨


update t1 set xxx=xxx where id>1000 ? -->一共500w行,row模式怎么记录的日志
为什么row模式严谨?
id name intime
insert into t1 values(1,'zs',now())


我们建议使用:row记录模式


Master [binlog]>show binlog events in 'mysql-bin.000003';
+------------------+-----+----------------+-----------+-------------+----------------------------------------+
| Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                                   |
+------------------+-----+----------------+-----------+-------------+----------------------------------------+
| mysql-bin.000003 |   4 | Format_desc    |         6 |         123 | Server ver: 5.7.20-log, Binlog ver: 4  |
| mysql-bin.000003 | 123 | Previous_gtids |         6 |         154 |                                        |
| mysql-bin.000003 | 154 | Anonymous_Gtid |         6 |         219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'   |
| mysql-bin.000003 | 219 | Query          |         6 |         319 | create database binlog                 |
| mysql-bin.000003 | 319 | Anonymous_Gtid |         6 |         384 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'   |
| mysql-bin.000003 | 384 | Query          |         6 |         486 | use `binlog`; create table t1 (id int) |
+------------------+-----+----------------+-----------+-------------+----------------------------------------+

Log_name:binlog文件名
Pos:开始的position    *****
Event_type:事件类型
  Format_desc:格式描述,每一个日志文件的第一个事件,多用户没有意义,MySQL识别binlog必要信息
Server_id:mysql服务号标识
End_log_pos:事件的结束位置号 *****
Info:事件内容*****

补充:
SHOW BINLOG EVENTS
 [IN 'log_name']
 [FROM pos]
 [LIMIT [offset,] row_count]
[root@db01 binlog]# mysql -e "show binlog events in 'mysql-bin.000004'" |grep drop

mysqlbinlog /data/mysql/mysql-bin.000006
mysqlbinlog --base64-output=decode-rows -vvv /data/binlog/mysql-bin.000003
mysqlbinlog  -d binlog /data/binlog/mysql-bin.000003
[root@db01 binlog]# mysqlbinlog --start-datetime='2019-05-06 17:00:00' --stop-datetime='2019-05-06 17:01:00'  /data/binlog/mysql-bin.000004 


基于Position号进行日志截取

### 核心就是找截取的起点和终点
--start-position=321
--stop-position=513
mysqlbinlog --start-position=219 --stop-position=1347 /data/binlog/mysql-bin.000003 >/tmp/bin.sql

### 案例: 使用binlog日志进行数据恢复
#### 模拟:
1. 
[(none)]>create database binlog charset utf8;
2. 
[(none)]>use binlog;
[binlog]>create table t1(id int);
3. 
[binlog]>insert into t1 values(1);
[binlog]>commit;
[binlog]>insert into t1 values(2);
[binlog]>commit;
[binlog]>insert into t1 values(3);
[binlog]>commit;
4. 
[binlog]>drop database binlog;
#### 恢复:
[(none)]>show master status ;
[(none)]>show binlog events in 'mysql-bin.000004';
[root@db01 binlog]# mysqlbinlog --start-position=1227 --stop-position=2342 /data/binlog/mysql-bin.000004 >/tmp/bin.sql
[(none)]>set sql_Log_bin=0;
[(none)]>source /tmp/bin.sql

### 面试案例:
1. 备份策略每天全备,有全量的二进制日志
2.业务中一共10个库,其中一个被误drop了
3. 需要在其他9个库正常工作过程中进行数据恢复


==双一说明(双一标准)==

  • innodb_flush_log_at_trx_commit=1
  • 每次提交事务,必然log buffer中的redo落到磁盘
  • sync_binlog=1
  • 每次事务提交,必然保证binlog cache中的日志落到磁盘

binlog的GTID模式管理

介绍

5.6 版本新加的特性,5.7中做了加强
5.6 中不开启,没有这个功能.
5.7 中的GTID,即使不开也会有自动生成
==SET @@SESSION.GTID_NEXT= 'ANONYMOUS'==


是对于一个已提交事务的编号,并且是一个全局唯一的编号。
它的官方定义如下:

GTID =server_uuid :transaction_id
7E11FA47-31CA-19E1-9E56-C43AA21293967:29

GTID(Global Transaction ID)

  • 重要参数

    • ### 开启gtid
      vim /etc/my.cnf
      gtid-mode=on
      enforce-gtid-consistency=true
      systemctl restart mysqld
      
      Master [(none)]>create database gtid charset utf8;
      Query OK, 1 row affected (0.01 sec)
      
      Master [(none)]>show master status ;
      +------------------+----------+--------------+------------------+----------------------------------------+
      | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                      |
      +------------------+----------+--------------+------------------+----------------------------------------+
      | mysql-bin.000004 |      326 |              |                  | dff98809-55c3-11e9-a58b-000c2928f5dd:1 |
      +------------------+----------+--------------+------------------+----------------------------------------+
      1 row in set (0.00 sec)
      
      Master [(none)]>use gtid
      Database changed
      Master [gtid]>create table t1 (id int);
      Query OK, 0 rows affected (0.01 sec)
      
      Master [gtid]>show master status ;
      +------------------+----------+--------------+------------------+------------------------------------------+
      | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |
      +------------------+----------+--------------+------------------+------------------------------------------+
      | mysql-bin.000004 |      489 |              |                  | dff98809-55c3-11e9-a58b-000c2928f5dd:1-2 |
      +------------------+----------+--------------+------------------+------------------------------------------+
      1 row in set (0.00 sec)
      
      Master [gtid]>create table t2 (id int);
      Query OK, 0 rows affected (0.01 sec)
      
      Master [gtid]>create table t3 (id int);
      Query OK, 0 rows affected (0.02 sec)
      
      Master [gtid]>show master status ;
      +------------------+----------+--------------+------------------+------------------------------------------+
      | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |
      +------------------+----------+--------------+------------------+------------------------------------------+
      | mysql-bin.000004 |      815 |              |                  | dff98809-55c3-11e9-a58b-000c2928f5dd:1-4 |
      +------------------+----------+--------------+------------------+------------------------------------------+
      1 row in set (0.00 sec)
      
      Master [gtid]>begin;
      Query OK, 0 rows affected (0.00 sec)
      
      Master [gtid]>insert into t1 values(1);
      Query OK, 1 row affected (0.00 sec)
      
      Master [gtid]>commit;
      Query OK, 0 rows affected (0.00 sec)
      
      Master [gtid]>show master status ;
      +------------------+----------+--------------+------------------+------------------------------------------+
      | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |
      +------------------+----------+--------------+------------------+------------------------------------------+
      | mysql-bin.000004 |     1068 |              |                  | dff98809-55c3-11e9-a58b-000c2928f5dd:1-5 |
      +------------------+----------+--------------+------------------+------------------------------------------+
      1 row in set (0.00 sec)
      
      Master [gtid]>begin;
      Query OK, 0 rows affected (0.00 sec)
      
      Master [gtid]>insert into t2 values(1);
      Query OK, 1 row affected (0.00 sec)
      
      Master [gtid]>commit;
      Query OK, 0 rows affected (0.01 sec)
      
      Master [gtid]>show master status ;
      +------------------+----------+--------------+------------------+------------------------------------------+
      | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |
      +------------------+----------+--------------+------------------+------------------------------------------+
      | mysql-bin.000004 |     1321 |              |                  | dff98809-55c3-11e9-a58b-000c2928f5dd:1-6 |
      +------------------+----------+--------------+------------------+------------------------------------------+
      1 row in set (0.00 sec)
      
      
      
  • ==select @@gtid_mode;==

    • ==select @@enforce-gtid-consistency;==
    • ==show master status;==
  • select @@server_uuid;

基于GTID进行查看binlog

  • ==--include-gtids==
  • ==--exclude-gtids==
### 具备GTID后,截取查看某些事务日志:
    --include-gtids
    --exclude-gtids

mysqlbinlog --include-gtids='dff98809-55c3-11e9-a58b-000c2928f5dd:5-16' --exclude-gtids='dff98809-55c3-11e9-a58b-000c2928f5dd:4'  /data/binlog/mysql-bin.000004

GTID的幂等性

  • ==--skip-gtids==
### 开启GTID后,MySQL恢复Binlog时,重复GTID的事务不会再执行了
#### 就想恢复?怎么办?
    --skip-gtids
    
mysqlbinlog --skip-gtids --include-gtids='dff98809-55c3-11e9-a58b-000c2928f5dd:5-16' --exclude-gtids='dff98809-55c3-11e9-a58b-000c2928f5dd:4'  /data/binlog/mysql-bin.000004 /data/binlog/mysql-bin.000005  >/tmp/binlog-gtid.sql

set sql_log_bin=0;
source /tmp/binlog-gtid.sql
set sql_log_bin=1;

mysqldumpslow 分析慢日志

mysqldumpslow -s c -t 10 /data/mysql/slow.log
#### -s 排序 c 次数 -t top

###  第三方工具(自己扩展)
https://www.percona.com/downloads/percona-toolkit/LATEST/
yum install perl-DBI perl-DBD-MySQL perl-Time-HiRes perl-IO-Socket-SSL perl-Digest-MD5

#### toolkit工具包中的命令:

./pt-query-diagest  /data/mysql/slow.log

#### Anemometer基于pt-query-digest将MySQL慢查询可视化

复习命令

  • show master status;
  • show binlog events in 'mysql-bin.000001' limit 100;
  • mysql -e "show binlog events in 'mysql-bin.000001'" |grep xxx
  • mysqlbinlog --start-position x --stop-position xx mysql-bin.000001 > /tmp/binlog.sql
  • mysqlbinlog -d database_name -start-position x --stop-position xx mysql-bin.000001 > /tmp/binlog.sql
  • mysqlbinlog --start-datetime x --stop-datetime xx mysql-bin.000001 mysql-bin.000002 > /tmp/binlog.sql
  • mysqlbinlog --skip-gtids --include-gtids='servier-uuid:2-19' --exclude-gtids='server-uuid:10-11' mysql-bin.000001 mysql-bin.000002 > /tmp/gtid.sql
  • mysqlbinlog --skip-gtids --include-gtids='servier-uuid:2-9','server-uuid:12-19' mysql-bin.000001 mysql-bin.000002 > /tmp/gtid.sql
  • mysqlbinlog --skip-gtids --include-gtids='servier-uuid:2-19' mysql-bin.000001 mysql-bin.000002 > /tmp/gtid.sql
  • set sql_log_bin=0;
  • flush logs;
  • purge binary logs to ......;
  • mysqldumpslow -s c -t N xxx-slow.log

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容