MySQL 優(yōu)化學習筆記

簡述

目錄

  • 《高性能高可用 MySQL》視頻中的前置課程 1~4
    • (1~2) 前置課程橙垢,搭建 Centos7 集群環(huán)境
    • (3) 前置課程敷燎,linux 下 RMP 版 MySQL 安裝缸夹、啟停
    • (4) 前置課程磕瓷,MySQL 啟動問題膘螟,配置文件笙僚,編碼問題
  • 《SQL 優(yōu)化》視頻中的 3~25
    • (3) MySQL 分層芳肌、存儲引擎
    • (4) SQL 優(yōu)化
    • (5) B 樹與索引
    • (6) SQL 優(yōu)化準備
    • (7) explain 中的 id、table
    • (8) type 級別詳解
    • (9) 索引類型及逐步優(yōu)化肋层、key_len 計算方法
    • (10) ref亿笤、rows
    • (11) Extra 字段
    • (12) 優(yōu)化示例
    • (13) 單表優(yōu)化及總結
    • (14~15) 多表優(yōu)化及總結,避免索引失效原則
    • (16) 常見優(yōu)化方法及慢查詢 SQL 排查
    • (17) 慢查詢閾值和 mysqldumpslow 工具
    • (18) 模擬并通過 profiles 分析海量數據
    • (19) 全局查詢日志
    • (20) 鎖機制詳解
    • (21) 寫鎖示例與 MyISAM 模式特征
    • (22) 寫鎖情況分析及行鎖解析
    • (23) 行鎖的注意事項
    • (24) 查詢行鎖

1~2. 前置課程栋猖,搭建 Centos7 集群環(huán)境

DT 課堂顏群:高性能高可用 MySQL
https://www.bilibili.com/video/BV1ry4y1v7Tr

配置網絡

  • 進入 Centos7 修改 hostname
    • hostnamectl set-hostname bigdata01
  • 關機后配置網絡
    • 編輯-編輯虛擬機網絡編輯器
    • 子網:192.168.2.0
    • 子網掩碼:255.255.255.0
    • 起始:192.168.2.128
    • 終止:192.168.2.254
  • 在 windows 設置 VMnet8
    • IP: 192.168.2.2
    • 網關:192.168.2.1
  • 查看虛擬機網卡
    • cd /etc/sysconfig/network-scripts/
  • 編輯網卡
    • vi /etc/sysconfig/network-scripts/ifcfg-ens33
IPADDR=192.168.2.128
GATEWAY=192.168.2.1
BROADCAST=192.168.2.255
DNS1=114.114.114.114
DNS2=8.8.8.8
  • 增加映射
    • vi /etc/hosts
    • 192.168.2.128 bigdata01
    • windows 下
      • C:\Windows\System32\drivers\etc\hosts
      • 192.168.2.128 bigdata01
  • 配置網絡服務
    • service NetworkManager stop
    • /etc/init.d/network restart
    • chkconfig NetworkManager off
    • 追加 nameserver
      • vi /etc/resolv.conf
      • nameserver 192.168.2.1
  • 重啟網關
    • systemctl restart network
  • 檢查是否配置成功
    • centos
      • ping baidu.com
        • 成功則返回
        • 64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=1 ttl=128 time=7.16 ms
      • ping 192.168.2.2
        • 成功則返回
        • 64 bytes from 220.181.38.148 (220.181.38.148): icmp_seq=1 ttl=128 time=7.16 ms
    • windows
      • ping 192.168.2.128
        • 成功則返回
        • 來自 192.168.2.128 的回復: 字節(jié)=32 時間<1ms TTL=64

設置時間同步

  • yum -y install npt ntpdate
  • ntpdate cn.pool.ntp.org
  • hwclock --systoch

安裝 JDK

  • 上傳 jdk-8u171-linux-x64.rpm 到 /usr 目錄
  • 安裝
    • rpm -ivh jdk-8u171-linux-x64.rpm
    • /usr/java/jdk1.8.0_171-amd64

配置環(huán)境變量

  • vi /etc/profile
  • 追加如下內容
    • export JAVA_HOME=/usr/java/jdk1.8.0_171-amd64
    • export CLASSPATH=$JAVA_HOME\lib:$CLASSPATH
    • export PATH=$JAVA_HOME\bin:$PATH
  • 環(huán)境變量生效
    • source /etc/profile
  • 測試是否配置成功
    • java -version

克隆并修改網絡配置

  • IP
    • vi /etc/sysconfig/network-scripts/ifcfg-ens33
    • IPADDR=192.168.2.129
    • IPADDR=192.168.2.130
    • 同時刪除網卡唯一標識 UID 屬性
  • hostname
    • 分別修改克隆機的 hostname
      • hostnamectl set-hostname bigdata02
      • hostnamectl set-hostname bigdata03
    • centos
      • vi /etc/hosts
      • 192.168.2.129 bigdata02
      • 192.168.2.130 bigdata03
    • windows
      • C:\Windows\System32\drivers\etc\hosts
      • 192.168.2.129 bigdata02
      • 192.168.2.130 bigdata03
  • 重啟網絡
    • systemctl restart network
  • 檢查三個節(jié)點之間的連通
    • ping bigdata01
    • ping bigdata02
    • ping bigdata03

設置節(jié)點之間免密登錄

  • 生成密鑰
    • ssh-keygen -t rsa
  • 私鑰拷貝給自己
    • ssh-copy-id localhost
  • 公鑰拷貝給其他節(jié)點
    • ssh-copy-id bigdata01
    • ssh-copy-id bigdata02
    • ssh-copy-id bigdata03
  • 測試連通
    • ssh bigdata01
    • ssh bigdata02
    • ssh bigdata03

