日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Mysql数据库读书笔记

發布時間:2023/12/20 数据库 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mysql数据库读书笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

(三)數據庫

1.數據庫的分類及常用的數據庫:

數據庫分為:關系型數據庫和非關系型數據庫

常用的關系型數據庫有:MySQL、oracle、sqlserver等; 常用的非關系型數據庫有:redis、memcache、mogodb、hadoop等。

2.簡單介紹一下關系型數據庫的三大范式:

什么是范式? (簡單理解,就是關系型數據庫在設計表的時候需要遵循的規范)

要想滿足第二范式,必須先滿足第一范式;要滿足第三范式,必須先滿足第二范式;

第一范式(1NF):[列數據的不可分割] 指數據庫表的每一列都是不可分割的基本數據項,同一列中不能有多個值,即實體中的每一個屬性都不能有多個值、或者不能有重復的屬性;

第二范式(2NF):[主鍵唯一] 要求數據庫表中的每一行必須被唯一區分,為實現區分通常需要為表加上一個列,以存儲各個實例的唯一標識; 除主鍵以外的其他字段,都必須完全依賴與主鍵;

第三范式(3NF):[外鍵] 要求一個數據庫表中不包含已經在其表中已包含的非主鍵信息,即要求除主鍵之外的其他字段不能存在依賴關系;

反三范式:有的時候,為了效率,可以設置重復或者可以推導出的字段;例如:一張表中已經有"訂單項(單價)",但是,當訂單項太多的時候,為了效率,會在表中添加"訂單(總價)"【即使由單價和數量可以推導出總價】。

3.MySQL的SQL語句:

SQL:(Structure Query Language)結構化查詢語言

DDL:數據庫定義語言(定義數據庫、數據表的結構)【create:創建; drop :刪除;alter:修改】

DML:數據操縱語言(主要用來操作語言)【insert:插入; update:修改; delete:刪除】

DCL:數據控制語言(定義訪問權限、取消訪問權限:條件管理、安全設置)【grant:分配權限】

DQL:數據查詢語言【select:查詢;from子句;where子句】

登錄數據庫服務器:mysql -u賬戶 -p密碼

1)數據庫的增刪改查(CRUD)的操作:

  • 創建數據庫:

    • (1)【create database+數據庫名字】;
    • (2)【create database +數據庫名字+character set UTF-8】:創建數據庫的時候,可以指定字符集;
    • (3)【create database+數據庫名字+character set UTF-8+collate+校對規則】:設置默認的校對規則,不區分大小;
  • 查看數據庫:

    • 【show databases】:查看所有數據庫;
    • 【show create database+數據庫名稱】:查看數據庫定義的語句;
  • 修改數據庫:(一個數據庫創建出來,基本上就不會去動它了)

    • 【alter database+數據庫名稱+character set+字符集名稱】:修改數據庫名稱指定字符集;
  • 刪除數據庫:

    • 【drop database+數據庫名稱】;

2)數據表的增刪改查操作:

  • 創建表:

    create table+表名(
    列名1 列的類型(長度) 約束,
    列名2 列的類型(長度) 約束);

    列的約束:
    (1)primary key(主鍵約束):默認不能為空,內容必須唯一;一張表只能有一個主鍵;外鍵都是指向另一張表的主鍵;
    (2)unique(唯一約束):列里面的內容必須唯一,不能出現重復,可以為空;一張表可以有多個唯一約束;唯一約束不可以作為其他表的外鍵;
    (3)not null(非空約束);
    (4)auto_increment(自動增長);

    列的類型:
    int(整型類型);
    char(固定長度的字符類型);
    varchar(可變長度的字符類型);
    double / float / boolean
    blob(存放的是二進制);
    text(主要用來存放文本)
    data:YYYY-MM-DD;
    time: hh:mm:ss;
    datatime: YYYY-MM-DD hh:mm:ss(默認值為null);
    timestamp: YYYY-MM-DD hh:mm:ss(默認使用當前時間);

  • 查看表:

    • 查看所有表:【show tables】;
    • 查看表的創建過程:【show create table+表名】;
    • 查看表結構:【desc +表名】;
  • 修改表:

    • 添加列(add):【alter table+表名+add+列名+列的類型+列的約束】;
    • 修改列(modify):【alter table+表名+modify+列名+列的類型+列的約束】;
    • 修改列名(change):【alter table+表名+change+舊表名+新表名+列的類型】;
    • 刪除列(drop):【alter table+表名+drop+列名】;
    • 修改表的字符集:【alter table+表名+character set+字符集名稱】;
    • 修改表名:【rename table+舊表名+to+新表名】;
  • 刪除表:

    • 【drop table+表名】;

3)對表中數據的增刪改查操作:

  • 為表中增加數據:

    方法1:

    insert into +表名(列名1,列名2)+values+(值1,值2);(注:插入部分列,列名不能省略);

    方法2:

    insert into +表名+values+(值1,值2,值3);

    方法3(批量插入):

    insert into +表名+values+(值11,值12,值13)+(值21,值22,值23)+(值31,值32,值33);

    注:命令行中插入中文,會出現亂碼問題:
    (1)臨時的解決方案是:set names GBK;
    (2)永久的解決方案是:修改 my.ini 配置(在MySQL軟件的安裝路徑里),具體步驟:

    step1:在任務管理器中,暫停MySQL的服務;step2:在MySQL安裝路徑中找到 my.ini 配置文件;step3:將文件中第57行的編碼改為GBK,保存文件,退出;step4:啟動MySQL服務。
  • 為表中刪除數據:

delete from +表名+ where 條件
(如果沒有指定條件,會將表中的數據全部刪除)

請說一下,delete刪除數據和truncate刪除數據有什么區別: (1)delete:屬于DML,一條一條的刪除表中的數據; (2)truncate:屬于DDL,先刪除表,再重建表; 如果數據比較少,delete比較高效;如果數據比較多,truncate比較高效;
  • 為表中修改數據:

update +表名+set+ 列名1=列的值1,列名2=列的值2 where 條件;

例如:update student set sname="李四" where sid=3;//表示將sid為5的名字改為李四;如果沒有寫where條件,那么整個表的每一行都會被修改;
  • 為表中查詢數據:

新建商品分類表(包含屬性:分類ID、分類名稱、分類描述),如下:
create table category(
cid int primary key auto_increament,
cname varchar(10),
cdesc varchar(30)
);
為分類表中插入數據,如下:
insert into category values(null,‘手機數碼’,‘電子產品’);
insert into category values(null,‘鞋靴箱包’,‘江南皮革廠制造’);
insert into category values(null,‘香煙酒水’,‘二鍋頭’);
insert into category values(null,‘酸奶餅干’,‘安慕希’);

新建商品表(包含屬性:商品ID、商品名稱、商品價格、商品生產日期),如下: create table product(pid int primary key auto_increament,pname varchar(10),price double,pdate timestamp,cno int ); 為商品表中插入數據,如下: insert into product values(null,'小米mix4',1000,null,1),(null,'阿迪王',988,null,2),(null,'老村長',89,null,3),(null,'勁酒',100,null,3),(null,'小熊餅干',2.5,null,4),(null,'衛龍辣條',3.5,null,4);

