前言
導致數據庫慢的原因有呢些?
- 頻繁的磁盤操作
- 數據量過大
針對原因我們可以選擇優化的幾個方面
- 設計數據庫數據表時選擇正確的字段以及存儲引擎
- 利用好mysql服務器提供的功能(索引,分區等等)
- 橫向擴展,負載均衡,讀寫分離
字段設計
結論:
- 越小越好,夠用就好
更小的數據類型通常更快,因爲他們佔用更少的磁盤、內存和CPU緩存,並且處理時有需要的CPU週期也更少
如果無法確定哪個數據類型是最好的,就選擇你認爲不會超過範圍的最小類型
- 簡單就好,沒null最好
簡單數據類型的操作通常需要更少的CPU週期,例如整型比字符操作代價更低,因爲字符集和校對規則使字符比較比整型比較更復雜
當可爲null的列被索引時,每個索引記錄需要一個額外的字節,會佔用更多的存儲空間
整數類型
- TINYINT:8位,範圍-2^(8-1) ~ 2^(8-1)即-256~256
- SMALLINT:16位,範圍-2^(16-1) ~ 2^(16-1)即-65536~65536
- MEDIUMINT:24位,範圍-2^(24-1) ~ 2^(24-1)即-16,777,216~16,777,216
- INT:32位,範圍-2^(32-1) ~ 2^(32-1)即-4,294,967,296~4,294,967,296
- BIGINT:64位,範圍-2^(64-1) ~ 2^(64-1)即-18,446,744,073,709,551,616~18,446,744,073,709,551,616
注:整數類型有可選的UNSIGNED屬性,表示不允許負值,可使正數上限提高一倍
儘量使用整形表示字符串(例如表示枚舉),存儲空間固定,佔用空間少,運算速度快
實數類型
FLOAT(4個字節)和DOUBLE(8個字節)(定長型)支持使用標準的浮點運算進行近似計算,存在精度丟失問題,佔據固定的存儲空間,運算效率高,無論數據多大,佔用的空間是固定的,所以當超過最大空間時會導致精度丟失
DECIMAL(變長型)用於存儲精確地小數,不會導致數據丟失,佔用的空間會隨着數據的增大而增大
DECIMAL(M,D)依賴於M(小數點之前最大位數)和D(小數點之後最大位數)的值,如果M>D,爲M+2否則爲D+2
整數運算沒有精度問題,小數運算存在精度問題,計算機無法將小數完全轉爲二進制,所以可以通過縮小單位通過整數運算體高精度
例:1.2萬元>>>>>12000元
字符串類型
VARCHAR與CHAR
- VARCHAR類型用於存儲可變長字符串,他比定長型更節省空間,因爲他僅使用必要的空間,越短的字符串佔用空間越少 ,比如varchar(10), 然後輸入abc三個字符,那麼實際存儲大小爲4個字節。
- VARCHAR需要需要使用1或2個額外字節記錄字符串的長度,如果列的最大長度小於等於255,則使用一個字節表示,否則使用兩個字節
- CHAR類型是定長的,即當定義的是char(10),輸入的是"abc"這三個字符時,它們佔的空間一樣是10個字節,包括7個空字節。當輸入的字符長度超過指定的數時,char會截取超出的字符。而且,當存儲char值時,MySQL是自動刪除輸入字符串末尾的空格。CHAR很適合存儲短的字符串,或者所有的值都接近一個長度(比如賬號密碼姓名)。對於非常短的列,CHAR比VARCHAR在存儲空間上更有效率。例如用CHAR(1)來存儲只有Y和N的值,CHAR(1)只需要一個字節,VARCHAR(1)需要2個字節,需要額外一個字節記錄長度
- 所以,從空間上考慮,varchar較合適;從效率上考慮,用char合適。二者之間需要權衡。
QUESTION:使用VARCHAR(5)和VARCHAR(200)存儲'hello'的空間開銷是一樣的,那麼使用短列更有優勢嗎?
是的!更長的列會消耗更多的內存,因爲MYSQL通常會分配固定大小的內存塊來保存內部值。
所以最好的策略就是分配真正需要的空間
BLOB與TEXT
- 他們都是爲了存儲很大的數據而設計的字符串數據類型,比如文章,或者bpm中的xml,分別採用二進制和字符串方式存儲
- 字符類型:TINYTEXT,SMALLTEXT,TEXT,MEDIUMTEXT,LONGTEXT
- 二進制類型:TINYBLOB,SMALLBLOB,BLOB,MEDIUMBLOB,LONGBLOB
- 因爲BLOB類型存儲的是二進制數據,所以沒有排序規則和字符集,但可以存儲圖片,TEXT與VARCHAR類似,只能存儲純文本,只不過他能存儲的字符數更多
TIPS:
字符集:字符集是針對不同語言的字符編碼的集合,比如UTF-8字符集,GBK字符集,GB2312字符集等等,不同的字符集使用不同的規則給字符進行編碼
排序規則:是在特定字符集的基礎上特定的字符排序方式,排序規則是基於字符集的,是對字符集在排序方式維度上的一個劃分。
排序規則是依賴於字符集的,一種字符集可以有多種排序規則,但是一種排序規則只能基於某一種字符集的
日期和時間類型
MYSQL存儲的最小時間粒度爲秒,但是他支持使用爲微秒級別的粒度進行臨時運算
-
DATETIME類型:範圍1001年到9999年,精度爲秒。他把日期和時間封裝到格式爲YYYYMMDDHHMMSS的整數中,與時區無關。使用8個字節的存儲空間
-
TIMESTAMP類型:保存了從1970年1月1日零點零分零秒以來的秒數。只使用4個字節的存儲空間,因此它的範圍比DATETIME小很多:只能表示從1970年到2038年,超出這個範圍則值記錄爲’0000-00-00 00:00:00’
- TIMESTAP顯示的值也依賴於時區。因此存儲值爲0的TIMESTAP在美國東部時區顯示爲"1969-12-31 19:00:00".與格林尼治時間相差5個小時
-
DATE類型:就是日期,用於具有日期部分但沒有時間部分的值。date類型的字段,佔用3個字節。在數據庫中存儲數據格式爲:YYYY-MM-DD,它支持的範圍爲’1000-01-01’到’9999-12-31’,並且允許使用字符串或數字爲此列賦值。
-
TIME類型:time數據類型指的是具體的不包括日期的時間,佔用3個字節。以’hh:mm:ss’格式(或 'hhh:mm:ss’大小時數格式)顯示值 。TIME值的範圍可以從 '-838:59:59’到 ‘838:59:59’。小時部分可能會很大,因爲該TIME類型不僅可以用來表示一天中的某個時間(必須少於24小時),而且可以用來表示經過的時間或兩個事件之間的時間間隔(可能遠大於24小時,甚至是負的)。
TIME值的範圍 是 '-838:59:59.000000’至 ‘838:59:59.000000’。
-
YEAR類型:YYYY類型用於表示年份值,佔用1個字節。以YYYY格式顯示值, 範圍 1901爲2155,和 0000。
位數據類型
MySQL有少數幾種存儲類型使用緊湊的位存儲數據。不管底層存儲格式如何處理,從技術上說都是字符型。
BIT:BIT(1)表示定義一個包含一位的長度,BIT(2)表示2個位,以此類推,BIT列最大長度是64位,MySQL把BIT當字符串處理,而不是數字,當檢索BIT(1)的值時,結果是一個包含二進制0或1的字符串,而不是ASCII碼的0或1,例如00111001,它的二進制等於57,檢索它時得到是一個字符碼爲57的字符,也就是ASCII碼57對應字符爲9。
整合JPA
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
//定義了被標註字段在數據庫表中所對應字段的名稱;
String name() default "";
//表示該字段是否爲唯一標識,默認爲false。如果表中有一個字段需要唯一標識,則既可以使用該標記,也可以使用@Table標記中的UniqueConstraint。
boolean unique() default false;
//表示該字段是否可以爲null值,默認爲true。
boolean nullable() default true;
//表示在使用“INSERT”腳本插入數據時,是否需要插入該字段的值。(即插入時是否不忽略該字段)
boolean insertable() default true;
//表示在使用“UPDATE”腳本插入數據時,是否需要更新該字段的值。(即修改時是否不忽略該字段)
boolean updatable() default true;
//字段定義,一旦使用,jpa不會自動識別類型創建,注意sql語法
String columnDefinition() default "";
//表示當映射多個表時,指定表的表中的字段。默認值爲主表的表名。
String table() default "";
//表示字段的長度,當字段的類型爲varchar時,該屬性纔有效,默認爲255個字符。
int length() default 255;
//precision屬性和scale屬性表示精度,當字段類型爲double(或decimal)時,precision表示數值的總長度,scale表示小數點所佔的位數。
int precision() default 0;
int scale() default 0;
}
@Getter
@Setter
@ToString
@Table(name = "audit_log", indexes = {@Index(columnList = "cost_time")})
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class AuditLog extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Long id;
@ApiModelProperty("用戶id")
@Column(name = "user_id", columnDefinition = "SMALLINT(15) UNSIGNED COMMENT '用戶id'")
private Integer userId;
@ApiModelProperty("traceId")
@Column(name = "trace_id", columnDefinition = "CHAR(20) COMMENT 'traceId'")
private String uuid;
@ApiModelProperty("服務端ip")
@Column(name = "service_ip", columnDefinition = "CHAR(15) COMMENT '服務端ip'")
private String serviceIp;
@ApiModelProperty("用戶姓名")
@Column(name = "admin_user", columnDefinition = "CHAR(10) COMMENT '用戶姓名'")
private String adminUser;
@ApiModelProperty("響應時間(毫秒)")
@Column(name = "cost_time", columnDefinition = "SMALLINT(15) UNSIGNED COMMENT '響應時間(毫秒)'")
private Integer costTime;
@ApiModelProperty("請求地址")
@Column(name = "uri", columnDefinition = "varchar(255) COMMENT '請求地址'")
private String uri;
@ApiModelProperty("入參")
@Column(name = "param_json", columnDefinition = "varchar(255) COMMENT '入參'")
private String paramJson;
@ApiModelProperty("請求方式")
@Column(name = "method", columnDefinition = "CHAR(8) COMMENT '請求方式'")
private String method;
@ApiModelProperty("用戶地址")
@Column(name = "ip", columnDefinition = "varchar(255) COMMENT '用戶地址'")
private String ip;
@ApiModelProperty("事件時間")
@Column(name = "event_time", columnDefinition = "DATETIME COMMENT '事件時間'")
private Date eventTime;
@ApiModelProperty("異常堆棧")
@Column(name = "exception", columnDefinition = "varchar(255) COMMENT '異常堆棧'")
private String exception;
@ApiModelProperty("事件級別")
@Column(name = "event_level", columnDefinition = "TINYINT(1) UNSIGNED COMMENT '事件級別'")
private Integer eventLevel;
@ApiModelProperty("事件類型")
@Column(name = "event_type", columnDefinition = "TINYINT(1) UNSIGNED COMMENT '事件類型'")
private Integer eventType;
@ApiModelProperty("事件模塊")
@Column(name = "event_module", columnDefinition = "SMALLINT(10) UNSIGNED COMMENT '事件類型'")
private Integer eventModule;
@ApiModelProperty("事件描述")
@Column(name = "event_description", columnDefinition = "varchar(255) COMMENT '事件描述'")
private String eventDescription;
@ApiModelProperty("出參結果")
@Column(name = "result_json", columnDefinition = "varchar(500) COMMENT '出參結果'")
private String resultJson;
@ApiModelProperty("事件結果")
@Column(name = "event_result")
private Boolean eventResult;
}
越小越好,夠用就好
越小越好,夠用就好
越小越好,夠用就好
參考文獻
1.《高性能MYSQL》