3. 前置課程净薛,linux 下 RMP 版 MySQL 安裝、啟停

  • 安裝目錄 /app
  • 下載
  • 安裝 5.5.58
    • rpm -ivh MySQL-server-5.5.58-1.el6.x86_64.rpm
    • rpm -ivh MySQL-client-5.5.58-1.el6.x86_64.rpm
    • 卸載沖突
      • yum -y remove xxx
    • 驗證安裝
      • mysqladmin --version
  • 安裝 5.6.63
    • 課程視頻中安裝的是 5.5.58
    • 5.6.63 安裝方式與 5.5.58 略有不同
    • 參考文章
    • 安裝前
      • 查看 centos7 系統(tǒng)自帶的 mariadb 版本
        • rpm -qa|grep mariadb
      • 如果存在則卸載
        • --nodeps 避免依賴問題
        • rpm -e mariadb-libs --nodeps
      • 安裝 perl-Data-Dumper蒲拉,否則 server 安裝時會報錯
        • yum install -y perl-Data-Dumper
    • rpm -ivh MySQL-server-5.6.36-1.el7.x86_64
      • server 安裝完成后會自動初始化
    • rpm -ivh MySQL-client-5.6.36-1.el7.x86_64.rpm
    • 驗證安裝
      • mysqladmin --version
  • 啟動肃拜、關閉、重啟
    • service mysql start
    • service mysql stop
    • service mysql restart
  • 在系統(tǒng)里啟動 mysql 服務
    • 系統(tǒng)開機手動啟動
      • /etc/init.d/mysql start
    • 開機自動啟動
      • chkconfig mysql on
    • 關閉開機自動啟動
      • chkconfig mysql off
    • 檢查開機自啟動項
      • ntsysv
  • 設置密碼
    • /usr/bin/mysqladmin -u root password root
    • /usr/bin/mysqladmin -u root -h bigdata01 password root
  • 設置密碼后進入 mysql
    • mysql -u root -p
  • 5.6 及以上版本初始密碼修改
    • 無法查看初始密碼雌团,進入 mysql 報錯
      • error: 'Access denied for user 'root'@'localhost' (using password: NO)'
    • 重置密碼
      • 參考文章
      • /etc/init.d/mysql stop
      • mysqld_safe --user=mysql --skip-grant-tables --skip-networking &
      • mysql -u root mysql
      • mysql> UPDATE user SET Password=PASSWORD('root') where USER='root';
      • mysql> FLUSH PRIVILEGES;
      • mysql> quit
      • mysql> mysql -u root -p

4. 前置課程燃领,MySQL 啟動問題,配置文件锦援,編碼問題

  • ps -ef|grep mysql
    • 數據庫文件目錄
      • --datadir=/var/lib/mysql
    • pid (唯一標識符)文件目錄
      • --pid-file=/var/lib/mysql/bigdata01.pid

mysql 核心目錄

  • 安裝目錄
    • /var/lib/mysql
  • 配置文件目錄
    • /user/share/mysql
  • 命令目錄
    • /usr/bin
    • mysqladmin
    • mysqldump

mysql 配置文件

  • 4 個配置文件模板
    • my-huge.cnf 高端服務器 1~2G 內存
    • my-large.cnf
    • my-medium.cnf
    • my-small.cnf
  • 將模板中的一個拷貝給系統(tǒng)默認配置
    • MySQL 5.5:/etc/my.cnf
    • MySQL 5.6:/etc/mysql-default.cnf
    • cp /usr/share/mysql/my-huge.cnf /etc/my.cnf

mysql 字符編碼

  • 查看編碼
    • show variables like '%char%';
  • 將所有編碼設置為 UTF-8
    • vi /etc/my.cnf
[mysql]
default-character-set=utf8

[client]
default-character-set=utf8

[mysqld]
character_set_server=utf8
character_set_client=utf8
collation_server=utf8_general_ci
  • 重啟 mysql
    • service mysql restart
  • 再次查看字符編碼集
    • show variables like '%char%';
  • 編碼修改只對之后生成的數據庫有效

mysql 清屏

  • ctrl + L
  • system clear

3. MySQL 分層猛蔽、存儲引擎

DT 課堂顏群:SQL優(yōu)化
https://www.bilibili.com/video/BV1es411u7we

mysql 分層

  • 連接層
    • 提供與客戶端連接的服務
  • 服務層
    • 提供各種用戶使用的接口
    • 提供 SQL 優(yōu)化器(MySQL QUery Optimizer)
  • 引擎層
    • 提供各種存儲數據的方式
    • InnoDB
    • MyISAM
  • 存儲層
    • 存儲數據

InnoDB 和 MyISAM 區(qū)別

  • InnoDB
    • 事務優(yōu)先
    • 適合高并發(fā)操作
    • 行鎖
  • MyISAM
    • 性能優(yōu)先
    • 表鎖
  • 查詢數據庫支持哪些引擎
    • show engines;
  • 查詢默認引擎
    • show variables like '%storage_engine%';
  • 創(chuàng)建表時指定引擎
mysql>create database myDB;

mysql>use myDB;

mysql>
create table tb(
id int(4) auto_increment,
name varchar(5),
dept varchar(5),
primary key(id)
) ENGINE=MyISAM AUTO_INCREMENT=1 
DEFAULT CHARSET=utf8;

4. SQL 優(yōu)化

原因

  • 性能低
  • 執(zhí)行時間長
  • 等待時間長
  • SQL 語句欠佳(連接查詢)
  • 索引失效
  • 服務器參數設置不佳
    • 緩沖
    • 線程數

編寫過程和解析過程的差異

  • 編寫過程
    • select distinct ... from ... join ... on ... where ... group by ... having ... order by ... limit
  • 解析過程
    • from ... on ... join ... where ... group by ... having ... select distinct... order by ... limit
  • 參考文章
    • https://www.cnblogs.com/annsshadow/p/5037667.html

優(yōu)化索引

  • 索引是幫助 MYSQL 高效獲取數據的數據結構
  • 索引一般采用樹結構
    • B 樹
    • Hash 樹
  • 索引弊端
    • 索引本身需要空間
    • 索引不適用
      • 少量數據
      • 頻繁更新的字段
      • 很少使用的字段
    • 提高查詢,降低增刪改效率
  • 優(yōu)點
    • 降低 IO 使用率
    • 降低 CPU 使用率
      • B 樹本身已經排好序灵寺,可以直接使用

5. B 樹與索引

  • 三層 B 樹可以存放百萬級別數據
  • B 樹一般都是指 B+ 樹
    • 數據都保存在葉結點
  • B + 樹中查找數據的次數
    • n 次
    • 即 B+ 樹的高度

索引

分類

  • 單值索引
    • 單列
    • 一個表可以有多個單值索引
  • 主鍵索引
    • 不能重復
    • 如 id
    • 不能為 null
  • 唯一索引
    • 不能重復
    • 如 id
    • 可以為 null
  • 復合索引
    • 多個列構成的索引
    • 相當于二級目錄