1)簡單查詢(select…from):

select * from product;//查詢所有商品 select pname,price from product;//查詢商品名稱和商品價格

2)別名查詢(as):

select p.pname,p.price from product as p;//表別名 select pname as 商品名稱, price as 商品價格 from product;//列別名

3)去掉重復的值(distinct):

select price from product;//查詢商品表中所有的商品價格 select distinct price from product;//查詢所有的未重復的商品價格

4)運算查詢(在查詢結果上做了加減乘除運算):

select *,price*1.5 as 折后價 from product; //顯示商品表,并多出來一列:折后價

5)條件查詢(where):

select * from product where price>60;//查詢商品價格大于60的所有商品信息where后的條件寫法: (1)關系運算符: >, >=, <, <=, =, !=( 相當于<> )select * from product where price <> 88;//查詢商品價格不等于88的所有商品(2)邏輯運算:and,or,notselect * from product where price>10 and price<100;或者select * from product where price between 10 and 100;(3)模糊查詢(like):_:代表一個字符; %:代表多個字符;例如:select * from product where pname like '%餅%';//查詢出名字中帶有'餅'的所有商品select * from product where pname like '_熊%';//查詢第二個字為'熊'的所有商品(4)在某個范圍中獲得值(in):例如:select * from product where cno in (1,3,4);//查詢出商品分類ID在1,3,4里面的商品

6)排序查詢(order by):

注:asc:(ascend)升序;desc:(descend) 降序; 例如: select * from product order by price;//查詢所有商品,并按照商品價格排序 select * from product order by price desc;//查詢所有商品,并按照商品價格降序排序 select * from product where pname like '%小%' order by price asc;//查詢商品名稱中帶‘小’的商品,并按照價格升序排序

7)聚合函數:

sum():求和; avg():求平均值; count():統計數量; max():求最大值; min():求最小值;注意:where條件后面不能接聚合函數select sum(price) from product;//獲取所有商品的價格總和 select avg(price) from product;//獲取所有商品的平均價格 select count(*) from product;//獲取所有商品的個數

8)分組(group by … having):

select cno,count(*) from product group by cno; //根據cno字段分組,分組后統計商品的數量select cno,avg(price) from product group by cno having avg(price)>70; //根據cno字段分組,分組統計出平均價格大于70 的每組商品的平均價格

小結:

編寫順序:select ... from ... where ... group by ... having ... order by ;執行順序:from ... where ... group by ... having ... select ...order by ;

4.SQL的多表操作:

1)多表之間的關系如何維護?

添加外鍵約束:foreign key例如:在商品表product中添加外鍵 cno ,指向商品分類表category的 cid; 方法1: alter table product add foreign key (cno) references category (cid);方法2:直接在新建商品表product時,增加外鍵,如下: create table product(pid int primary key auto_increament,pname varchar(10),price double,pdate timestamp,cno int,foreign key (cno) references category (cid) );

2)刪除的時候,先刪除外鍵關聯的所有數據,再刪除分類的數據;

例如:從商品分類表category中刪除分類ID為4的信息,需要:(1)首先,在商品表product中,刪除 cno=4 的商品;(2)然后,才能刪除商品分類表category中 cid=4的信息;

3)多表之間的建表原則:

一對多:(如商品和分類)在多的一方(商品表)中添加一個外鍵指向一的一方(分類表)的主鍵; 多對多:(如老師和學生)多建一張中間表,把多對多的關系拆分為一對多的關系,中間表中至少要有兩個外鍵,這兩個外鍵指向原來的那兩張表; 一對一:不常用,拆表操作

4)數據庫客戶端軟件(SQLyog)

5)多表查詢:

  • 交叉連接查詢:

    • select * from product,category;
    • select * from product,category where cno=cid;//過濾出有意義的數據
  • 內連接查詢:

    • 隱式內連接:(交叉連接實際上就是隱式內連接)

      select * from product p, category c where p.cno=c.cid;

    • 顯式內連接(inner join … on ):

      select * from product p inner join category c on p.cno=c.cid

    • 隱式內連接和顯式內連接的區別:

    隱式內連接是,在查詢出的結果的基礎上 去做where條件的過程;
    顯式內連接是,帶著條件去查詢結果的過程,所以效率較高

  • 外連接查詢:

    • 左外連接:會將左表中的所有數據都查詢出來,如果右表中沒有對應的數據全是null;
      select * from product p left outer join category c on p.cno=c.cid
    • 右外連接:會將右表中的所有數據都查詢出來,如果左表中沒有對應的數據全是null;
      select * from product p right outer join category c on p.cno=c.cid

  • 分頁查詢(limit):

    • 假設每頁數據的上限為3條,起始索引為 0:
      select * from product limit 0,3;//第一個參數 0 表示起始索引;第二個參數 3 表示每頁顯示的個數

    • 計算每一頁的起始索引:

      startindex = (index-1)*num

      其中,index表示當前顯示為第幾頁;num表示每頁的數據上限;頁數從1開始,索引從0開始;

  • 子查詢(實際上就是SQL的嵌套):

例子1:查詢出分類名稱為’手機數碼’的所有商品

step1:查詢分類名稱為'手機數碼'的分類ID:select cid from category where cname="手機數碼"; step2:查詢出響應分類ID等于1的所有商品:select * from product where cno=1; 合并以上,有:select * from product where cno=(select cid from category where cname='手機數碼');

例子2:查詢商品名稱、商品分類名稱的信息:

左連接:select p.pname,c.cname from product p left outer join category c on p.cno=c.cid;子查詢:select p.pname,(select c.cname from category c where c.cid=p.cno) as 商品分類名稱 from product p;

5.JDBC(JAVA database connection):

簡單說一下你對JDBC的理解:

JAVA database connection,數據庫連接。數據庫管理系統(MySQL、oracle等)有很多,每個數據庫管理系統支持的命令是不一樣的。Java只定義接口,讓數據庫廠商自己實現接口,對于我們開發者而言,只需要導入對應廠商開發的實現即可。然后以接口的方式進行調用(MySQL+MySQL驅動(實現)+jdbc)我們的Java代碼只要使用sun公司提供jdbc驅動即可。

寫一個簡單的JDBC的程序(或者寫一個訪問oracle數據的JDBC程序):

在Java Eclipse 中驅動JDBC,首先,需要導入jar包,具體操作如下:在項目下新建一個文件夾lib,右擊Build Path/Configure build path/java build path/libraries/add external jars/選擇文件"MySQL-connector-Java-5.1.46-bin.jar"

JDBC 的基本步驟: (1)注冊驅動;(com.mysql.jdbc.Driver; oracle.jdbc.driver.OracleDriver) (2)建立連接;(DriverManager.getConnection(url,username,password)) (3)創建statement,設置參數;(PreparedStatement/Statement; ps.setXXX(index,value)) (4)執行sql語句;(executeQuery(); executeUpdate()) (5)釋放連接;(是否連接要從小到大,必須放到finally)

具體如下:

1)首先,新建一個配置文件jdbc.properties,如下:

driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/數據庫名稱 name=賬戶 password=密碼

2)然后,JDBC 工具類整合:

