关系型数据库的范式设计

  • 范式
    • 1NF-消除非主属性对键的部分函数依赖
    • 2NF-消除非主要属性对键的传递函数依赖
    • 3NF-消除主属性对键的传递函数依赖
    • BCNF-主属性不依赖于主属性
  • 概念

    • 范式化设计的主要目标是 减少不必要的更新
    • 副作用:查询缓慢,多表联查
    • 范式化虽节省空间,但存储越来越便宜

      ES反范式-嵌套对象

      1. post my_movies/_doc/1
      2. {
      3. "title":"Speed",
      4. "actors":[
      5. {"first_name":"keanu","last_name":"Reeves"},
      6. {"first_name":"Dennis","last_name":"Hopper"}
      7. ]
      8. }
      9. // JSON格式呗处理成扁平式键值对的结构
      10. // 这个文档的存储:
      11. "title":"Speed"
      12. "actors.first_name":["keanu","Dennis"]
      13. "actors.last_name":["Reeves","Hopper"]
  • nested Data Type

    • 允许对象数组中的对象被独立索引
    • nested文档会被保存在两个Lucene文档中,在查询时做join处理
    • 每次更新,需要重新索引整个对象(包括根对象和嵌套对象)
      1. put my_movies
      2. {
      3. "mappings":{
      4. "properties":{
      5. "actors":{
      6. "type":"nested",
      7. "properties":{
      8. "first_name":{"type":"keyword"},
      9. "last_name":{"type":"keyword"}
      10. }
      11. },
      12. "title":{
      13. "type":"text",
      14. "fields":{
      15. "keyword":{
      16. "type":"keyword",
      17. "ignore_above":256
      18. }
      19. }
      20. }
      21. }
      22. }
      23. }
      24. // nested 查询
      25. post my_movie/_search
      26. {
      27. "query":{
      28. "bool":{
      29. "must":[
      30. {"match":{"title":"Speed"}},
      31. {
      32. "nested":{
      33. "path":"actors",
      34. "query":{
      35. "bool":{
      36. "must":[
      37. {"match":{"actors.first_name":"Keanu"}}
      38. ]
      39. }
      40. }
      41. }
      42. }
      43. ]
      44. }
      45. }
      46. }

      示例

      1. PUT /test_spu
      2. {
      3. "mappings": {
      4. "properties": {
      5. "spu_name":{
      6. "type": "text",
      7. "null_value": "null"
      8. },
      9. "skus":{
      10. "type": "nested",
      11. "properties": {
      12. "sku_name":{
      13. "type":"text"
      14. }
      15. }
      16. }
      17. }
      18. }
      19. }

      父子文档

  • ES提供类似关系型数据库的Join实现,可以维护父子关系

  • 父文档和子文档是两个独立的文档
  • 更新父文档无需重新索引子文档。子文档被添加、更新或者删除也不会影响到父文档和其他子文档

    1. // 设置mapping
    2. put my_blogs
    3. {
    4. "mappings":{
    5. "properties":{
    6. "blog_comments_relation":{
    7. "type":"join",
    8. "relations":{
    9. "blog":"comment" -- parent名称:child名称
    10. }
    11. }
    12. }
    13. }
    14. }
    15. // 索引父文档
    16. put my_blogs/_doc/blog1
    17. {
    18. "title":"ES牛逼",
    19. "content":"bilibili",
    20. "blog_comments_relation":{
    21. "name":"blog" -- 申明文档类型
    22. }
    23. }
    24. // 索引子文档
    25. put my_blogs/_doc/comment1?routing=blog1 -- 指定routing,确保和付文档索引到相同的分片,提高join性能
    26. {
    27. "comment":"说的好",
    28. "username":"张傲霜",
    29. "blog_comments_relation":{
    30. "name":"comment",
    31. "parent":"blog1" -- 父文档id
    32. }
    33. }
    34. // 父查子
    35. //parent_id查询
    36. post my_blogs/_search
    37. {
    38. "query":{
    39. "parent_id":{
    40. "type":"comment",
    41. "id":"blog1"
    42. }
    43. }
    44. }
    45. // has_parent查询
    46. post my_blogs/_search
    47. {
    48. "query":{
    49. "has_parent":{
    50. "parent_type":"blog",
    51. "query":{
    52. "match":{
    53. "title": "java"
    54. }
    55. }
    56. }
    57. }
    58. }
    59. // 子查父
    60. post my_blogs/_search
    61. {
    62. "query":{
    63. "has_child":{
    64. "type":"comment",
    65. "query":{
    66. "match":{
    67. "username":"jack"
    68. }
    69. }
    70. }
    71. }
    72. }
    73. // 访问子文档
    74. get my_blogs/_doc/comment1?routing=blog1
    75. // 更新子文档
    76. put my_blogs/_doc/comment1?routing=blog1
    77. {
    78. "comment":"java is best"
    79. }

    示例

    1. //创建索引
    2. PUT /test_spu
    3. {
    4. "mappings": {
    5. "properties": {
    6. "name":{
    7. "type": "text"
    8. },
    9. "relation":{
    10. "type": "join",
    11. "relations":{
    12. "spu":"sku"
    13. }
    14. }
    15. }
    16. }
    17. }
    18. // 索引父文档