創(chuàng)建索引

方式一

  • create 索引類型 索引名 on 表(字段)
  • 單值索引
    • create index dept_index on tb(dept);
  • 唯一索引
    • create unique index name_index on tb(name);
  • 復合索引
    • create index dept_name_index on tb(dept, name);

方式二

  • alter table 表名 索引類型 索引名(字段)
  • 單值
    • alter table tb add index dept_index(dept);
  • 唯一
    • alter table tb add unique index name_index(name);
  • 復合
    • alter table tb add index dept_name_index(dept, name);
  • DDL 語句不需要 commit; 自動提交
  • 如果一個字段是 primary key曼库,該字段默認是主鍵索引

刪除索引

  • drop index 索引名 on 表名;
  • drop index name_index on tb;

查詢索引

  • show index from 表名
  • show index from tb;

SQL 性能問題

  • 分析 sql 執(zhí)行計劃
    • explain
    • 可以模擬 SQL 優(yōu)化器執(zhí)行 SQL 語句
  • MYSQL 查詢優(yōu)化會干擾我們的優(yōu)化

6. SQL 優(yōu)化準備

  • explain SQL 語句
  • id 編號
  • select_type 查詢類型
  • table 表名
  • type 類型
  • possible_keys 預測用到的索引
  • key 實際用到的索引
  • key_len 實際使用索引的長度
  • ref 表之間的引用
  • rows 通過索引查詢到的數據量
  • Extra 額外信息

準備數據

create table course
(cid int(3),
cname varchar(20),
tid int(3)
);

create table teacher
(tid int(3),
tname varchar(20),
tcid int(3)
);

create table teacherCard
(
tcid int(3),
tcdesc varchar(200)
);

insert into course values(1,'java', 1);
insert into course values(2,'html', 1);
insert into course values(3,'sql', 2);
insert into course values(4,'web', 3);

insert into teacher values(1, 'tz', 1);
insert into teacher values(2, 'tw', 2);
insert into teacher values(3, 'tl', 3);
insert into teacher values(4, 'ta', 4);
insert into teacher values(5, 'tb', 5);
insert into teacher values(6, 'tc', 6);

insert into teacherCard values(1, 'tzdesc');
insert into teacherCard values(2, 'twdesc');
insert into teacherCard values(3, 'tldesc');

7. explain 中的 id、table

id 值相同

  • id 值相同略板,從上往下毁枯,順序執(zhí)行
  • 表的執(zhí)行順序,跟隨數據量變化叮称,原理是笛卡爾積
  • 數據量小的表優(yōu)先查詢

查詢課程編號為 2 或教師證編號為 3 的老師信息

explain select t.* from teacher t, course c, teacherCard tc 
where t.tid=c.tid and t.tid=tc.tcid and (c.cid = 2 or tc.tcid=3);

查詢教授 SQL 課程的老師描述信息

  • 多表連接形式
explain select tc.tcdesc from teacherCard tc, course c, teacher t 
where c.tid = t.tid and t.tcid = tc.tcid and c.cname='sql';

id 值不同

  • id 值不同后众,id 值大的優(yōu)先查詢
  • 本質:在嵌套子查詢時,先查內層颅拦,再查外層

查詢教授 SQL 課程的老師描述信息

  • 子查詢形式
explain select tc.tcdesc from teacherCard tc where tc.tcid=
(select t.tcid from teacher t where t.tid = 
(select c.tid from course c)
);

id 值相同 + id 值不同

  • id 值大的優(yōu)先
  • id 值相同的從上往下順序執(zhí)行

查詢教授 SQL 課程的老師描述信息

  • 多表+子查詢
explain select t.tname, tc.tcdesc from teacher t, teacherCard tc 
where t.tcid=tc.tcid and t.tid=(select c.tid from course c where cname='sql');

select_type

  • primary 包含子查詢 SQL 中的主查詢(最外層)
  • SUBQUERY 包含子查詢 SQL 中的子查詢(非最外層)
  • simple 簡單查詢蒂誉,不包含子查詢和 union
  • derived 衍生查詢,使用到了臨時表
    • from 子查詢中只有一張表
      • explain select cr.cname from (select * from course where tid in (1, 2)) cr;
    • from 子查詢中距帅,如果有 table1 union table2右锨,table1 就是 derived
      • explain select cr.cname from (select * from course where tid=1 union select * from course where tid =2) cr;
  • union result
    • 告知關聯(lián)關系的表是哪兩張

8. type 級別詳解

system

create table test01
(
tid int(3),
tname varchar(20)
);

alter table test01 add constraint tid_pk primary key(tid);

insert into test01 values(1, 'a');

explain select * from (select * from test01) t where tid=1;
  • system>const>eq_ref>ref>range>index>all
  • system 和 const 只是理想情況,一般優(yōu)化很難達到
  • system 只有一條數據的系統(tǒng)表碌秸,或衍生表只有一條數據的主查詢

const

explain select tid from test01 where tid=1;
/* 刪除 primary 索引 */
alter table test01 drop primary key;
/* 修改索引為一般索引 */
create index test01_index on test01(tid);
  • const 只能查到一條數據的 SQL
  • 只能用于 primary key 或 unique 索引
  • 如果是一般索引绍移,不會出現(xiàn) const

eq_ref

alter table teacherCard add constraint pk_tcid primary key(tcid);
alter table teacher add constraint uk_tcid unique index(tcid);
delete from teacher where tcid>3;
explain select t.tcid from teacher t, teacherCard tc where t.tcid = tc.tcid;
  • 對于每個索引鍵的查詢悄窃,返回匹配有且只有一行數據
  • 常見于唯一索引和主鍵索引
  • 上述語句用到的索引是 teacher 表的 tcid 字段
  • 如果 teacher 表的數據個數和連接查詢的數據個數一致,才有可能滿足 eq_ref 級別

ref

insert into teacher values(4, 'tz', 4);
insert into teacherCard values(4, 'tzc');

alter table teacher add index index_name(tname);

explain select * from teacher where tname='tz';
  • 非唯一索引
  • 對于每個索引鍵的查詢蹂窖,返回匹配的所有行