public class JDBCUtil{static String driverClass=null;static String url=null;static String name=null;static String password=null;static{try{//創建一個屬性配置對象Properties properties=new Properties();//兩種方式導入輸入流InpurStream is=new FileInputStream("jdbc.properties");//或者 InputStream is = JDBCUtil.class.getClassLoader.getResourceAsStream("jdbc.properties");properties.load(is);//讀取屬性driverClass=properties.getProperty("jdbc.properties");url=properties.getProperty("url");name=properties.getProperty("name");password=properties.getProperty("password");}catch(Exception e){e.printStackTrace();}}//獲取連接對象public static getConn(){Connection conn=null;try{//驅動class.forName(driverClass);//建立連接conn=DriverManage.getConnection(url,name,password);}catch(Exception e){e.printStackTrace();}return conn;}//釋放資源public static void release(Connection conn,Statement st, ResultSet rs) {closeConn(conn);closeSt(st);closeRs(rs);}public static void release(Connection conn,Statement st) {closeConn(conn);closeSt(st);}private static void closeConn(Connection conn) {try {if(conn!=null) {conn.close();}}catch(SQLException e) {e.printStackTrace();}finally {conn=null;}}private static void closeSt(Statement st) {try {if(st!=null) {st.close();}}catch(SQLException e) {e.printStackTrace();}finally {st=null;}}private static void closeRs(ResultSet rs) {try {if(rs!=null) {rs.close();}}catch(SQLException e) {e.printStackTrace();}finally {rs=null;}} }

3)利用JDBC Dao模式,去實現數據庫的登錄和查詢操作:

首先,新建一個Dao接口,里面聲明數據庫的訪問規則,如下:

//定義操作數據庫的方法 public interface UserDao {// 查詢所有void findAll();/**登錄* @param username* @param password*/void login(String username,String password); }

然后,新建一個Dao的實現類,具體實現之前接口中定義的規則,如下:

public class UserDaoImpl implements UserDao {@Overridepublic void findAll() {Connection conn=null;Statement st=null;ResultSet rs =null;try {conn = JDBCUtil.getConn();st = conn.createStatement();String sql = "select * from t_user";rs = st.executeQuery(sql);while(rs.next()) {String username=rs.getString("username");int password=rs.getInt("password");System.out.println("username="+username+",password="+password);}} catch (Exception e) {e.printStackTrace();}finally {JDBCUtil.release(conn, st, rs);}}public void login(String username, String password) {Connection conn=null;PreparedStatement ps=null;ResultSet rs=null;try {conn = JDBCUtil.getConn();//SELECT * FROM t_user WHERE username= 'zhangsan' AND PASSWORD=9876String sql = "select * from t_user where username=?and password=?";ps = conn.preparedStatement(sql);ps.setString(1,username);ps.setString(2,password);rs = ps.executeQuery();if(rs.next()) {System.out.println("登錄成功");}else {System.out.println("登錄失敗");}} catch (Exception e) {e.printStackTrace();}finally{JDBCUtil.release(conn, st, rs);}} }

最后,直接使用實現,如下:

public class testUserDaoImpl {@Testpublic void testFindAll() {UserDao dao=new UserDaoImpl();dao.findAll();}@Testpublic void testLogin() {UserDao dao=new UserDaoImpl();dao.login("lisi", "9856");}}

JDBC中 PreparedStatemnt 相比 Statement 的好處:

大多數,我們使用PreparedStatement代替Statement,好處如下:

1)PreparedStatement是預編譯的,比Statement速度快;

2)使用PreparedStatement,代碼的可讀性和可維護性更強;

3)安全性:PreparedStatement可以防止SQL注入攻擊,而Statement卻不能;例如:

String sql="select * from propuct where name="+name+"and password"+password; 如果我們把['or' 1 '=' 1]作為password傳入進來,因為'1=1'肯定成立,所以任意用戶都可以通過驗證。如果使用預處理編譯語句,傳入的任何內容就不會和原來的語句發生任何匹配的關系; 只要全使用預編譯語句,就用不著對傳入的數據做任何過濾; 而如果使用普通的Statement,有可能要對drop等做費盡心機的判斷和過濾。

6. 事務

Transaction 其實指的是一組操作,里面包含許多個單一事務邏輯。只要有一個邏輯沒有執行成功,那么都算失敗,所有的數據都回歸到最初的狀態(回滾)。

  • 為什么要有事務?
    為了確保邏輯的成功。例子:銀行的轉賬

使用命令行方式演示事務

  • 開啟事務

    start transaction;

  • 提交或者回滾

    commit; 提交事務,數據將會寫到磁盤上的數據庫;
    rollback;數據回滾,回到最初的狀態;

1). 關閉自動提交功能。

cmd ---> MySQL -u root -p ********** use bank; select * from count;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 1000 || 2 | xierdun | 1000 |+----+---------+-------+ update count set money = money -100 where id = 1;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 900 || 2 | xierdun | 1000 |+----+---------+-------+ show variables like'%commit%';+--------------------------------+-------+| Variable_name | Value |+--------------------------------+-------+| autocommit | ON || innodb_commit_concurrency | 0 || innodb_flush_log_at_trx_commit | 1 |+--------------------------------+-------+ set autocommit = off;+--------------------------------+-------+| Variable_name | Value |+--------------------------------+-------+| autocommit | OFF || innodb_commit_concurrency | 0 || innodb_flush_log_at_trx_commit | 1 |+--------------------------------+-------+

2). 演示事務

update count set money = money -100 where id = 1; select * from count;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 800 || 2 | xierdun | 1000 |+----+---------+-------+ 但是,磁盤上的money沒有變化; commit;(commit之后,磁盤上的money就變化了) start transaction; update count set money = money -100 where id =1; select * from acount;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 700 || 2 | xierdun | 1000 |+----+---------+-------+ 但是,磁盤上的money沒有變化; rollback;(rollback之后,磁盤上的money就變化了) select * from count;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 800 || 2 | xierdun | 1000 |+----+---------+-------+