9. 索引類型及逐步優(yōu)化轧抗,ken_len 計算方法

range

alter table teacher add index tid_index(tid);

explain select t.* from teacher t where t.tid <3;
  • 檢索指定范圍的行,where 后面是一個范圍查詢
  • between, in, <, >, >=, <=
  • in 查詢瞬测,有時會失效横媚,從 range 級別轉為 all 無索引級別

index,all

/* tid 有索引月趟,只掃描 tid 列 */
explain select tid from teacher;
/* course 表無索引灯蝴,掃描全部數據 */
explain select cid from course;
  • index 查詢全部索引數據
  • all 查詢全部數據

總結

  • system/const
    • 結果只有一條
  • eq_ref
    • 結果多條
    • 每條數據唯一
  • ref
    • 結果多條
    • 每條數據可能是多條

possible_keys, key

alter table course add index cname_index(cname);

explain select t.tname, tc.tcdesc from teacher t, teacherCard tc 
where t.tcid=tc.tcid and t.tid=(select c.tid from course c where cname='sql');

explain select tc.tcdesc from teacherCard tc, course c, teacher t 
where c.tid = t.tid and t.tcid = tc.tcid and c.cname='sql';

  • possible_keys
    • 可能用到的索引
    • 是一種預測
  • key
    • 實際使用到的索引
  • null 表示無索引

key_len

create table test_kl 
(
name char(20) not null default ''
);

alter table test_kl add index index_name(name);

explain select * from test_kl where name='';

alter table test_kl add column name1 char(20);
alter table test_kl add index index_name1(name1);

explain select * from test_kl where name1='';

drop index index_name on test_kl;
drop index index_name1 on test_kl;

alter table test_kl add index name_name1_index (name, name1);

explain select * from test_kl where name1='';

alter table test_kl add column name2 varchar(20);
alter table test_kl add index name2_index(name2);

/* key_len=63 = 60+1(null)+2(varchar) */
explain select * from test_kl where name2='';
  • 索引的長度
  • 用于判斷復合索引是否被完全使用
  • utf8 中,1 個字符占 3 個字節(jié)
    • char(20)孝宗,key_len = 60
  • gbk 中穷躁,1 個字符 2 個字節(jié)
  • latin 中,1 個字符 1 個字節(jié)
  • 如果索引字段可以為 null因妇,mysql 底層會用 1 個字節(jié)用于標識
  • 索引字段為 varchar问潭,用 2 個字節(jié)代表可變長度

10. ref, rows

ref

alter table course add index tid_index(tid);

explain select * from course c, teacher t where c.tid = t.tid and t.tname='tw';
  • 與 type 中的 ref 區(qū)分
  • 指明當前表所參照的字段
  • select ... where a.c=b.x
  • 其中 b.x 可以是常量,const

rows

explain select * from course c, teacher t where c.tid = t.tid and t.tname='tz';
  • 實際通過索引查詢到的數據個數

11. Extra 字段

Using filesort

create table test02
(
a1 char(3),
a2 char(3),
a3 char(3),
index idx_a1(a1),
index idx_a2(a2),
index idx_a3(a3)
);

/* 排序和查找不是同一個字段 Using filesort */
explain select * from test02 where a1 = '' order by a2;

drop index idx_a1 on test02;
drop index idx_a2 on test02;
drop index idx_a3 on test02;

alter table test02 add index idx_a1_a2_a3(a1, a2, a3);
/* 復合索引跨列 */
explain select * from test02 where a1='' order by a3;
explain select * from test02 where a2='' order by a3;
explain select * from test02 where a1='' order by a2;
  • 性能消耗大婚被,需要額外一次排序或查詢
  • 如果排序和查找不是同一個字段睦授,則會出現(xiàn) Using filesort
  • 如果符合索引跨列,會出現(xiàn) Using filesort
    • where 和 order by 按照符合索引的順序使用摔寨,不要跨列或無序
  • 常見于 order by

Using temporary

explain select a1 from test02 where a1 in ('1', '2', '3') group by a2;
  • 用到了臨時表
  • 常見于 group by
  • 避免
    • 查詢哪列就使用哪列 group by

Using index

explain select a1, a2 from test02 where a1='' or a2='';

drop index idx_a1_a2_a3 on test02;
alter table test02 add index id_a1_a2(a1, a2);
explain select a1, a3 from test02 where a1='' or a3='';

/* 對 possible_keys 和 key 的影響 */
explain select a1, a2 from test02 where a1='' or a2='';
explain select a1, a2 from test02;
  • 使用到的列都在索引中,稱為索引覆蓋
  • 性能提升
  • 不讀取原文件怖辆,只從索引文件中獲取數據
  • 不需要回表查詢
  • 索引覆蓋對 possible_keys 和 key 的影響
    • 如果沒有 where是复,則索引只出現(xiàn)在 key 中
    • 如果有 where,則索引出現(xiàn)在 key 和 possible_keys 中

Using where

explain select a1, a3 from test02 where a3='';
  • 回表查詢

impossible where

explain select * from test02 where a1='x' and a1='y';
  • where 子句永遠為 false

12. 優(yōu)化示例

create table test03
(
a1 int(4) not null,
a2 int(4) not null,
a3 int(4) not null,
a4 int(4) not null
);

alter table test03 add index idx_a1_a2_a3_4(a1, a2, a3, a4);

/* Using index */
/* 推薦按照復合索引的順序查詢 */
explain select a1, a2, a3, a4 from test03 where a1=1 and a2=2 and a3=3 and a4=4;

/* Using index */
/* 經過 SQL 優(yōu)化器后竖螃,效果與上一個查詢語句一致 */
explain select a1, a2, a3, a4 from test03 where a4=1 and a3=2 and a2=3 and a1=4;

/* Using where; Using index */
/* a4 跨列淑廊,索引失效,造成回表查詢 */
/* where a1=1 and a2=2 ... order by a3 仍然遵循復合索引的順序特咆,因此有 Using index */
explain select a1, a2, a3, a4 from test03 where a1=1 and a2=2 and a4=4 order by a3;

/* Using where; Using index; Using filesort */
/* where a1=1 ... order by a3 跨列季惩,多了一次查找/排序,出現(xiàn) Using filesort */
explain select a1, a2, a3, a4 from test03 where a1=1 and a4=4 order by a3;

總結

  • 如果復合索引使用順序完全一致腻格,索引全部使用
  • 如果部分一致画拾,索引部分使用
  • where 和 order 拼接不要跨列

13. 單表優(yōu)化及總結

create table book
(
bid int(4) primary key,
name varchar(20) not null,
authorid int(4) not null,
publicid int(4) not null,
typeid int(4) not null
);

insert into book values(1, 'java', 1, 1, 2);
insert into book values(2, 'html', 2, 1, 2);
insert into book values(3, 'sql', 3, 2, 1);
insert into book values(4, 'C', 4, 4, 3);

commit;

/* type:All*/
/* Using where; Using filesort */
explain select bid from book where typeid in(2, 3) and authorid=1 order by typeid desc;

/* type:index */
/* Using where; Using index; Using filesort */
alter table book add index idx_bta(bid, typeid, authorid);

/* 為避免干擾,優(yōu)化之前刪除老的索引 */
drop index idx_bta on book;

/* 根據 sql 實際解析的順序菜职,調整索引順序 */
/* type:index */
/* Using where; Using index */
alter table book add index idx_tab(typeid, authorid, bid);


/* 刪除索引青抛,創(chuàng)建新索引測試 */
drop index idx_tab on book;

/* 將出現(xiàn)范圍查詢的字段 typeid 放到后面 */
alter table book add index idx_atb(authorid, typeid, bid);

/* 將范圍查詢 typeid in (2, 3) 放到 authorid=1 后面 */
/* type:ref */
/* Using where; Using index */
/* key_len: 4 */
explain select bid from book where authorid=1 and typeid in(2, 3) order by typeid desc;

/* Using index */
/* key_len: 8 */
/* typeid in(2, 3) 改為 typeid=3,不使用范圍查詢酬核,typeid 索引有效 */
/* 通過 key_len 也可以佐證蜜另,此處有 2 個索引适室,typeid 索引有效 */
explain select bid from book where authorid=1 and typeid=3 order by typeid desc;

小結

  • 索引不能跨列使用,保持索引定義和使用順序一致性
  • 索引需要逐步優(yōu)化
  • 將含 in 的范圍查詢放到條件最后举瑰,防止整個索引失效
  • Using index
    • where authorid=1 ... authorid 在索引中捣辆,不需要回原表
  • Using where
    • ... and typeid in (2,3) typeid 在索引中,但是使用了 in 范圍查詢此迅,索引失效汽畴,需要回原表

14~15. 多表優(yōu)化及總結,避免索引失效原則

兩張表

create table teacher2
(
tid int(4) primary key,
cid int(4) not null
);

insert into teacher2 values(1, 2);
insert into teacher2 values(2, 1);
insert into teacher2 values(3, 3);

create table course2
(
cid int(4),
cname varchar(20)
);

insert into course2 values(1, 'java');
insert into course2 values(2, 'python');
insert into course2 values(3, 'kotlin');
commit;

/* 左連接邮屁,將數據量少的表放到左邊 */
/* type:All */
/* Extra:  */
/* type:All */
/* Extra: Using where; Using join buffer  */
select * from teacher2 t left outer join course2 c 
on t.cid=c.cid where c.cname='java';

/* 增加索引 */
/* type: index */
/* Extra: Using index */
/* type: All */
/* Extra: Using where; Using join buffer*/
alter table teacher2 add index index_teacher2_cid(cid);

/* type: ref */
/* Extra: Using where */
/* type: ref */
/* Extra: Using index*/
alter table course2 add index index_course2_cname(cname);
  • 索引添加原則
    • 小表驅動大表
    • 索引建立在經常使用的字段上
    • 三表或更多表使用相同的原則
  • 左外連接整袁,給左表加索引
  • 右外連接,給右表加索引
  • Using join buffer
    • mysql 引擎使用了連接緩存

避免索引失效的原則

/* 2 個索引都有效 */
/* type:ref */
/* Extra: */
/* key_len: 8 */
explain select * from book where authorid=1 and typeid=2;

/* 只有 1 個索引有效 */
/* type:ref */
/* Extra: using where */
/* key_len: 4 */
explain select * from book where authorid=1 and typeid*2=2;


/* 2 個索引都失效 */
/* type:All */
/* Extra: using where */
/* key_len: NULL */
explain select * from book where authorid*2=1 and typeid*2=2;

/* 2 個索引都失效佑吝,復合索引左邊失效坐昙,整個索引失效 */
/* type:All */
/* Extra: using where */
/* key_len: NULL */
explain select * from book where authorid*2=1 and typeid=2;

/* 刪除復合索引 */
drop index idx_atb on book;

alter table book add index idx_authorid(authorid);
alter table book add index idx_typeid(typeid);

/* 1 個索引都失效,獨立索引芋忿,第 1 個索引失效炸客,不影響后面的索引 */
/* type:ref */
/* Extra: using where */
/* key_len: 4 */
explain select * from book where authorid*2 = 1 and typeid=2;

/* 索引有效 */
explain select * from book where authorid =1 and typeid =2;
/* 使用了不等于,索引失效 */
explain select * from book where authorid !=1 and typeid =2;
  • 復合索引戈钢,不要跨列或無序使用
  • 盡量使用全索引匹配
  • 不要在索引上進行任何操作
    • 計算
    • 函數
    • 類型轉換
    • ... where a.x*3
  • 復合索引痹仙,左邊索引失效,所有索引失效
  • 復合索引使用不等于或者 is null殉了,自身索引會失效开仰,右側索引可能會失效
  • MySQL 本身有 sql 優(yōu)化器,實際優(yōu)化效果并非百分之百達到預期

索引優(yōu)化與預期不符合的情況

drop index idx_typeid on book;
drop index idx_authorid on book;

alter table book add index idx_book_at(authorid, typeid);

/* 復合索引全部使用 */
/* key_len:8 */
/* type: ref */
explain select * from book where authorid =1 and typeid =2;

/* where 中最左側的索引字段有 > 號薪铜,復合索引中自身及右側全部失效 */
/* type:All */
/* Extra: Using where */
/* key_len: NULL */
explain select * from book where authorid >1 and typeid =2;

/* 最右側索引使用了 > 號众弓,復合索引沒有失效 */
/* type: range */
/* Extra: Using where */
/* key_len: 8 */
explain select * from book where authorid =1 and typeid>2;