使用代碼行方式演示事務

  • 新建一個Java project;然后在Java project下面新建一個folder(命名為lib);然后將connection的jar包導入lib,并右擊build path --> add…;

  • 將之前的jdbc工具包和jdbc.properties復制到src下面,并且在src下面新建一個test包,并在包里新建一個測試類;

  • 右擊項目名,點擊build path --> add library --> JUnit —> JUnit4(導入org.junit.Test);可以在方法上面加@Test(并導入import org.junit.Test;)

  • 注意: 代碼里面的事務,主要是針對于連接的,

    1.通過 conn.setAutoCommit(false) 來關閉自動提交的設置;2.提交事務 conn.commit();3.回滾事務 conn.rollback();

    下面的代碼中,可以發現:因為異常,第二次加錢沒有成功

    @Test public void test2(){Connection conn=null;PreparedStatement ps=null;ResultSet rs=null;try {conn = JDBCUtil.getConn();String sql = "update count set money = money -? where id =?";ps = conn.prepareStatement(sql);//扣錢,扣id為1 的100 塊ps.setInt(1, 100);ps.setInt(2, 1);ps.executeUpdate();int a=10/0;//加錢,加id為2 的100 塊ps.setInt(1, -100);ps.setInt(2, 2);ps.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally{JDBCUtil.release(conn, ps);} }

    改進上面的代碼,演示事務

    @Test public void testTransaction(){Connection conn=null;PreparedStatement ps=null;ResultSet rs=null;try {conn = JDBCUtil.getConn();//連接:事務默認的是自動提交conn.setAutoCommit(false);String sql = "update count set money = money -? where id =?";ps = conn.prepareStatement(sql);//扣錢,扣id為1 的100 塊ps.setInt(1, 100);ps.setInt(2, 1);ps.executeUpdate();int a=10/0;//這里加入一個異常//加錢,加id為2 的100 塊ps.setInt(1, -100);ps.setInt(2, 2);ps.executeUpdate();//成功,則提交事務;conn.commit();} catch (SQLException e) {try {conn.rollback();} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();}finally{JDBCUtil.release(conn, ps);} }

    事務的特性(ACID)

    • 原子性

    指的是:事務中包含的邏輯,不可分割;

    • 一致性

    指的是:事務執行前后,數據完整性;

    • 隔離性

    指的是:事務在執行期間不應該受到其他事物的影響;

    • 持久性

    指的是:事務執行成功,那么事務應該持久保存到磁盤上;

    不考慮事務的隔離級別設置,那么會出現下面的安全隱患:

    • 讀問題:

      • 臟讀

      一個事務讀到另外一個事務還未提交的數據;

      • 不可重復讀

      一個事務讀到了另外一個事務提交的數據,造成了前后兩次查詢結果不一致;

      • 幻讀

      一個事務讀到另外一個事務已提交的插入的數據,導致多次查詢結果不一致;

    • 寫問題:

      • 丟失更新

      理解(見圖)

    事務的隔離級別

    MySQL默認的隔離級別是 可重復讀 ;Oracle默認的隔離級別是 讀已提交;

    • read uncommitted[讀未提交]

    指的是:一個事務可以讀到另一個事務還未提交的數據。

    這會引發"臟讀":(讀取到的是數據庫內存中的數據,而并非真正磁盤上的數據)

    • read committed[讀已提交]

    指的是:只能讀取到其他事務已經提交的數據,那些沒有提交的數據是讀不出來的。

    這解決了"臟讀"問題,但是會引發另一個問題"不可重復讀":(前后讀到的結果不一樣)

    • repeatable read[重復讀]----MySQL默認的隔離級別

    該隔離級別,可以讓事務在自己的會話中重復讀取數據,并且不會出現結果不一樣的狀況,即使其他事務已經提交了,也依然還是顯示以前的數據(不會因為其他事務已經提交了,而改變)

    • serializable[可串行化]

    如果有一個連接的隔離級別設置為可串行化,那么誰先打開了事務,誰就有了先執行的權利;誰后打開事務,誰就只能等著,等前面的那個事務提交或者回滾后,才能執行。

    但是這種隔離級別一般比較少用,因為容易造成性能上的問題,效率比較低;

    該事務隔離級別是最高級別的事務級別了。比前面幾個都要強大一些,也就是前面的幾種問題【臟讀、不可重復讀、幻讀】都能解決,但是效率上有一些缺陷。


    按效率劃分,從高到低:
    read uncommitted > read committed > repeatable read > serializable

    按攔截程度劃分,從高到低
    realizable > repeatable read > read committed > read uncommitted

    讀未提交 演示

    1). cmd打開兩個MySQL連接;設置A窗口的隔離級別為 讀未提交;

    //查看當前連接的隔離級別select @@tx_isolation;+-----------------+| @@tx_isolation |+-----------------+| REPEATABLE-READ |+-----------------+ //設置事務A的隔離級別為read committed set session transaction isolation level read committed; select @@tx_isolation;+----------------+| @@tx_isolation |+----------------+| READ-COMMITTED |+----------------+

    2). 兩個事務(A、B)都分別開啟事務;

    start transaction;

    讀已提交 演示

  • 設置A窗口的隔離級別為 讀已提交;
  • A B兩個窗口都開啟事務,在B窗口執行更新操作;
  • 在A窗口執行的查詢結果不一致,一次是在B窗口提交事務之前,一次是在B窗口提交事務之后;
  • 這個隔離級別 能夠屏蔽"臟讀"的現象,但引發了另一個問題:“不可重復讀”;

    解決丟失更新

    • 悲觀鎖(for update)
      可以在查詢的時候,加入 for update 加鎖:
      select * from count for update;
      但是,實際開發中,加鎖的操作是很浪費資源的,因此,出現了樂觀鎖。
      分析(見圖):

    • 樂觀鎖
      要求程序員自己控制;
      應用場景:買家不是特別多,沖突的機會不多的情況下,可以使用樂觀鎖;(在沖突機會多的情況下,如:網上搶票,用樂觀鎖是不可以的)
      分析(見圖):

    7. 數據庫連接池

    主要用于管理數據庫連接(詳情見圖)

    1.數據庫的連接對象創建工作,比較消耗性能;

    2.一開始先在內存中開辟一塊空間(集合),往池子里面放置多個連接對象;后面需要連接的話,直接從池子里取,不需要再自己去創建連接了;使用完畢,要記得歸還連接,確保連接對象能循環利用。

    簡單搭建

    DataSource 是數據庫連接池接口

    1). 新建一個Java project(ConnectionPool),并導入connection的jar包,并且add build path;

    2). 復制jdbc的工具包和jdbc.properties到src下;

    3). 然后新建一個類,負責搭建一個數據庫連接池, 如下:
    新建一個數據庫連接池的代碼(MyDataSource.java):

    /*** 這是一個數據庫連接池* 1.開始先往池子里放10個連接(構造函數)* 2.來的程序通過getConnection()方法獲取連接* 3.用完之后,使用addBack()歸還連接* 4.擴容*/ public class MyDataSource implements DataSource {List<Connection> list=new ArrayList<Connection>();public MyDataSource() {for (int i = 0; i < 10; i++) {Connection conn = JDBCUtil.getConn();list.add(conn);}}//該連接池對外公布的獲取連接的方法public Connection getConnection() throws SQLException {//來拿連接的時候,先看看池子里面還有沒有連接;if(list.size()==0){for (int i = 0; i < 5; i++) {Connection conn = JDBCUtil.getConn();list.add(conn);}}//remove(0)---->移除第一個(移除的是集合中的第一個)Connection conn = list.remove(0);return conn;}/*** 用完連接后,記得歸還* @param conn*/public void addBack(Connection conn){list.add(conn);}public Connection getConnection(String arg0, String arg1)throws SQLException {return null;}public PrintWriter getLogWriter() throws SQLException {// TODO Auto-generated method stubreturn null;}public int getLoginTimeout() throws SQLException {// TODO Auto-generated method stubreturn 0;}public void setLogWriter(PrintWriter arg0) throws SQLException {// TODO Auto-generated method stub}public void setLoginTimeout(int arg0) throws SQLException {// TODO Auto-generated method stub} }

    簡單使用

    4). 建立一個測試類:來從連接池取一個連接使用;

    public class TestPool {@Testpublic void testPool(){Connection conn =null;PreparedStatement ps =null;MyDataSource dataSource=new MyDataSource();try {conn = dataSource.getConnection();String sql="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "lisi");ps.setInt(2, 100);ps.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally{try {ps.close();} catch (SQLException e) {e.printStackTrace();}//歸還連接dataSource.addBack(conn);}} }

    自定義數據庫連接池

    • 代碼實現(如上)

    • 出現的問題:

    /* 問題:
    sun公司針對數據庫連接池定義的一套規范(DataSource接口)
    1.需要額外記住 addBack()方法;
    2.單例;
    3.無法面向接口編程:
    UserDao dao=new UserDaoImp1();//編譯看左邊,運行看右邊
    dao.insert();
    MyDataSource dataSource=new MyDataSource();
    //如果把前面的 MyDataSource 換成 DataSource,
    //那么后面的dataSource.addBack(conn);編譯就不能通過;
    //因為接口里面沒有定義addBack()方法。
    4.怎么解決? 以addBack()為切入點
    * /

    自定義數據庫連接池出現的問題

    1)由于多了一個addBack()方法,所以使用這個連接池的地方,需要額外記住這個方法,并且還不能面向接口編程;

    2)我們打算修改接口中的close()方法。原來的Connection對象的close()方法,是真的關閉連接;

    3)打算修改close()方法,以后再調用close(),并不是真的關閉,而是歸還連接對象;

    如何擴展某一個方法? ( 原有的方法邏輯,不是我們想要的,想修改自己的邏輯 );方法1:直接改源碼 ----> 無法實現方法2:繼承(重寫方法): 必須得知道這個接口(Connection)的具體實現類是誰,但是我們不知道方法3:使用裝飾者模式方法4:動態代理
    裝飾者模式(例子)

    1). 新建一個Java project(WraperDemo),在src下面新建一個接口(Waiter),并新建兩個實現類(第二個實現類是第一個實現類的包裝類)

    接口Waiter.java代碼如下:

    public interface Waiter {void service(); }

    實現類Waitress.java代碼如下:

    public class Waitress implements Waiter {public void service() {System.out.println("在服務-------");} }

    包裝類WaitressWrap.java代碼如下:

    public class WaitressWrap implements Waiter{Waiter waiter=null;public WaitressWrap(Waiter waiter) {this.waiter=waiter;}public void service() {System.out.println("。。。微笑。。。");waiter.service();} }

    2). 新建一個測試類

    public class MainTest {public static void main(String[] args) {/*Waiter waiter=new Waitress();waiter.service();*/WaitressWrap waitressWrap=new WaitressWrap(new Waitress());waitressWrap.service();} }
    • 題外話—面向接口編程(好處)

    接口:[領導]規定需要的一些方法,具體方法的實現可以由[下屬]來寫實現類;

    接口名 dao = new 實現類名(); UserDao dao = new UserDaoImp(); dao.insert();
    • 裝飾者模式(解決自定義數據庫連接池出現的問題)

    修改上面的新建數據庫連接池的MyDataSource.java代碼:

    public class MyDataSource implements DataSource {List<Connection> list=new ArrayList<Connection>();public MyDataSource() {for (int i = 0; i < 10; i++) {Connection conn = JDBCUtil.getConn();list.add(conn);}}//該連接池對外公布的獲取連接的方法public Connection getConnection() throws SQLException {//來拿連接的時候,先看看池子里面還有沒有連接;if(list.size()==0){for (int i = 0; i < 5; i++) {Connection conn = JDBCUtil.getConn();list.add(conn);}}//remove(0)---->移除第一個(移除的是集合中的第一個)Connection conn = list.remove(0);//把這個連接對象拋出去的時候,對這個對象進行包裝Connection connection = new ConnectionWrap(conn, list);return connection;}/*** 用完連接后,記得歸還* @param conn*//*public void addBack(Connection conn){list.add(conn);}*/

    Connection的包裝類(ConectionWrap.java)代碼修改如下:

    public class ConnectionWrap implements Connection {Connection connection=null;List<Connection> list;public ConnectionWrap(Connection connectionon,List<Connection> list) {super();this.connection = connectionon;this.list = list;}public void close() throws SQLException {//connection.close();System.out.println("有人來歸還連接對象了,歸還前,數據庫連接池中的連接數量是"+list.size());list.add(connection);System.out.println("有人來歸還連接對象了,歸還后。。。,數據庫連接池中的連接數量是"+list.size());}public PreparedStatement prepareStatement(String sql) throws SQLException {return connection.prepareStatement(sql);}

    測試類的代碼(TestPool.java)修改如下:

    public class TestPool {@Testpublic void testPool(){Connection conn =null;PreparedStatement ps =null;MyDataSource dataSource=new MyDataSource();try {conn = dataSource.getConnection();String sql="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "lisi");ps.setInt(2, 100);ps.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally{//歸還連接//dataSource.addBack(conn);JDBCUtil.release(conn, ps);}} }

    開源數據庫連接池:DBCP & C3P0

    用來處理:連接、管理、創建、回收

    DBCP(DataBase Connection Pool)

    DBCP是數據庫連接池,是Java數據庫連接池中的一種,由Apache開發,通過數據庫連接池,也可以讓程序自動管理數據庫連接的釋放和斷開;

    1). 導入jar包 commons-dbcp.jar和commons-pool.jar;

    2). 代碼連接(不使用配置文件方式)

    DataSource是數據庫連接池的開源接口;BasicDataSource實現了接口DataSource;

    @Test public void testDBCP(){Connection conn =null;PreparedStatement ps =null;try {//1.構建數據源對象BasicDataSource dataSource=new BasicDataSource();//2.連的是什么類型的數據庫,訪問的是哪個數據庫,用戶名,密碼//jdbc:mysql://localhost/bank 主協議:子協議://本地/數據庫名dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost/bank");dataSource.setUsername("root");dataSource.setPassword("hxzgnpy123");//3.得到連接對象conn = dataSource.getConnection();String sql ="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "zhangsan");ps.setInt(2, 500);ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally{try {ps.close();conn.close();} catch (SQLException e) {e.printStackTrace();}} }

    3). 代碼連接(配置文件方式)

    BasicDataSourceFactory類實現了ObjectFactory接口,方法有creatDataSource(Properties properties)

    @Test public void testDBCP02(){Connection conn =null;PreparedStatement ps =null;try {BasicDataSourceFactory factory=new BasicDataSourceFactory();Properties properties=new Properties();InputStream is=new FileInputStream("src//jdbc.properties");properties.load(is);BasicDataSource dataSource = factory.createDataSource(properties);//2.得到連接對象conn = dataSource.getConnection();String sql ="insert into count values (null, ?, ?)";ps = conn.preparedStatement(sql);ps.setString(1, "zhangsan");ps.setInt(2, 500);ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally{try {ps.close();conn.close();} catch (SQLException e) {e.printStackTrace();}} }

    C3P0(***)

    C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規范和JDBC2的標準擴展。

    目前使用它的開源項目有Hibernate,Spring等。

    1). 導入jar包— c3p0-0.9.5.2.jar

    2). 代碼連接(不使用配置文件方式)

    @Test public void test01(){Connection conn =null;PreparedStatement ps =null;try {ComboPooledDataSource dataSource=new ComboPooledDataSource();dataSource.setDriverClass("com.mysql.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://localhost/bank");dataSource.setUser("root");dataSource.setPassword("*****");conn = dataSource.getConnection();String sql="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "KAIKAI");ps.setInt(2, 300);ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally{try {ps.close();conn.close();} catch (Exception e) {e.printStackTrace();}} }
  • 代碼連接(配置文件方式)
  • 配置文件是支持兩種方式:c3p0.properities 和 c3p0-config.xml(后一種比較常用,注意,文件名一定要是c3p0-config.xml,comboPooledDataSource類的源碼中由類加載器自動加載這個文件,所以不能改名)

    1). 創建在src 下面創建 c3p0-config.xml:

    <?xml version="1.0" encoding="UTF-8"?> <c3p0-config><!-- 默認的數據庫 --><default-config><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost/bank</property><property name="user">root</property><property name="password">*****</property><property name="initialPoolSize">10</property><property name="maxIdleTime">30</property><property name="maxPoolSize">100</property><property name="minPoolSize">10</property><property name="maxStatements">200</property><!-- <user-overrides user="test-user"><property name="maxPoolSize">10</property><property name="minPoolSize">1</property><property name="maxStatements">0</property></user-overrides> --> </default-config><!-- 這里可以通過設置name的值,來控制什么類型的數據庫;例如這里可以令name="oracle". This app is massive ! --> <!-- This app is massive ! --> <!-- <named-config name="intergalactoApp"><property name="acquireIncrement">50</property><property name="initialPoolSize">100</property><property name="mixPoolSize">50</property><property name="maxPoolSize">1000</property> --><!-- intergalactoApp adopts a different approach to configuring statement caching --><!-- <property name="maxStatements">0</property><property name="maxStatementsPerConnection">5</property>--><!-- he's important, but there's only one of him --><!-- <user-overrides user="master-of-the-universe"><property name="acquireIncrement">1</property><property name="initialPoolSize">1</property><property name="mixPoolSize">1</property><property name="maxPoolSize">5</property><property name="maxStatementsPerConnection">50</property></user-overrides> </named-config>--> </c3p0-config>

    2)C3P0Demo.java的代碼:

    public class C3P0Demo02 { @Test public void test02(){Connection conn =null;PreparedStatement ps =null;try {//就只是new了一個對象,默認會找xml中的<default-config>ComboPooledDataSource dataSource=new ComboPooledDataSource();//也可以是別的,比如之后連接oracle數據庫時;可以通過設置<named-config name="oracle">//但是,一般一個應用只用一個數據庫//ComboPooledDataSource dataSource2=new ComboPooledDataSource("oracle");conn = dataSource.getConnection();String sql="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "KAIKAI");ps.setInt(2, 300);ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally{try {ps.close();conn.close();} catch (Exception e) {e.printStackTrace();}} } }

    注:(修改JDBCUtil.java為C3P0的形式,如下:
    之后,JDBCUtil.java 可以向之前一樣使用

    public class JDBCUtil {static ComboPooledDataSource dataSource =null; static{dataSource =new ComboPooledDataSource(); }/*** 獲取連接對象*/ public static Connection getConn() throws SQLException{return dataSource.getConnection(); }/*** 釋放資源*/ public static void release(Connection conn, java.sql.PreparedStatement ps,ResultSet rs) {closeRs(rs);closePs(ps);closeConn(conn); } public static void release(Connection conn, java.sql.PreparedStatement ps) {closePs(ps);closeConn(conn); } private static void closeConn(Connection conn) {try {if (conn != null) {conn.close();}} catch (Exception e) {e.printStackTrace();}finally{conn=null;} } private static void closePs(java.sql.PreparedStatement ps) {try {if (ps != null) {ps.close();}} catch (Exception e) {e.printStackTrace();}finally{ps=null;} } private static void closeRs(ResultSet rs) {try {if (rs != null) {rs.close();}} catch (Exception e) {e.printStackTrace();}finally{rs=null;} } }

    8. mysql 數據庫的默認的最大連接數是:

    為什么需要最大連接數?

    特定服務器上面的數據庫只能支持一定數目同時連接,這時候我們設置最大連接數(即最多同時服務多少連接),在數據庫安裝時都會有一個默認的最大連接數。

    可以在MySQL安裝的默認路徑中,可以找到配置文件 my.ini,打開后,查找max,就能知道MySQL的默認最大連接數。max_connections=100實際工作中,一般都會設置更大的最大連接數。

    9. 簡單說一下MySQL的分頁、oracle的分頁:

    為什么需要分頁?(在很多數據中,不可能完全顯示數據,需要進行分段顯示)

    1)MySQL是使用關鍵字limit來進行分頁的:limit offset,size表示從多少索引去多少位;

    String sql="select * from product order by pid limit"+pageSize*(pageNumber-1)+","+pageSize;

    2)oracle的分頁,思路是:要使用三層嵌套查詢;具體記不住,如果在工作中使用,可以到原來的項目中拷貝、或者上網查詢;

    String sql="select * from"+(select *,rownum rid from (select * from students order by postime desc) where rid <="+pageSize*pageNumber+") as t"+"where t>" + pageSize*(pageNumber-1);

    10. 觸發器的使用場景:

    觸發器,需要有觸發條件,當條件滿足以后做什么操作;

    觸發器的使用場景有很多,比如:校內網、開心網、Facebook,具體地:你發一個日志,自動通知好友,其實就是在增加日志時做一個后觸發,再向通知表中寫入條目,因為觸發器效率高。(而UCH沒有觸發器,效率和數據處理能力都很低)。

    例如:每插入一個帖子,都希望將版面表中的最后發帖時間、帖子總數字段進行同步更新,用觸發器做效率就很高。

    create table board1 (id int primary key auto_increment,name varchar(50),articleCount int );creat table article1(id int primary key auto_increment,title varchar(50),bid int references board1(id) );delimiter| //把分隔符;改為|create tigger insertArticle_Trigger after insert on article1 for each row beginupdate board1 set articleCount=articleCount+1 where id=NEW.bid; end;|delimiter;insert into board1 value(null,'test',0); insert into article1 value(null,'test',1);

    11. 數據庫的存儲過程的優點:

    1)存儲過程只在創建時進行編譯,以后每次執行存儲過程都不需要再重新編譯,而一般SQL語句每執行一次就編譯一次,因此,使用存儲過程可以大大提高數據庫執行速度;

    2)通常,復雜的業務邏輯需要多條SQL語句,這些語句要分別地從客戶機發送到服務器,當客戶機和服務器之間的操作很多時,將產生大量的網絡傳輸,如果將這些操作放在一個存儲過程中,那么客戶機和服務器之間的網絡傳輸就會大大減少,降低了網絡負載;

    3)存儲過程創建一次便可以重復使用,從而可以減少數據庫開發人員的工作量;

    4)安全性高,存儲過程可以屏蔽對底層數據庫對象的直接訪問,使用execute權限調用存儲過程,無需擁有訪問底層數據庫對象的顯示權限。

    目前常用的數據庫都支持存儲過程,例如:SQLServer、Oracle、Access等,開源數據庫系統MySQL在5.0的時候實現了對存儲過程的支持

    定義存儲過程: create procedure insert_Student(_name varchar(50),_age int,out_id int ); begininsert into student value(null,_name,_age);select max(stuId) into _id from student; end;call insert_Student('wfz',23,@id); select @id;

    12.用JDBC怎么調用存儲過程?

    (賈璉欲執事) 加載驅動 獲取連接 設置參數 執行 釋放連接Class.forName("com.mysql.jdbc.Driver"); conn=DriverManager.getConnection("jdbc:mysql:///test","root","root"); cstmt = conn.prepareCall("{call insert_Student (?,?,?)}"); catmt.registerOutParameter(3,Types.INTEGER); catmt.setString(1,"wangwu"); catmt.setInt(2,25); catmt.execute();

    13. 數據庫連接池的作用:

    1)限定數據庫連接的個數,不會導致由于數據庫連接過多導致系統運行緩慢或崩潰;

    2)數據庫連接不需要每次都去創建或者銷毀,節約了資源;

    3)數據庫連接不需要每次都去創建,響應時間更快。

    14. MySQL數據庫優化方面的事情:

    定位:查詢、定位慢查詢,并優化;

    優化手段:(其他數據庫類似)
    a. 創建索引:(創建合適的索引,我們就可以利用索引查詢,查詢到以后,直接找對應的記錄)
    b. 分表:(當一張表的數據比較多、或者一張表的某些字段的值比較多并且很少使用時,采用水平分表和垂直分表來優化)
    c. 讀寫分離:(當一臺服務器不能滿足需求時,采用讀寫分離的方式進行集群)
    d. 緩存:(使用redis來進行緩存)
    e. 一些常用的優化技巧

    如何查找慢查詢、并定位慢查詢?

    在項目自驗項目轉測試之前,在啟動MySQL數據庫時開啟慢查詢,并且把執行慢的語句寫到日志中,在運行一定時間后,通過查看日志找到慢查詢語句。

    要找出項目中的慢sql時: 1)關閉數據庫服務器(關閉服務); 2)把慢查詢記錄到日志中; 3)設置慢查詢時間:set global long_query_time=1; 4)找出日志中的慢查詢SQL.

    使用explain 慢查詢語句,來詳細分析語句的問題

    mysql> explain select * from 數據庫名稱 where 列名='aaa'\G

    注: 數據庫優化,數據庫表設計時需要遵循:
    三范式(列不可分割、主鍵唯一,外鍵)、反三范式(特殊情況,允許依賴)

    選擇合適的數據庫存儲引擎:

    在開發過程中,我們經常使用的存儲引擎有:myisam、innodb、memory

    1)myisam存儲引擎:

    如果表對事務的要求不高,同時是以查詢和添加為主的,我們考慮使用myisam存儲引擎;比如:bbs中的發帖表、回復表。

    2)innodb存儲引擎:

    如果表對事務的要求高,保存的數據都是重要數據;我們建議使用innodb存儲引擎;比如:訂單表、賬號表。

    3)memory存儲引擎:

    如果數據變化頻繁、不需要入庫,同時又需要頻繁的查詢和修改;我們考慮使用memory存儲引擎,速度極快。

    mysql數據庫中使用的是myisam、innodb存儲引擎,它們的主要區別是:

    (1)事務安全(myisam不支持事務,innodb支持);
    (2)查詢和添加速度(myisam不支持事務,就不需要考慮同步鎖,所以查找和添加數據的速度快);
    (3)支持全文索引(myisam支持全文索引,但是innodb不支持);
    (4)鎖機制(myisam只支持表鎖,而innodb支持行鎖);
    (5)外鍵(myisam不支持外鍵,innodb支持外鍵)

    選擇合適的索引:

    索引(index)是幫助DBMS高效獲取數據的數據結構;

    索引的分類:普通索引、唯一索引、主鍵索引、全文索引,具體如下:

    普通索引:允許重復的值出現;
    唯一索引:處理不允許重復值的記錄外,其他和普通索引一樣(例如:用戶名、用戶身份證等);
    主鍵索引:唯一且沒有null值;主鍵索引隨著設定主鍵而創建,也就是把某個列設為主鍵的時候,數據庫就會給該列創建索引;
    全文索引:用來對表中的文本域(char、varchar、text)進行索引,全文索引針對myisam存儲引擎;
    explain select * from 數據庫名稱 where match (title,body) against(‘database’);//會使用全文索引

    索引使用的小技巧:

    索引的弊端:

    a. 占用磁盤空間
    b. 對dml(插入、修改、刪除)操作有影響,變慢

    索引的使用場景:

    a. 肯定在where條件中經常使用,如果不做查詢就沒有意義
    b. 該字段的內容不是唯一的幾個值
    c. 字段內容不會頻繁變化

    索引使用的具體技巧:

    a. 對于創建的多列索引(復合索引),不是使用的第一部分就不會使用索引;
    alter table 表名 add index 索引名 (左邊的列名,右邊的列名);
    explain select * from 表名 where 左邊的列名=‘aaa’\G;\會使用到索引
    explain select * from 表名 where 右邊的列名=‘aaa’\G; \就不會使用到索引

    b. 對于使用like的查詢,查詢如果是’%aaa’,就不會使用到索引;而’aaa%‘會使用到索引;
    explain select * from 表名 where 列名=’%aaa’\G;\不會使用到索引
    explain select * from 表名 where 列名=‘aaa%’\G; \會使用到索引
    因此,在like查詢的時候,'關鍵字’的最前面不能使用 % 或 _ 這樣的字符,如果一定要前面有變化的值,則考慮使用全文索引 sphinx。

    c. 如果條件中有or,有條件沒有使用索引,即使其中有條件帶索引也不會使用。換言之,就是要求使用的所有字段,都必須單獨使用時能使用索引;

    d. 如果列類型是字符串,那一定要在條件中將數據使用引號引用起來,否則不使用索引;

    e. 如果mysql估計使用全表掃描要比使用索引快,則不使用索引。例如:表里面只有一條記錄。

    分表:

    分表分為:水平分表(按行)、垂直分表(按列)

    水平分表:就是按行分表,一般情況下,**表數據達到百萬級(行很多)**時,查詢效率會很低,容易造成表鎖,甚至堆積很多鏈接,直接掛掉。這種情況下,水平分表能夠很大程度上減少這些壓力;

    垂直分表:就是按列分表,如果一張表中的某個字段值非常長(如長文本、二進制等),并且只在很少情況下會查詢;這是就可以把這個字段單獨放到一個表中,可以通過外鍵關聯起來。比如:考試詳情,字段很長,并且一般我們只會關注開始成績,此時可以采用垂直分表。

    水平分表的策略:(常用按區間范圍分表、hash分表)
    a. 按時間分表: 這種分表方式有一定的局限性,數據具有較強的時效性,例如:微博發送記錄、微信消息記錄等,這種數據很少有用戶會查詢幾個月以前的數據,這種數據就可以按月分表;

    b. 按區間范圍分表: 一般在有嚴格的自增id需求上,例如:按照user_id水平分表,如下:
    table_1 user_id從1~100w
    table_2 user_id從101~200w
    table_3 user_id從201~300w

    c. hash分表:通過一個原始目標的ID或者名稱、通過一定的hash算法,算出數據存儲表的表名,然后訪問相應的表。

    讀寫分離:

  • 什么情況下數據庫優化需要讀寫分離?
  • 由于一臺數據庫支持的最大并發連接數是有限的,如果用戶并發訪問太多,并且一臺服務器滿足不了要求時,就可以集群處理。mysql的集群處理技術最常用的就是讀寫分離。

  • 讀寫分離的具體操作:

    (1)主從分離:數據庫最終會把數據持久化到磁盤上,如果集群處理必須確保每個數據庫服務器的數據都是一致的,因此,將能改變數據庫數據的操作都往主數據庫去寫,而其他的數據庫從主數據庫上同步數據。

    (2)讀寫分離:使用負載均衡來實現寫的操作都往主數據庫服務器去,而讀的操作都往從數據庫服務器去。

  • 緩存:(最經典的優化技術)

    在持久層(dao)和數據庫(db)之間添加一個緩存層,如果用戶訪問的數據已經緩存起來時,在用戶訪問時直接從緩存中獲取,不用再訪問數據庫,而緩存是在操作內存,訪問速度快。

    緩存的作用:減少數據庫服務器壓力,減少訪問時間。

    Java中常用的緩存有:

    a. hibernate的二級緩存,該緩存不能完成分布式緩存;

    b. 可以使用redis、memcahe作為中央緩存;對緩存的數據進行集中處理

    sql語句優化小技巧:(DDL/DML優化比較常用)

    DDL優化:

    a. 通過禁用索引來提供導入數據性能,這個操作主要針對有數據庫的表,追加數據;//去除鍵alter table 表名 DISABLE keys;//批量插入數據insert into 表名1 select * from 表名2;//恢復鍵alter table 表名 ENABLE keys;b. 關閉唯一校驗:set unique_checks=0 //關閉set unique_checks=1 //開啟c. 修改事務提交方式(導入):(變多次提交為一次)set autocommit=0 //關閉//批量插入set autocommit=1 //開啟

    DML優化:(變多次提交為一次)

    insert into 表名 values(1,2); insert into 表名 values(1,3); insert into 表名 values(1,4); //合并多條為一條 insert into 表名 values(1,2),(1,3),(1,4);

    DQL優化:

    order by 優化:a. 多用索引排序b. 普通結果排序(非索引排序)Filesortgroup by 優化:是使用order by null,取消默認排序;子查詢優化://在'客戶列表'找到不在'支付列表'的客戶,查詢沒買過東西的客戶explainselect * from customer where customer_id not in (select DISTINCT customer_id from payment);//這種是基于func外鏈explainselect * from customer c left join payment p on (c.customer_id=p.customer_id) where p.customer_id is null;//這種是基于'索引'外鏈or 優化:在兩個獨立索引上使用 or 的性能優于:a. or 兩邊都是用索引字段做判斷,性能好!b. or兩邊,有一邊不用,性能差;c. 如果employee表的name和email這兩列是一個復合索引,但是如果是:name='A' or email='B' 這種方式,不會用到索引limit 優化:select film_id,description from film order by title limit 50,5;select a.film_id,a.description from file a inner join (select film_id from film order by title limit 50,5) b on a.film_id = b.film_id;

    JDBC批量插入幾百萬條數據怎么實現?(***)

    原理:變多次提交為一次,使用批量操作

    conn=DriverManager.getConnection(url,user,password);conn.setAutoCommit(false);//(1)String sql="insert into product (id,name) values (?,?)"; ps=conn.preparedStatement(sql); for(int i=1;i<count;i+=batch_size){ps.clearBatch();//(2)for(int j=0;j<batch_size;j++){ps.setInt(1,i+j);ps.setString(2,data);ps.addBatch();//(3)}ps.executeBatch();//(4)if((i+batch_size-1)%commit_size==0){conn.commit();//(5)} }

    省出的時間可觀。

    像這樣的批量插入操作能不使用代碼操作就不使用,可以使用存儲過程來實現。

    15. Redis

    簡單介紹Redis:

    Redis是一個key-value的nosql數據庫;

    (好處):先存到內存中,會根據一定的策略持久化到磁盤,即使斷電也不會丟失數據,支持的數據類型比較多。

    (作用):redis主要用來緩存數據庫的數據;web集群時,redis可以當做中央緩存存放session;

    mysql、redis、memcache的比較:

    mysqlredismemcache
    類型關系型非關系型非關系型
    存儲位置磁盤磁盤和內存內存
    存儲過期不支持支持支持
    讀寫性能非常高非常高

    1)redis和memcache都是把數據存放在內存中,不過memcache還可以用于緩存其他東西,例如:圖片、視頻等;

    2)redis不僅支持簡單的key-value類型的數據,同時還提供list、set、hash等數據結構的存儲;

    3)虛擬存儲—redis當物理內存用完時,可以將一些很久沒用的value交換到磁盤。

    Redis的使用場景:

    1)作為緩存:把經常需要查詢、很少修改的數據;(redis是內存級別的,因此訪問時間很快)

    2)作為計數器:redis中提供了一種計數器是原子性的內存操作;(最簡單的例子:可以解決庫存溢出問題)

    3)session緩存服務器(中央緩存):web集群時,redis可以作為session緩存服務器;

    具體地:當一臺web服務器不能滿足需求時,需要提供多臺web服務器;作為集群,需要在前面增加一個負載均衡,負責將請求均衡的分配到多臺web服務器上面;每臺web服務器都有自己獨立的session,并有一個專門管理session的區域;導致存在一個web服務器上的數據,另一個服務器不知道;因此,在服務器和數據庫之間增加一個中央緩存(redis)。

    redis存儲對象的方式:(經常使用的是json字符串)

    1)json字符串:需要把對象轉換為json字符串,當做字符串處理,直接使用set、get來設置或者獲取;

    優點:設置和獲取比較簡單
    缺點:沒有提供專門的方法,需要把對象轉換為json字符串(直接使用jsonlib,就可以實現);

    2)字節流:需要做序列化,就是把對象序列化為字節保存。

    如果是存儲百萬級的大數據對象,建議采用存儲序列化對象方式;如果是少量的數據對象、或者是數據對象字段不多,還是建議采用json轉換成字符串方式

    redis數據淘汰機制:

    由于內存大小有限,需要保存有效的數據,就需要淘汰一些暫時不用的數據;

    redis提供了6種數據淘汰機制:

    volatile-lru:從已設置過期時間的數據集中挑選最近最少使用的數據淘汰

    volatile-ttl:從已設置過期時間的數據集中挑選將要過期的數據淘汰;

    volatile-random:從已設置過期時間的數據集中任意選擇數據淘汰;

    allkeys-lru:從數據集中挑選最近最少使用的數據淘汰;

    allkeys-random:從數據集中任意選擇數據淘汰;

    no-enviction:禁止驅逐數據

    Java訪問redis的方式:

    1)使用jedis Java客戶端來訪問redis服務器(類似于通過jdbc訪問mysql);

    2)如果是spring進行集成時,可以使用spring data來訪問redis;spring data只是對jedis的二次封裝(類似于jdbcTemplate和jdbc的關系);

    redis集群:

    當一臺數據庫無法滿足要求時,可以使用redis集群來處理,類似于mysql的讀寫分離。

    總結

    以上是生活随笔為你收集整理的Mysql数据库读书笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。