/* 復合索引只有 1 個生效 */
/* type: range */
/* key_len: 4 */
/* Extra: Using where */
explain select * from book where authorid <1 and typeid=2;

/* 相比上一條 SQL,只將 authorid<1 改為 authorid<4隔箍,右側索引也失效 */
/* type: ALL */
/* key_len: NULL */
/* Extra: Using where */
explain select * from book where authorid <4 and typeid=2;

/* 使用百分號開頭谓娃,索引失效 */
/* type: ALL */
/* key_len: NULL */
/* Extra: Using where */
explain select * from teacher where tname like '%x%';

/* 不使用百分號開頭,索引仍然有效 */
/* type: range */
/* key_len: NULL */
/* Extra: Using where */
explain select * from teacher where tname like 'x%';

/* 使用百分號開頭蜒滩,但是實現(xiàn)索引覆蓋滨达,仍然起到了一定的優(yōu)化作用 */
/* type: index */
/* key_len: 63 */
/* Extra: Using where; Using index */
explain select tname from teacher where tname like '%x%';

/* tname 和 'abc' 都是字符形式,索引有效 */
/* type: ref */
/* key_len: 63 */
/* Extra: Using where */
explain select * from teacher where tname = 'abc';

/* tname 是字符類型俯艰,123 是整數捡遍,查找時有類型轉換操作,導致索引失效 */
/* type: ALL */
/* key_len: NULL */
/* Extra: Using where */
explain select * from teacher where tname = 123;

/* 使用 and竹握,索引仍然有效 */
/* type: ref */
/* key_len: 63 */
/* Extra: Using where */
explain select * from teacher where tname = '' and tcid>1;

/* 使用了 or稽莉,導致 or 左側的索引也失效 */
/* type: ALL */
/* key_len: NULL */
/* Extra: Using where */
explain select * from teacher where tname = '' or tcid>1;
  • 一般情況,范圍查詢之后的索引失效
  • 使用索引覆蓋,索引優(yōu)化會完全符合預期
  • like 盡量以常量開頭污秆,不以 '%' 開頭劈猪,否則索引失效
  • 使用類型轉換(顯式或隱式),會導致索引失效
  • 使用 or 會導致索引失效良拼,甚至會影響左側的索引

16. 常見的優(yōu)化方法及慢 SQL 排查

exist 和 in

/* 有數據 */
select tname from teacher where exists(select * from teacher);

/* 無數據 */
select tname from teacher where exists(select * from teacher where tid=9999);
  • 如果主查詢數據集大战得,使用 in
  • 如果子查詢數據集大,使用 exist
  • 將主查詢的結構放到子查詢結果中進行條件校驗
    • 如果子查詢有數據庸推,則校驗成功
    • 如果符合校驗常侦,則保留數據

order by 優(yōu)化

  • Using filesort
    • 雙路排序 MySQL 4.1 之前
      • 掃描 2 次磁盤
      • 第 1 次
        • 從磁盤讀取排序字段
        • 對排序字段進行排序
        • 在 buffer 中進行排序
      • 第 2 次:掃描其他字段
    • 單路排序
      • 一次性讀取全部磁盤
      • 在 buffer 中進行排序
      • 不一定是真正的單路,仍然可能是多次 IO
        • 數據量過大時贬媒,分片讀取
  • 單路排序比雙路排序占用更多 buffer
  • 調整 buffer
    • set max_length_for_sort_data=1024
  • 單路自動切換到雙路的條件
    • 需要排序的列總大小超過 set max_length_for_sort_data=1024 定義的字節(jié)數
  • 提供 order by 效率的策略
    • 選擇使用單路聋亡,雙路
    • 調整 buffer 容量大小
    • 避免使用 select *
    • 復合索引避免跨列
    • 保證全部排序字段順序的一致性

慢查詢日志

  • MySQL 用于記錄響應時間超過閾值的 SQL 語句
  • long_query_time 閾值默認 10 秒
  • 慢查詢日志默認關閉
  • 建議在調優(yōu)時打開,部署上線時關閉
  • 檢查是否開啟了慢查詢日志
    • show variables like '%slow_query_log%';
  • 開啟慢查詢日志
    • 臨時開啟
      • set global slow_query_log =1;
      • mysql 服務重啟后失效
    • 永久開啟
      • vi /etc/my.cnf
      • [mysqld] slow_query_log=1 slow_query_log_file=/var/lib/mysql/localhost-slow.log
  • 慢查詢閾值修改
    • show variables like '%long_query_time%';
    • 臨時修改
      • set global long_query_time=5;
      • 重新登錄后生效
    • 永久修改
      • vi /etc/my.cnf
      • [mysqld] long_query_time=3

17. 慢查詢閾值和 mysqldumpslow 工具

  • 休眠模擬慢查詢
    • select sleep(4);
  • 查詢超過閾值的 SQL 數量
    • show global status like '%slow_queries%';
  • 在 linux 命令行际乘,通過日志查看慢查詢 SQL 的詳情
    • cat /var/lib/mysql/localhost-slow.log

通過 mysqldumpslow 工具查看慢 SQL

/* 模擬慢查詢 */
select sleep(5);
select sleep(4);
select sleep(3);

/* 獲取返回記錄最多的 3 個 SQL */
mysqldumpslow -s r -t 3 /var/lib/mysql/bigdata01-slow.log

/* 獲取訪問次數最多的 3 個 SQL */
mysqldumpslow -s c -t 3 /var/lib/mysql/bigdata01-slow.log

/* 按照時間排序坡倔,前 10 條包含 left join 查詢語句的 SQL */
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/bigdata01-slow.log

  • mysqldumpslow
  • 常用參數
    • s 排序方式
    • r 逆序
    • l 鎖定時間
    • g 正則匹配模式
  • 標準語法
    • mysqldumpslow 各種參數 慢查詢日志文件路徑

18. 模擬并通過 profiles 分析海量數據

模擬海量數據,存儲過程/存儲函數

create database testdata;
use testdata;

create table dept 
(
dno int(5) primary key default 0,
dname varchar(20) not null default '',
loc varchar(30) default ''
) engine=innodb default charset=utf8;

create table emp
(
eid int(5) primary key,
ename varchar(20) not null default '',
job varchar(20) not null default '',
deptno int(5) not null default 0
)engine=innodb default charset=utf8;

創(chuàng)建存儲函數

use testdata;
delimiter $
create function randstring(n int) returns varchar(255)
begin 

    declare all_str varchar(100) default 'abcdefghijklmnopqrestuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    declare return_str varchar(255) default '';
    declare i int default 0;
    while i<n
    do
        set return_str=concat(return_str, substring(all_str, FLOOR(1+rand()*52), 1));
        set i=i+1;
    end while;
    return return_str;
end $

沖突與解決

/* 開啟慢查詢日志脖含,再創(chuàng)建存儲過程/存儲函數罪塔,報如下錯誤   */
/* ERROR 1418 (HY000): 
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA 
in its declaration and binary logging is enabled 
(you *might* want to use the less safe log_bin_trust_function_creators variable) */

/* 臨時解決 */
set global log_bin_trust_function_creators=1;
  • 永久解決
    • vi /etc/my.cnf
    • [mysqld] log_bin_trust_function_creators=1

通過存儲函數插入隨機整數

use testdata;
create function ran_num() returns int(5)
begin 

declare i int default 0;
set i=floor(rand()*100);
return i;

end$

通過存儲過程插入海量數據

emp 表

create procedure insert_emp(in eid_start int(10), in data_times int(10))
begin 
declare i int default 0;
set autocommit =0;

repeat 
insert into emp values(eid_start+i, randstring(5), 'other', ran_num());
set i=i+1;
until i=data_times
end repeat;

commit;

end $

dept 表

create procedure insert_dept(in dno_start int(10), in data_times int(10))
begin 
declare i int default 0;
set autocommit =0;

repeat 
insert into dept values(dno_start+i, randstring(6), randstring(8));
set i=i+1;
until i=data_times
end repeat;

commit;

end $

插入數據

delimiter ;

call insert_emp(1000, 800000);
call insert_dept(10, 30);

/* 驗證插入數據量 */
select count(1) from emp;

分析海量數據

show variables like '%profiling%';
/* profiling 影響性能,在部署實施前养葵,應關閉此項 */
set profiling=on;

/* 記錄 profiling 打開之后的所有 SQL 語句消耗的時間 */
show profiles;

/* 精確查詢更多詳情征堪,Query_Id 參考上個語句的查詢結果 */
show profile all for query 2;
show profile cpu, block io for query 2;

19. 全局查詢日志

show variables like '%general_log%';

/* 開啟全局日志,記錄開啟之后的所有 SQL 語句 */
set global general_log=1;
/* 將日志記入表中 */
set global log_output='table';

/* 設置后執(zhí)行一條查詢 */
select count(1) from dept;

/* 顯示日志信息 */
select * from mysql.general_log;

/* 將日志記入文件 */
set global log_output='file';

/* 通過默認保存地址查看日志文件 */
cat /var/lib/mysql/bigdata01.log;
  • 開啟 general_log 后关拒,所有 SQL 會被記錄到系統(tǒng)自帶的 mysql.general_log 表中

20. 鎖機制詳解

  • 解決因資源共享造成的并發(fā)問題

分類

  • 操作類型
    • 讀鎖(共享鎖)
      • 對同一條數據佃蚜,多個讀操作可以同時進行,互不干擾
    • 寫鎖(互斥鎖)
      • 如果當前寫操作沒有完畢着绊,則無法進行其他讀操作
  • 操作范圍
    • 表鎖
      • 對整張表加鎖
      • 開銷小谐算,加鎖快
      • 無死鎖
      • 容易發(fā)生鎖沖突
        • 同時操作一條數據的概率增高
      • 并發(fā)度低
      • MyISAM 采用表鎖
    • 行鎖
      • 對一條數據加鎖
      • 開銷大,加鎖慢
      • 容易出現(xiàn)死鎖
      • 鎖的范圍較小畔柔,不易發(fā)生鎖沖突
      • 高并發(fā)概率低
      • InnoDB 行鎖
    • 頁鎖

表鎖

/* MYSQL/SQLSERVER 支持自增,Oracle 需要借助于序列來實現(xiàn)自增 */
create table tablelock
(
id int primary key auto_increment,
name varchar(20)
) engine myisam;

insert into tablelock(name) values('a1');
insert into tablelock(name) values('a2');
insert into tablelock(name) values('a3');
insert into tablelock(name) values('a4');
insert into tablelock(name) values('a5');

/* 查看加鎖情況 */
show open tables;

/* 加鎖 */
lock table tablelock read;

/* 加鎖后可以讀 */
select * from tablelock;

/* 加鎖后不能寫 */
/* ERROR 1099 (HY000): Table 'tablelock' was locked with a READ lock and can't be updated */
delete from tablelock where id=1;

/* 加鎖后臣樱,當前會話不能對其他表進行讀操作 */
/* ERROR 1100 (HY000): Table 'dept' was not locked with LOCK TABLES */
select count(1) from dept;

/* 加鎖后靶擦,當前會話不能對其他表進行寫操作 */
/* ERROR 1100 (HY000): Table 'dept' was not locked with LOCK TABLES */
insert into dept values(39,'xxxxxx', 'yyyyyyyy');

/* 釋放鎖 */
unlock tables;
  • 會話
    • 每一個訪問數據庫的 dos 命令行,數據庫客戶端工具雇毫,都是一個會話
  • 如果一個會話玄捕,對 A 表加了 read 鎖
    • 該會話對 A 表
      • 讀:可以
      • 寫:不能
    • 該會話對其他表
      • 讀:不能
      • 寫:不能
  • 此時其他會話
    • 對 A 表
      • 讀:可以
      • 寫,可以棚放,需要等待鎖釋放
    • 對其他表
      • 讀:可以
      • 寫:可以

21. 寫鎖示例與 MyISAM 模式特征

/* 加寫鎖 */
lock table tablelock write;

/* 不能對其他表進行任何操作 */
/* ERROR 1100 (HY000): Table 'dept' was not locked with LOCK TABLES */
select count(1) from dept;
  • 對 A 表加寫鎖
    • 當前會話對 A 表
      • 可以進行任何操作
    • 當前會話對其他表
      • 不能進行任何操作
  • 其他會話
    • 對 A 表進行操作的前提是等待寫鎖釋放

MySQL 表級鎖的鎖模式

  • MyISAM 在執(zhí)行查詢語句前枚粘,會自動給涉及的所有表加讀鎖
  • MyISAM 在執(zhí)行更新操作(DML)前,會自動給涉及的表加寫鎖
  • 對 MyISAM 表進行讀操作
    • 其他進程對同一表的操作
      • 讀:不阻塞
      • 寫:阻塞
    • 只有讀鎖釋放后飘蚯,才會執(zhí)行其他進程的寫操作
  • 對 MyISAM 表進行寫操作
    • 其他進程對同一表操作
      • 讀:阻塞
      • 寫:阻塞
    • 只有寫鎖釋放后馍迄,才會執(zhí)行其他進程的寫操作

22. 表鎖情況分析及行鎖解析

  • 分析表鎖定
    • 查看哪些表加了鎖
      • show open tables;
    • 分析表鎖定的嚴重程度
      • show status like '%table%'
        • Table_locks_immediate 能夠獲取到的鎖
        • Table_locks_waited 需要等待的鎖
      • Table_locks_immediate/Table_locks_waited> 5000
        • 建議采用 InnoDB 引擎
        • 否則使用 MyISAM 引擎
        • 能夠獲取到的資源充分時福也,使用行鎖,因此采用 InnoDB

行鎖

create table linelock
(
id int(5) primary key auto_increment,
name varchar(20)
)engine=innodb;

insert into linelock(name) values('1');
insert into linelock(name) values('2');
insert into linelock(name) values('3');
insert into linelock(name) values('4');
insert into linelock(name) values('5');

set autocommit=0;
/* 當前會話操作第 6 行 */
insert into linelock values(6, 'a6');
/* 其他會話操作第 6 行 */
/* 無法操作攀圈,需要等待鎖釋放 */
update linelock set name='ax' where id=6;
/* 其他會話操作第 8 行暴凑,沒有鎖,可以操作 */
insert into linelock values(8, 'a8');

  • 某個會話對一行數據進行 DML 操作時赘来,其他會話需要等待鎖釋放
  • 釋放鎖
    • 表鎖:unlock tables;commit/rollback 事務提交
    • 行鎖:commit/rollback 事務提交

23. 行鎖的主意事項及使用情況分析

行鎖轉為表鎖

show index from linelock;
/* 為 name 列增加索引 */
alter table linelock add index idx_linelock_name(name);

/* 當前會話操作 name='3' 的行 */
update linelock set name='a3x' where name='3';
/* 其他會話操作 name='4' 的行 */
/* name 列索引有效现喳,不同的行操作互不影響 */
update linelock set name='a4x' where name='4';

/* 當前會話操作 name=3 的行 */
/* name 列是 varchar 類型,而 3 是整數類型犬辰,類型轉換時索引失效嗦篱,行鎖轉為表鎖 */
update linelock set name='a3x' where name=3;
/* 其他會話操作 name='4' 的行 */
/* name 列索引失效,表被鎖定幌缝,無法操作 name='4' 行灸促,需要等待鎖釋放 */
update linelock set name='a4x' where name='4';

  • 如果沒有索引,行鎖會轉為表鎖

間隙鎖

/* 不存在 id=7 的數據狮腿,此時 MySQL 會自動加上間隙鎖 */
update linelock set name='x' where id>1 and id<9;

/* 其他會話操作 id=7 需要等待鎖釋放 */
insert into linelock value(7, 'a7');
  • 行鎖的一種特殊情況
  • MySQL 會自動給間隙加鎖

行鎖

  • 如果加鎖時有 where 語句腿宰,where 范圍內的數據都會被加鎖
  • 并發(fā)能力強,效率高

行鎖分析

show status like '%innodb_row_lock%';
類型 說明
Innodb_row_lock_current_waits 當前正在等待鎖的進程數量
Innodb_row_lock_time 從系統(tǒng)啟動到現(xiàn)在缘厢,等待總時長
Innodb_row_lock_time_avg 從系統(tǒng)啟動到現(xiàn)在吃度,平均等待時長
Innodb_row_lock_time_max 從系統(tǒng)啟動到現(xiàn)在,最大等待時長
Innodb_row_lock_waits 從系統(tǒng)啟動到現(xiàn)在贴硫,等待次數

24. 查詢行鎖

/* for update 為查詢語句加鎖 */
select * from linelock where id=2 for update;

/* 其他會話操作該行要等待鎖釋放 */
update linelock set name='x' where id=2;
  • 通過 for update 對 query 語句加鎖
  • 關閉事務自動提交的三種方式
    • set autocommit =0;
    • start transaction;
    • begin;
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末椿每,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子英遭,更是在濱河造成了極大的恐慌间护,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挖诸,死亡現(xiàn)場離奇詭異汁尺,居然都是意外死亡,警方通過查閱死者的電腦和手機多律,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門痴突,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狼荞,你說我怎么就攤上這事辽装。” “怎么了相味?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵拾积,是天一觀的道長。 經常有香客問我,道長拓巧,這世上最難降的妖魔是什么斯碌? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮玲销,結果婚禮上输拇,老公的妹妹穿的比我還像新娘。我一直安慰自己贤斜,他們只是感情好策吠,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瘩绒,像睡著了一般猴抹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锁荔,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天蟀给,我揣著相機與錄音,去河邊找鬼阳堕。 笑死跋理,一個胖子當著我的面吹牛,可吹牛的內容都是我干的恬总。 我是一名探鬼主播前普,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼壹堰!你這毒婦竟也來了拭卿?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤贱纠,失蹤者是張志新(化名)和其女友劉穎峻厚,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體谆焊,經...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡惠桃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辖试。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辜王。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖剃执,靈堂內的尸體忽然破棺而出誓禁,到底是詐尸還是另有隱情懈息,我是刑警寧澤肾档,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響怒见,放射性物質發(fā)生泄漏俗慈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一遣耍、第九天 我趴在偏房一處隱蔽的房頂上張望闺阱。 院中可真熱鬧,春花似錦舵变、人聲如沸酣溃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赊豌。三九已至,卻和暖如春绵咱,著一層夾襖步出監(jiān)牢的瞬間碘饼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工悲伶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留艾恼,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓麸锉,卻偏偏與公主長得像钠绍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子淮椰,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內容